// 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 . import Common import SwiftTreeSitter import TreeSitterExtensions import TreeSitterP4 extension AST.Declaration: Compilable { public typealias C = AST.AnDeclaration public static func Compile( node: Node, withContext context: ASTCompilerContext ) -> Result { // Be kind to our user -- if we are at a declaration node, dive into it! let declaration_node = if node.nodeType == "declaration" { node.child(at: 0)! } else { node } let declaration_compilers: [String: any Compilable.Type] = [ "function_declaration": AST.FunctionDeclaration.self, "control_declaration": AST.Control.self, //"type_declaration": AST.P4Struct.self, "parserDeclaration": AST.Parser.self, /// ASSUME: Type declarations are struct declarations. "extern_declaration": AST.ExternDeclaration.self, ] guard let declaration_compiler = declaration_compilers[declaration_node.nodeType!] else { return .Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Could not find parser for declaration \(declaration_node.nodeType!)")) } return declaration_compiler.Compile(node: declaration_node, withContext: context).map { result in .Ok(result) } } } @deriveCompilableStatement extension AST.Declaration: CompilableStatement {} extension AST.FunctionDeclaration: Compilable { public typealias C = AST.AnDeclaration public static func Compile( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Common.Result { let function_declaration_node = node #RequireNodeType( node: function_declaration_node, type: "function_declaration", nice_type_name: "Function Declaration") var walker = Walker(node: function_declaration_node) var current_node: Node? = .none #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: function_declaration_node.toSourceLocation(), withError: "Missing function declaration component"))) let maybe_function_type = AST.Types.CompileType(type: current_node!, withContext: context) guard case .Ok(let function_type) = maybe_function_type else { return .Error(maybe_function_type.error()!) } walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: function_declaration_node.toSourceLocation(), withError: "Missing function declaration component"))) let maybe_function_name = AST.Identifier.CompileExpression( node: current_node!, withContext: context) guard case .Ok(let function_name) = maybe_function_name else { return .Error(maybe_function_name.error()!) } walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: function_declaration_node.toSourceLocation(), withError: "Missing function declaration component"))) let maybe_function_parameters = AST.ParameterList.Compile( node: current_node!, withContext: context) guard case .Ok(let function_parameters) = maybe_function_parameters else { return .Error(maybe_function_parameters.error()!) } var function_body: AST.BlockStatement? = .none walker.next() if let body = walker.getNext() { let maybe_function_body = AST.BlockStatement.Compile(node: body, withContext: context) guard case .Ok(let parsed_function_body) = maybe_function_body else { return .Error(maybe_function_body.error()!) } function_body = parsed_function_body } else { // If we are in an extern context, no body is okay! if !context.extern_context { return Result.Error( ErrorWithLocation( sourceLocation: function_declaration_node.toSourceLocation(), withError: "Missing function declaration component")) } } let function_declaration = AST.FunctionDeclaration( named: function_name as! AST.Identifier, ofType: function_type, withParameters: function_parameters, withBody: function_body) // Do not use the updated context returned by parsing the body // and do not use the function_scope, either. // And, do not update the context if we are compiling in an // extern context -- the wrapping extern declaration will take care of that. return .Ok( function_declaration, ) } } extension AST.StructDeclaration: Compilable { public typealias C = AST.StructDeclaration static public func Compile( node: Node, withContext context: ASTCompilerContext ) -> Result { let struct_declaration_node = node.child(at: 0)! var walker = Walker(node: struct_declaration_node) var currentNode: Node? = .none #RequireNodeType>( node: struct_declaration_node, type: "struct_declaration", nice_type_name: "struct declaration") // Skip the keyword struct walker.next() #MustOr( result: currentNode, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: struct_declaration_node.toSourceLocation(), withError: "Missing function declaration component"))) // The name of the struct type. let maybe_struct_identifier = AST.Identifier.CompileExpression( node: currentNode!, withContext: context) guard case Result.Ok(let struct_identifier) = maybe_struct_identifier else { return Result.Error(maybe_struct_identifier.error()!) } walker.next() // Skip the '{' walker.next() #MustOr( result: currentNode, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: struct_declaration_node.toSourceLocation(), withError: "Missing function declaration component"))) // If there are no fields, it will be a "}" if currentNode!.nodeType == "}" { let struc = AST.StructDeclaration(struct_identifier as! AST.Identifier, []) return Result.Ok(struc) } var parse_errs: (any Errorable)? = .none var parsed_fields: [AST.VariableDeclarationStatement] = Array() if currentNode!.nodeType == "struct_declaration_fields" { currentNode!.enumerateNamedChildren { declaration_field in switch AST.VariableDeclarationStatement.Compile( node: declaration_field, withContext: context) { case .Ok(let variable_declaration): parsed_fields.append(variable_declaration) case .Error(let e): parse_errs = if let e = parse_errs { parse_errs?.append(error: e) } else { e } } } } if let parse_errs = parse_errs { return .Error(ErrorWithLabel("Error(s) parsing select cases", parse_errs)) } let declared_struct = AST.StructDeclaration(struct_identifier as! AST.Identifier, parsed_fields) return .Ok(declared_struct) } } extension AST.Parser: Compilable { public typealias C = AST.AnDeclaration public static func Compile( node: Node, withContext context: ASTCompilerContext ) -> Result { let parser_node = node #RequireNodeType>( node: parser_node, type: "parserDeclaration", nice_type_name: "parser declaration") var walker = Walker(node: parser_node) var current_node: Node? = .none #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: parser_node.toSourceLocation(), withError: "Missing elements of parser declaration"))) if current_node!.nodeType != "parserType" { return .Error( ErrorWithLocation( sourceLocation: current_node!.toSourceLocation(), withError: "Missing type for parser declaration")) } let type_node = current_node var parser_name: AST.Identifier? = .none /// TODO: Handle parser parameter lists. var parameter_list = AST.ParameterList() do { var type_node_walker = Walker(node: type_node!) var type_node_child: Node? = .none #MustOr( result: type_node_child, thing: type_node_walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: parser_node.toSourceLocation(), withError: "Missing elements of parser type in parser declaration"))) if type_node_child!.nodeType == "annotations" { return .Error( ErrorWithLocation( sourceLocation: type_node_child!.toSourceLocation(), withError: "Annotations in parser type are not yet handled.")) // Will increment indexes here. } type_node_walker.next() #MustOr( result: type_node_child, thing: type_node_walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: type_node_child!.toSourceLocation(), withError: "Missing name in parser type declaration"))) switch AST.Identifier.CompileExpression(node: type_node_child!, withContext: context) { case .Ok(let id): parser_name = (id as! AST.Identifier) case .Error(let e): return .Error(e) } type_node_walker.next() #MustOr( result: type_node_child, thing: type_node_walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: type_node_child!.toSourceLocation(), withError: "Missing parser parameters"))) switch AST.ParameterList.Compile(node: type_node_child!, withContext: context) { case .Ok(let parsed_parameter_list): parameter_list = parsed_parameter_list case .Error(let e): return .Error(e) } } walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: parser_node.toSourceLocation(), withError: "Missing parser declaration component"))) walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: parser_node.toSourceLocation(), withError: "Missing elements of parser declaration"))) if current_node!.nodeType == "parserLocalElements" { return .Error( ErrorWithLocation( sourceLocation: current_node!.toSourceLocation(), withError: "Parser Local Elements are not yet handled.")) // Will increment indexes here. } #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: parser_node.toSourceLocation(), withError: "Missing body of parser declaration"))) if current_node!.nodeType != "parserStates" { return .Error(Error(withMessage: "Missing parser states in parser declaration")) } /// TODO: Handle extern parsers. switch SpecialCompilers.CompileParserBody( withName: parser_name!, withParameters: parameter_list, node: current_node!, withContext: context) { case Result.Ok(let parser): return .Ok(parser) case Result.Error(let error): return .Error(error) } } } extension AST.Control: Compilable { public typealias C = AST.AnDeclaration public static func Compile( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Common.Result { #RequireNodeType>( node: node, type: "control_declaration", nice_type_name: "control declaration") var walker = Walker(node: node) var current_node: Node? = .none walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing control declaration component"))) guard case .Ok(let control_name) = AST.Identifier.CompileExpression( node: current_node!, withContext: context) else { return Result.Error( Error(withMessage: "Could not parse a parameter name from \(current_node!.text!)")) } walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing control declaration component"))) let maybe_control_parameters = AST.ParameterList.Compile( node: current_node!, withContext: context) guard case .Ok(let control_parameters) = maybe_control_parameters else { return .Error(maybe_control_parameters.error()!) } walker.next() // Skip the '{' walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing control declaration component"))) var actions: [AST.Action] = Array() var tables: [AST.Table] = Array() var apply: AST.ApplyStatement? = .none // 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 = AST.Action.Compile( node: current_node, withContext: context) guard case .Ok(let action_declaration) = maybe_action_declaration else { return .Error(maybe_action_declaration.error()!) } actions.append(action_declaration) } else if current_node.nodeType == "table_declaration" { let maybe_table_declaration = AST.Table.Compile( node: current_node, withContext: context) guard case .Ok(let table_declaration) = maybe_table_declaration else { return .Error(maybe_table_declaration.error()!) } tables.append(table_declaration) } 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 = AST.ApplyStatement.Compile( node: current_node, withContext: context) guard case .Ok(let apply_statement) = maybe_apply_statement else { return .Error(maybe_apply_statement.error()!) } apply = apply_statement // The apply is the last thing in a control declaration. // But, that is handled by the compiler. } else { return .Error( ErrorWithLocation( sourceLocation: current_node.toSourceLocation(), 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. if tables.count > 1 { /// 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( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "More than one table in control declaration")) } // Check to make sure that there is an apply. guard let apply = apply else { return .Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing apply in control declaration" )) } return .Ok( AST.Control( named: control_name as! AST.Identifier, withParameters: control_parameters, withTable: tables[0], withActions: AST.Actions(withActions: actions), withApply: apply)) } } extension AST.Action: Compilable { public typealias C = AST.Action public static func Compile( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Result { #RequireNodeType( node: node, type: "action_declaration", nice_type_name: "Action Declaration") var walker = Walker(node: node) var current_node: Node? = .none // Skip action keyword walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing action declaration component" )) ) guard case .Ok(let action_name) = AST.Identifier.CompileExpression( node: current_node!, withContext: context) else { return Result.Error( Error(withMessage: "Could not parse an action name from \(current_node!.text!)")) } walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing action declaration component" )) ) let maybe_action_parameters = AST.ParameterList.Compile( node: current_node!, withContext: context) guard case .Ok(let action_parameters) = maybe_action_parameters else { return .Error(maybe_action_parameters.error()!) } // Check whether the parameters are in the proper order. let remaining_parameters = action_parameters.parameters.drop(while: { $0.type.tipe.direction() != .none }) if remaining_parameters.contains(where: { $0.type.tipe.direction() != .none }) { return .Error( ErrorWithLocation( sourceLocation: current_node!.toSourceLocation(), withError: "All parameters with direction must precede directionless parameters")) } walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing action declaration component" )) ) let maybe_action_body = AST.BlockStatement.Compile( node: current_node!, withContext: context) guard case .Ok(let action_body) = maybe_action_body else { return .Error(maybe_action_body.error()!) } /// TODO: Actions cannot contain switches! return .Ok( AST.Action( named: action_name as! AST.Identifier, withParameters: action_parameters, withBody: action_body)) } } extension AST.TableKeyEntry: Compilable { public typealias C = AST.TableKeyEntry public static func Compile( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Result { #RequireNodeType( node: node, type: "table_key_entry", nice_type_name: "Table Key Entry") var walker = Walker(node: node) var current_node: Node? = .none #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing table key entry declaration component"))) let maybe_keyset_expression = AST.KeysetExpression.CompileExpression( node: current_node!, withContext: context) guard case .Ok(let keyset_expression) = maybe_keyset_expression else { return Result.Error(maybe_keyset_expression.error()!) } walker.next() // Skip the ':' walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing table key entry declaration component"))) let maybe_match_type = AST.TableKeyMatchType.Compile( node: current_node!, withContext: context) guard case .Ok(let match_type) = maybe_match_type else { return .Error(maybe_match_type.error()!) } return .Ok(AST.TableKeyEntry(keyset_expression as! AST.KeysetExpression, match_type)) } } extension AST.TableKeyMatchType: Compilable { public typealias C = AST.TableKeyMatchType public static func Compile( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Result { #RequireNodeType( node: node, type: "table_key_match_type", nice_type_name: "Table Key Match Type") if node.text! == "exact" { return .Ok(AST.TableKeyMatchType.Exact) } return .Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "\(node.text!) is not a valid match type)")) } } extension AST.TableKeys: Compilable { public typealias C = AST.TableKeys public static func Compile( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Result { #RequireNodeType( node: node, type: "table_keys", nice_type_name: "Table Keys") var walker = Walker(node: node) // Skip the // keys = { // 1 2 3 walker.next() // 1 walker.next() // 2 walker.next() // 3 var current_node: Node? = .none #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing table keys declaration component in control declaration")) ) let (keys, errors) = walker.try_map(n: node.childCount - 1, onlyNamed: true) { current_node in return switch AST.TableKeyEntry.Compile(node: current_node, withContext: context) { case .Ok(let keyset_expression): .Ok(keyset_expression) case .Error(let e): .Error(e) } } if !errors.isEmpty { return .Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Error(s) parsing table key: " + (errors.map { error in return "\(error.msg())" }.joined(separator: ";")))) } return .Ok(AST.TableKeys(withEntries: keys)) } } extension AST.TableActionsProperty: Compilable { public typealias C = AST.TableActionsProperty public static func Compile( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Result { #RequireNodeType( node: node, type: "table_actions", nice_type_name: "Table Actions") var walker = Walker(node: node) // Skip the // actions = { // 1 2 3 walker.next() // 1 walker.next() // 2 walker.next() // 3 var current_node: Node? = .none #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing table actions declaration component in control declaration")) ) let (actions, errors) = walker.try_map(n: node.childCount - 1, onlyNamed: true) { current_node in switch AST.Identifier.CompileExpression(node: current_node, withContext: context) { case .Ok(let listed_action): return .Ok(listed_action as! AST.Identifier) case .Error(let e): return .Error(e) } } if !errors.isEmpty { return .Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Error(s) parsing table actions: " + (errors.map { error in return "\(error.msg())" }.joined(separator: ";")))) } return .Ok(AST.TableActionsProperty(actions)) } } extension AST.TablePropertyList: Compilable { public typealias C = AST.TablePropertyList public static func Compile( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Result { #RequireNodeType( node: node, type: "table_property_list", nice_type_name: "Table Property List") var keys: [AST.TableKeys] = Array() var actions: [AST.TableActionsProperty] = Array() var errors: [any Errorable] = Array() node.enumerateNamedChildren { child in if child.nodeType == "table_keys" { switch AST.TableKeys.Compile(node: child, withContext: context) { case .Ok(let table_key): keys.append(table_key) case .Error(let e): errors.append(e) } } else if child.nodeType == "table_actions" { switch AST.TableActionsProperty.Compile(node: child, withContext: context) { case .Ok(let table_action_property): actions.append(table_action_property) case .Error(let e): errors.append(e) } } else { errors.append( ErrorWithLocation( sourceLocation: child.toSourceLocation(), withError: "Uknown node type in control declaration")) } } if !errors.isEmpty { return .Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Error(s) parsing property list: " + (errors.map { error in return "\(error.msg())" }.joined(separator: ";")))) } // There should be only one table keys! if keys.count > 1 { // Todo: Make this error message better. return .Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "More than one key set in table property list")) } // There should be only one table actions! if actions.count > 1 { // Todo: Make this error message better. return .Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "More than one actions in table property list")) } if actions.isEmpty { actions.append(AST.TableActionsProperty()) } return .Ok(AST.TablePropertyList(withActions: actions[0], withKeys: keys[0])) } } extension AST.Table: Compilable { public typealias C = AST.Table public static func Compile( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Result { let table_declaration_node = node #RequireNodeType( node: table_declaration_node, type: "table_declaration", nice_type_name: "Table Declaration") var walker = Walker(node: table_declaration_node) var current_node: Node? = .none walker.next() // Skip the XXX? #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing table declaration component") )) guard case .Ok(let table_name) = AST.Identifier.CompileExpression( node: current_node!, withContext: context) else { return Result.Error( Error(withMessage: "Could not parse a table name from \(current_node!.text!)")) } walker.next() // Skip the '{' walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing table declaration component") )) let maybe_table_property_list = AST.TablePropertyList.Compile( node: current_node!, withContext: context) guard case .Ok(let table_property_list) = maybe_table_property_list else { return Result.Error(maybe_table_property_list.error()!) } return .Ok( AST.Table(withName: table_name as! AST.Identifier, withPropertyList: table_property_list)) } } extension AST.ExternDeclaration: Compilable { public typealias C = AST.AnDeclaration public static func Compile( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Result { let extern_declaration_node = node #RequireNodeType( node: extern_declaration_node, type: "extern_declaration", nice_type_name: "Extern Declaration") let declaration_node = extern_declaration_node.child(at: 1)! #RequireNodeType( node: declaration_node, type: "declaration", nice_type_name: "Declaration") let declarationed_node = declaration_node.child(at: 0)! let maybe_declared = AST.Declaration.Compile( node: declarationed_node, withContext: context.update(withExtern: true)) guard case .Ok(let declared) = maybe_declared else { return .Error(maybe_declared.error()!) } let extern_declaration = AST.ExternDeclaration(declared) return .Ok( extern_declaration, ) } } func parameter_list_compiler( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Common.Result { var walker = Walker(node: node) var current_node: Node? = .none if node.text == ")" { // There are no parameters! return Result.Ok(AST.ParameterList([])) } #RequireNodeType( node: node, type: "parameter_list", nice_type_name: "Parameter List") var parameters: AST.ParameterList = AST.ParameterList([]) #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing parameter list component"))) 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) } walker.next() } #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing parameter list component"))) // If this is a ')', we are done. if current_node?.text == ")" { return Result.Ok(parameters) } // If this is a comma, we skip it! if current_node?.text == "," { walker.next() } #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing parameter list component"))) // Otherwise, there should be one parameter left! switch AST.Parameter.Compile(node: current_node!, withContext: context) { case .Ok(let parsed_parameter): return Result.Ok(parameters.addParameter(parsed_parameter)) case .Error(let e): return Result.Error(e) } } extension AST.ParameterList: Compilable { public typealias C = AST.ParameterList public static func Compile( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Result { let parameter_node = node #RequireNodeType( node: parameter_node, type: "parameters", nice_type_name: "Parameters") var walker = Walker(node: parameter_node) var current_node: Node? = .none #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing '(' in parameter list component"))) walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing parameter list component"))) return parameter_list_compiler(node: current_node!, withContext: context) } } extension Direction: Compilable { public typealias C = Direction public static func Compile( node: Node, withContext context: ASTCompilerContext ) -> Result { let direction_node = node #RequireNodeType( node: direction_node, type: "direction", nice_type_name: "direction") let directions = [ "in": Direction.In, "out": Direction.Out, "inout": Direction.InOut, ] guard let parsed_direction = directions[direction_node.text!] else { return .Error( ErrorWithLocation( sourceLocation: direction_node.toSourceLocation(), withError: "\(direction_node.text!) is not a valid direction")) } return .Ok(parsed_direction) } } extension AST.Parameter: Compilable { public typealias C = AST.Parameter public static func Compile( node: Node, withContext context: ASTCompilerContext ) -> Result { #RequireNodeType( node: node, type: "parameter", nice_type_name: "parameter") var walker = Walker(node: node) var current_node: Node? = .none #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing parameter declaration component"))) // Annotation? if current_node!.nodeType == "annotations" { return .Error( ErrorWithLocation( sourceLocation: current_node!.toSourceLocation(), withError: "Annotations in parameter declarations are not yet handled")) // Will increment indexes here. } #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing parameter declaration component"))) var direction: Direction? = .none // Direction? if current_node!.nodeType == "direction" { 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 walker.next() } #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing parameter declaration component"))) if current_node!.nodeType != "typeRef" { return Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Did not find type name for parameter declaration")) } guard case .Ok(let parameter_type) = AST.Types.CompileType( type: current_node!, withContext: context) else { return Result.Error( Error(withMessage: "Could not parse a P4 type from \(current_node!.text!)")) } walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing parameter declaration component"))) if current_node!.nodeType != "identifier" { return Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Did not find identifier for parameter statement")) } guard case .Ok(let parameter_name) = AST.Identifier.CompileExpression( node: current_node!, withContext: context) else { return Result.Error( Error(withMessage: "Could not parse a parameter name from \(current_node!.text!)")) } let qualified_parameter_type = direction != nil ? parameter_type.tipe.update(addAttribute: P4TypeQualifier.Direction(direction!)) : parameter_type.tipe return Result.Ok( AST.Parameter( identifier: parameter_name as! AST.Identifier, withType: AST.Tipe(qualified_parameter_type))) } } func argument_list_compiler( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Common.Result { var walker = Walker(node: node) var current_node: Node? = .none if node.text == ")" { // There are no arguments! return Result.Ok(AST.ArgumentList([])) } #RequireNodeType( node: node, type: "argument_list", nice_type_name: "argument List") var arguments: AST.ArgumentList = AST.ArgumentList([]) #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing argument list component"))) 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) } walker.next() } // We may have moved nodes, check/reset current_node. #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing argument list component"))) // If this is a ')', we are done. if current_node?.text == ")" { return Result.Ok(arguments) } // If this is a comma, we skip it! if current_node?.text == "," { walker.next() } #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing argument list component"))) // Otherwise, there should be one argument left! switch AST.Argument.Compile(node: current_node!, withContext: context) { case .Ok(let ce): return Result.Ok( arguments.addArgument(AST.Argument(ce, atIndex: arguments.arguments.count + 1))) case .Error(let e): return Result.Error(e) } } extension AST.ArgumentList: Compilable { public typealias C = AST.ArgumentList public static func Compile( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Common.Result { let argument_node = node #RequireNodeType( node: argument_node, type: "arguments", nice_type_name: "arguments") var walker = Walker(node: argument_node) var current_node: Node? = .none #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing '(' in argument list component"))) walker.next() #MustOr( result: current_node, thing: walker.getNext(), or: Result.Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "Missing argument list component"))) return argument_list_compiler(node: current_node!, withContext: context) } } extension AST.Argument: Compilable { public typealias C = AST.AnExpression public static func Compile( node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext ) -> Common.Result { let argument_node = node #RequireNodeType( node: argument_node, type: "argument", nice_type_name: "argument") let expression_node = node.child(at: 0)! return switch AST.Expression.Compile(node: expression_node, withContext: context) { case .Ok(let compiled_expression): .Ok(compiled_expression) case .Error(let e): .Error(e) } } }