compiler, testing: Build System for Compilation

The compilation code was written as a precursor for implementation
with macros. The updates in this commit make the switch.

There is still plenty to do:

1. Comment Walker.
2. Comment Macros.

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
Will Hawkins
2026-04-27 08:39:40 -04:00
parent f2bd53ce5f
commit 0f0662709e
12 changed files with 698 additions and 565 deletions
+2 -1
View File
@@ -75,7 +75,8 @@ let package = Package(
),
.testTarget(
name: "Tests",
dependencies: ["P4Compiler", "P4Runtime", "P4Lang", "Macros", "TreeSitterExtensions", "Common"]
dependencies: ["P4Compiler", "P4Runtime", "P4Lang", "Macros", "TreeSitterExtensions", "Common"],
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")],
),
],
)
+4 -1
View File
@@ -148,5 +148,8 @@ public func Map<T, U>(input: T, block: (T) -> U) -> U {
nodes: N, type: [String], nice_type_names: [String]
) =
#externalMacro(module: "Macros", type: "RequireNodesType")
@freestanding(codeItem) public macro SkipUnlessNodeType<N, T>(node: N, type: String) =
@freestanding(codeItem) public macro SkipUnlessNodeType<N>(node: N, type: String) =
#externalMacro(module: "Macros", type: "SkipUnlessNodeType")
@freestanding(codeItem) public macro MustOr<E, N>(result: E, thing: E?, or: N) =
#externalMacro(module: "Macros", type: "MustOr")
+29 -1
View File
@@ -225,10 +225,38 @@ public struct SkipUnlessNodeType: CodeItemMacro {
}
}
public struct MustOr: 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 result = node.arguments[arg_index].expression
arg_index = arguments.index(after: arg_index)
let thing = node.arguments[arg_index].expression
arg_index = arguments.index(after: arg_index)
let or = node.arguments[arg_index].expression
return [
CodeBlockItemSyntax(
"""
if let __thing = \(thing) {
\(result) = __thing
} else {
return \(or)
}
""")
]
}
}
@main
struct P4Macros: CompilerPlugin {
var providingMacros: [Macro.Type] = [
RequireResult.self, RequireErrorResult.self, UseOkResult.self, UseErrorResult.self,
RequireNodeType.self, SkipUnlessNodeType.self, RequireNodesType.self,
RequireNodeType.self, SkipUnlessNodeType.self, RequireNodesType.self, MustOr.self,
]
}
+115 -117
View File
@@ -26,9 +26,8 @@ func parameter_list_compiler(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(ParameterList, CompilerContext)> {
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: node)
var current_node: Node? = .none
if node.text == ")" {
// There are no parameters!
@@ -40,49 +39,45 @@ func parameter_list_compiler(
var parameters: ParameterList = ParameterList([])
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing parameter list component"))
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParameterList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter list component")))
currentChild = node.child(at: currentChildIdx)
if currentChild?.nodeType == "parameter_list" {
switch parameter_list_compiler(node: currentChild!, withContext: context) {
if current_node?.nodeType == "parameter_list" {
switch parameter_list_compiler(node: current_node!, withContext: context) {
case .Ok(let (ps, _)):
parameters = ps
case .Error(let e): return Result.Error(e)
}
currentChildIdx += 1
currentChildIdxSafe += 1
walker.next()
}
// We may have moved nodes, check/reset currentChild.
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing parameter list component"))
}
currentChild = node.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParameterList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter list component")))
// If this is a ')', we are done.
if currentChild?.text == ")" {
if current_node?.text == ")" {
return Result.Ok((parameters, context))
}
// If this is a comma, we skip it!
if currentChild?.text == "," {
currentChildIdx += 1
currentChildIdxSafe += 1
if current_node?.text == "," {
walker.next()
}
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing parameter list component"))
}
currentChild = node.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParameterList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter list component")))
// Otherwise, there should be one parameter left!
switch Parameter.Compile(node: currentChild!, withContext: context) {
switch Parameter.Compile(node: current_node!, withContext: context) {
case .Ok(let (parsed_parameter, updated_context)):
return Result.Ok((parameters.addParameter(parsed_parameter), updated_context))
case .Error(let e): return Result.Error(e)
@@ -99,24 +94,24 @@ extension ParameterList: Compilable {
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
node: parameter_node, type: "parameters", nice_type_name: "Parameters")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var walker = Walker(node: parameter_node)
// Let's eat the '(' before we start ...
if parameter_node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: parameter_node, withError: "Missing '(' in parameter list component"))
}
var current_node: Node? = .none
currentChildIdx += 1
currentChildIdxSafe += 1
if parameter_node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: parameter_node, withError: "Missing parameter list component"))
}
let currentChild = parameter_node.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParameterList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing '(' in parameter list component")))
return parameter_list_compiler(node: currentChild!, withContext: context)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParameterList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter list component")))
return parameter_list_compiler(node: current_node!, withContext: context)
}
}
@@ -153,74 +148,80 @@ extension Parameter: Compilable {
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
node: node, type: "parameter", nice_type_name: "parameter")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: node)
var current_node: Node? = .none
if node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: node, withError: "Missing parameter declaration component"))
}
currentChild = node.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Parameter, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter declaration component")))
// Annotation?
if currentChild!.nodeType == "annotations" {
if current_node!.nodeType == "annotations" {
return .Error(
ErrorOnNode(
node: currentChild!,
node: current_node!,
withError: "Annotations in parameter declarations are not yet handled"))
// Will increment indexes here.
}
currentChild = node.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Parameter, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter declaration component")))
var direction: Direction? = .none
// Direction?
if currentChild!.nodeType == "direction" {
if current_node!.nodeType == "direction" {
let maybe_parsed_direction = Direction.Compile(node: currentChild!, withContext: context)
let maybe_parsed_direction = Direction.Compile(node: current_node!, withContext: context)
guard case .Ok((let parsed_direction, _)) = maybe_parsed_direction else {
return .Error(maybe_parsed_direction.error()!)
}
direction = parsed_direction
currentChildIdx += 1
currentChildIdxSafe += 1
walker.next()
}
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType != "typeRef" {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Parameter, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter declaration component")))
if current_node!.nodeType != "typeRef" {
return Result.Error(
ErrorOnNode(
node: node, withError: "Did not find type name for parameter declaration"))
}
guard
case .Ok(let parameter_type) = Types.CompileType(type: currentChild!, withContext: context)
case .Ok(let parameter_type) = Types.CompileType(type: current_node!, withContext: context)
else {
return Result.Error(
Error(withMessage: "Could not parse a P4 type from \(currentChild!.text!)"))
Error(withMessage: "Could not parse a P4 type from \(current_node!.text!)"))
}
currentChildIdx += 1
currentChildIdxSafe += 1
if node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: node, withError: "Missing parameter declaration component"))
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Parameter, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter declaration component")))
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType != "identifier" {
if current_node!.nodeType != "identifier" {
return Result.Error(
ErrorOnNode(
node: node, withError: "Did not find identifier for parameter statement"))
}
guard
case .Ok(let parameter_name) = Identifier.Compile(node: currentChild!, withContext: context)
case .Ok(let parameter_name) = Identifier.Compile(node: current_node!, withContext: context)
else {
return Result.Error(
Error(withMessage: "Could not parse a parameter name from \(currentChild!.text!)"))
Error(withMessage: "Could not parse a parameter name from \(current_node!.text!)"))
}
return Result.Ok(
@@ -239,9 +240,8 @@ func argument_list_compiler(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(ArgumentList, CompilerContext)> {
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: node)
var current_node: Node? = .none
if node.text == ")" {
// There are no arguments!
@@ -253,49 +253,47 @@ func argument_list_compiler(
var arguments: ArgumentList = ArgumentList([])
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing argument list component"))
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ArgumentList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing argument list component")))
currentChild = node.child(at: currentChildIdx)
if currentChild?.nodeType == "argument_list" {
switch argument_list_compiler(node: currentChild!, withContext: context) {
if current_node?.nodeType == "argument_list" {
switch argument_list_compiler(node: current_node!, withContext: context) {
case .Ok(let (ps, _)):
arguments = ps
case .Error(let e): return Result.Error(e)
}
currentChildIdx += 1
currentChildIdxSafe += 1
walker.next()
}
// We may have moved nodes, check/reset currentChild.
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing argument list component"))
}
currentChild = node.child(at: currentChildIdx)
// We may have moved nodes, check/reset current_node.
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ArgumentList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing argument list component")))
// If this is a ')', we are done.
if currentChild?.text == ")" {
if current_node?.text == ")" {
return Result.Ok((arguments, context))
}
// If this is a comma, we skip it!
if currentChild?.text == "," {
currentChildIdx += 1
currentChildIdxSafe += 1
if current_node?.text == "," {
walker.next()
}
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing argument list component"))
}
currentChild = node.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ArgumentList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing argument list component")))
// Otherwise, there should be one argument left!
switch Argument.Compile(node: currentChild!, withContext: context) {
switch Argument.Compile(node: current_node!, withContext: context) {
case .Ok(let (ce, updated_context)):
return Result.Ok(
(arguments.addArgument(Argument(ce, atIndex: arguments.count() + 1)), updated_context))
@@ -313,24 +311,24 @@ extension ArgumentList: Compilable {
#RequireNodeType<Node, (ArgumentList, CompilerContext)>(
node: argument_node, type: "arguments", nice_type_name: "arguments")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var walker = Walker(node: argument_node)
var current_node: Node? = .none
// Let's eat the '(' before we start ...
if argument_node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: argument_node, withError: "Missing '(' in argument list component"))
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ArgumentList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing '(' in argument list component")))
currentChildIdx += 1
currentChildIdxSafe += 1
if argument_node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: argument_node, withError: "Missing argument list component"))
}
let currentChild = argument_node.child(at: currentChildIdx)
walker.next()
return argument_list_compiler(node: currentChild!, withContext: context)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ArgumentList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing argument list component")))
return argument_list_compiler(node: current_node!, withContext: context)
}
}
+218 -256
View File
@@ -51,46 +51,42 @@ extension FunctionDeclaration: CompilableDeclaration {
node: function_declaration_node, type: "function_declaration",
nice_type_name: "Function Declaration")
var walker = Walker(node: function_declaration_node)
var context = context
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
if function_declaration_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(
node: function_declaration_node, withError: "Missing function declaration component"))
}
currentChild = function_declaration_node.child(at: currentChildIdx)
let maybe_function_type = Types.CompileType(type: currentChild!, withContext: context)
var current_node: Node? = .none
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: function_declaration_node, withError: "Missing function declaration component")))
let maybe_function_type = Types.CompileType(type: current_node!, withContext: context)
guard case .Ok(let function_type) = maybe_function_type else {
return .Error(maybe_function_type.error()!)
}
currentChildIdx += 1
currentChildIdxSafe += 1
if function_declaration_node.childCount < currentChildIdxSafe {
return Result.Error(
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: function_declaration_node, withError: "Missing function declaration component"))
}
currentChild = function_declaration_node.child(at: currentChildIdx)
node: function_declaration_node, withError: "Missing function declaration component")))
let maybe_function_name = Identifier.Compile(node: currentChild!, withContext: context)
let maybe_function_name = Identifier.Compile(node: current_node!, withContext: context)
guard case .Ok(let function_name) = maybe_function_name else {
return .Error(maybe_function_name.error()!)
}
currentChildIdx += 1
currentChildIdxSafe += 1
if function_declaration_node.childCount < currentChildIdxSafe {
return Result.Error(
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: function_declaration_node, withError: "Missing function declaration component"))
}
currentChild = function_declaration_node.child(at: currentChildIdx)
node: function_declaration_node, withError: "Missing function declaration component")))
let maybe_function_parameters = ParameterList.Compile(node: currentChild!, withContext: context)
let maybe_function_parameters = ParameterList.Compile(node: current_node!, withContext: context)
guard case .Ok((let function_parameters, let updated_context)) = maybe_function_parameters
else {
return .Error(maybe_function_parameters.error()!)
@@ -99,11 +95,8 @@ extension FunctionDeclaration: CompilableDeclaration {
var function_body: BlockStatement? = .none
currentChildIdx += 1
currentChildIdxSafe += 1
if currentChildIdxSafe <= function_declaration_node.childCount {
currentChild = function_declaration_node.child(at: currentChildIdx)
walker.next()
if let body = walker.getNext() {
// Add the parameters into scope.
var function_scope = context.instances.enter()
for parameter in function_parameters.parameters {
@@ -112,7 +105,7 @@ extension FunctionDeclaration: CompilableDeclaration {
}
let maybe_function_body = BlockStatement.Compile(
node: currentChild!,
node: body,
withContext: context.update(newInstances: function_scope).update(
newExpectation: function_type))
@@ -160,59 +153,38 @@ extension P4Struct: CompilableDeclaration {
static public func Compile(
node: Node, withContext context: CompilerContext
) -> Result<(Declaration, CompilerContext)?> {
let struct_declaration_node = node.child(at: 0)!
var currentChildIdx = 0
var currentChildIdxSafe = 1
var walker = Walker(node: struct_declaration_node)
var currentNode: Node? = .none
var currentChild: Node? = .none
guard let node_type = struct_declaration_node.nodeType,
node_type == "struct_declaration"
else {
return Result.Error(
ErrorOnNode(node: struct_declaration_node, withError: "Did not find a struct declaration"))
}
if struct_declaration_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(
node: struct_declaration_node, withError: "Missing elements in struct declaration"))
}
#SkipUnlessNodeType(node: struct_declaration_node, type: "struct_declaration")
// Skip the keyword struct
currentChildIdx += 1
currentChildIdxSafe += 1
if struct_declaration_node.childCount < currentChildIdxSafe {
return Result.Error(
walker.next()
#MustOr(
result: currentNode, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: struct_declaration_node, withError: "Missing elements in struct declaration"))
}
node: struct_declaration_node, withError: "Missing function declaration component")))
// The name of the struct type.
currentChild = struct_declaration_node.child(at: currentChildIdx)
let maybe_struct_identifier = Identifier.Compile(
node: currentChild!, withContext: context)
node: currentNode!, withContext: context)
guard case Result.Ok(let struct_identifier) = maybe_struct_identifier else {
return Result.Error(maybe_struct_identifier.error()!)
}
currentChildIdx += 1
currentChildIdxSafe += 1
walker.next()
// Skip the '{'
currentChildIdx += 1
currentChildIdxSafe += 1
if struct_declaration_node.childCount < currentChildIdxSafe {
return Result.Error(
walker.next()
#MustOr(
result: currentNode, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: struct_declaration_node, withError: "Missing element of struct declaration"))
}
currentChild = struct_declaration_node.child(at: currentChildIdx)
node: struct_declaration_node, withError: "Missing function declaration component")))
// If there are no fields, it will be a "}"
if currentChild!.nodeType == "}" {
if currentNode!.nodeType == "}" {
let struc = Declaration(
TypedIdentifier(
id: struct_identifier,
@@ -232,8 +204,8 @@ extension P4Struct: CompilableDeclaration {
var current_context = context
var parsed_fields: [P4StructFieldIdentifier] = Array()
if currentChild!.nodeType == "struct_declaration_fields" {
currentChild!.enumerateNamedChildren { declaration_field in
if currentNode!.nodeType == "struct_declaration_fields" {
currentNode!.enumerateNamedChildren { declaration_field in
switch VariableDeclarationStatement.Compile(
node: declaration_field, withContext: current_context)
{
@@ -281,75 +253,67 @@ extension P4Lang.Parser: CompilableDeclaration {
node: Node, withContext context: CompilerContext
) -> Result<(Declaration, CompilerContext)?> {
let parser_node = node
#SkipUnlessNodeType<Node, (P4DataType, CompilerContext)?>(
node: parser_node, type: "parserDeclaration")
#SkipUnlessNodeType<Node>(node: parser_node, type: "parserDeclaration")
var current_context = context
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: parser_node)
var current_node: Node? = .none
if parser_node.childCount < currentChildIdxSafe {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: parser_node, withError: "Missing elements of parser declaration")))
if current_node!.nodeType != "parserType" {
return .Error(
ErrorOnNode(node: parser_node, withError: "Missing elements of parser declaration"))
ErrorOnNode(node: current_node!, withError: "Missing type for parser declaration"))
}
currentChild = parser_node.child(at: currentChildIdx)
if currentChild!.nodeType != "parserType" {
return .Error(
ErrorOnNode(node: currentChild!, withError: "Missing type for parser declaration"))
}
let type_node = currentChild
let type_node = current_node
var parser_name: Common.Identifier? = .none
// TODO: Handle parser parameter lists.
var parameter_list = ParameterList()
do {
// Parse the parser type (type_node)
var currentChildIdx = 0
var currentChildIdxSafe = 1
var type_node_walker = Walker(node: type_node!)
var type_node_child: Node? = .none
if type_node!.childCount < currentChildIdxSafe {
#MustOr(
result: type_node_child, thing: type_node_walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: parser_node, withError: "Missing elements of parser type in parser declaration")))
if type_node_child!.nodeType == "annotations" {
return .Error(
ErrorOnNode(
node: parser_node, withError: "Missing elements of parser type in parser declaration"))
}
var currentChild = type_node!.child(at: currentChildIdx)
if currentChild!.nodeType == "annotations" {
return .Error(
ErrorOnNode(
node: currentChild!, withError: "Annotations in parser type are not yet handled."))
node: type_node_child!, withError: "Annotations in parser type are not yet handled."))
// Will increment indexes here.
}
// Skip the parser keyword
currentChildIdx += 1
currentChildIdxSafe += 1
if type_node!.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: type_node!, withError: "Missing name in parser type declaration"))
}
currentChild = type_node?.child(at: currentChildIdx)
type_node_walker.next()
#MustOr(
result: type_node_child, thing: type_node_walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: type_node_child!, withError: "Missing name in parser type declaration")))
switch Identifier.Compile(node: currentChild!, withContext: current_context) {
switch Identifier.Compile(node: type_node_child!, withContext: current_context) {
case .Ok(let id): parser_name = id
case .Error(let e):
return .Error(e)
}
currentChildIdx += 1
currentChildIdxSafe += 1
if type_node!.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: type_node!, withError: "Missing parser parameters"))
}
type_node_walker.next()
#MustOr(
result: type_node_child, thing: type_node_walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: type_node_child!, withError: "Missing parser parameters")))
currentChild = type_node?.child(at: currentChildIdx)
switch ParameterList.Compile(node: currentChild!, withContext: current_context) {
switch ParameterList.Compile(node: type_node_child!, withContext: current_context) {
case .Ok(let (parsed_parameter_list, updated_context)):
parameter_list = parsed_parameter_list
current_context = updated_context
@@ -365,37 +329,38 @@ extension P4Lang.Parser: CompilableDeclaration {
identifier: parameter.name, withValue: parameter.type))
}
currentChildIdx += 1
currentChildIdxSafe += 1
if parser_node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: parser_node, withError: "Missing parser declaration component"))
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: parser_node, withError: "Missing parser declaration component")))
// Skip the '{'
currentChildIdx += 1
currentChildIdxSafe += 1
if parser_node.childCount < currentChildIdxSafe {
return .Error((Error(withMessage: "Missing body of parser declaration")))
}
currentChild = parser_node.child(at: currentChildIdx)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: parser_node, withError: "Missing elements of parser declaration")))
if currentChild!.nodeType == "parserLocalElements" {
if current_node!.nodeType == "parserLocalElements" {
return .Error(
ErrorOnNode(node: currentChild!, withError: "Parser Local Elements are not yet handled."))
ErrorOnNode(node: current_node!, withError: "Parser Local Elements are not yet handled."))
// Will increment indexes here.
}
if parser_node.childCount < currentChildIdxSafe {
return .Error((Error(withMessage: "Missing body of parser declaration")))
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: parser_node, withError: "Missing body of parser declaration")))
if currentChild!.nodeType != "parserStates" {
if current_node!.nodeType != "parserStates" {
return .Error(Error(withMessage: "Missing parser states in parser declaration"))
}
switch Parser.Compile(
withName: parser_name!, withParameters: parameter_list, node: currentChild!,
withName: parser_name!, withParameters: parameter_list, node: current_node!,
withContext: current_context)
{
case Result.Ok((let parser, let updated_context)):
@@ -421,47 +386,37 @@ extension Control: CompilableDeclaration {
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(Declaration, CompilerContext)?> {
#SkipUnlessNodeType<Node, (P4DataType, CompilerContext)?>(
node: node, type: "control_declaration")
#SkipUnlessNodeType<Node>(node: node, type: "control_declaration")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: node)
var current_node: Node? = .none
var local_context = context
// Skip control keyword
if node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: node, withError: "Missing control declaration component"))
}
currentChildIdx += 1
currentChildIdxSafe += 1
if node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: node, withError: "Missing control declaration component"))
}
currentChild = node.child(at: currentChildIdx)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: node, withError: "Missing control declaration component")))
guard
case .Ok(let control_name) = Identifier.Compile(
node: currentChild!, withContext: local_context)
node: current_node!, withContext: local_context)
else {
return Result.Error(
Error(withMessage: "Could not parse a parameter name from \(currentChild!.text!)"))
Error(withMessage: "Could not parse a parameter name from \(current_node!.text!)"))
}
currentChildIdx += 1
currentChildIdxSafe += 1
if node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: node, withError: "Missing control declaration component"))
}
currentChild = node.child(at: currentChildIdx)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: node, withError: "Missing control declaration component")))
let maybe_control_parameters = ParameterList.Compile(
node: currentChild!, withContext: local_context)
node: current_node!, withContext: local_context)
guard case .Ok((let control_parameters, let updated_context)) = maybe_control_parameters
else {
return .Error(maybe_control_parameters.error()!)
@@ -476,26 +431,26 @@ extension Control: CompilableDeclaration {
}
local_context = local_context.update(newInstances: control_scope)
walker.next()
// Skip the '{'
currentChildIdx += 2
currentChildIdxSafe += 2
if node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: node, withError: "Missing control declaration component"))
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorOnNode(
node: node, withError: "Missing control declaration component")))
var actions: [Action] = Array()
var tables: [Table] = Array()
var apply: ApplyStatement? = .none
// Because the final child
// is the '}'.
// \/\/
for currentChildIdx in currentChildIdx..<(node.childCount - 1) {
let currentChild = node.child(at: currentChildIdx)!
if currentChild.nodeType == "action_declaration" {
// Because the final child
// is the '}'.
// \/\/
let body_parse_results = walker.overUntil(n: node.childCount - 1) { current_node in
if current_node.nodeType == "action_declaration" {
let maybe_action_declaration = Action.Compile(
node: currentChild, withContext: local_context)
node: current_node, withContext: local_context)
guard
case .Ok((let action_declaration, let updated_context)) = maybe_action_declaration
else {
@@ -503,9 +458,9 @@ extension Control: CompilableDeclaration {
}
actions.append(action_declaration)
local_context = updated_context
} else if currentChild.nodeType == "table_declaration" {
} else if current_node.nodeType == "table_declaration" {
let maybe_table_declaration = Table.Compile(
node: currentChild, withContext: local_context)
node: current_node, withContext: local_context)
guard
case .Ok((let table_declaration, let updated_context)) = maybe_table_declaration
else {
@@ -513,10 +468,10 @@ extension Control: CompilableDeclaration {
}
tables.append(table_declaration)
local_context = updated_context
} else if currentChild.nodeType == "apply_statement" {
} else if current_node.nodeType == "apply_statement" {
// When we see an apply, that is it for the actions and the tables.
let maybe_apply_statement = ApplyStatement.Compile(
node: currentChild, withContext: local_context)
node: current_node, withContext: local_context)
guard
case .Ok((let apply_statement, let updated_context)) = maybe_apply_statement
else {
@@ -526,18 +481,23 @@ extension Control: CompilableDeclaration {
apply = (apply_statement as! ApplyStatement)
// The apply is the last thing in a control declaration.
break
// But, that is handled by the compiler.
} else {
return .Error(
ErrorOnNode(node: currentChild, withError: "Uknown node type in control declaration"))
ErrorOnNode(node: current_node, withError: "Uknown node type in control declaration"))
}
return .Ok(())
}
if case .Error(let e) = body_parse_results {
return .Error(e)
}
// There should only be a single table!
// TODO: Check the semantics here.
/// TODO: Check the semantics here.
if tables.count > 1 {
// TODO: Make this error message better.
// IDEA: Add a "compilation context" for the error message into the `CompilationContext`
/// TODO: Make this error message better.
/// IDEA: Add a "compilation context" for the error message into the `CompilationContext`
// that can be retrieved to make the error messages nicer.
return .Error(
ErrorOnNode(node: node, withError: "More than one table in control declaration"))
@@ -580,50 +540,48 @@ extension Action: Compilable {
#RequireNodeType<Node, (P4DataType, CompilerContext)>(
node: node, type: "action_declaration", nice_type_name: "Action Declaration")
var currentChildIdx = 1
var currentChildIdxSafe = 2
var currentChild: Node? = .none
var walker = Walker(node: node)
var current_node: Node? = .none
var current_context = context
// Skip action keyword
if node.childCount < currentChildIdxSafe {
return .Error(
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(P4Lang.Action, CompilerContext)>.Error(
ErrorOnNode(node: node, withError: "Missing action declaration component"))
}
currentChild = node.child(at: currentChildIdx)
)
guard
case .Ok(let action_name) = Identifier.Compile(
node: currentChild!, withContext: current_context)
node: current_node!, withContext: current_context)
else {
return Result.Error(
Error(withMessage: "Could not parse an action name from \(currentChild!.text!)"))
Error(withMessage: "Could not parse an action name from \(current_node!.text!)"))
}
currentChildIdx += 1
currentChildIdxSafe += 1
if node.childCount < currentChildIdxSafe {
return .Error(
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(P4Lang.Action, CompilerContext)>.Error(
ErrorOnNode(node: node, withError: "Missing action declaration component"))
}
currentChild = node.child(at: currentChildIdx)
)
let maybe_action_parameters = ParameterList.Compile(
node: currentChild!, withContext: current_context)
node: current_node!, withContext: current_context)
guard case .Ok((let action_parameters, let updated_context)) = maybe_action_parameters
else {
return .Error(maybe_action_parameters.error()!)
}
current_context = updated_context
currentChildIdx += 1
currentChildIdxSafe += 1
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(
node: node, withError: "Missing action declaration component"))
}
currentChild = node.child(at: currentChildIdx)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(P4Lang.Action, CompilerContext)>.Error(
ErrorOnNode(node: node, withError: "Missing action declaration component"))
)
// Add the parameters into scope.
var function_scope = context.instances.enter()
@@ -633,12 +591,12 @@ extension Action: Compilable {
}
let maybe_action_body = BlockStatement.Compile(
node: currentChild!, withContext: context.update(newInstances: function_scope))
node: current_node!, withContext: context.update(newInstances: function_scope))
guard case .Ok((let action_body, _)) = maybe_action_body else {
return .Error(maybe_action_body.error()!)
}
// TODO: Actions cannot contain switches!
/// TODO: Actions cannot contain switches!
return .Ok(
(
@@ -659,35 +617,35 @@ extension TableKeyEntry: Compilable {
#RequireNodeType<Node, (P4DataType, CompilerContext)>(
node: node, type: "table_key_entry", nice_type_name: "Table Key Entry")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: node)
var current_node: Node? = .none
let current_context = context
if node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: node, withError: "Missing table key entry declaration component"))
}
currentChild = node.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(P4Lang.TableKeyEntry, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing table key entry declaration component")))
let maybe_keyset_expression = KeysetExpression.compile(
node: currentChild!, withContext: current_context)
node: current_node!, withContext: current_context)
guard case .Ok(let keyset_expression) = maybe_keyset_expression else {
return Result.Error(maybe_keyset_expression.error()!)
}
walker.next()
// Skip the ':'
currentChildIdx += 2
currentChildIdxSafe += 2
if node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: node, withError: "Missing table key entry declaration component"))
}
currentChild = node.child(at: currentChildIdx)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(P4Lang.TableKeyEntry, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing table key entry declaration component")))
let maybe_match_type = TableKeyMatchType.Compile(
node: currentChild!, withContext: current_context)
node: current_node!, withContext: current_context)
guard case .Ok((let match_type, _)) = maybe_match_type else {
return .Error(maybe_match_type.error()!)
}
@@ -719,26 +677,29 @@ extension TableKeys: Compilable {
#RequireNodeType<Node, (TableKeyMatchType, CompilerContext)>(
node: node, type: "table_keys", nice_type_name: "Table Keys")
var walker = Walker(node: node)
// Skip the
// keys = {
// 0 1 2
let currentChildIdx = 3
let currentChildIdxSafe = 4
var currentChild: Node? = .none
// 1 2 3
walker.next() // 1
walker.next() // 2
walker.next() // 3
var current_node: Node? = .none
var current_context = context
if node.childCount < currentChildIdxSafe {
return .Error(
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(P4Lang.TableKeys, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing table keys declaration component in control declaration"))
}
currentChild = node.child(at: currentChildIdx)
)
var entries: [TableKeyEntry] = Array()
var errors: [Error] = Array()
currentChild!.enumerateNamedChildren { entry in
switch TableKeyEntry.Compile(node: currentChild!, withContext: current_context) {
current_node!.enumerateNamedChildren { entry in
switch TableKeyEntry.Compile(node: current_node!, withContext: current_context) {
case .Ok((let keyset_expression, let updated_context)):
entries.append(keyset_expression)
current_context = updated_context
@@ -823,37 +784,38 @@ extension Table: Compilable {
#RequireNodeType<Node, (P4DataType, CompilerContext)>(
node: table_declaration_node, type: "table_declaration", nice_type_name: "Table Declaration")
var currentChildIdx = 1
var currentChildIdxSafe = 2
var currentChild: Node? = .none
var walker = Walker(node: table_declaration_node)
var current_node: Node? = .none
let current_context = context
if table_declaration_node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: table_declaration_node, withError: "Missing table declaration component"))
}
currentChild = table_declaration_node.child(at: currentChildIdx)
walker.next() // Skip the XXX?
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(P4Lang.Table, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing table declaration component")))
guard
case .Ok(let table_name) = Identifier.Compile(
node: currentChild!, withContext: current_context)
node: current_node!, withContext: current_context)
else {
return Result.Error(
Error(withMessage: "Could not parse a table name from \(currentChild!.text!)"))
Error(withMessage: "Could not parse a table name from \(current_node!.text!)"))
}
walker.next()
// Skip the '{'
currentChildIdx += 2
currentChildIdxSafe += 2
if table_declaration_node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: table_declaration_node, withError: "Missing table declaration component"))
}
currentChild = table_declaration_node.child(at: currentChildIdx)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(P4Lang.Table, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing table declaration component")))
let maybe_table_property_list = TablePropertyList.Compile(
node: currentChild!, withContext: current_context)
node: current_node!, withContext: current_context)
guard case .Ok((let table_property_list, _)) = maybe_table_property_list else {
return Result.Error(maybe_table_property_list.error()!)
}
+105 -125
View File
@@ -39,7 +39,7 @@ extension TypedIdentifier: CompilableExpression {
) -> Result<EvaluatableExpression?> {
let node = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: node, type: "identifier")
guard
@@ -59,7 +59,7 @@ extension TypedIdentifier: CompilableLValueExpression {
) -> Result<EvaluatableLValueExpression?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: expression, type: "identifier")
let maybe_parsed_expression = TypedIdentifier.compile(node: node, withContext: context)
@@ -78,7 +78,7 @@ extension P4BooleanValue: CompilableExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableExpression?> {
let node = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: node, type: "booleanLiteralExpression")
if node.text == "false" {
@@ -97,7 +97,7 @@ extension P4IntValue: CompilableExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableExpression?> {
let node = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(node: node, type: "integer")
#SkipUnlessNodeType<SwiftTreeSitter.Node>(node: node, type: "integer")
if let parsed_int = Int(node.text!) {
return .Ok(P4Value(P4IntValue(withValue: parsed_int)))
} else {
@@ -111,7 +111,7 @@ extension P4StringValue: CompilableExpression {
node: SwiftTreeSitter.Node, withContext scopes: CompilerContext
) -> Result<EvaluatableExpression?> {
let node = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: node, type: "string_literal")
return .Ok(P4Value(P4StringValue(withValue: node.text!)))
}
@@ -339,20 +339,18 @@ extension BinaryOperatorExpression: CompilableExpression {
) -> Result<(EvaluatableExpression)?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
#SkipUnlessNodeType<Node>(
node: expression, type: "binaryOperatorExpression")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
let binary_operator_expression_node = expression.child(at: 0)!
var walker = Walker(node: binary_operator_expression_node)
if expression.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Malformed binary operator expression"))
}
currentChild = expression.child(at: currentChildIdx)
let binary_operator_expression_node = currentChild!
var current_node: Node? = .none
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Malformed binary operator expression")))
// TODO: This macro cannot handle new lines in the arrays
// swift-format-ignore
@@ -360,32 +358,29 @@ extension BinaryOperatorExpression: CompilableExpression {
nodes: binary_operator_expression_node,
type: ["binaryEqualOperatorExpression", "binaryLessThanOperatorExpression", "binaryLessThanEqualOperatorExpression", "binaryGreaterThanOperatorExpression", "binaryGreaterThanEqualOperatorExpression", "binaryAndOperatorExpression", "binaryOrOperatorExpression", "binaryAddOperatorExpression", "binarySubtractOperatorExpression", "binaryMultiplyOperatorExpression", "binaryDivideOperatorExpression"],
nice_type_names: [ "binary equal operator", "binary less than operator", "binary less than or equal to operator", "binary greater than operator", "binary greater than or equal to operator", "binary and operator", "binary or operator", "binary add operator", "binary subtract operator", "binary multiply operator", "binary divide operator"])
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing LHS for binary operator expression")))
if binary_operator_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing LHS for binary operator expression"))
}
currentChild = binary_operator_expression_node.child(at: currentChildIdx)
let left_hand_side_raw = currentChild!
let left_hand_side_raw = current_node!
currentChildIdx = currentChildIdx + 1
currentChildIdxSafe = currentChildIdxSafe + 1
if binary_operator_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing binary operator for binary operator expression")
)
}
currentChild = binary_operator_expression_node.child(at: currentChildIdx)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing binary operator for binary operator expression")))
currentChildIdx = currentChildIdx + 1
currentChildIdxSafe = currentChildIdxSafe + 1
if binary_operator_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing binary operator for binary operator expression")
)
}
currentChild = binary_operator_expression_node.child(at: currentChildIdx)
let right_hand_side_raw = currentChild!
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing RHS for binary operator expression")))
let right_hand_side_raw = current_node!
let maybe_left_hand_side = Expression.Compile(node: left_hand_side_raw, withContext: context)
guard case Result.Ok(let left_hand_side) = maybe_left_hand_side else {
@@ -465,54 +460,48 @@ extension BinaryOperatorExpression: CompilableExpression {
extension ArrayAccessExpression: CompilableExpression {
static func compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(any Common.EvaluatableExpression)?> {
) -> Common.Result<EvaluatableExpression?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
#SkipUnlessNodeType<Node>(
node: expression, type: "arrayAccessExpression")
let array_access_expression_node = expression
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: array_access_expression_node)
var current_node: Node? = .none
// What is the "name" of the array?
if array_access_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Malformed array access expression"))
}
currentChild = expression.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Malformed array access expression")))
#RequireNodeType<Node, EvaluatableExpression?>(
node: currentChild!, type: "expression",
node: current_node!, type: "expression",
nice_type_name: "array identifier expression")
let array_access_identifier_node = currentChild!
let array_access_identifier_node = current_node!
// Check for the [
currentChildIdx = currentChildIdx + 1
currentChildIdxSafe = currentChildIdxSafe + 1
if array_access_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing [ for array access expression")
)
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing [ for array access expression")))
// What is the indexor of the array?
currentChildIdx = currentChildIdx + 1
currentChildIdxSafe = currentChildIdxSafe + 1
if array_access_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing indexor expression for array access expression")
)
}
currentChild = array_access_expression_node.child(at: currentChildIdx)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing indexor expression for array access expression")))
#RequireNodeType<Node, EvaluatableExpression?>(
node: currentChild!, type: "expression",
node: current_node!, type: "expression",
nice_type_name: "array indexor expression")
let array_access_indexor_node = currentChild!
let array_access_indexor_node = current_node!
let maybe_array_identifier = Expression.Compile(
node: array_access_identifier_node, withContext: context)
@@ -547,51 +536,44 @@ extension FieldAccessExpression: CompilableExpression {
) -> Common.Result<(any Common.EvaluatableExpression)?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
#SkipUnlessNodeType<Node>(
node: expression, type: "fieldAccessExpression")
let field_access_expression_node = expression
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: field_access_expression_node)
var current_node: Node? = .none
// What is the "name" of the struct?
if field_access_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Malformed field access expression"))
}
currentChild = expression.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Malformed field access expression")))
#RequireNodeType<Node, EvaluatableExpression?>(
node: currentChild!, type: "expression",
node: current_node!, type: "expression",
nice_type_name: "struct identifier expression")
let struct_identifier_node = currentChild!
let struct_identifier_node = current_node!
// Check for the .
currentChildIdx = currentChildIdx + 1
currentChildIdxSafe = currentChildIdxSafe + 1
if field_access_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing . for field access expression")
)
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing . for field access expression")))
// What is the field of the struct?
currentChildIdx = currentChildIdx + 1
currentChildIdxSafe = currentChildIdxSafe + 1
if field_access_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing field name for field access expression")
)
}
currentChild = field_access_expression_node.child(at: currentChildIdx)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing field name for field access expression")))
#RequireNodeType<Node, EvaluatableExpression?>(
node: currentChild!, type: "identifier",
node: current_node!, type: "identifier",
nice_type_name: "field name")
let field_name_node = currentChild!
let field_name_node = current_node!
// Make sure that the identifier really identifies a struct.
let maybe_struct_identifier = Expression.Compile(
@@ -633,7 +615,7 @@ extension FieldAccessExpression: CompilableLValueExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableLValueExpression?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
#SkipUnlessNodeType<Node>(
node: expression, type: "fieldAccessExpression")
let maybe_parsed_expression = FieldAccessExpression.compile(node: node, withContext: context)
@@ -652,7 +634,7 @@ extension ArrayAccessExpression: CompilableLValueExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableLValueExpression?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
#SkipUnlessNodeType<Node>(
node: expression, type: "arrayAccessExpression")
let maybe_parsed_expression = ArrayAccessExpression.compile(node: node, withContext: context)
@@ -672,22 +654,20 @@ extension FunctionCall: CompilableExpression {
) -> Result<EvaluatableExpression?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
#SkipUnlessNodeType<Node>(
node: expression, type: "function_call")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: expression)
var current_node: Node? = .none
if expression.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing function call component"))
}
currentChild = expression.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing function call component")))
let maybe_callee_name = Identifier.Compile(
node: currentChild!, withContext: context)
node: current_node!, withContext: context)
guard case .Ok(let callee_name) = maybe_callee_name else {
return Result.Error(maybe_callee_name.error()!)
}
@@ -700,7 +680,7 @@ extension FunctionCall: CompilableExpression {
Result<(FunctionDeclaration?, Declaration?)>.Ok((callee, .none)) // What we found is actually a function declaration
default:
Result<(FunctionDeclaration?, Declaration?)>.Error(
ErrorOnNode(node: currentChild!, withError: "\(callee_name) is not a function"))
ErrorOnNode(node: current_node!, withError: "\(callee_name) is not a function"))
}
case .Error(let e): Result<(FunctionDeclaration?, Declaration?)>.Error(e)
}
@@ -713,7 +693,7 @@ extension FunctionCall: CompilableExpression {
switch callee.identifier.type.dataType() {
case is FunctionDeclaration: Result.Ok((.none, callee))
default:
.Error(ErrorOnNode(node: currentChild!, withError: "\(callee_name) is not a function"))
.Error(ErrorOnNode(node: current_node!, withError: "\(callee_name) is not a function"))
}
default: .Error(e)
}
@@ -725,15 +705,15 @@ extension FunctionCall: CompilableExpression {
return .Error(maybe_callee.error()!)
}
currentChildIdx += 1
currentChildIdxSafe += 1
if expression.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing function call component"))
}
currentChild = expression.child(at: currentChildIdx)
walker.next()
let maybe_argument_list = ArgumentList.Compile(node: currentChild!, withContext: context)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing function call component")))
let maybe_argument_list = ArgumentList.Compile(node: current_node!, withContext: context)
guard case .Ok((let arguments, _)) = maybe_argument_list else {
return .Error(maybe_argument_list.error()!)
+33 -35
View File
@@ -179,11 +179,9 @@ public struct Parser {
static func Compile(
node: Node, withContext context: CompilerContext
) -> Result<(InstantiatedParserState, CompilerContext)> {
var walker = Walker(node: node)
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var current_node: Node? = .none
guard let node_type = node.nodeType,
node_type == "parserState"
@@ -192,50 +190,50 @@ public struct Parser {
ErrorOnNode(node: node, withError: "Did not find a parser state declaration"))
}
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing elements in parser state declaration"))
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing elements in parser state declaration")))
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType == "annotations" {
if current_node!.nodeType == "annotations" {
return Result.Error(
ErrorOnNode(
node: currentChild!, withError: "Annotations in parser state are not yet handled."))
node: current_node!, withError: "Annotations in parser state are not yet handled."))
// Would increment here.
}
// Skip the keyword state
currentChildIdx += 1
currentChildIdxSafe += 1
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing elements in parser state declaration"))
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing elements in parser state declaration")))
currentChild = node.child(at: currentChildIdx)
let maybe_state_identifier = Identifier.Compile(
node: currentChild!, withContext: context)
node: current_node!, withContext: context)
guard case Result.Ok(let state_identifier) = maybe_state_identifier else {
return Result.Error(maybe_state_identifier.error()!)
}
walker.next()
// Skip the '{'
currentChildIdx += 2
currentChildIdxSafe += 2
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing body of state declaration")))
var parse_errs: [Error] = Array()
var current_context = context
var parsed_s: [EvaluatableStatement] = Array()
if node.childCount < currentChildIdxSafe {
return Result.Error(ErrorOnNode(node: node, withError: "Missing body of state declaration"))
}
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType == "parserStatements" {
if current_node!.nodeType == "parserStatements" {
switch Statements.Compile(
node: currentChild!, withContext: current_context)
node: current_node!, withContext: current_context)
{
case .Ok(let (state_statements, updated_context)):
parsed_s = state_statements
@@ -243,8 +241,7 @@ public struct Parser {
case .Error(let error):
parse_errs.append(error)
}
currentChildIdx += 1
currentChildIdxSafe += 1
walker.next()
}
if !parse_errs.isEmpty {
@@ -255,13 +252,14 @@ public struct Parser {
}.joined(separator: ";")))
}
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing transition statement of state declaration"))
}
currentChild = node.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing transition statement of state declaration")))
return TransitionStatement.Compile(
node: currentChild!, forState: state_identifier, withStatements: parsed_s,
node: current_node!, forState: state_identifier, withStatements: parsed_s,
withContext: current_context)
}
}
+25 -28
View File
@@ -29,34 +29,32 @@ extension BlockStatement: CompilableStatement {
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
node: node, type: "blockStatement", nice_type_name: "block statement")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: node)
var current_node: Node? = .none
if node.childCount < currentChildIdxSafe {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(EvaluatableStatement, CompilerContext)>.Error(
ErrorOnNode(node: node, withError: "Malformed block statement")))
if current_node!.nodeType != "{" {
return Result.Error(
ErrorOnNode(node: node, withError: "Malformed block statement"))
ErrorOnNode(node: current_node!, withError: "Missing { on block statement"))
}
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType != "{" {
return Result.Error(
ErrorOnNode(node: currentChild!, withError: "Missing { on block statement"))
}
currentChildIdx += 1
currentChildIdxSafe += 1
var statements: [EvaluatableStatement] = Array()
var parse_err: Error? = .none
var current_context = context
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Malformed block statement"))
}
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType == "statements" {
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(EvaluatableStatement, CompilerContext)>.Error(
ErrorOnNode(node: node, withError: "Malformed block statement")))
if current_node!.nodeType == "statements" {
switch Parser.Statements.Compile(
node: currentChild!, withContext: current_context)
node: current_node!, withContext: current_context)
{
case .Ok(let (parsed_statements, updated_context)):
current_context = updated_context
@@ -65,22 +63,21 @@ extension BlockStatement: CompilableStatement {
parse_err = error
}
currentChildIdx += 1
currentChildIdxSafe += 1
walker.next()
}
if let err = parse_err {
return .Error(err)
}
if node.childCount < currentChildIdxSafe {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(EvaluatableStatement, CompilerContext)>.Error(
ErrorOnNode(node: node, withError: "Malformed block statement")))
if current_node!.nodeType != "}" {
return Result.Error(
ErrorOnNode(node: node, withError: "Malformed block statement"))
}
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType != "}" {
return Result.Error(
ErrorOnNode(node: currentChild!, withError: "Missing } on block statement"))
ErrorOnNode(node: current_node!, withError: "Missing } on block statement"))
}
return .Ok((BlockStatement(statements), current_context))
+54
View File
@@ -0,0 +1,54 @@
// 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
import SwiftTreeSitter
import TreeSitterP4
public struct Walker {
var currentChildIdx: Int
let childCount: Int
let node: Node
public init(node: Node) {
self.currentChildIdx = 0
self.childCount = node.childCount
self.node = node
}
public mutating func next() {
self.currentChildIdx += 1
}
public func getNext() -> Node? {
// If it is safe, then return the node!
if self.currentChildIdx < self.childCount {
return self.node.child(at: self.currentChildIdx)!
}
return .none
}
public func overUntil(n: Int, todo: (Node) -> Result<()>) -> Result<()> {
for currentChildIdx in currentChildIdx..<n {
let currentChild = node.child(at: currentChildIdx)!
if case Result.Error(let e) = todo(currentChild) {
return Result<()>.Error(e)
}
}
return Result.Ok(())
}
}
-1
View File
@@ -64,7 +64,6 @@ public struct FunctionDeclaration: P4DataType, P4DataValue {
}
public func eq(rhs: any Common.P4DataType) -> Bool {
print("Checking a type: me: \(self) vs them: \(rhs)!")
switch rhs {
case let frhs as FunctionDeclaration:
return frhs.tipe.eq(self.tipe) && frhs.params == self.params
@@ -107,6 +107,36 @@ import P4Lang
#expect(#RequireOkResult(Program.Compile(simple_parser_declaration)))
}
@Test func test_simple_control_declaration_with_multiple_tables() async throws {
let simple_parser_declaration = """
control simple(bool x, bool y, bool a, bool b) {
action a() {
}
table t {
key = {
x: exact;
y: exact;
}
}
table u {
key = {
a: exact;
b: exact;
}
}
apply {
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage: "{0, 215}: More than one table in control declaration"
),
Program.Compile(simple_parser_declaration))
)
}
@Test func test_simple_control_declaration_with_action_using_parameter() async throws {
let simple_parser_declaration = """
control simple(bool x, bool y) {
@@ -150,4 +180,36 @@ import P4Lang
),
Program.Compile(simple_parser_declaration))
)
}
@Test func test_simple_control_declaration_with_element_after_apply() async throws {
let simple_parser_declaration = """
control simple(bool x, bool y) {
action a(int z) {
z = false;
}
table t {
key = {
x: exact;
y: exact;
}
}
apply {
}
table x {
key = {
x: exact;
y: exact;
}
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"Could not compile the P4 program"
),
Program.Compile(simple_parser_declaration))
)
}
@@ -0,0 +1,51 @@
// 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
import Foundation
import Macros
import P4Lang
import P4Runtime
import SwiftTreeSitter
import Testing
import TreeSitter
import TreeSitterP4
@testable import Macros
func wrapper_test_mustor() -> Int {
let x: Int? = 2
var i = 0
#MustOr(result: i, thing: x, or: 1)
return i
}
func wrapper_test_mustor_none() -> Int {
let x: Int? = .none
var i = 0
#MustOr(result: i, thing: x, or: 1)
return i
}
@Test func test_mustor() async throws {
#expect(wrapper_test_mustor() == 2)
}
@Test func test_mustor_none() async throws {
#expect(wrapper_test_mustor_none() == 1)
}