// 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 CST.Declaration: Parsable { public typealias C = CST.AnDeclaration public static func Parse( node: Node, withContext context: CSTCompilerContext ) -> 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 Parsable.Type] = [ "function_declaration": CST.FunctionDeclaration.self, "control_declaration": CST.Control.self, //"type_declaration": AST.P4Struct.self, "parserDeclaration": CST.Parser.self, /// ASSUME: Type declarations are struct declarations. "extern_declaration": CST.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.Parse(node: declaration_node, withContext: context).map { result in .Ok(result) } } } @deriveParsableStatement extension CST.Declaration: ParsableStatement {} extension CST.FunctionDeclaration: Parsable { public typealias C = CST.AnDeclaration public static func Parse( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> 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 = CST.Types.ParseType(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 = CST.Identifier.ParseExpression( 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 = CST.ParameterList.Parse( node: current_node!, withContext: context) guard case .Ok(let function_parameters) = maybe_function_parameters else { return .Error(maybe_function_parameters.error()!) } var function_body: CST.BlockStatement? = .none walker.next() if let body = walker.getNext() { let maybe_function_body = CST.BlockStatement.Parse(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 = CST.FunctionDeclaration( named: function_name as! CST.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 CST.StructDeclaration: Parsable { public typealias C = CST.StructDeclaration static public func Parse( node: Node, withContext context: CSTCompilerContext ) -> 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 = CST.Identifier.ParseExpression( 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 = CST.StructDeclaration(struct_identifier as! CST.Identifier, []) return Result.Ok(struc) } var parse_errs: (any Errorable)? = .none var parsed_fields: [CST.VariableDeclarationStatement] = Array() if currentNode!.nodeType == "struct_declaration_fields" { currentNode!.enumerateNamedChildren { declaration_field in switch CST.VariableDeclarationStatement.Parse( 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 = CST.StructDeclaration(struct_identifier as! CST.Identifier, parsed_fields) return .Ok(declared_struct) } } extension CST.Parser: Parsable { public typealias C = CST.AnDeclaration public static func Parse( node: Node, withContext context: CSTCompilerContext ) -> 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: CST.Identifier? = .none /// TODO: Handle parser parameter lists. var parameter_list = CST.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 CST.Identifier.ParseExpression(node: type_node_child!, withContext: context) { case .Ok(let id): parser_name = (id as! CST.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 CST.ParameterList.Parse(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")) } var parser = CST.Parser(withName: parser_name!, withParameters: parameter_list) // Build a state from each one listed. var errors: (any Errorable)? = .none /// TODO: Assert that there is only one. current_node!.enumerateNamedChildren { parser_state in if parser_state.nodeType != "parserState" { return } // Parse a state in a nested scope. switch CST.ParserState.Parse( node: parser_state, withContext: context) { case Result.Ok(let state): // All states are instances inside the parser. parser.states = parser.states.append(state: state) case Result.Error(let e): errors = if let errors = errors { errors.append(error: e) } else { e } } } if let errors = errors { return .Error(errors) } return .Ok(parser) } } extension CST.Control: Parsable { public typealias C = CST.AnDeclaration public static func Parse( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> 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) = CST.Identifier.ParseExpression( 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 = CST.ParameterList.Parse( 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: [CST.Action] = Array() var tables: [CST.Table] = Array() var apply: CST.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 = CST.Action.Parse( 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 = CST.Table.Parse( 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 = CST.ApplyStatement.Parse( 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( CST.Control( named: control_name as! CST.Identifier, withParameters: control_parameters, withTable: tables[0], withActions: CST.Actions(withActions: actions), withApply: apply)) } } extension CST.Action: Parsable { public typealias C = CST.Action public static func Parse( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> 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) = CST.Identifier.ParseExpression( 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 = CST.ParameterList.Parse( 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 = CST.BlockStatement.Parse( 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( CST.Action( named: action_name as! CST.Identifier, withParameters: action_parameters, withBody: action_body)) } } extension CST.TableKeyEntry: Parsable { public typealias C = CST.TableKeyEntry public static func Parse( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> 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 = CST.KeysetExpression.ParseExpression( 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 = CST.TableKeyMatchType.Parse( node: current_node!, withContext: context) guard case .Ok(let match_type) = maybe_match_type else { return .Error(maybe_match_type.error()!) } return .Ok(CST.TableKeyEntry(keyset_expression as! CST.KeysetExpression, match_type)) } } extension CST.TableKeyMatchType: Parsable { public typealias C = CST.TableKeyMatchType public static func Parse( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> Result { #RequireNodeType( node: node, type: "table_key_match_type", nice_type_name: "Table Key Match Type") if node.text! == "exact" { return .Ok(CST.TableKeyMatchType.Exact) } return .Error( ErrorWithLocation( sourceLocation: node.toSourceLocation(), withError: "\(node.text!) is not a valid match type)")) } } extension CST.TableKeys: Parsable { public typealias C = CST.TableKeys public static func Parse( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> 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 CST.TableKeyEntry.Parse(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(CST.TableKeys(withEntries: keys)) } } extension CST.TableActionsProperty: Parsable { public typealias C = CST.TableActionsProperty public static func Parse( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> 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 CST.Identifier.ParseExpression(node: current_node, withContext: context) { case .Ok(let listed_action): return .Ok(listed_action as! CST.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(CST.TableActionsProperty(actions)) } } extension CST.TablePropertyList: Parsable { public typealias C = CST.TablePropertyList public static func Parse( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> Result { #RequireNodeType( node: node, type: "table_property_list", nice_type_name: "Table Property List") var keys: [CST.TableKeys] = Array() var actions: [CST.TableActionsProperty] = Array() var errors: [any Errorable] = Array() node.enumerateNamedChildren { child in if child.nodeType == "table_keys" { switch CST.TableKeys.Parse(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 CST.TableActionsProperty.Parse(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(CST.TableActionsProperty()) } return .Ok(CST.TablePropertyList(withActions: actions[0], withKeys: keys[0])) } } extension CST.Table: Parsable { public typealias C = CST.Table public static func Parse( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> 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) = CST.Identifier.ParseExpression( 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 = CST.TablePropertyList.Parse( 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( CST.Table(withName: table_name as! CST.Identifier, withPropertyList: table_property_list)) } } extension CST.ExternDeclaration: Parsable { public typealias C = CST.AnDeclaration public static func Parse( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> 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 = CST.Declaration.Parse( node: declarationed_node, withContext: context.update(withExtern: true)) guard case .Ok(let declared) = maybe_declared else { return .Error(maybe_declared.error()!) } let extern_declaration = CST.ExternDeclaration(declared) return .Ok( extern_declaration, ) } } func parameter_list_compiler( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> Common.Result { var walker = Walker(node: node) var current_node: Node? = .none if node.text == ")" { // There are no parameters! return Result.Ok(CST.ParameterList([])) } #RequireNodeType( node: node, type: "parameter_list", nice_type_name: "Parameter List") var parameters: CST.ParameterList = CST.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 CST.Parameter.Parse(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 CST.ParameterList: Parsable { public typealias C = CST.ParameterList public static func Parse( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> 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: Parsable { public typealias C = Direction public static func Parse( node: Node, withContext context: CSTCompilerContext ) -> 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 CST.Parameter: Parsable { public typealias C = CST.Parameter public static func Parse( node: Node, withContext context: CSTCompilerContext ) -> 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.Parse(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) = CST.Types.ParseType( 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) = CST.Identifier.ParseExpression( 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( CST.Parameter( identifier: parameter_name as! CST.Identifier, withType: CST.Tipe(qualified_parameter_type))) } } func argument_list_compiler( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> Common.Result { var walker = Walker(node: node) var current_node: Node? = .none if node.text == ")" { // There are no arguments! return Result.Ok(CST.ArgumentList([])) } #RequireNodeType( node: node, type: "argument_list", nice_type_name: "argument List") var arguments: CST.ArgumentList = CST.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 CST.Argument.Parse(node: current_node!, withContext: context) { case .Ok(let ce): return Result.Ok( arguments.addArgument(CST.Argument(ce, atIndex: arguments.arguments.count + 1))) case .Error(let e): return Result.Error(e) } } extension CST.ArgumentList: Parsable { public typealias C = CST.ArgumentList public static func Parse( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> 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 CST.Argument: Parsable { public typealias C = CST.AnExpression public static func Parse( node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext ) -> 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 CST.Expression.Parse(node: expression_node, withContext: context) { case .Ok(let compiled_expression): .Ok(compiled_expression) case .Error(let e): .Error(e) } } }