Compare commits

..

3 Commits

Author SHA1 Message Date
Will Hawkins a7d8fd1304 grammar,compiler: Add Support For Fixed-Width Integers
Continuous Integration / Grammar Tests (push) Successful in 4m13s
Continuous Integration / Library Format Tests (push) Successful in 5m17s
Continuous Integration / Library Tests (push) Failing after 8m34s
Continuous Integration / Cli Tests (push) Failing after 4m40s
Distinguishing between signed and unsigned fixed-width integer
types must still be done.

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-05-18 06:53:22 -04:00
Will Hawkins cbebcae20a language: Remove Attributed Type
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-05-18 05:06:28 -04:00
Will Hawkins 979fa69ab8 Rename Project (again) And Describe Purpose
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-05-18 04:01:28 -04:00
18 changed files with 372 additions and 88 deletions
+44 -12
View File
@@ -1,12 +1,43 @@
## P4CE: P4 Continuous Evolution ## 🏎️ GP4: Generalized P4
_P4CE_[^pronounce] is a P4 parser and runtime written in Swift that supports an _evolved_ version of P4. "P4 is a high-level language for programming protocol-independent packet processors"[^1] and it is awesome. The language is robust and includes many features that make writing
[^pronounce]: The acronym is pronounced "p force". - _packet_ parsing pipelines easy to write,
- _packet_ transformation pipelines easy to write,
- _packet_ routing pipelines easy to write.
### Evolved How? The adjacent ecosystem is great, too: There are now myriad tools available that make it possible to write applications to configure the _packet_ parsing, transformation and routing pipelines written in P4.
Coming soon. For example, a developer could use P4 to write code that parses raw bytes received from the network into a structured representation of headers/payloads used to transmit those bytes. A developer could use P4 to write a tool that designates the fields of the parsed packet that a network administrator could name when definining configuration for modifying (or not) a parsed packet and defining configuration about how (even _if_) to route the packet. In such a scenario, the system administrator might use a CLI and write
```console
PI CLI> table_add ipv4_lpm 10.0.0.1/24 => set_nhop 10.0.0.1 1
```
which the system running the P4 code written by the developer could read when making a routing decision. Later, if the system administrator wanted to change the way packets were routed, they could use the CLI and write
```console
PI CLI> table_add ipv4_lpm 10.0.0.1/24 => set_nhop 10.0.0.2 1
```
and the system running the P4 code written by the developer would immediately start to route _packets_ differently.
So, why is _packet_ highlighted?
Because we believe that thinking about P4 in exactly the way described above -- after dropping the word _packet_ -- makes it a perfect system for building general-purpose parsing, transformation and routing pipelines. Although there are many ways such tools could be used, we believe that a generalized P4 system would be a perfect candidate for writing online, streaming ETL pipelines (c.f., [Apache Kafka](https://kafka.apache.org/)) or log filter/classification pipelines (c.f., [Sigma](https://sigmahq.io/)).
Our goal is to build generalized P4 (GP4): The best of P4 and a little more.
Please join us!
[^1]: Pat Bosshart, Dan Daly, Glen Gibb, Martin Izzard, Nick McKeown, Jennifer Rexford, Cole Schlesinger, Dan Talayco, Amin Vahdat, George Varghese, and David Walker. 2014. P4: programming protocol-independent packet processors. SIGCOMM Comput. Commun. Rev. 44, 3 (July 2014), 8795. https://doi.org/10.1145/2656877.2656890
### Benefits
1. Reuse the extensive existing work from the P4 community.
2. ... more coming soon.
### Status ### Status
@@ -85,13 +116,14 @@ We will try to maintain the following headline format for commit messages:
where `<component>` is one of: where `<component>` is one of:
1. `grammar`: For the tree-sitter-based grammar. 1. `grammar`: For the tree-sitter-based grammar.
2. `compiler`: For the Swift-based P4 compiler of tree-sitter-based-parser parsed programs into AST. 1. `compiler`: For the Swift-based P4 compiler of tree-sitter-based-parser parsed programs into AST.
3. `runtime`: For the Swift-based P4 interpreter. 1. `language`: For the Swift-based AST of a compiled P4 program.
4. `common`: For any Swift-based components common to the entire project (and macros). 1. `runtime`: For the Swift-based P4 interpreter.
5. `documentation`: For any documentation updates. 1. `common`: For any Swift-based components common to the entire project (and macros).
6. `testing`: For Swift-based tests. 1. `documentation`: For any documentation updates.
7. `cli`: For Cli components. 1. `testing`: For Swift-based tests.
7. `codegen`: For code generation components. 1. `cli`: For Cli components.
1. `codegen`: For code generation components.
where `<subcomponent>` can be more free-form and `<change>` is a pithy description of the changes in the commit. where `<subcomponent>` can be more free-form and `<change>` is a pithy description of the changes in the commit.
+20 -5
View File
@@ -424,19 +424,30 @@ public class P4BooleanValue: P4DataValue {
} }
} }
public enum BitWidth: Equatable {
case Infinite
case Width(Int)
}
/// A P4 int type /// A P4 int type
public struct P4Int: P4Type { public struct P4Int: P4Type {
public init() {} let width: BitWidth
public init(_ width: BitWidth = BitWidth.Infinite) {
self.width = width
}
public var description: String { public var description: String {
return "Int" return "Int (width: \(self.width))"
} }
public func eq(rhs: P4Type) -> Bool { public func eq(rhs: P4Type) -> Bool {
return switch rhs { return switch rhs {
case is P4Int: true case let rrhs as P4Int: rrhs.width == self.width
default: false default: false
} }
} }
public func def() -> P4DataValue? { public func def() -> P4DataValue? {
return P4IntValue(withValue: 0) return P4IntValue(withValue: 0)
} }
@@ -444,12 +455,16 @@ public struct P4Int: P4Type {
/// An instance of a P4 integer /// An instance of a P4 integer
public class P4IntValue: P4DataValue { public class P4IntValue: P4DataValue {
let int_type: P4Int
public func type() -> P4Type { public func type() -> P4Type {
return P4Int() return int_type
} }
let value: Int let value: Int
public init(withValue value: Int) { public init(withValue value: Int, andWidth width: BitWidth = BitWidth.Infinite) {
self.int_type = P4Int(width)
self.value = value self.value = value
} }
+2
View File
@@ -101,6 +101,8 @@ public func Fold<T, A>(input: [T], initial: A, block: (T, A) -> A) -> A {
#externalMacro(module: "Macros", type: "RequireNodesType") #externalMacro(module: "Macros", type: "RequireNodesType")
@freestanding(codeItem) public macro SkipUnlessNodeType<N>(node: N, type: String) = @freestanding(codeItem) public macro SkipUnlessNodeType<N>(node: N, type: String) =
#externalMacro(module: "Macros", type: "SkipUnlessNodeType") #externalMacro(module: "Macros", type: "SkipUnlessNodeType")
@freestanding(codeItem) public macro SkipUnlessNodesTypes<N>(node: N, types: [String]) =
#externalMacro(module: "Macros", type: "SkipUnlessNodesTypes")
@freestanding(codeItem) public macro MustOr<E, N>(result: E, thing: E?, or: N) = @freestanding(codeItem) public macro MustOr<E, N>(result: E, thing: E?, or: N) =
#externalMacro(module: "Macros", type: "MustOr") #externalMacro(module: "Macros", type: "MustOr")
+34 -1
View File
@@ -229,6 +229,38 @@ public struct SkipUnlessNodeType: CodeItemMacro {
} }
} }
public struct SkipUnlessNodesTypes: CodeItemMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax] {
let arguments = node.arguments.indices
var arg_index = arguments.startIndex
let node_to_check = node.arguments[arg_index].expression
arg_index = arguments.index(after: arg_index)
guard let expected_types = node.arguments[arg_index].expression.as(ArrayExprSyntax.self) else {
throw MacroError(withMessage: "Node(s) to check must be in an array")
}
arg_index = arguments.index(after: arg_index)
let ifs = expected_types.elements.map { l in
"\(node_to_check).nodeType != \(l.expression)"
}.joined(separator: " && ")
return [
CodeBlockItemSyntax(
"""
if \(raw: ifs) {
return Result.Ok(.none)
}
""")
]
}
}
public struct MustOr: CodeItemMacro { public struct MustOr: CodeItemMacro {
public static func expansion( public static func expansion(
of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext
@@ -261,6 +293,7 @@ public struct MustOr: CodeItemMacro {
struct P4Macros: CompilerPlugin { struct P4Macros: CompilerPlugin {
var providingMacros: [Macro.Type] = [ var providingMacros: [Macro.Type] = [
RequireResult.self, RequireErrorResult.self, UseOkResult.self, UseErrorResult.self, RequireResult.self, RequireErrorResult.self, UseOkResult.self, UseErrorResult.self,
RequireNodeType.self, SkipUnlessNodeType.self, RequireNodesType.self, MustOr.self, RequireNodeType.self, SkipUnlessNodeType.self, SkipUnlessNodesTypes.self, RequireNodesType.self,
MustOr.self,
] ]
} }
+35 -3
View File
@@ -101,9 +101,41 @@ extension P4IntValue: CompilableExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableExpression?> { ) -> Result<EvaluatableExpression?> {
let node = node.child(at: 0)! let node = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node>(node: node, type: "integer")
if let parsed_int = Int(node.text!) { #SkipUnlessNodesTypes<SwiftTreeSitter.Node>(
return .Ok(P4Value(P4IntValue(withValue: parsed_int))) node: node, types: ["integer", "integer_elaborated"])
var bit_width: BitWidth = BitWidth.Infinite
let value_source: String
if node.nodeType == "integer_elaborated" {
let re = /([0-9]+)([ws])([\-0-9]+)/
let integer_components = node.text!.matches(of: re)
if integer_components.isEmpty || integer_components.count > 1 {
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Failed to parse elaborated integer: \(node.text!)"))
}
let width_source = "\(integer_components[0].1)"
guard let width = Int(width_source) else {
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Failed to parse width from elaborated integer: \(width_source)"))
}
/// TODO: Handle signed vs. unsigned.
bit_width = BitWidth.Width(width)
value_source = "\(integer_components[0].3)"
} else {
value_source = node.text!
}
if let parsed_int = Int(value_source) {
return .Ok(P4Value(P4IntValue(withValue: parsed_int, andWidth: bit_width)))
} else { } else {
return .Error( return .Error(
ErrorWithLocation( ErrorWithLocation(
+36 -1
View File
@@ -34,7 +34,42 @@ extension P4Int: CompilableType {
public static func CompileType( public static func CompileType(
type: SwiftTreeSitter.Node, withContext: CompilerContext type: SwiftTreeSitter.Node, withContext: CompilerContext
) -> Common.Result<(any Common.P4Type)?> { ) -> Common.Result<(any Common.P4Type)?> {
return type.text == "int" ? .Ok(P4Int()) : .Ok(.none)
// Drill down, as appropriate.
let base_type_node = type.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: base_type_node, type: "baseType")
let type_node = base_type_node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: type_node, type: "int_type")
var walker = Walker(node: type_node)
var int_node: Node? = .none
#MustOr(
result: int_node, thing: walker.getNext(),
or: Result<P4Type?>.Error(
ErrorWithLocation(
sourceLocation: type_node.toSourceLocation(),
withError: "Missing elements in int type declaration")))
// Move passed the keyword.
walker.next()
if let bit_width_node = walker.getNext() {
guard let bit_width = Int(bit_width_node.child(at: 1)!.text!),
bit_width != 0
else {
return .Error(
ErrorWithLocation(
sourceLocation: bit_width_node.toSourceLocation(),
withError: "Could not parse \(bit_width_node.text!) into integer"))
}
return .Ok(P4Int(BitWidth.Width(bit_width)))
}
return .Ok(P4Int())
} }
} }
-28
View File
@@ -1,28 +0,0 @@
// p4rse, Copyright 2026, Will Hawkins
//
// This file is part of p4rse.
//
// This file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import Common
public struct AttributedP4Type {
public let type: P4Type
public let attributes: P4QualifiedType
public init(_ type: P4Type, _ attributes: P4QualifiedType) {
self.type = type
self.attributes = attributes
}
}
+2 -2
View File
@@ -17,6 +17,7 @@
import Common import Common
import P4Lang import P4Lang
import P4Protos
public struct Generated { public struct Generated {
let gen: String let gen: String
@@ -48,13 +49,12 @@ public struct CodeGenerator: LanguageVisitor {
return visitor.start(node, context: generated) return visitor.start(node, context: generated)
} }
/// TODO: Can we generate these implementations somehow?
public typealias Context = Generated public typealias Context = Generated
public func visit( public func visit(
_ v: Program, _ c: VisitorContext<Generated> _ v: Program, _ c: VisitorContext<Generated>
) -> Result<VisitorContext<Generated>> { ) -> Result<VisitorContext<Generated>> {
var result: Result<VisitorContext<Generated>> = Fold( var result: Result<VisitorContext<Generated>> = Fold(
input: v.types, initial: .Ok(c.next(uc: c.getUserContext().append("["))) input: v.types, initial: .Ok(c.next(uc: c.getUserContext().append("[")))
) { (current, acc) in ) { (current, acc) in
+1 -1
View File
@@ -283,7 +283,7 @@ import P4Lang
#RequireErrorResult( #RequireErrorResult(
Error( Error(
withMessage: withMessage:
"{57, 10}: Failed to parse a statement element: {57, 1}: Cannot assign value with type Boolean to identifier z with type Int" "{57, 10}: Failed to parse a statement element: {57, 1}: Cannot assign value with type Boolean to identifier z with type Int (width: Infinite)"
), ),
Program.Compile(simple_parser_declaration)) Program.Compile(simple_parser_declaration))
) )
@@ -182,5 +182,5 @@ import TreeSitterP4
let error = try #UseErrorResult(Program.Compile(simple_parser_declaration)) let error = try #UseErrorResult(Program.Compile(simple_parser_declaration))
#expect(error.msg().contains("{29, 12}: Type of expression in return statement (Boolean) is not compatible with function return type (Int)")) #expect(error.msg().contains("{29, 12}: Type of expression in return statement (Boolean) is not compatible with function return type (Int (width: Infinite))"))
} }
@@ -132,7 +132,7 @@ import TreeSitterP4
#RequireErrorResult( #RequireErrorResult(
Error( Error(
withMessage: withMessage:
"Error(s) parsing select cases: {81, 4}: Key expression of type Boolean is not compatible with selector type Int" "Error(s) parsing select cases: {81, 4}: Key expression of type Boolean is not compatible with selector type Int (width: Infinite)"
), ),
Program.Compile(simple_parser_declaration))) Program.Compile(simple_parser_declaration)))
} }
+1 -1
View File
@@ -147,7 +147,7 @@ import TreeSitterP4
#expect( #expect(
#RequireErrorResult<(InstantiatedParserState, ProgramExecution)>( #RequireErrorResult<(InstantiatedParserState, ProgramExecution)>(
Error(withMessage: "Cannot call parser: Argument 1's type (Int) is incompatible with the parameter type (Boolean)"), Error(withMessage: "Cannot call parser: Argument 1's type (Int (width: Infinite)) is incompatible with the parameter type (Boolean)"),
runtime.run(withArguments: args))) runtime.run(withArguments: args)))
} }
+2 -2
View File
@@ -327,7 +327,7 @@ import TreeSitterP4
#expect( #expect(
#RequireErrorResult( #RequireErrorResult(
Error( Error(
withMessage: "{49, 13}: Failed to parse a statement element: {49, 8}: Cannot assign value of type Int to field yesno of type Boolean" withMessage: "{49, 13}: Failed to parse a statement element: {49, 8}: Cannot assign value of type Int (width: Infinite) to field yesno of type Boolean"
), ),
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
) )
@@ -457,7 +457,7 @@ import TreeSitterP4
#expect( #expect(
#RequireErrorResult( #RequireErrorResult(
Error( Error(
withMessage: "{49, 20}: Failed to parse a statement element: {49, 11}: Cannot assign value of type Boolean to field count of type Int" withMessage: "{49, 20}: Failed to parse a statement element: {49, 11}: Cannot assign value of type Boolean to field count of type Int (width: Infinite)"
), ),
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
) )
+1 -1
View File
@@ -45,5 +45,5 @@ struct StringConvertible: CustomStringConvertible {
@Test func test_result_type_p4value_convertible() async throws { @Test func test_result_type_p4value_convertible() async throws {
let result = Result.Ok(P4Value(P4IntValue(withValue: 5))) let result = Result.Ok(P4Value(P4IntValue(withValue: 5)))
#expect("\(result)" == "Ok: Value: 5 of Int type of type Int") #expect("\(result)" == "Ok: Value: 5 of Int (width: Infinite) type of type Int (width: Infinite)")
} }
+114 -20
View File
@@ -18,8 +18,8 @@
import Common import Common
import Foundation import Foundation
import Macros import Macros
import P4Runtime
import P4Lang import P4Lang
import P4Runtime
import SwiftTreeSitter import SwiftTreeSitter
import Testing import Testing
import TreeSitter import TreeSitter
@@ -36,13 +36,15 @@ import TreeSitterP4
transition reject; transition reject;
} }
}; };
"""; """
let err = Program.Compile(simple_parser_declaration) let err = Program.Compile(simple_parser_declaration)
guard case Result.Error(let e) = err else { guard case Result.Error(let e) = err else {
assert(false, "Expected an error, but had success") assert(false, "Expected an error, but had success")
} }
#expect(e.msg().contains("Failed to parse a statement element: Could not parse a P4 type from \(invalid_type_name)")) #expect(
e.msg().contains(
"Failed to parse a statement element: Could not parse a P4 type from \(invalid_type_name)"))
} }
} }
@@ -62,7 +64,7 @@ import TreeSitterP4
#RequireErrorResult( #RequireErrorResult(
Error( Error(
withMessage: withMessage:
"{112, 16}: Failed to parse a statement element: {112, 8}: Cannot assign value with type Boolean to identifier where_to with type String" "{112, 16}: Failed to parse a statement element: {112, 8}: Cannot assign value with type Boolean to identifier where_to with type String"
), ),
Program.Compile(simple_parser_declaration))) Program.Compile(simple_parser_declaration)))
} }
@@ -83,7 +85,7 @@ import TreeSitterP4
#RequireErrorResult( #RequireErrorResult(
Error( Error(
withMessage: withMessage:
"{114, 22}: Failed to parse a statement element: {114, 8}: Cannot assign value with type String to identifier where_to with type Boolean" "{114, 22}: Failed to parse a statement element: {114, 8}: Cannot assign value with type String to identifier where_to with type Boolean"
), ),
Program.Compile(simple_parser_declaration))) Program.Compile(simple_parser_declaration)))
} }
@@ -128,6 +130,90 @@ import TreeSitterP4
Program.Compile(simple_parser_declaration))) Program.Compile(simple_parser_declaration)))
} }
@Test func test_invalid_type_in_declaration3() async throws {
let simple_parser_declaration = """
parser main_parser() {
state start {
int<5> specific_width_int = 5;
int unspecific_width_int = specific_width_int;
transition reject;
}
};
"""
let error = try! #UseErrorResult(Program.Compile(simple_parser_declaration))
#expect(
error.msg().contains(
"Cannot initialize specific_width_int (with type Int (width: Width(5))) from expression with type Int (width: Infinite)"
))
}
@Test func test_valid_specific_width_int_type_in_declaration() async throws {
let simple_parser_declaration = """
parser main_parser() {
state start {
int<5> specific_width_int = 5w5;
int<5> unspecific_width_int = specific_width_int;
transition reject;
}
};
"""
#expect(#RequireOkResult(Program.Compile(simple_parser_declaration)))
}
@Test func test_expression_in_declaration_initializer_specific_width_int_type() async throws {
let simple_parser_declaration = """
parser main_parser() {
state start {
int<5> specific_width_int = 5w5;
bool where_to = 5w5 == specific_width_int == true;
transition select (where_to) {
true: accept;
false: reject;
};
}
};
"""
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
let runtime = try #UseOkResult(
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
let (state_result, _) = try! #UseOkResult(runtime.run())
// 5w5 == specific_width_int == true
// true == true
// true
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
}
@Test func test_expression_in_declaration_initializer_specific_width_int_type2() async throws {
let simple_parser_declaration = """
parser main_parser() {
state start {
int<5> specific_width_int = 5w5;
bool where_to = 5w6 == specific_width_int == false;
transition select (where_to) {
true: accept;
false: reject;
};
}
};
"""
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
let runtime = try #UseOkResult(
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
let (state_result, _) = try! #UseOkResult(runtime.run())
// 5w6 == specific_width_int == false
// false == false
// true
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
}
@Test func test_expression_in_declaration_initializer() async throws { @Test func test_expression_in_declaration_initializer() async throws {
let simple_parser_declaration = """ let simple_parser_declaration = """
parser main_parser() { parser main_parser() {
@@ -141,7 +227,8 @@ import TreeSitterP4
}; };
""" """
let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program)) let runtime = try #UseOkResult(
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
let (state_result, _) = try! #UseOkResult(runtime.run()) let (state_result, _) = try! #UseOkResult(runtime.run())
// 5 == 5 == true // 5 == 5 == true
@@ -163,7 +250,8 @@ import TreeSitterP4
}; };
""" """
let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program)) let runtime = try #UseOkResult(
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
let (state_result, _) = try! #UseOkResult(runtime.run()) let (state_result, _) = try! #UseOkResult(runtime.run())
// 5 == 5 == true // 5 == 5 == true
@@ -185,7 +273,8 @@ import TreeSitterP4
}; };
""" """
let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program)) let runtime = try #UseOkResult(
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
let (state_result, _) = try! #UseOkResult(runtime.run()) let (state_result, _) = try! #UseOkResult(runtime.run())
// 6 == 5 == true // 6 == 5 == true
@@ -207,7 +296,8 @@ import TreeSitterP4
}; };
""" """
let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program)) let runtime = try #UseOkResult(
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
let (state_result, _) = try! #UseOkResult(runtime.run()) let (state_result, _) = try! #UseOkResult(runtime.run())
// 6 == 5 == false // 6 == 5 == false
@@ -229,7 +319,8 @@ import TreeSitterP4
}; };
""" """
let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program)) let runtime = try #UseOkResult(
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
let (state_result, _) = try! #UseOkResult(runtime.run()) let (state_result, _) = try! #UseOkResult(runtime.run())
// TODO: This test should throw an error. // TODO: This test should throw an error.
@@ -253,29 +344,32 @@ import TreeSitterP4
}; };
""" """
var test_types = VarTypeScopes().enter() var test_types = VarTypeScopes().enter()
test_types = test_types.declare(identifier: Identifier(name: "ta"), withValue: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Int())))) test_types = test_types.declare(
identifier: Identifier(name: "ta"),
withValue: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Int()))))
#expect( #expect(
#RequireErrorResult( #RequireErrorResult(
Error( Error(
withMessage: withMessage:
"{49, 22}: Failed to parse a statement element: Cannot initialize where_to (with type Boolean) from expression with type Int" "{49, 22}: Failed to parse a statement element: Cannot initialize where_to (with type Boolean) from expression with type Int (width: Infinite)"
), ),
Program.Compile(simple_parser_declaration, withGlobalInstances: test_types))) Program.Compile(simple_parser_declaration, withGlobalInstances: test_types)))
} }
@Test func test_simple_compiler_parser_parameters_invalid_types() async throws { @Test func test_simple_compiler_parser_parameters_invalid_types() async throws {
let simple_parser_declaration = """ let simple_parser_declaration = """
parser main_parser(bool pmtr, string smtr, int imtr) { parser main_parser(bool pmtr, string smtr, int imtr) {
state start { state start {
pmtr = 1; pmtr = 1;
transition accept; transition accept;
} }
}; };
""" """
#expect( #expect(
#RequireErrorResult( #RequireErrorResult(
Error( Error(
withMessage: "{85, 9}: Failed to parse a statement element: {85, 4}: Cannot assign value with type Int to identifier pmtr with type Boolean" withMessage:
"{85, 9}: Failed to parse a statement element: {85, 4}: Cannot assign value with type Int (width: Infinite) to identifier pmtr with type Boolean"
), ),
Program.Compile(simple_parser_declaration))) Program.Compile(simple_parser_declaration)))
} }
+6 -2
View File
@@ -40,9 +40,12 @@ export default grammar({
// Common - Types // Common - Types
typeRef: $ => choice($.baseType, $.type_identifier), typeRef: $ => choice($.baseType, $.type_identifier),
baseType: $ => choice($.bool, $.error, $.string, $.int, $.bit /* omitting "templated" types" */), baseType: $ => choice($.bool, $.error, $.string, $.int_type, $.bit /* omitting "templated" types" */),
constructor_parameters: $ => seq('(', optional($.parameter_list), ')'), constructor_parameters: $ => seq('(', optional($.parameter_list), ')'),
int_type: $ => seq($.int, optional($.bit_width)),
bit_width: $ => seq('<', $.integer, '>'),
// Common - Parsers // Common - Parsers
parserType: $ => seq(optional($.annotations), $.parser, field('parser_name', $.identifier), optional($.typeParameters), $.parameters), parserType: $ => seq(optional($.annotations), $.parser, field('parser_name', $.identifier), optional($.typeParameters), $.parameters),
@@ -120,7 +123,7 @@ export default grammar({
// Expressions // Expressions
expression: $ => choice($.grouped_expression, $.simple_expression), expression: $ => choice($.grouped_expression, $.simple_expression),
grouped_expression: $ => seq('(', $.expression, ')'), grouped_expression: $ => seq('(', $.expression, ')'),
simple_expression: $ => choice($.identifier, $.integer, $.booleanLiteralExpression, $.string_literal, $.binaryOperatorExpression, $.arrayAccessExpression, $.fieldAccessExpression, $.function_call), // Very limited. simple_expression: $ => choice($.identifier, $.integer, $.integer_elaborated, $.booleanLiteralExpression, $.string_literal, $.binaryOperatorExpression, $.arrayAccessExpression, $.fieldAccessExpression, $.function_call), // Very limited.
booleanLiteralExpression: $ => choice($.true, $.false), booleanLiteralExpression: $ => choice($.true, $.false),
selectExpression: $ => seq($.select, '(', $.expression, ')', '{', $.selectBody, '}'), // TODO: Should be expression list and not just a single expression selectExpression: $ => seq($.select, '(', $.expression, ')', '{', $.selectBody, '}'), // TODO: Should be expression list and not just a single expression
transitionSelectionExpression: $ => choice($.identifier, $.selectExpression), transitionSelectionExpression: $ => choice($.identifier, $.selectExpression),
@@ -211,6 +214,7 @@ export default grammar({
type_identifier: $ => /[A-Za-z_]+/, type_identifier: $ => /[A-Za-z_]+/,
string_literal: $ => /"[^"]*"/, string_literal: $ => /"[^"]*"/,
integer: $ => /[0-9]+/, integer: $ => /[0-9]+/,
integer_elaborated: $ => /[0-9]+[ws][-0-9]+/,
annotation_literal: $ => /@[A-Za-z_]+/, annotation_literal: $ => /@[A-Za-z_]+/,
default_keyset: $=> '_', default_keyset: $=> '_',
+66 -5
View File
@@ -46,7 +46,7 @@ parser simple() {
) )
========================= =========================
Simple Declaration (int) Simple Declaration (infinite-precision int)
========================= =========================
parser simple() { parser simple() {
state start { state start {
@@ -73,7 +73,9 @@ parser simple() {
(variableDeclaration (variableDeclaration
(typeRef (typeRef
(baseType (baseType
(int) (int_type
(int)
)
) )
) )
(identifier) (identifier)
@@ -92,6 +94,59 @@ parser simple() {
) )
) )
=========================
Simple Declaration (fixed-precision int)
=========================
parser simple() {
state start {
int<55> l;
transition accept;
}
};
---
(p4program
(declaration
(parserDeclaration
(parserType
(parser)
(identifier)
(parameters)
)
(parserStates
(parserState
(state)
(identifier)
(parserStatements
(parserStatement
(variableDeclaration
(typeRef
(baseType
(int_type
(int)
(bit_width
(integer)
)
)
)
)
(identifier)
)
)
)
(parserTransitionStatement
(transition)
(transitionSelectionExpression
(identifier)
)
)
)
)
)
)
)
========================= =========================
Simple Declaration (string) Simple Declaration (string)
========================= =========================
@@ -331,7 +386,9 @@ bool functionb(bool a, int b) {
(parameter (parameter
(typeRef (typeRef
(baseType (baseType
(int) (int_type
(int)
)
) )
) )
(identifier) (identifier)
@@ -393,7 +450,9 @@ bool functionb(in bool a, out int b, inout string c) {
) )
(typeRef (typeRef
(baseType (baseType
(int) (int_type
(int)
)
) )
) )
(identifier) (identifier)
@@ -468,7 +527,9 @@ extern bool functionb(in bool a, out int b, inout string c);
) )
(typeRef (typeRef
(baseType (baseType
(int) (int_type
(int)
)
) )
) )
(identifier) (identifier)
+6 -2
View File
@@ -38,7 +38,9 @@ int fun() {
(function_declaration (function_declaration
(typeRef (typeRef
(baseType (baseType
(int) (int_type
(int)
)
) )
) )
(identifier) (identifier)
@@ -382,7 +384,9 @@ parser simple() {
(variableDeclaration (variableDeclaration
(typeRef (typeRef
(baseType (baseType
(int) (int_type
(int)
)
) )
) )
(identifier) (identifier)