diff --git a/Sources/Common/Execution.swift b/Sources/Common/Execution.swift index bbf75d7..ea94a4d 100644 --- a/Sources/Common/Execution.swift +++ b/Sources/Common/Execution.swift @@ -15,25 +15,32 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +public typealias StatementInterloper = (EvaluatableStatement, ControlFlow, ProgramExecution) -> Void +public typealias ExpressionInterloper = (EvaluatableExpression, Result, ProgramExecution) -> Void + open class ProgramExecution: CustomStringConvertible { public var scopes: VarValueScopes = VarValueScopes() - let initialValues: VarValueScopes? + var globalValues: VarValueScopes? var error: Error? var debug: DebugLevel = DebugLevel.Error + var statement_interloper: StatementInterloper? + var expression_interloper: ExpressionInterloper? init(copy: ProgramExecution) { self.scopes = copy.scopes - self.initialValues = copy.initialValues + self.globalValues = copy.globalValues self.error = copy.error self.debug = copy.debug + self.statement_interloper = copy.statement_interloper + self.expression_interloper = copy.expression_interloper } public init() { - initialValues = .none + globalValues = .none } public init(withGlobalValues values: VarValueScopes) { - initialValues = values + globalValues = values } open var description: String { @@ -64,6 +71,26 @@ open class ProgramExecution: CustomStringConvertible { return pe } + public func getStatementInterloper() -> StatementInterloper? { + return self.statement_interloper + } + + public func setStatementInterloper(_ interloper: @escaping StatementInterloper) -> ProgramExecution { + let pe = ProgramExecution(copy: self) + pe.statement_interloper = interloper + return pe + } + + public func getExpressionInterloper() -> ExpressionInterloper? { + return self.expression_interloper + } + + public func setExpressionInterloper(_ interloper: @escaping ExpressionInterloper) -> ProgramExecution { + let pe = ProgramExecution(copy: self) + pe.expression_interloper = interloper + return pe + } + open func isDone() -> Bool { return false } @@ -101,9 +128,17 @@ open class ProgramExecution: CustomStringConvertible { return new_pe } - public func initial_values() -> VarValueScopes? { - return self.initialValues + public func getGlobalValues() -> VarValueScopes { + return self.globalValues ?? VarValueScopes() } + + public func setGlobalValues(_ global_values: VarValueScopes) -> ProgramExecution { + let new_pe = ProgramExecution(copy: self) + new_pe.globalValues = global_values + return new_pe + } + + } /// A scope that resolves variable identifiers to their values. diff --git a/Sources/Common/Support.swift b/Sources/Common/Support.swift index 1ef4ae4..ebc62b6 100644 --- a/Sources/Common/Support.swift +++ b/Sources/Common/Support.swift @@ -117,24 +117,13 @@ public enum Result: Equatable { } } -extension Result where OKT: CustomStringConvertible { +extension Result: CustomStringConvertible where OKT: CustomStringConvertible { public var description: String { switch self { case Result.Error(let e): return e.msg case Result.Ok(let o): - return "\(o)" - } - } -} - -extension Result: CustomStringConvertible { - public var description: String { - switch self { - case Result.Error(let e): - return e.msg - case Result.Ok(_): - return "Ok" + return "Ok: \(o)" } } } diff --git a/Sources/P4Lang/Common.swift b/Sources/P4Lang/Common.swift index 7500006..3b785c2 100644 --- a/Sources/P4Lang/Common.swift +++ b/Sources/P4Lang/Common.swift @@ -90,7 +90,7 @@ public struct ParameterList: CustomStringConvertible, Equatable { public struct ArgumentList { public let arguments: [Argument] - public init(_ arguments: [Argument]) { + public init(_ arguments: [Argument] = []) { self.arguments = arguments } diff --git a/Sources/P4Runtime/Common.swift b/Sources/P4Runtime/Common.swift index 87d0fe5..f6fc9d9 100644 --- a/Sources/P4Runtime/Common.swift +++ b/Sources/P4Runtime/Common.swift @@ -32,7 +32,8 @@ public func Call( for (parameter, argument) in zip(params.parameters, args.arguments) { let arg_idx = argument.index let arg_value = argument.argument - let maybe_argument_value = arg_value.evaluate(execution: called_execution) + //let maybe_argument_value = arg_value.evaluate(execution: called_execution) + let maybe_argument_value = EvaluateExpression(arg_value, inExecution: called_execution) guard case (.Ok(let argument_value), let updated_execution) = maybe_argument_value else { return ( .Error(Error(withMessage: "Cannot evaluate argument \(arg_idx): \(argument)")), @@ -82,3 +83,63 @@ public func Call( } return (.Ok(call_result), updated_execution.replaceScopes(inout_scopes)) } + +public typealias ExecuteStatementResultHandler = (ControlFlow, ProgramExecution) -> (ControlFlow, ProgramExecution) + +public func ExecuteStatement( + _ statements: [EvaluatableStatement], handleResult handler: ExecuteStatementResultHandler, + inExecution execution: ProgramExecution, +) -> (ControlFlow, ProgramExecution) { + + var debugger: StatementInterloper? = .none + var hasDebugInterloper = false + if let found_deb = execution.getStatementInterloper() { + debugger = found_deb + hasDebugInterloper = true + } + + var execution = execution + for s in statements { + let (control_flow, next_execution) = s.evaluate(execution: execution) + + if hasDebugInterloper { + debugger!(s, control_flow, next_execution) + } + + switch handler(control_flow, next_execution) { + case (ControlFlow.Next, let handled_next_execution): execution = handled_next_execution + case (ControlFlow.Return(let value), let handled_next_execution): + return (ControlFlow.Return(value), handled_next_execution) + case (let handled_control_flow, let handled_next_execution): + return (handled_control_flow, handled_next_execution) + } + } + return (ControlFlow.Next, execution) +} + +public func ExecuteStatement( + _ statement: EvaluatableStatement, handleResult handler: ExecuteStatementResultHandler, + inExecution execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { + return ExecuteStatement([statement], handleResult: handler, inExecution: execution) +} + +public func EvaluateExpression( + _ expression: EvaluatableExpression, inExecution execution: ProgramExecution, +) -> (Result, ProgramExecution) { + + var debugger: ExpressionInterloper? = .none + var hasDebugInterloper = false + if let found_deb = execution.getExpressionInterloper() { + debugger = found_deb + hasDebugInterloper = true + } + + let (result, execution) = expression.evaluate(execution: execution) + + if hasDebugInterloper { + debugger!(expression, result, execution) + } + + return (result, execution) + +} diff --git a/Sources/P4Runtime/Expressions.swift b/Sources/P4Runtime/Expressions.swift index c374a9c..90c5d5d 100644 --- a/Sources/P4Runtime/Expressions.swift +++ b/Sources/P4Runtime/Expressions.swift @@ -30,14 +30,14 @@ extension SelectCaseExpression: EvaluatableExpression { extension SelectExpression: EvaluatableExpression { public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) { - switch self.selector.evaluate(execution: execution) { + switch EvaluateExpression(self.selector, inExecution: execution) { case (.Ok(let selector_value), let updated_execution): for sce in self.case_expressions { - if case (.Ok(let kse), let updated_execution) = sce.key.evaluate( - execution: updated_execution), + if case (.Ok(let kse), let updated_execution) = EvaluateExpression(sce.key, inExecution: updated_execution), kse.eq(selector_value) { - let result = sce.evaluate(execution: updated_execution) + //let result = sce.evaluate(execution: updated_execution) + let result = EvaluateExpression(sce, inExecution: updated_execution) return result } } @@ -210,12 +210,14 @@ public func binary_int_math_operator_checker( extension BinaryOperatorExpression: EvaluatableExpression { public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) { let updated_execution = execution - let maybe_evaluated_left = self.left.evaluate(execution: updated_execution) + //let maybe_evaluated_left = self.left.evaluate(execution: updated_execution) + let maybe_evaluated_left = EvaluateExpression(self.left, inExecution: updated_execution) guard case (.Ok(let evaluated_left), let updated_execution) = maybe_evaluated_left else { return maybe_evaluated_left } - let maybe_evaluated_right = self.right.evaluate(execution: updated_execution) + //let maybe_evaluated_right = self.right.evaluate(execution: updated_execution) + let maybe_evaluated_right = EvaluateExpression(self.right, inExecution: updated_execution) guard case (.Ok(let evaluated_right), let updated_execution) = maybe_evaluated_right else { return maybe_evaluated_right } @@ -231,12 +233,14 @@ extension BinaryOperatorExpression: EvaluatableExpression { extension ArrayAccessExpression: EvaluatableExpression { public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) { let updated_execution = execution - let maybe_name = self.name.evaluate(execution: updated_execution) + //let maybe_name = self.name.evaluate(execution: updated_execution) + let maybe_name = EvaluateExpression(self.name, inExecution: updated_execution) guard case (.Ok(let name), let updated_execution) = maybe_name else { return maybe_name } - let maybe_indexor = self.indexor.evaluate(execution: updated_execution) + //let maybe_indexor = self.indexor.evaluate(execution: updated_execution) + let maybe_indexor = EvaluateExpression(self.indexor, inExecution: updated_execution) guard case (.Ok(let indexor), let updated_execution) = maybe_indexor else { return maybe_indexor } @@ -265,7 +269,8 @@ extension ArrayAccessExpression: EvaluatableLValueExpression { ) -> Common.Result<(Common.VarValueScopes, P4Value)> { let updated_execution = execution - let maybe_value = self.name.evaluate(execution: updated_execution) + //let maybe_value = self.name.evaluate(execution: updated_execution) + let maybe_value = EvaluateExpression(self.name, inExecution: updated_execution) guard case (.Ok(let value), let updated_execution) = maybe_value else { return .Error( Error(withMessage: "\(self.name) cannot be evaluated: \(maybe_value.0.error()!)")) @@ -275,7 +280,8 @@ extension ArrayAccessExpression: EvaluatableLValueExpression { } // Now, get the indexor! - let maybe_indexor_value = self.indexor.evaluate(execution: updated_execution) + //let maybe_indexor_value = self.indexor.evaluate(execution: updated_execution) + let maybe_indexor_value = EvaluateExpression(self.indexor, inExecution: updated_execution) guard case (.Ok(let indexor_value), let updated_execution) = maybe_indexor_value else { return Result.Error( Error(withMessage: "\(self.indexor) cannot be evaluated: \(maybe_indexor_value.0.error()!)") @@ -341,7 +347,8 @@ extension FieldAccessExpression: EvaluatableExpression { public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) { let updated_execution = execution - let maybe_struct = self.strct.evaluate(execution: updated_execution) + //let maybe_struct = self.strct.evaluate(execution: updated_execution) + let maybe_struct = EvaluateExpression(self.strct, inExecution: updated_execution) guard case (.Ok(let strct), let updated_execution) = maybe_struct else { return maybe_struct } @@ -375,7 +382,8 @@ extension FieldAccessExpression: EvaluatableLValueExpression { let updated_execution = execution // First, evaluate strct_id and make sure that it names a struct. - let maybe_value = self.strct.evaluate(execution: updated_execution) + //let maybe_value = self.strct.evaluate(execution: updated_execution) + let maybe_value = EvaluateExpression(self.strct, inExecution: updated_execution) guard case (.Ok(let value), let updated_execution) = maybe_value else { return .Error( Error(withMessage: "\(self.strct) cannot be evaluated: \(maybe_value.0.error()!)")) @@ -444,7 +452,8 @@ extension FieldAccessExpression: EvaluatableLValueExpression { extension KeysetExpression: EvaluatableExpression { public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) { - return self.key.evaluate(execution: execution) + //return self.key.evaluate(execution: execution) + return EvaluateExpression(self.key, inExecution: execution) } public func type() -> P4Type { diff --git a/Sources/P4Runtime/Parser.swift b/Sources/P4Runtime/Parser.swift index 1fe6812..e2db657 100644 --- a/Sources/P4Runtime/Parser.swift +++ b/Sources/P4Runtime/Parser.swift @@ -21,7 +21,8 @@ import P4Lang extension ParserAssignmentStatement: EvaluatableStatement { public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { let updated_execution = execution - let result = self.value.evaluate(execution: updated_execution) + //let result = self.value.evaluate(execution: updated_execution) + let result = EvaluateExpression(self.value, inExecution: updated_execution) guard case (.Ok(let value), let updated_execution) = result else { return (ControlFlow.Error, execution.setError(error: result.0.error()!)) } @@ -43,19 +44,23 @@ extension ParserStateDirectTransition: EvaluatableParserState { ) -> (any EvaluatableParserState, Common.ProgramExecution) { var program = program.enter_scope() - for statement in statements { - let (control_flow, next_program) = statement.evaluate(execution: program) - switch control_flow { - case .Next: program = next_program // Ok! - case .Error: return (reject, next_program) - default: - return ( - reject, - next_program.setError( - error: Error(withMessage: "Invalid control flow (\(control_flow) in parser)")) - ) - } + + let (control_flow, next_execution) = ExecuteStatement(statements, handleResult: { (control_flow, execution) in + return (control_flow, execution) + }, inExecution: program) + + + switch control_flow { + case .Next: program = next_execution + case .Error: return (reject, next_execution.exit_scope()) + default: + return ( + reject, + next_execution.exit_scope().setError( + error: Error(withMessage: "Invalid control flow (\(control_flow) in parser)")) + ) } + let res = program.scopes.lookup(identifier: get_next_state()) if case .Ok(let value) = res { @@ -79,7 +84,6 @@ extension ParserStateDirectTransition: EvaluatableParserState { } extension ParserStateNoTransition: EvaluatableParserState { - public func execute( program: Common.ProgramExecution ) -> (any EvaluatableParserState, Common.ProgramExecution) { @@ -96,28 +100,30 @@ extension ParserStateNoTransition: EvaluatableParserState { } extension ParserStateSelectTransition: EvaluatableParserState { - public func execute( program: Common.ProgramExecution ) -> (any EvaluatableParserState, Common.ProgramExecution) { var program = program.enter_scope() - // First, evaluate the statements. - for statement in statements { - let (control_flow, next_program) = statement.evaluate(execution: program) - switch control_flow { - case .Next: program = next_program // Ok! - case .Error: return (reject, next_program) - default: - return ( - reject, - next_program.setError( - error: Error(withMessage: "Invalid control flow (\(control_flow) in parser)")) - ) - } + let (control_flow, next_execution) = ExecuteStatement(statements, handleResult: { (control_flow, execution) in + return (control_flow, execution) + }, inExecution: program) + + + switch control_flow { + case .Next: program = next_execution + case .Error: return (reject, next_execution.exit_scope()) + default: + return ( + reject, + next_execution.exit_scope().setError( + error: Error(withMessage: "Invalid control flow (\(control_flow) in parser)")) + ) } - switch self.selectExpression.evaluate(execution: program) { + + //switch self.selectExpression.evaluate(execution: program) { + switch EvaluateExpression(self.selectExpression, inExecution: program) { case (.Ok(let value), let program): if value.type().dataType().eq(rhs: self) { return (value.dataValue() as! EvaluatableParserState, program.exit_scope()) @@ -156,10 +162,8 @@ extension Parser: LibraryCallable { withValue: P4Value(reject, P4Type.ReadOnly(reject.type()))) // Add initial values to the global scope - if let initial = execution.initial_values() { - for (name, value) in initial { - execution = execution.declare(identifier: name, withValue: value) - } + for (name, value) in execution.getGlobalValues() { + execution = execution.declare(identifier: name, withValue: value) } // First, add every state to the scope! diff --git a/Sources/P4Runtime/Runtime.swift b/Sources/P4Runtime/Runtime.swift index e2f254e..0e2b45f 100644 --- a/Sources/P4Runtime/Runtime.swift +++ b/Sources/P4Runtime/Runtime.swift @@ -29,22 +29,22 @@ public struct ParserRuntime: CustomStringConvertible { self.initialValues = .none } - init(parser: Parser, withInitialValues initial: VarValueScopes?) { + init(parser: Parser, withGlobalValues initial: VarValueScopes?) { self.parser = parser self.initialValues = initial } /// Create a parser runtime from a P4 program public static func create(program: P4Lang.Program) -> Result { - return ParserRuntime.create(program: program, withInitialValues: .none) + return ParserRuntime.create(program: program, withGlobalValues: .none) } public static func create( - program: P4Lang.Program, withInitialValues initial: VarValueScopes? + program: P4Lang.Program, withGlobalValues initial: VarValueScopes? ) -> Result { return switch program.starting_parser() { case .Ok(let parser): - .Ok(P4Runtime.ParserRuntime(parser: parser, withInitialValues: initial)) + .Ok(P4Runtime.ParserRuntime(parser: parser, withGlobalValues: initial)) case .Error(let error): .Error(error) } } @@ -52,12 +52,10 @@ public struct ParserRuntime: CustomStringConvertible { /// Run a P4 parser with no arguments public func run() -> Result<(ParserState, ProgramExecution)> { return self.run(withArguments: ArgumentList([])) + } - /// Run the P4 parser on a given packet - public func run(withArguments arguments: ArgumentList) -> Result<(ParserState, ProgramExecution)> - { - + public func run(withArguments arguments: ArgumentList) -> Result<(ParserState, ProgramExecution)> { let pe = if let initial = initialValues { ProgramExecution(withGlobalValues: initial) @@ -65,6 +63,19 @@ public struct ParserRuntime: CustomStringConvertible { ProgramExecution() } + return self.run(withArguments: arguments, inExecution: pe) + } + + /// Run the P4 parser on a given packet + public func run(withArguments arguments: ArgumentList, inExecution pe: ProgramExecution) -> Result<(ParserState, ProgramExecution)> + { + + let pe = if let globals = initialValues { + pe.setGlobalValues(globals) + } else { + pe + } + let (end_state, execution) = parser.call(execution: pe, arguments: arguments) if let error = execution.getError() { return .Error(error) diff --git a/Sources/P4Runtime/Statements.swift b/Sources/P4Runtime/Statements.swift index 16af979..4400b0f 100644 --- a/Sources/P4Runtime/Statements.swift +++ b/Sources/P4Runtime/Statements.swift @@ -20,29 +20,31 @@ import P4Lang extension BlockStatement: EvaluatableStatement { public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { - var execution = execution - for s in self.statements { - let (control_flow, next_execution) = s.evaluate(execution: execution) - switch control_flow { - case ControlFlow.Return(let value): return (ControlFlow.Return(value), next_execution) - case ControlFlow.Next: execution = next_execution - case ControlFlow.Error: return (ControlFlow.Error, next_execution) - default: - return ( - ControlFlow.Next, - next_execution.setError( - error: Error(withMessage: "Invalid control flow \(control_flow) in block statement")) - ) - } - } - return (ControlFlow.Next, execution) + print("I am going to evaluate a block statement?") + return ExecuteStatement( + self.statements, + handleResult: { (cf, execution) in + switch cf { + case ControlFlow.Return(let value): return (ControlFlow.Return(value), execution) + case ControlFlow.Next: return (cf, execution) + case ControlFlow.Error: return (ControlFlow.Error, execution) + default: + return ( + ControlFlow.Error, + execution.setError( + error: Error(withMessage: "Invalid control flow \(cf) in block statement")) + ) + } + }, + inExecution: execution) } } extension VariableDeclarationStatement: EvaluatableStatement { public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { guard - case (.Ok(let initial_value), let execution) = self.initializer.evaluate(execution: execution) + //case (.Ok(let initial_value), let execution) = self.initializer.evaluate(execution: execution) + case (.Ok(let initial_value), let execution) = EvaluateExpression(self.initializer, inExecution: execution) else { return ( ControlFlow.Error, @@ -58,8 +60,8 @@ extension VariableDeclarationStatement: EvaluatableStatement { extension ConditionalStatement: EvaluatableStatement { public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { guard - case (.Ok(let evaluated_condition), let execution) = self.condition.evaluate( - execution: execution) + //case (.Ok(let evaluated_condition), let execution) = self.condition.evaluate(execution: execution) + case (.Ok(let evaluated_condition), let execution) = EvaluateExpression(self.condition, inExecution: execution) else { return ( ControlFlow.Error, @@ -107,7 +109,8 @@ extension ExpressionStatement: EvaluatableStatement { public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { // Evaluate, there might be side effects! - return switch self.expression.evaluate(execution: execution) { + //return switch self.expression.evaluate(execution: execution) { + return switch EvaluateExpression(self.expression, inExecution: execution) { case (.Ok(_), let updated_context): (ControlFlow.Next, updated_context) case (.Error(let e), let updated_context): (ControlFlow.Next, updated_context.setError(error: e)) @@ -117,7 +120,8 @@ extension ExpressionStatement: EvaluatableStatement { extension ReturnStatement: EvaluatableStatement { public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { - return switch self.value.evaluate(execution: execution) { + //return switch self.value.evaluate(execution: execution) { + return switch EvaluateExpression(self.value, inExecution: execution) { case (.Ok(let v), let execution): (ControlFlow.Return(v), execution) case (.Error(let e), let execution): (ControlFlow.Error, execution.setError(error: e)) } diff --git a/Tests/p4rseTests/ArrayTests.swift b/Tests/p4rseTests/ArrayTests.swift index 6fa800a..b71d537 100644 --- a/Tests/p4rseTests/ArrayTests.swift +++ b/Tests/p4rseTests/ArrayTests.swift @@ -49,7 +49,7 @@ import TreeSitterP4 ]))) let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) @@ -101,7 +101,7 @@ import TreeSitterP4 ]))) let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.reject) @@ -128,7 +128,7 @@ import TreeSitterP4 ]))) let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) @@ -155,7 +155,7 @@ import TreeSitterP4 ]))) let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) @@ -187,7 +187,7 @@ import TreeSitterP4 let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) @@ -216,7 +216,7 @@ import TreeSitterP4 ]))) let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) @@ -249,7 +249,7 @@ import TreeSitterP4 let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) diff --git a/Tests/p4rseTests/InterloperTests.swift b/Tests/p4rseTests/InterloperTests.swift new file mode 100644 index 0000000..f6f3640 --- /dev/null +++ b/Tests/p4rseTests/InterloperTests.swift @@ -0,0 +1,114 @@ +// 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_statement_interloper() async throws { + let simple_parser_declaration = """ + parser main_parser() { + state starts { + bool where_to = false; + int va = 5; + transition accept; + } + state start { + bool where_to = true; + where_to = true; + transition select (where_to) { + false: reject; + true: starts; + }; + } + }; + """ + + let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) + + var statements_executed: [String] = Array() + + let pe = ProgramExecution().setStatementInterloper({ (statement, cf, execution) in + statements_executed.append("\(statement)") + }) + + let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: ArgumentList(), inExecution: pe)) + + #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) + + #expect(statements_executed[0].hasPrefix("VariableDeclarationStatement")) + #expect(statements_executed[1].hasPrefix("ParserAssignmentStatement")) + // Moved into starts + #expect(statements_executed[2].hasPrefix("VariableDeclarationStatement")) + #expect(statements_executed[3].hasPrefix("VariableDeclarationStatement")) +} + +@Test func test_expression_interloper() async throws { + let simple_parser_declaration = """ + parser main_parser() { + state starts { + bool where_to = false; + int va = 5; + transition accept; + } + state start { + bool where_to = true; + where_to = true; + transition select (where_to) { + false: reject; + true: starts; + }; + } + }; + """ + + let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) + + var expressions_evaluated: [String] = Array() + + let pe = ProgramExecution().setExpressionInterloper() { expression, result, execution in + print("Expression: \(expression)") + expressions_evaluated.append("\(expression)") + } + + let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: ArgumentList(), inExecution: pe)) + + #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) + + #expect(expressions_evaluated[0].hasPrefix("Value: true of Boolean")) + #expect(expressions_evaluated[1].hasPrefix("Value: true of Boolean")) + #expect(expressions_evaluated[2].hasPrefix("where_to")) + #expect(expressions_evaluated[3].hasPrefix("Value: false of Boolean")) + #expect(expressions_evaluated[4].hasPrefix("KeysetExpression")) + #expect(expressions_evaluated[5].hasPrefix("Value: true of Boolean")) + #expect(expressions_evaluated[6].hasPrefix("KeysetExpression")) + #expect(expressions_evaluated[7].hasPrefix("SelectCaseExpression")) + #expect(expressions_evaluated[8].hasPrefix("SelectExpression")) + // Moved into starts + #expect(expressions_evaluated[9].hasPrefix("Value: false of Boolean")) + #expect(expressions_evaluated[10].hasPrefix("Value: 5 of Int")) +} diff --git a/Tests/p4rseTests/StructTests.swift b/Tests/p4rseTests/StructTests.swift index 5da57be..d4a15a1 100644 --- a/Tests/p4rseTests/StructTests.swift +++ b/Tests/p4rseTests/StructTests.swift @@ -57,7 +57,7 @@ import TreeSitterP4 let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) } @@ -151,7 +151,7 @@ import TreeSitterP4 let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.reject) } @@ -186,7 +186,7 @@ import TreeSitterP4 let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) } @@ -220,7 +220,7 @@ import TreeSitterP4 let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.reject) } @@ -266,7 +266,7 @@ import TreeSitterP4 ]))) let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) } @@ -302,7 +302,7 @@ import TreeSitterP4 let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) } @@ -375,7 +375,7 @@ import TreeSitterP4 ]))) let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) } @@ -423,7 +423,7 @@ import TreeSitterP4 ]))) let program = try #UseOkResult( Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations)) - let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withGlobalValues: test_values)) let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) } diff --git a/Tests/p4rseTests/SupportTests.swift b/Tests/p4rseTests/SupportTests.swift index 38b24d2..aad8ed8 100644 --- a/Tests/p4rseTests/SupportTests.swift +++ b/Tests/p4rseTests/SupportTests.swift @@ -35,10 +35,15 @@ struct StringConvertible: CustomStringConvertible { @Test func test_result_type_description_not_convertible() async throws { let result: Result = Result.Ok(NotStringConvertible()); - #expect(result.description == "Ok") + #expect("\(result)" == "Ok(Tests.NotStringConvertible())") } @Test func test_result_type_description_convertible() async throws { let result: Result = Result.Ok(StringConvertible()); - #expect(result.description == "CONVERTED") + #expect("\(result)" == "Ok: CONVERTED") +} + +@Test func test_result_type_p4value_convertible() async throws { + let result = Result.Ok(P4Value(P4IntValue(withValue: 5))) + #expect("\(result)" == "Ok: Value: 5 of Int type of type Int") } \ No newline at end of file diff --git a/tree-sitter-p4/grammar.js b/tree-sitter-p4/grammar.js index 5f33005..ba28e70 100644 --- a/tree-sitter-p4/grammar.js +++ b/tree-sitter-p4/grammar.js @@ -111,8 +111,7 @@ export default grammar({ // Parser statements parserStatements: $ => repeat1($.parserStatement), - parserStatement: $ => choice($.conditionalStatement, $.parserBlockStatement, $.expressionStatement, $.assignmentStatement, $.variableDeclaration), // Limited, so far. - parserBlockStatement: $ => seq(optional($.annotations), '{', $.parserStatements, '}'), + parserStatement: $ => choice($.conditionalStatement, $.expressionStatement, $.assignmentStatement, $.variableDeclaration), // Limited, so far. parserTransitionStatement: $ => seq($.transition, $.transitionSelectionExpression, $._semicolon), // Expressions