diff --git a/Sources/P4Compiler/Declarations.swift b/Sources/P4Compiler/Declarations.swift index f0a8e11..9540ba4 100644 --- a/Sources/P4Compiler/Declarations.swift +++ b/Sources/P4Compiler/Declarations.swift @@ -459,6 +459,10 @@ extension Control: CompilableDeclaration { } actions.append(action_declaration) local_context = updated_context + // Now, add the declaration into the context. + local_context = local_context.update( + newTypes: local_context.types.declare( + identifier: action_declaration.name, withValue: action_declaration)) } else if current_node.nodeType == "table_declaration" { let maybe_table_declaration = Table.Compile( node: current_node, withContext: local_context) @@ -721,6 +725,58 @@ extension TableKeys: Compilable { } } +extension TableActionsProperty: Compilable { + public typealias T = TableActionsProperty + public static func Compile( + node: SwiftTreeSitter.Node, withContext context: CompilerContext + ) -> Common.Result<(TableActionsProperty, CompilerContext)> { + #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<(TableActionsProperty, CompilerContext)>.Error( + ErrorOnNode( + node: node, 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 Identifier.Compile(node: current_node, withContext: context) { + case .Ok(let listed_action): + switch context.types.lookup(identifier: listed_action) { + case .Ok(let maybe_action): + if maybe_action.eq(rhs: Action()) { + return .Ok(TypedIdentifier(id: listed_action, withType: P4Type(maybe_action))) + } + return .Error( + ErrorOnNode(node: node, withError: "\(listed_action) does not name an action")) + case .Error(let e): return .Error(e) + } + case .Error(let e): return .Error(e) + } + } + + if !errors.isEmpty { + return .Error( + ErrorOnNode(node: node, withError: "Error(s) parsing table actions: " + + (errors.map { error in + return "\(error.msg)" + }.joined(separator: ";")))) + } + + return .Ok((TableActionsProperty(actions), context)) + } +} extension TablePropertyList: Compilable { public typealias T = TablePropertyList public static func Compile( @@ -733,7 +789,7 @@ extension TablePropertyList: Compilable { var current_context = context var keys: [TableKeys] = Array() - var _: [Action] = Array() // Actions are not yet supported + var actions: [TableActionsProperty] = Array() var errors: [Error] = Array() node.enumerateNamedChildren { child in @@ -745,9 +801,12 @@ extension TablePropertyList: Compilable { case .Error(let e): errors.append(e) } } else if child.nodeType == "table_actions" { - errors.append( - ErrorOnNode( - node: child, withError: "Actions in table property lists are not yet supported")) + switch TableActionsProperty.Compile(node: child, withContext: current_context) { + case .Ok((let table_action_property, let updated_context)): + current_context = updated_context + actions.append(table_action_property) + case .Error(let e): errors.append(e) + } } else { errors.append( ErrorOnNode(node: child, withError: "Uknown node type in control declaration")) @@ -756,8 +815,7 @@ extension TablePropertyList: Compilable { if !errors.isEmpty { return .Error( - Error( - withMessage: "Error(s) parsing property list: " + ErrorOnNode(node: node, withError: "Error(s) parsing property list: " + (errors.map { error in return "\(error.msg)" }.joined(separator: ";")))) @@ -770,7 +828,17 @@ extension TablePropertyList: Compilable { ErrorOnNode(node: node, withError: "More than one key set in table property list")) } - return .Ok((TablePropertyList(withActions: TableActions(), withKeys: keys[0]), current_context)) + // There should be only one table keys! + if actions.count > 1 { + // Todo: Make this error message better. + return .Error( + ErrorOnNode(node: node, withError: "More than one actions in table property list")) + } + if actions.isEmpty { + actions.append(TableActionsProperty()) + } + + return .Ok((TablePropertyList(withActions: actions[0], withKeys: keys[0]), current_context)) } } diff --git a/Sources/P4Compiler/Walker.swift b/Sources/P4Compiler/Walker.swift index e36dc3d..a9cf8f8 100644 --- a/Sources/P4Compiler/Walker.swift +++ b/Sources/P4Compiler/Walker.swift @@ -51,4 +51,21 @@ public struct Walker { } return Result.Ok(()) } + + public func try_map(n: Int, onlyNamed: Bool = false, todo: (Node) -> Result) -> ([T], [Error]) { + var errors: [Error] = Array() + var results: [T] = Array() + + for currentChildIdx in currentChildIdx.. any Common.P4DataType { + return self + } + + public func eq(rhs: any Common.P4DataValue) -> Bool { + return switch rhs { + case let arhs as Action: self.name == arhs.name + default: false + } + } + + public func eq(rhs: any Common.P4DataType) -> Bool { + return switch rhs { + case is Action: true + default: false + } + } + public func lt(rhs: any Common.P4DataValue) -> Bool { + switch rhs { + case let arhs as Action: return self.name < arhs.name + default: return false + } + } + + public func lte(rhs: any Common.P4DataValue) -> Bool { + switch rhs { + case let arhs as Action: return self.name <= arhs.name + default: return false + } + + } + + public func gt(rhs: any Common.P4DataValue) -> Bool { + switch rhs { + case let arhs as Action: return self.name > arhs.name + default: return false + } + } + + public func gte(rhs: any Common.P4DataValue) -> Bool { + switch rhs { + case let arhs as Action: return self.name >= arhs.name + default: return false + } + } + + public func def() -> any Common.P4DataValue { + return Action() + } + public var description: String { return "Action: " + "\(self.name) with parameters \(self.params) and body \(String(describing: self.body))" @@ -28,8 +78,8 @@ public struct Action: CustomStringConvertible { public var name: Identifier public init( - named name: Identifier, withParameters parameters: ParameterList, - withBody body: BlockStatement? + named name: Identifier = Identifier(name: ""), withParameters parameters: ParameterList = ParameterList([]), + withBody body: BlockStatement? = .none ) { self.name = name self.params = parameters @@ -88,15 +138,24 @@ public struct TableKeys: CustomStringConvertible { } } -/// TODO -public struct TableActions { - public init() {} +public struct TableActionsProperty: CustomStringConvertible { + public let actions: [TypedIdentifier] + public init(_ actions: [TypedIdentifier] = []) { + self.actions = actions + } + + public var description: String { + return "Table Actions: " + + self.actions.map { action in + return action.description + }.joined(separator: ";") + } } public struct TablePropertyList: CustomStringConvertible { - let actions: TableActions + let actions: TableActionsProperty let keys: TableKeys - public init(withActions actions: TableActions, withKeys keys: TableKeys) { + public init(withActions actions: TableActionsProperty, withKeys keys: TableKeys) { self.actions = actions self.keys = keys } @@ -208,7 +267,8 @@ public struct Control: P4DataType, P4DataValue, Equatable, CustomStringConvertib withParameters: ParameterList(), withTable: Table( withName: Identifier(name: "empty"), - withPropertyList: TablePropertyList(withActions: TableActions(), withKeys: TableKeys())), + withPropertyList: TablePropertyList( + withActions: TableActionsProperty(), withKeys: TableKeys())), withActions: Actions(withActions: []), withApply: ApplyStatement()) } diff --git a/Tests/p4rseTests/ControlCompilerTests.swift b/Tests/p4rseTests/ControlCompilerTests.swift index 67e29e7..140d3be 100644 --- a/Tests/p4rseTests/ControlCompilerTests.swift +++ b/Tests/p4rseTests/ControlCompilerTests.swift @@ -89,6 +89,113 @@ import P4Lang #expect(program.InstancesWithTypes(filter).count == 2) } +@Test func test_simple_control_declaration_with_actions() async throws { + let simple_parser_declaration = """ + control simple() { + action a() { + } + table t { + key = { + true: exact; + } + actions = { + a; + } + } + apply { + } + }; + """ + let x = { (tipe: P4Type) -> Bool in + switch tipe.dataType() { + case let c as Control: c.name == "simple" + default: false + } + } + let program = try! #UseOkResult(Program.Compile(simple_parser_declaration)) + #expect(program.InstancesWithTypes(x).count == 1) +} + +@Test func test_simple_control_declaration_with_misnamed_actions() async throws { + let simple_parser_declaration = """ + control simple() { + action a() { + } + table t { + key = { + true: exact; + } + actions = { + b; + } + } + apply { + } + }; + """ + #expect( + #RequireErrorResult( + Error( + withMessage: "{54, 63}: Error(s) parsing property list: {91, 26}: Error(s) parsing table actions: Cannot find b in lexical scope." + ), + Program.Compile(simple_parser_declaration)) + ) +} + +@Test func test_simple_control_declaration_with_misnamed_actions2() async throws { + let simple_parser_declaration = """ + control simple() { + action a() { + } + table t { + key = { + true: exact; + } + actions = { + a; + b; + } + } + apply { + } + }; + """ + #expect( + #RequireErrorResult( + Error( + withMessage: "{54, 72}: Error(s) parsing property list: {91, 35}: Error(s) parsing table actions: Cannot find b in lexical scope." + ), + Program.Compile(simple_parser_declaration)) + ) +} + +@Test func test_simple_control_declaration_with_mistyped_actions() async throws { + let simple_parser_declaration = """ + bool a() { + return true; + }; + control simple() { + table t { + key = { + true: exact; + } + actions = { + a; + } + } + apply { + } + }; + """ + #expect( + #RequireErrorResult( + Error( + withMessage: "{64, 63}: Error(s) parsing property list: {101, 26}: Error(s) parsing table actions: {101, 26}: a does not name an action" + ), + Program.Compile(simple_parser_declaration)) + ) +} + @Test func test_simple_control_declaration_with_parameters() async throws { let simple_parser_declaration = """ control simple(bool x, bool y) { diff --git a/tree-sitter-p4/grammar.js b/tree-sitter-p4/grammar.js index c1df9ac..5cf9778 100644 --- a/tree-sitter-p4/grammar.js +++ b/tree-sitter-p4/grammar.js @@ -94,7 +94,7 @@ export default grammar({ table_property_list: $ => repeat1(choice($.table_keys, $.table_actions)), table_keys: $=> seq($.key, '=', '{', repeat($.table_key_entry), '}'), table_key_entry: $=> seq($.keysetExpression, ':', $.table_key_match_type, $._semicolon), - table_actions: $=> seq($.actions, '=', '{', optional(repeat1($.identifier)), '}'), + table_actions: $=> seq($.actions, '=', '{', optional(repeat1(seq($.identifier, $._semicolon))), '}'), // match types table_key_match_type: $ => choice($.exact), // support exact only for now. diff --git a/tree-sitter-p4/test/corpus/control.txt b/tree-sitter-p4/test/corpus/control.txt index 04c5075..777845b 100644 --- a/tree-sitter-p4/test/corpus/control.txt +++ b/tree-sitter-p4/test/corpus/control.txt @@ -54,4 +54,77 @@ control simple() { ) ) ) +========================= +Simple Control Declaration With Actions +========================= +control simple() { + action a() { + } + action b() { + } + table t { + key = { + x: exact; + } + actions = { + a; + b; + } + } + apply { + } +}; + +--- +(p4program + (declaration + (control_declaration + (control) + (identifier) + (parameters) + (action_declaration + (action) + (identifier) + (parameters) + (blockStatement) + ) + (action_declaration + (action) + (identifier) + (parameters) + (blockStatement) + ) + (table_declaration + (table) + (identifier) + (table_property_list + (table_keys + (key) + (table_key_entry + (keysetExpression + (expression + (simple_expression + (identifier) + ) + ) + ) + (table_key_match_type + (exact) + ) + ) + ) + (table_actions + (actions) + (identifier) + (identifier) + ) + ) + ) + (apply_statement + (apply) + (blockStatement) + ) + ) + ) +)