diff --git a/Package.swift b/Package.swift index 7e884f7..3b2148d 100644 --- a/Package.swift +++ b/Package.swift @@ -31,8 +31,6 @@ let package = Package( .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), ]), - // Targets are the basic building blocks of a package, defining a module or a test suite. - // Targets can depend on other targets in this package and products from dependencies. .target( name: "Parser", dependencies: [ @@ -52,11 +50,11 @@ let package = Package( ), .target( name: "P4", - dependencies: ["P4Macros"] + dependencies: ["Macros", "TreeSitterExtensions"] ), .testTarget( name: "ParserTests", - dependencies: ["Parser", "P4", "P4Macros"] + dependencies: ["Parser", "P4", "Macros", "TreeSitterExtensions"] ), ] ) diff --git a/Sources/P4/Parser.swift b/Sources/P4/Parser.swift index d0fde36..40f6e31 100644 --- a/Sources/P4/Parser.swift +++ b/Sources/P4/Parser.swift @@ -65,15 +65,13 @@ public struct ParserTransitionStatement: ParserStatement { } public struct VariableDeclarationStatement: ParserStatement { - public var id: Identifier - public init(withIdentifier id: Identifier) { - self.id = id + public var variable: Variable + public init(withVariable variable: Variable) { + self.variable = variable } public func evaluate(execution: ParserExecution) -> ParserExecution { - print("Evaluating!") - execution.scopes.scopes[0].variables.append(Variable(name: id.name, withValue: id.value, isConstant: false)) - print("Execution: \(execution)") + execution.scopes.scopes[0].variables.append(self.variable) return execution } } diff --git a/Sources/P4/Program.swift b/Sources/P4/Program.swift index 116eb70..523f7a6 100644 --- a/Sources/P4/Program.swift +++ b/Sources/P4/Program.swift @@ -15,30 +15,40 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -public class Identifier: CustomStringConvertible { +public class Identifier: CustomStringConvertible, Equatable { var name: String - var value: Value - public init(name: String, withValue value: Value) { + public init(name: String) { self.name = name - self.value = value } public var description: String { - return "\(name) = \(value)" + return "\(name)" + } + + public static func ==(lhs: Identifier, rhs: Identifier) -> Bool { + return lhs.name == rhs.name } } public class Variable: Identifier { var constant: Bool + var value: ValueType - public init(name: String, withValue value: Value, isConstant constant: Bool) { + public init(name: String, withValue value: ValueType, isConstant constant: Bool) { self.constant = constant - super.init(name: name, withValue: value) + self.value = value + super.init(name: name) } public override var description: String { - return "\(super.description) \(constant ? "(constant)" : "")" + return "\(super.description) = \(value) \(constant ? "(constant)" : "")" + } + + public var value_type: ValueType { + get { + value + } } } @@ -49,10 +59,25 @@ public struct Scope: CustomStringConvertible{ public var description: String { var result = String() for v in variables { - result += "\(v)" + result += "\(v)\n" } return result } + + public var count: Int { + get { + variables.count + } + } + + public func lookup(identifier: Identifier) -> Variable? { + for v in variables { + if v == identifier { + return v + } + } + return .none + } } public struct Scopes: CustomStringConvertible { @@ -71,11 +96,23 @@ public struct Scopes: CustomStringConvertible { public var description: String { var result = String() for s in scopes { - result += "Scope: \(s)\n" + result += "Scope:\n\(s)\n" } return result } + + public var current: Scope? { + get { + scopes.last + } + } + + public var count: Int { + get { + scopes.count + } + } } public struct Program { diff --git a/Sources/P4/Runtime.swift b/Sources/P4/Runtime.swift index d1f9ff3..581e981 100644 --- a/Sources/P4/Runtime.swift +++ b/Sources/P4/Runtime.swift @@ -41,14 +41,12 @@ public class ParserRuntime: CustomStringConvertible { return Result.Ok(P4.ParserRuntime(execution: P4.ParserExecution(start_state))) } - public func run(input: P4.Packet) -> Result { + public func run(input: P4.Packet) -> Result { execution.scopes.enter() - print("Execution: \(execution)") while execution.state != P4.accept && execution.state != P4.reject { execution = execution.state.evaluate(execution: execution) - print("Execution: \(execution)") } - return .Ok(Nothing()) + return .Ok(execution) } public var description: String { diff --git a/Sources/P4/Types.swift b/Sources/P4/Types.swift index 1bbdbc2..dc0ae7d 100644 --- a/Sources/P4/Types.swift +++ b/Sources/P4/Types.swift @@ -15,15 +15,34 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -public enum ValueType: CustomStringConvertible { +public enum ValueType: CustomStringConvertible, Equatable { case Boolean(Bool) + case Int(Int) + case String(String) public var description: String { switch self { case ValueType.Boolean(let b): return "\(b) of Boolean" + case ValueType.Int(let i): + return "\(i) of Int" + case ValueType.String(let s): + return "\(s) of String" } } + + public static func==(lhs: ValueType, rhs: ValueType) -> Bool { + switch (lhs,rhs) { + case (ValueType.Boolean(let lhsb), ValueType.Boolean(let rhsb)): + return lhsb == rhsb + case (ValueType.String(let lhsb), ValueType.String(let rhsb)): + return lhsb == rhsb + case (ValueType.Int(let lhsb), ValueType.Int(let rhsb)): + return lhsb == rhsb + default: return false + } + } + } public struct Value: CustomStringConvertible { diff --git a/Sources/Parser/Parser.swift b/Sources/Parser/Parser.swift index 3f7be2c..a9ca741 100644 --- a/Sources/Parser/Parser.swift +++ b/Sources/Parser/Parser.swift @@ -18,284 +18,349 @@ import P4 import SwiftTreeSitter import TreeSitterP4 - -extension MutableTree { - public func isError(lang: Language) -> Bool { - guard - let parser_error_query = try? SwiftTreeSitter.Query( - language: lang, - data: String( - "(ERROR)" - ).data(using: String.Encoding.utf8)!) - else { - return false - } - - let error_qr = parser_error_query.execute(in: self) - for _ in error_qr { - return true - } - return false - } -} +import TreeSitterExtensions let p4lang = Language(tree_sitter_p4()) +public protocol ParseableValueType { + static func Parse(type: String, withValue value: String) -> Result +} + +// This seems unnecessary because all the value types are in a single enum? +extension P4.ValueType: ParseableValueType { + public static func Parse(type: String, withValue value: String) -> Result { + if type == "bool" { + // Default + if value == "" { + return .Ok(P4.ValueType.Boolean(false)) + } + + if value == "true" { + return .Ok(P4.ValueType.Boolean(true)) + } else if value == "false" { + return .Ok(P4.ValueType.Boolean(false)) + } + return .Error(Error(withMessage: "Cannot convert \(value) into boolean value")) + + } else if type == "string" { + return .Ok(P4.ValueType.String(value)) + + } else if type == "int" { + // Default + if value == "" { + return .Ok(P4.ValueType.Int(0)) + } + + guard let parsed_value = Swift.Int(value) else { + return .Error(Error(withMessage: "Cannot convert \(value) into integer value")) + } + return .Ok(P4.ValueType.Int(parsed_value)) + } + + return .Error(Error(withMessage: "Invalid type")) + } +} + public protocol ParseableParserStatement { - static func Parse(node: Node, inTree tree: MutableTree) -> Result + static func Parse(node: Node, inTree tree: MutableTree) -> Result } extension P4.ExpressionStatement: ParseableParserStatement { - public static func Parse(node: Node, inTree tree: MutableTree) -> Result { - guard - let parser_state_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(expressionStatement (expression) @expression)" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Ok(.none) - } - - let qr = parser_state_query.execute(node: node, in: tree) - let query_result = qr.next()! - let expression_capture = query_result.captures(named: "expression") - if !expression_capture.isEmpty { - // TODO: Actually create an ExpressionStatement - return Result.Ok(P4.ExpressionStatement()) - } - - return Result.Ok(.none) - + public static func Parse(node: Node, inTree tree: MutableTree) -> Result { + guard + let parser_state_query = try? SwiftTreeSitter.Query( + language: p4lang, + data: String( + "(expressionStatement (expression) @expression)" + ).data(using: String.Encoding.utf8)!) + else { + return Result.Ok(.none) } + + let qr = parser_state_query.execute(node: node, in: tree) + let query_result = qr.next()! + let expression_capture = query_result.captures(named: "expression") + if !expression_capture.isEmpty { + // TODO: Actually create an ExpressionStatement + return Result.Ok(P4.ExpressionStatement()) + } + + return Result.Ok(.none) + + } } extension P4.VariableDeclarationStatement: ParseableParserStatement { - public static func Parse(node: Node, inTree tree: MutableTree) -> Result { - guard - let parser_state_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "((annotations)? (typeRef) @type-name variable_name: (identifier) @identifier)" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Ok(.none) - } - - let qr = parser_state_query.execute(node: node, in: tree) - let parser_declaration = qr.next()! - - let type_name_capture = parser_declaration.captures(named: "type-name") - let variable_name_capture = parser_declaration.captures(named: "identifier") - - // There must be a state name and there must be a transition statement. - guard !type_name_capture.isEmpty, - !variable_name_capture.isEmpty, - let variable_name = variable_name_capture[0].node.text - else { - return Result.Error(Error(withMessage: "Could not parse a parser declaration")) - } - - return Result.Ok( - // TODO: Add support for parsing the value. - P4.VariableDeclarationStatement( - withIdentifier: Identifier( - name: variable_name, withValue: Value(withValue: ValueType.Boolean(true))))) + public static func Parse(node: Node, inTree tree: MutableTree) -> Result { + guard + let parser_state_query = try? SwiftTreeSitter.Query( + language: p4lang, + data: String( + "((annotations)? (typeRef) @type-name variable_name: (identifier) @identifier ((assignment) (expression) @value)?)" + ).data(using: String.Encoding.utf8)!) + else { + return Result.Ok(.none) } + + let qr = parser_state_query.execute(node: node, in: tree) + let parser_declaration = qr.next()! + + let type_name_capture = parser_declaration.captures(named: "type-name") + let variable_name_capture = parser_declaration.captures(named: "identifier") + let value_capture = parser_declaration.captures(named: "value") + + // There must be a type name and a variable name + guard !type_name_capture.isEmpty, + !variable_name_capture.isEmpty, + let variable_name = variable_name_capture[0].node.text, + let type_name = type_name_capture[0].node.text + else { + return Result.Error(Error(withMessage: "Could not parse a parser declaration")) + } + + let value = + if !value_capture.isEmpty { + value_capture[0].node.text! + } else { + "" + } + + return switch P4.ValueType.Parse(type: type_name, withValue: value) { + case Result.Ok(let value_type): + Result.Ok( + P4.VariableDeclarationStatement( + withVariable: Variable(name: variable_name, withValue: value_type, isConstant: false))) + case Result.Error(let e): + Result.Error(e) + } + } } public struct Parser { - static func ParserLocalElements(capture: [QueryCapture], inTree tree: MutableTree) -> Result< - [P4.ParserStatement] - > { - let localElementsParsers: [ParseableParserStatement.Type] = [ - P4.VariableDeclarationStatement.self - ] - var localElements: [P4.ParserStatement] = Array() + public struct P4Parser { + static func LocalElements( + node: Node, inTree tree: MutableTree + ) -> Result<[P4.ParserStatement]> { - for raw_le_statement in capture { - var parsed_le_statement: P4.ParserStatement? = .none + guard + let parser_le_statement_query = try? SwiftTreeSitter.Query( + language: p4lang, + data: String( + "(parserLocalElement) @parser-local-element" + ).data(using: String.Encoding.utf8)!) + else { + return Result.Error(Error(withMessage: "Could not compile the tree sitter query")) + } - for le_parser in localElementsParsers { - if case Result.Ok(.some(let parsed)) = le_parser.Parse(node: raw_le_statement.node, inTree: tree) { - parsed_le_statement = parsed - break - } - } + let localElementsParsers: [ParseableParserStatement.Type] = [ + P4.VariableDeclarationStatement.self + ] - if let le_statement = parsed_le_statement { - localElements.append(le_statement) - } else { - // There were no parseable statements. - return Result.Error( - Error(withMessage: "Failed to parse a local element: \(raw_le_statement)")) - } + var localElements: [P4.ParserStatement] = Array() + + let qr = parser_le_statement_query.execute(node: node, in: tree) + for raw_le_statement in qr { + let raw_le_statement_capture = raw_le_statement.captures(named: "parser-local-element") + var parsed_le_statement: P4.ParserStatement? = .none + + for le_parser in localElementsParsers { + if case Result.Ok(.some(let parsed)) = le_parser.Parse( + node: raw_le_statement_capture[0].node, inTree: tree) + { + parsed_le_statement = parsed + break + } } - return Result.Ok(localElements) - } - - static func ParserStatements(capture: [QueryCapture], inTree tree: MutableTree) -> Result< - [P4.ParserStatement] - > { - let statementParsers: [ParseableParserStatement.Type] = [ - P4.ExpressionStatement.self, P4.VariableDeclarationStatement.self, - ] - - var statements: [P4.ParserStatement] = Array() - - for raw_statement in capture { - var parsed_statement: P4.ParserStatement? = .none - - // Iterate through statement parsers and give each one a chance. - for parser in statementParsers { - if case Result.Ok(.some(let parsed)) = parser.Parse(node: raw_statement.node, inTree: tree) { - parsed_statement = parsed - break - } - } - - if let statement = parsed_statement { - statements.append(statement) - } else { - // There were no parseable statements. - return Result.Error( - Error(withMessage: "Failed to parse a statement element: \(raw_statement)")) - } + if let le_statement = parsed_le_statement { + localElements.append(le_statement) + } else { + // There were no parseable statements. + return Result.Error( + Error(withMessage: "Failed to parse a local element: \(raw_le_statement)")) } - return Result.Ok(statements) + } + + return Result.Ok(localElements) } - static func ParserTransitionStatement(node: Node, inTree tree: MutableTree) -> P4 - .ParserTransitionStatement? + static func Statements( + node: Node, inTree tree: MutableTree + ) -> Result<[P4.ParserStatement]> { + + guard + let parser_statement_query = try? SwiftTreeSitter.Query( + language: p4lang, + data: String( + "(parserStatement) @parser-statement" + ).data(using: String.Encoding.utf8)!) + else { + return Result.Error(Error(withMessage: "Could not compile the tree sitter query")) + } + + let statementParsers: [ParseableParserStatement.Type] = [ + P4.ExpressionStatement.self, P4.VariableDeclarationStatement.self, + ] + + var statements: [P4.ParserStatement] = Array() + + let qr = parser_statement_query.execute(node: node, in: tree) + for raw_statement in qr { + let raw_statement_capture = raw_statement.captures(named: "parser-statement") + + var parsed_statement: P4.ParserStatement? = .none + + // Iterate through statement parsers and give each one a chance. + for parser in statementParsers { + if case Result.Ok(.some(let parsed)) = parser.Parse( + node: raw_statement_capture[0].node, inTree: tree) + { + parsed_statement = parsed + break + } + } + + if let statement = parsed_statement { + statements.append(statement) + } else { + // There were no parseable statements. + return Result.Error( + Error(withMessage: "Failed to parse a statement element: \(raw_statement)")) + } + } + return Result.Ok(statements) + } + + static func TransitionStatement( + node: Node, inTree tree: MutableTree + ) -> P4.ParserTransitionStatement? { - return P4.ParserTransitionStatement() + return P4.ParserTransitionStatement() } - static func ParserState(node: Node, inTree tree: MutableTree) -> Result { - guard - let parser_state_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(parserState (state) (identifier) @state-name (parserLocalElements)? @state-local-elements (parserStatements)? @state-statements (parserTransitionStatement) @transition)" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Error(Error(withMessage: "Could not compile the tree sitter query")) + static func State(node: Node, inTree tree: MutableTree) -> Result { + guard + let parser_state_query = try? SwiftTreeSitter.Query( + language: p4lang, + data: String( + "(parserState (state) (identifier) @state-name (parserLocalElements)? @state-local-elements (parserStatements)? @state-statements (parserTransitionStatement) @transition)" + ).data(using: String.Encoding.utf8)!) + else { + return Result.Error(Error(withMessage: "Could not compile the tree sitter query")) + } + + let qr = parser_state_query.execute(node: node, in: tree) + + let parser_declaration = qr.next()! + + let transition_capture = parser_declaration.captures(named: "transition") + let state_name_capture = parser_declaration.captures(named: "state-name") + let state_le_capture = parser_declaration.captures(named: "state-local-elements") + let statements_capture = parser_declaration.captures(named: "state-statements") + + // There must be a state name and there must be a transition statement. + guard !state_name_capture.isEmpty, + !transition_capture.isEmpty, + let parsed_state_name = state_name_capture[0].node.text, + let transition_statement = TransitionStatement( + node: transition_capture[0].node, inTree: tree) + else { + return Result.Error(Error(withMessage: "Could not parse a parser declaration")) + } + + let maybe_parsed_les = + if !state_le_capture.isEmpty { + LocalElements(node: state_le_capture[0].node, inTree: tree) + } else { + Result.Ok([P4.ParserStatement]()) } - let qr = parser_state_query.execute(node: node, in: tree) + guard case Result<[P4.ParserStatement]>.Ok(let parsed_les) = maybe_parsed_les else { + return Result.Error(maybe_parsed_les.error()!) + } - let parser_declaration = qr.next()! - - let transition_capture = parser_declaration.captures(named: "transition") - let state_name_capture = parser_declaration.captures(named: "state-name") - let state_le_capture = parser_declaration.captures(named: "state-local-elements") - let statements_capture = parser_declaration.captures(named: "state-statements") - - // There must be a state name and there must be a transition statement. - guard !state_name_capture.isEmpty, - !transition_capture.isEmpty, - let parsed_state_name = state_name_capture[0].node.text, - let transition_statement = ParserTransitionStatement( - node: transition_capture[0].node, inTree: tree) - else { - return Result.Error(Error(withMessage: "Could not parse a parser declaration")) + let maybe_parsed_statements = + if !statements_capture.isEmpty { + Statements(node: statements_capture[0].node, inTree: tree) + } else { + Result.Ok([P4.ParserStatement]()) } + guard case Result<[P4.ParserStatement]>.Ok(let parsed_statements) = maybe_parsed_statements + else { + return Result.Error(maybe_parsed_statements.error()!) + } - let maybe_parsed_les = - if !state_le_capture.isEmpty { - ParserLocalElements(capture: state_le_capture, inTree: tree) - } else { - Result.Ok([P4.ParserStatement]()) - } - - guard case Result<[P4.ParserStatement]>.Ok(let parsed_les) = maybe_parsed_les else { - return Result.Error(maybe_parsed_les.error()!) - } - - let maybe_parsed_statements = - if !statements_capture.isEmpty { - ParserStatements(capture: statements_capture, inTree: tree) - } else { - Result.Ok([P4.ParserStatement]()) - } - guard case Result<[P4.ParserStatement]>.Ok(let parsed_statements) = maybe_parsed_statements - else { - return Result.Error(maybe_parsed_statements.error()!) - } - - // TODO: Validate that there is only one! - return Result.Ok( - P4.ParserState( - name: parsed_state_name, withLocalElements: parsed_les, - withStatements: parsed_statements, - withTransition: transition_statement)) + // TODO: Validate that there is only one! + return Result.Ok( + P4.ParserState( + name: parsed_state_name, withLocalElements: parsed_les, + withStatements: parsed_statements, + withTransition: transition_statement)) + } + } + static func Parser(node: Node, inTree tree: MutableTree) -> Result { + guard + let parser_state_query = try? SwiftTreeSitter.Query( + language: p4lang, + data: String( + "(parserStates) @parser-states" + ).data(using: String.Encoding.utf8)!) + else { + return Result.Error( + Error(withMessage: "Could not compile the parser state tree sitter query")) } - static func Parser(node: Node, inTree tree: MutableTree) -> Result { - guard - let parser_state_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(parserStates) @parser-states" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Error( - Error(withMessage: "Could not compile the parser state tree sitter query")) - } + var parser = P4.Parser() - var parser = P4.Parser() + // Build a state from each one listed. + for parser_states in parser_state_query.execute(node: node, in: tree) { + switch P4Parser.State(node: parser_states.nodes[0], inTree: tree) { + case Result.Ok(let state): parser.states.append(state) + case Result.Error(let error): return Result.Error(error) + } + } + return Result.Ok(parser) + } - // Build a state from each one listed. - for parser_states in parser_state_query.execute(node: node, in: tree) { - switch ParserState(node: parser_states.nodes[0], inTree: tree) { - case Result.Ok(let state): parser.states.append(state) - case Result.Error(let error): return Result.Error(error) - } - } - return Result.Ok(parser) + public static func Program(_ source: String) -> Result { + let p = SwiftTreeSitter.Parser.init() + + do { + try p.setLanguage(p4lang) + } catch { + return Result.Error(Error(withMessage: "Could not configure the P4 parser")) } - public static func Program(_ source: String) -> Result { - - let p = SwiftTreeSitter.Parser.init() - - do { - try p.setLanguage(p4lang) - } catch { - return Result.Error(Error(withMessage: "Could not configure the P4 parser")) - } - - let result = p.parse(source) - guard let tree = result, - !tree.isError(lang: p4lang) - else { - return Result.Error(Error(withMessage: "Could not compile the P4 program")) - } - - guard - let parser_declaration_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(parserDeclaration (parserType) (parserStates) @parser-states)" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Error( - Error(withMessage: "Could not compile the parser declaration tree sitter query")) - } - - var program: P4.Program = P4.Program() - - let parser_qc = parser_declaration_query.execute(in: tree) - - for parser_declaration in parser_qc { - switch Parser(node: parser_declaration.nodes[0], inTree: tree) { - case Result.Ok(let parser): program.parsers.append(parser) - case Result.Error(let error): return Result.Error(error) - } - } - - return Result.Ok(program) + let result = p.parse(source) + guard let tree = result, + !tree.isError(lang: p4lang) + else { + return Result.Error(Error(withMessage: "Could not compile the P4 program")) } + + guard + let parser_declaration_query = try? SwiftTreeSitter.Query( + language: p4lang, + data: String( + "(parserDeclaration (parserType) (parserStates) @parser-states)" + ).data(using: String.Encoding.utf8)!) + else { + return Result.Error( + Error(withMessage: "Could not compile the parser declaration tree sitter query")) + } + + var program: P4.Program = P4.Program() + + let parser_qc = parser_declaration_query.execute(in: tree) + + for parser_declaration in parser_qc { + switch Parser(node: parser_declaration.nodes[0], inTree: tree) { + case Result.Ok(let parser): program.parsers.append(parser) + case Result.Error(let error): return Result.Error(error) + } + } + + return Result.Ok(program) + } } diff --git a/Tests/p4lmTests/ParserTests.swift b/Tests/p4lmTests/ParserTests.swift index faf4ec3..e27d116 100644 --- a/Tests/p4lmTests/ParserTests.swift +++ b/Tests/p4lmTests/ParserTests.swift @@ -22,7 +22,7 @@ import TreeSitterP4 import Foundation import P4 -import P4Macros +import Macros @testable import Parser diff --git a/Tests/p4lmTests/RuntimeTests.swift b/Tests/p4lmTests/RuntimeTests.swift index 126a3c1..df8aec3 100644 --- a/Tests/p4lmTests/RuntimeTests.swift +++ b/Tests/p4lmTests/RuntimeTests.swift @@ -17,7 +17,7 @@ import Foundation import P4 -import P4Macros +import Macros import SwiftTreeSitter import Testing import TreeSitter @@ -56,27 +56,41 @@ import TreeSitterP4 P4.ParserRuntime.create(program: program.parsers[0]))) } -@Test func test_simple_runtime_output() async throws { +@Test func test_simple_local_element_variable_declaration() async throws { let simple_parser_declaration = """ parser simple() { state start { - bool b = true; + bool b = false; + string s = "testing"; + true; + false; transition reject; } } """ - /* - TODO: Add tests for "semantic" parsing failures. Here's an example! - print(Parser.Program(simple_parser_declaration)) - #expect( - #RequireErrorResult( - Error( - withMessage: - "Failed to parse a local element: >" - ), Parser.Program(simple_parser_declaration))) - */ let program = try #UseOkResult(Parser.Program(simple_parser_declaration)) let runtime = try #UseOkResult(P4.ParserRuntime.create(program: program.parsers[0])) - #expect(runtime.run(input: P4.Packet()) == P4.Result.Ok(Nothing())) + + // This seems awkward to me! + // TODO: Is there a better way? + guard case P4.Result.Ok(let execution_result) = runtime.run(input: P4.Packet()) else { + assert(false) + } + + // There should be 1 scope. + #expect(execution_result.scopes.count == 1) + + guard let scope = execution_result.scopes.current else { + assert(false) + } + + // There are two variables declared. + #expect(scope.count == 2) + + // Check the names/values of the variables in scope. + let b = try #require(scope.lookup(identifier: Identifier(name: "b"))) + let s = try #require(scope.lookup(identifier: Identifier(name: "s"))) + #expect(b.value_type == ValueType.Boolean(false)) + #expect(s.value_type == ValueType.String("\"testing\"")) }