From cd26d1d22c86b18bdae7a14f7466c03d5a46a228 Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Fri, 27 Mar 2026 03:33:15 -0400 Subject: [PATCH] Parse Struct Declarations Signed-off-by: Will Hawkins --- Sources/P4Compiler/Compiler.swift | 16 +- Sources/P4Compiler/Declarations.swift | 262 ++++++++++++++++++++ Sources/P4Compiler/Expression.swift | 2 +- Sources/P4Compiler/Parser.swift | 3 +- Sources/P4Compiler/Program.swift | 154 +++--------- Sources/P4Compiler/Protocols.swift | 6 + Sources/P4Compiler/Statement.swift | 4 +- Sources/P4Compiler/Types.swift | 4 + Sources/P4Lang/Declarations.swift | 20 ++ Sources/P4Lang/Program.swift | 5 +- Tests/p4rseTests/Declarations.swift | 53 ++++ Tests/p4rseTests/ParserCompilerTests.swift | 18 +- tree-sitter-p4/grammar.js | 6 +- tree-sitter-p4/test/corpus/declarations.txt | 28 +++ 14 files changed, 426 insertions(+), 155 deletions(-) create mode 100644 Sources/P4Compiler/Declarations.swift create mode 100644 Sources/P4Lang/Declarations.swift create mode 100644 Tests/p4rseTests/Declarations.swift diff --git a/Sources/P4Compiler/Compiler.swift b/Sources/P4Compiler/Compiler.swift index e5f2c68..0a6ea97 100644 --- a/Sources/P4Compiler/Compiler.swift +++ b/Sources/P4Compiler/Compiler.swift @@ -42,16 +42,16 @@ public func ErrorOnNode(node: Node, withError error: String) -> Error { /// Context for compilation. public struct CompilerContext { - let names: VarTypeScopes + let instances: VarTypeScopes let types: TypeTypeScopes - public init(withNames _names: VarTypeScopes) { - names = _names + public init(withInstances _instances: VarTypeScopes) { + instances = _instances types = TypeTypeScopes() } - public init(withNames _names: VarTypeScopes, withTypes _types: TypeTypeScopes) { - names = _names + public init(withInstances _instances: VarTypeScopes, withTypes _types: TypeTypeScopes) { + instances = _instances types = _types } @@ -61,8 +61,8 @@ public struct CompilerContext { /// /// - Parameter names: a ``TypeScopes`` with the updated names for the newly created compiler context. /// - Returns: A new compiler context based on the current with the same types and new names. - public func update(newNames names: VarTypeScopes) -> CompilerContext { - return CompilerContext(withNames: names, withTypes: self.types) + public func update(newInstances instances: VarTypeScopes) -> CompilerContext { + return CompilerContext(withInstances: instances, withTypes: self.types) } /// Update a compiler context @@ -72,7 +72,7 @@ public struct CompilerContext { /// - Parameter types: a ``TypeScopes`` with the updated types for the newly created compiler context. /// - Returns: A new compiler context based on the current with the same names and new types. public func update(newTypes types: TypeTypeScopes) -> CompilerContext { - return CompilerContext(withNames: self.names, withTypes: types) + return CompilerContext(withInstances: self.instances, withTypes: types) } } diff --git a/Sources/P4Compiler/Declarations.swift b/Sources/P4Compiler/Declarations.swift new file mode 100644 index 0000000..b4d8857 --- /dev/null +++ b/Sources/P4Compiler/Declarations.swift @@ -0,0 +1,262 @@ +// 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 P4Lang +import P4Runtime +import SwiftTreeSitter +import TreeSitterExtensions +import TreeSitterP4 + +extension Declaration: CompilableDeclaration { + public static func Compile( + node: Node, withContext context: CompilerContext + ) -> Result<(P4Type, CompilerContext)?> { + + guard let node_type = node.nodeType, + node_type == "type_declaration" + else { + return .Ok(.none) + } + + // Assume that it is a struct declaration + return StructDeclaration.Compile(node: node.child(at: 0)!, withContext: context) + } +} + +struct StructDeclaration { + static func Compile( + node: Node, withContext context: CompilerContext + ) -> Result<(P4Type, CompilerContext)?> { + + var currentChildIdx = 0 + var currentChildIdxSafe = 1 + + var currentChild: Node? = .none + + guard let node_type = node.nodeType, + node_type == "struct_declaration" + else { + return Result.Error( + ErrorOnNode(node: node, withError: "Did not find a struct declaration")) + } + + if node.childCount < currentChildIdxSafe { + return Result.Error( + ErrorOnNode(node: node, withError: "Missing elements in struct declaration")) + } + + // Skip the keyword struct + currentChildIdx += 1 + currentChildIdxSafe += 1 + if node.childCount < currentChildIdxSafe { + return Result.Error( + ErrorOnNode(node: node, withError: "Missing elements in struct declaration")) + } + + // The name of the struct type. + currentChild = node.child(at: currentChildIdx) + let maybe_struct_identifier = Identifier.Compile( + node: currentChild!, withContext: context) + guard case Result.Ok(let struct_identifier) = maybe_struct_identifier else { + return Result.Error(maybe_struct_identifier.error()!) + } + + currentChildIdx += 1 + currentChildIdxSafe += 1 + + // Skip the '{' + currentChildIdx += 1 + currentChildIdxSafe += 1 + if node.childCount < currentChildIdxSafe { + return Result.Error( + ErrorOnNode(node: node, withError: "Missing element of struct declaration")) + } + + currentChild = node.child(at: currentChildIdx) + + // If there are no fields, it will be a "}" + if currentChild!.nodeType == "}" { + let struc = P4Struct(withName: struct_identifier, andFields: P4StructFields([])) + return Result.Ok( + ( + struc, + context.update( + newTypes: context.types.declare(identifier: struct_identifier, withValue: struc)) + )) + } + + var parse_errs: [Error] = Array() + var current_context = context + var parsed_fields: [P4StructFieldIdentifier] = Array() + + if currentChild!.nodeType == "struct_declaration_fields" { + currentChild!.enumerateNamedChildren { declaration_field in + print("declaration field: \(declaration_field)") + switch VariableDeclarationStatement.Compile( + node: declaration_field, withContext: current_context) + { + case .Ok((let declaration, let updated_context)): + let variable_declaration = declaration as! VariableDeclarationStatement + parsed_fields.append( + P4StructFieldIdentifier( + id: variable_declaration.identifier, withType: variable_declaration.initializer.type() + )) + current_context = updated_context + case .Error(let e): parse_errs.append(e) + } + } + } + + if !parse_errs.isEmpty { + return .Error( + Error( + withMessage: "Error(s) parsing select cases: " + + (parse_errs.map { error in + return "\(error.msg)" + }.joined(separator: ";")))) + } + + let declared_struct = P4Struct( + withName: struct_identifier, andFields: P4StructFields(parsed_fields)) + return .Ok( + ( + declared_struct, + current_context.update( + newTypes: current_context.types.declare( + identifier: struct_identifier, withValue: declared_struct)) + )) + } +} + +extension P4Lang.Parser: CompilableDeclaration { + public static func Compile( + node: Node, withContext context: CompilerContext + ) -> Result<(P4Type, CompilerContext)?> { + + let parser_node = node + if parser_node.nodeType != "parserDeclaration" { + return .Ok(.none) + } + + var currentChildIdx = 0 + var currentChildIdxSafe = 1 + var currentChild: Node? = .none + + if parser_node.childCount < currentChildIdxSafe { + return .Error( + ErrorOnNode(node: parser_node, withError: "Missing elements of 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 + var parser_name: Common.Identifier? = .none + + do { + // Parse the parser type (type_node) + var currentChildIdx = 0 + var currentChildIdxSafe = 1 + + if type_node!.childCount < currentChildIdxSafe { + 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.")) + // 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) + + switch Identifier.Compile(node: currentChild!, withContext: context) { + case .Ok(let id): parser_name = id + case .Error(let e): + return .Error(e) + } + } + + currentChildIdx += 1 + currentChildIdxSafe += 1 + if parser_node.childCount < currentChildIdxSafe { + return .Error( + ErrorOnNode(node: parser_node, withError: "Missing elements of parser declaration")) + } + + if currentChild!.nodeType == "constructorParameters" { + return .Error( + ErrorOnNode(node: currentChild!, withError: "Constructor parameters are not yet handled.") + ) + // Will increment indexes here. + } + + // 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) + + if currentChild!.nodeType == "parserLocalElements" { + return .Error( + ErrorOnNode(node: currentChild!, 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"))) + } + + if currentChild!.nodeType != "parserStates" { + return .Error(Error(withMessage: "Missing parser states in parser declaration")) + } + + switch Parser.Compile( + withName: parser_name!, node: currentChild!, withContext: context) + { + case Result.Ok((let parser, let updated_context)): + // Create a new context with the name of the parser that was just compiled in scope. + return .Ok( + ( + parser, + context.update( + newInstances: updated_context.instances.declare( + identifier: parser.name, withValue: parser)) + )) + case Result.Error(let error): return .Error(error) + } + + // Assume that there is only '}' after -- the parser guaranteed that for us! + } +} diff --git a/Sources/P4Compiler/Expression.swift b/Sources/P4Compiler/Expression.swift index 262b23f..8b58d8d 100644 --- a/Sources/P4Compiler/Expression.swift +++ b/Sources/P4Compiler/Expression.swift @@ -43,7 +43,7 @@ extension TypedIdentifier: CompilableExpression { node: node, type: "identifier") guard - case Result.Ok(let type) = context.names.lookup( + case Result.Ok(let type) = context.instances.lookup( identifier: Common.Identifier(name: node.text!)) else { return .Error(ErrorOnNode(node: node, withError: "Cannot find \(node.text!) in scope")) diff --git a/Sources/P4Compiler/Parser.swift b/Sources/P4Compiler/Parser.swift index 510c9ce..6293f0e 100644 --- a/Sources/P4Compiler/Parser.swift +++ b/Sources/P4Compiler/Parser.swift @@ -284,7 +284,8 @@ public struct Parser { // Parse a state in a nested scope. switch Parser.State.Compile( - node: parser_state, withContext: context.update(newNames: current_context.names.enter())) + node: parser_state, + withContext: context.update(newInstances: current_context.instances.enter())) { case Result.Ok(let (state, updated_context)): parser.states = parser.states.append(state: state) diff --git a/Sources/P4Compiler/Program.swift b/Sources/P4Compiler/Program.swift index 5bbe881..7327fbc 100644 --- a/Sources/P4Compiler/Program.swift +++ b/Sources/P4Compiler/Program.swift @@ -54,13 +54,14 @@ public struct Program { var program = P4Lang.Program() // Set up a context for parsing. - var compilation_context = CompilerContext(withNames: VarTypeScopes().enter()) + var compilation_context = CompilerContext( + withInstances: VarTypeScopes().enter(), withTypes: TypeTypeScopes().enter()) var errors: [Error] = Array() // If the caller gave any global instances, add them here. if let globalInstances = globalInstances { - compilation_context = compilation_context.update(newNames: globalInstances) + compilation_context = compilation_context.update(newInstances: globalInstances) } // If the caller gave any global types, add them here. @@ -68,135 +69,36 @@ public struct Program { compilation_context = compilation_context.update(newTypes: globalTypes) } - result?.rootNode?.enumerateNamedChildren { declaration_node in - if declaration_node.nodeType != "declaration" { - return - } + // Try to parse all top-level declarations. + result?.rootNode?.enumerateNamedChildren { (declaration_node: Node) in + let specific_declaration_node = declaration_node.child(at: 0)! - let parser_node = declaration_node.child(at: 0)! - if parser_node.nodeType != "parserDeclaration" { - return - } + let declaration_parsers: [CompilableDeclaration.Type] = [ + Declaration.self, P4Lang.Parser.self, + ] + var found_parser = false - var currentChildIdx = 0 - var currentChildIdxSafe = 1 - var currentChild: Node? = .none - - if parser_node.childCount < currentChildIdxSafe { - errors.append( - ErrorOnNode(node: parser_node, withError: "Missing elements of parser declaration")) - return - } - currentChild = parser_node.child(at: currentChildIdx) - if currentChild!.nodeType != "parserType" { - errors.append( - ErrorOnNode(node: currentChild!, withError: "Missing type for parser declaration")) - return - } - - let type_node = currentChild - var parser_name: Common.Identifier? = .none - - do { - // Parse the parser type (type_node) - var currentChildIdx = 0 - var currentChildIdxSafe = 1 - - if type_node!.childCount < currentChildIdxSafe { - errors.append( - ErrorOnNode( - node: parser_node, withError: "Missing elements of parser type in parser declaration") - ) - return - } - - var currentChild = type_node!.child(at: currentChildIdx) - if currentChild!.nodeType == "annotations" { - errors.append( - ErrorOnNode( - node: currentChild!, withError: "Annotations in parser type are not yet handled.")) - return - - // Will increment indexes here. - } - - // Skip the parser keyword - currentChildIdx += 1 - currentChildIdxSafe += 1 - if type_node!.childCount < currentChildIdxSafe { - errors.append( - ErrorOnNode(node: type_node!, withError: "Missing name in parser type declaration")) - return - } - currentChild = type_node?.child(at: currentChildIdx) - - switch Identifier.Compile(node: currentChild!, withContext: compilation_context) { - case .Ok(let id): parser_name = id + for parser in declaration_parsers { + switch parser.Compile(node: specific_declaration_node, withContext: compilation_context) { + case .Ok(.none): {}() + case .Ok(.some((_, let updated_context))): + found_parser = true + compilation_context = updated_context + break case .Error(let e): + found_parser = true errors.append(e) - return + break } } - // It's an error if there is no parser name. - if parser_name == .none { - return - } - - currentChildIdx += 1 - currentChildIdxSafe += 1 - if parser_node.childCount < currentChildIdxSafe { + // If none of the declaration parsers chose to parse, that's an error, too! + if !found_parser { errors.append( - ErrorOnNode(node: parser_node, withError: "Constructor parameters are not yet handled.")) - return + ErrorOnNode( + node: specific_declaration_node, withError: "Could not find parser for declaration node" + )) } - - if currentChild!.nodeType == "constructorParameters" { - errors.append( - ErrorOnNode(node: currentChild!, withError: "Constructor parameters are not yet handled.") - ) - return - - // Will increment indexes here. - } - - // Skip the '{' - currentChildIdx += 1 - currentChildIdxSafe += 1 - if parser_node.childCount < currentChildIdxSafe { - errors.append(Error(withMessage: "Missing body of parser declaration")) - return - } - currentChild = parser_node.child(at: currentChildIdx) - - if currentChild!.nodeType == "parserLocalElements" { - errors.append( - ErrorOnNode(node: currentChild!, withError: "Parser Local Elements are not yet handled.")) - return - // Will increment indexes here. - } - - if parser_node.childCount < currentChildIdxSafe { - errors.append(Error(withMessage: "Missing body of parser declaration")) - return - } - - if currentChild!.nodeType != "parserStates" { - errors.append(Error(withMessage: "Missing parser states in parser declaration")) - return - } - - switch Parser.Compile( - withName: parser_name!, node: currentChild!, withContext: compilation_context) - { - case Result.Ok((let parser, let updated_context)): - // Create a new context with the name of the parser that was just compiled in scope. - compilation_context = compilation_context.update( - newNames: updated_context.names.declare(identifier: parser.name, withValue: parser)) - case Result.Error(let error): errors.append(error) - } - - // Assume that there is only '}' after -- the parser guaranteed that for us! } if !errors.isEmpty { @@ -207,9 +109,15 @@ public struct Program { }.joined(separator: ";"))) } + // Any of the instances that are in the top-level scope should go into the program! + program.instances = Array( + compilation_context.instances.map { (_, v) in + v + }) + // Any of the types that are in the top-level scope should go into the program! program.types = Array( - compilation_context.names.map { (_, v) in + compilation_context.types.map { (_, v) in v }) return Result.Ok(program) diff --git a/Sources/P4Compiler/Protocols.swift b/Sources/P4Compiler/Protocols.swift index ee3b562..60d725f 100644 --- a/Sources/P4Compiler/Protocols.swift +++ b/Sources/P4Compiler/Protocols.swift @@ -37,3 +37,9 @@ public protocol CompilableType { type: SwiftTreeSitter.Node, withContext: CompilerContext ) -> Result } + +public protocol CompilableDeclaration { + static func Compile( + node: Node, withContext context: CompilerContext + ) -> Result<(P4Type, CompilerContext)?> +} diff --git a/Sources/P4Compiler/Statement.swift b/Sources/P4Compiler/Statement.swift index 42e0328..fa60e72 100644 --- a/Sources/P4Compiler/Statement.swift +++ b/Sources/P4Compiler/Statement.swift @@ -230,7 +230,7 @@ extension VariableDeclarationStatement: CompilableStatement { identifier: parsed_variablename, withInitializer: initializer), // Context with updated names to include the newly declared name. context.update( - newNames: context.names.declare( + newInstances: context.instances.declare( identifier: parsed_variablename, withValue: declaration_p4_type)) )) } @@ -282,7 +282,7 @@ extension ParserAssignmentStatement: CompilableStatement { return Result.Error(maybe_parsed_lvalue.error()!) } - let check_result = lvalue_identifier.check(to: rvalue, inScopes: context.names) + let check_result = lvalue_identifier.check(to: rvalue, inScopes: context.instances) guard case .Ok(_) = check_result else { return Result.Error( ErrorOnNode( diff --git a/Sources/P4Compiler/Types.swift b/Sources/P4Compiler/Types.swift index 904312e..fa5540d 100644 --- a/Sources/P4Compiler/Types.swift +++ b/Sources/P4Compiler/Types.swift @@ -50,10 +50,14 @@ extension P4Struct: CompilableType { public static func CompileType( type: SwiftTreeSitter.Node, withContext context: CompilerContext ) -> Common.Result<(any Common.P4Type)?> { + let maybe_parsed_type_id = Identifier.Compile(node: type, withContext: context) guard case .Ok(let parsed_type_id) = maybe_parsed_type_id else { return .Error(maybe_parsed_type_id.error()!) } + + print("Looking up \(parsed_type_id) in \(context.types)") + if case .Ok(let found_type) = context.types.lookup(identifier: parsed_type_id), let found_struct_type = found_type as? P4Struct { diff --git a/Sources/P4Lang/Declarations.swift b/Sources/P4Lang/Declarations.swift new file mode 100644 index 0000000..4562d5f --- /dev/null +++ b/Sources/P4Lang/Declarations.swift @@ -0,0 +1,20 @@ +// 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 + +public struct Declaration {} diff --git a/Sources/P4Lang/Program.swift b/Sources/P4Lang/Program.swift index 600640a..a2450ae 100644 --- a/Sources/P4Lang/Program.swift +++ b/Sources/P4Lang/Program.swift @@ -23,6 +23,7 @@ public struct ExpressionStatement { public struct Program { public var types: [P4Type] = Array() + public var instances: [P4Type] = Array() /// Find the program's main parser /// @@ -32,8 +33,8 @@ public struct Program { } public func find_parser(withName name: Identifier) -> Result { - for type in self.types { - guard let parser = type as? Parser else { + for instances in self.instances { + guard let parser = instances as? Parser else { continue } if parser.name == name { diff --git a/Tests/p4rseTests/Declarations.swift b/Tests/p4rseTests/Declarations.swift new file mode 100644 index 0000000..08c71d1 --- /dev/null +++ b/Tests/p4rseTests/Declarations.swift @@ -0,0 +1,53 @@ +// 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 Foundation +import Macros +import P4Runtime +import P4Lang +import SwiftTreeSitter +import Testing +import TreeSitter +import TreeSitterP4 + +@testable import P4Compiler + +@Test func test_struct_declaration_and_field_write() async throws { + let simple_parser_declaration = """ + struct Testing { + bool yesno; + int count; + }; + parser main_parser() { + state start { + Testing ts; + ts.yesno = true; + bool where_to = ts.yesno; + transition select (where_to) { + true: accept; + false: reject; + }; + } + }; + """ + let program = try #UseOkResult( + Program.Compile(simple_parser_declaration)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) + let (state_result, _) = try! #UseOkResult(runtime.run()) + #expect(state_result == P4Lang.accept) +} \ No newline at end of file diff --git a/Tests/p4rseTests/ParserCompilerTests.swift b/Tests/p4rseTests/ParserCompilerTests.swift index de86ec5..0b59392 100644 --- a/Tests/p4rseTests/ParserCompilerTests.swift +++ b/Tests/p4rseTests/ParserCompilerTests.swift @@ -61,22 +61,6 @@ import P4Lang #expect(state.statements.count == 1) } -@Test func test_simple_compilation_with_instantiation() async throws { - let simple_parser_declaration = """ - parser main_parser() { - state start { - true; - false; - transition start; - } - }; - bool() main; - """ - - let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) - #expect(#RequireOkResult(program.find_parser(withName: Identifier(name: "main_parser")))) -} - @Test func test_invalid_transition_expression_keyset_expressions() async throws { let simple_parser_declaration = """ parser main_parser() { @@ -114,5 +98,5 @@ import P4Lang #RequireErrorResult<(EvaluatableStatement, CompilerContext)>( Error(withMessage: "{2, 154}: Did not find assignment statement"), ParserAssignmentStatement.Compile( // Note: Calling ParserAssignmentStatement compilation directly. - node: result.rootNode!, withContext: CompilerContext(withNames: VarTypeScopes())))) + node: result.rootNode!, withContext: CompilerContext(withInstances: VarTypeScopes())))) } diff --git a/tree-sitter-p4/grammar.js b/tree-sitter-p4/grammar.js index 3c6b363..0a061e9 100644 --- a/tree-sitter-p4/grammar.js +++ b/tree-sitter-p4/grammar.js @@ -63,7 +63,11 @@ export default grammar({ instantiation: $ => seq($.typeRef, '(', optional($.parameterList), ')', $.identifier), // Declarations - declaration: $ => seq(choice($.parserDeclaration, $.parserTypeDeclaration)), + declaration: $ => seq(choice($.parserDeclaration, $.parserTypeDeclaration, $.type_declaration)), + + type_declaration: $=> choice($.struct_declaration), + struct_declaration: $ => seq($.struct, $.identifier, '{', optional($.struct_declaration_fields), '}'), + struct_declaration_fields: $=> repeat1(seq($.variableDeclaration)), // Make separate productions for the parser type and the parser type declaration because the latter can have type parameters. parserTypeDeclaration: $ => seq(optional($.annotations), $.parser, field('parser_name', $.identifier), optional($.typeParameters), '(', optional($.parameterList), ')'), diff --git a/tree-sitter-p4/test/corpus/declarations.txt b/tree-sitter-p4/test/corpus/declarations.txt index 0499221..de1321e 100644 --- a/tree-sitter-p4/test/corpus/declarations.txt +++ b/tree-sitter-p4/test/corpus/declarations.txt @@ -232,3 +232,31 @@ parser simple() { ) ) +========================= +Simple Struct Type Declaration +========================= +struct Testing { + string fieldA; +}; +--- +(p4program + (declaration + (type_declaration + (struct_declaration + (struct) + (identifier) + (struct_declaration_fields + (variableDeclaration + (typeRef + (baseType + (string) + ) + ) + (identifier) + ) + ) + ) + ) + ) +) +