diff --git a/Sources/P4Compiler/Parser.swift b/Sources/P4Compiler/Parser.swift index ad79633..acdc5a8 100644 --- a/Sources/P4Compiler/Parser.swift +++ b/Sources/P4Compiler/Parser.swift @@ -64,9 +64,6 @@ public struct Parser { "variableDeclaration": VariableDeclarationStatement.self, "conditionalStatement": ConditionalStatement.self, "blockStatement": BlockStatement.self, ] - - // Iterate through statement parsers and give each one a chance. - guard let parser = statementParsers[statement.nodeType ?? ""] else { return Result.Error( ErrorOnNode( @@ -151,7 +148,7 @@ public struct Parser { return Result.Error(ErrorOnNode(node: node, withError: "Did not find expected statements")) } - var parse_err: Error? = .none + var parse_errs: [Error] = Array() var current_context = context var parsed_s: [EvaluatableStatement] = Array() @@ -163,12 +160,14 @@ public struct Parser { current_context = updated_context parsed_s.append(parsed_statement) case .Error(let e): - parse_err = e + parse_errs.append(e) } } - if let parse_err = parse_err { - return Result.Error(parse_err) + if !parse_errs.isEmpty { + return Result.Error(Error(withMessage: parse_errs.map() { err in + return String(err.msg) + }.joined(separator: ";"))) } return Result.Ok((parsed_s, current_context)) } @@ -224,7 +223,7 @@ public struct Parser { currentChildIdx += 2 currentChildIdxSafe += 2 - var parse_err: Error? = .none + var parse_errs: [Error] = Array() var current_context = context var parsed_s: [EvaluatableStatement] = Array() @@ -240,14 +239,16 @@ public struct Parser { parsed_s = state_statements current_context = updated_context case .Error(let error): - parse_err = error + parse_errs.append(error) } currentChildIdx += 1 currentChildIdxSafe += 1 } - if let parse_err = parse_err { - return Result.Error(parse_err) + if !parse_errs.isEmpty { + return Result.Error(Error(withMessage: parse_errs.map() { err in + return String(err.msg) + }.joined(separator: ";"))) } if node.childCount < currentChildIdxSafe { diff --git a/Sources/P4Compiler/Protocols.swift b/Sources/P4Compiler/Protocols.swift index 62a97fa..0186aa6 100644 --- a/Sources/P4Compiler/Protocols.swift +++ b/Sources/P4Compiler/Protocols.swift @@ -33,5 +33,5 @@ public protocol CompilableValue { } public protocol CompilableType { - static func CompileType(type: String) -> Result + static func CompileType(type: SwiftTreeSitter.Node, withContext: CompilerContext) -> Result } diff --git a/Sources/P4Compiler/Statement.swift b/Sources/P4Compiler/Statement.swift index e5c6478..e77a01e 100644 --- a/Sources/P4Compiler/Statement.swift +++ b/Sources/P4Compiler/Statement.swift @@ -182,14 +182,6 @@ extension VariableDeclarationStatement: CompilableStatement { } let maybe_rvalue = node.childCount > 3 ? node.child(at: 3) : .none - guard let rvalue = maybe_rvalue, - rvalue.nodeType == "expression" - else { - return Result.Error( - ErrorOnNode( - node: node, - withError: "Did not find initial value expression for variable declaration statement")) - } guard case .Ok(let parsed_variablename) = Identifier.Compile( @@ -199,38 +191,47 @@ extension VariableDeclarationStatement: CompilableStatement { Error(withMessage: "Could not parse variable name")) } - let maybe_parsed_rvalue = Expression.Compile(node: rvalue, withContext: context) - - guard - case .Ok(let parsed_rvalue) = maybe_parsed_rvalue - else { - return .Error(maybe_parsed_rvalue.error()!) - } - - guard case .Ok(let declaration_p4_type) = Types.CompileBasicType(type: typeref.text!) else { + guard case .Ok(let declaration_p4_type) = Types.CompileType(type: typeref, withContext: context) else { return Result.Error( Error(withMessage: "Could not parse a P4 type from \(typeref.text!)")) } - if parsed_rvalue.type().eq(rhs: declaration_p4_type) { - return Result.Ok( - ( - VariableDeclarationStatement( - identifier: parsed_variablename, withInitializer: parsed_rvalue), - // Context with updated names to include the newly declared name. - context.update( - newNames: context.names.declare( - identifier: parsed_variablename, withValue: declaration_p4_type)) - )) + var initializer: EvaluatableExpression = declaration_p4_type.def() + // If there is an initializer, it must be an expression. + if let rvalue = maybe_rvalue { + guard rvalue.nodeType == "expression" else { + return Result.Error( + ErrorOnNode( + node: node, + withError: "initial value for declaration statement is not an expression")) + } - } else { - return Result.Error( - Error( - withMessage: - "Cannot initialize \(parsed_variablename) (with type \(declaration_p4_type)) from rvalue with type \(parsed_rvalue.type())" - )) + let maybe_parsed_rvalue = Expression.Compile(node: rvalue, withContext: context) + guard + case .Ok(let parsed_rvalue) = maybe_parsed_rvalue + else { + return .Error(maybe_parsed_rvalue.error()!) + } + if parsed_rvalue.type().eq(rhs: declaration_p4_type) { + initializer = parsed_rvalue + } else { + return Result.Error( + Error( + withMessage: + "Cannot initialize \(parsed_variablename) (with type \(declaration_p4_type)) from rvalue with type \(parsed_rvalue.type())" + )) + } } + return Result.Ok( + ( + VariableDeclarationStatement( + identifier: parsed_variablename, withInitializer: initializer), + // Context with updated names to include the newly declared name. + context.update( + newNames: context.names.declare( + identifier: parsed_variablename, withValue: declaration_p4_type)) + )) } } diff --git a/Sources/P4Compiler/Types.swift b/Sources/P4Compiler/Types.swift index 7bcd3f3..627f064 100644 --- a/Sources/P4Compiler/Types.swift +++ b/Sources/P4Compiler/Types.swift @@ -23,27 +23,42 @@ import TreeSitterExtensions import TreeSitterP4 extension P4Boolean: CompilableType { - public static func CompileType(type: String) -> Common.Result<(any Common.P4Type)?> { - return type == "bool" ? .Ok(P4Boolean()) : .Ok(.none) + public static func CompileType(type: SwiftTreeSitter.Node, withContext: CompilerContext) -> Common.Result<(any Common.P4Type)?> { + return type.text == "bool" ? .Ok(P4Boolean()) : .Ok(.none) } } extension P4Int: CompilableType { - public static func CompileType(type: String) -> Common.Result<(any Common.P4Type)?> { - return type == "int" ? .Ok(P4Int()) : .Ok(.none) + public static func CompileType(type: SwiftTreeSitter.Node, withContext: CompilerContext) -> Common.Result<(any Common.P4Type)?> { + return type.text == "int" ? .Ok(P4Int()) : .Ok(.none) } } extension P4String: CompilableType { - public static func CompileType(type: String) -> Common.Result<(any Common.P4Type)?> { - return type == "string" ? .Ok(P4String()) : .Ok(.none) + public static func CompileType(type: SwiftTreeSitter.Node, withContext: CompilerContext) -> Common.Result<(any Common.P4Type)?> { + return type.text == "string" ? .Ok(P4String()) : .Ok(.none) } } + +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()!) + } + if case .Ok(let found_type) = context.types.lookup(identifier: parsed_type_id), + let found_struct_type = found_type as? P4Struct { + return .Ok(found_struct_type) + } + return .Ok(.none) + } +} + public struct Types { - static func CompileBasicType(type: String) -> Result { - let type_parsers: [CompilableType.Type] = [P4Boolean.self, P4Int.self, P4String.self] + static func CompileType(type: SwiftTreeSitter.Node, withContext context: CompilerContext) -> Result { + let type_parsers: [CompilableType.Type] = [P4Boolean.self, P4Int.self, P4String.self, P4Struct.self] for type_parser in type_parsers { - switch type_parser.CompileType(type: type) { + switch type_parser.CompileType(type: type, withContext: context) { case .Ok(.some(let type)): return .Ok(type) case .Ok(.none): continue case .Error(let e): return .Error(e) diff --git a/Tests/p4rseTests/StructTests.swift b/Tests/p4rseTests/StructTests.swift index 8987142..70e4f68 100644 --- a/Tests/p4rseTests/StructTests.swift +++ b/Tests/p4rseTests/StructTests.swift @@ -62,6 +62,65 @@ import TreeSitterP4 #expect(state_result == P4Lang.accept) } +@Test func test_field_access_declared() async throws { + let simple_parser_declaration = """ + parser main_parser() { + state start { + Testing ts; + ts.yesno = true; + bool where_to = ts.yesno; + transition select (where_to) { + true: accept; + false: reject; + }; + } + }; + """ + var test_types = TypeTypeScopes().enter() + let fields = P4StructFields([ + P4StructFieldIdentifier(name: "yesno", withType: P4Boolean()), + P4StructFieldIdentifier(name: "count", withType: P4Int()), + ]) + let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields) + test_types = test_types.declare(identifier: Identifier(name: "Testing"), withValue: struct_type) + + let program = try #UseOkResult( + Program.Compile(simple_parser_declaration, withGlobalInstances: .none, withGlobalTypes: test_types)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) + let (state_result, _) = try! #UseOkResult(runtime.run()) + #expect(state_result == P4Lang.accept) +} + +@Test func test_field_access_declared2() async throws { + let simple_parser_declaration = """ + parser main_parser() { + state start { + Testing ts; + ts.yesno = true; + ts.count = 5; + bool where_to = ts.yesno; + transition select (ts.count == 5) { + true: accept; + false: reject; + }; + } + }; + """ + var test_types = TypeTypeScopes().enter() + let fields = P4StructFields([ + P4StructFieldIdentifier(name: "yesno", withType: P4Boolean()), + P4StructFieldIdentifier(name: "count", withType: P4Int()), + ]) + let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields) + test_types = test_types.declare(identifier: Identifier(name: "Testing"), withValue: struct_type) + + let program = try #UseOkResult( + Program.Compile(simple_parser_declaration, withGlobalInstances: .none, withGlobalTypes: test_types)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) + let (state_result, _) = try! #UseOkResult(runtime.run()) + #expect(state_result == P4Lang.accept) +} + @Test func test_field_access_opp() async throws { let simple_parser_declaration = """ parser main_parser() { diff --git a/tree-sitter-p4/grammar.js b/tree-sitter-p4/grammar.js index 2497937..1cd3aec 100644 --- a/tree-sitter-p4/grammar.js +++ b/tree-sitter-p4/grammar.js @@ -34,7 +34,7 @@ export default grammar({ direction: $ => choice($.in, $.out, $.inout), // Common - Types - typeRef: $ => $.baseType, + typeRef: $ => choice($.baseType, $.type_identifier), baseType: $ => choice($.bool, $.error, $.string, $.int, $.bit /* omitting "templated" types" */), constructorParameters: $ => seq('(', optional($.parameterList), ')'), @@ -148,11 +148,11 @@ export default grammar({ varbit: $ => "varbit", valueset: $ => "valueset", void: $ => "void", - identifier: $ => /[a-z_]+/, - type_identifier: $ => /[a-z]+/, + identifier: $ => /[A-Za-z_]+/, + type_identifier: $ => /[A-Za-z_]+/, string_literal: $ => /".*"/, integer: $ => /[0-9]+/, - annotation_literal: $ => /@[a-z_]+/, + annotation_literal: $ => /@[A-Za-z_]+/, double_equal: $=> '==', open_bracket: $=> '[', close_bracket: $=> ']', diff --git a/tree-sitter-p4/test/corpus/declarations.txt b/tree-sitter-p4/test/corpus/declarations.txt index 781df25..e04de27 100644 --- a/tree-sitter-p4/test/corpus/declarations.txt +++ b/tree-sitter-p4/test/corpus/declarations.txt @@ -186,3 +186,47 @@ parser simple() { ) ) +========================= +Non Basic Type Declaration (No Initial Value) +========================= +parser simple() { + state start { + header_t header; + transition accept; + } +}; + +--- +(p4program + (declaration + (parserDeclaration + (parserType + (parser) + (identifier) + ) + (parserStates + (parserState + (state) + (identifier) + (parserStatements + (parserStatement + (variableDeclaration + (typeRef + (type_identifier) + ) + (identifier) + ) + ) + ) + (parserTransitionStatement + (transition) + (transitionSelectionExpression + (identifier) + ) + ) + ) + ) + ) + ) +) +