From a24571222baedd075611081b6fe6bd0f815d4e87 Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Mon, 20 Apr 2026 16:28:01 -0400 Subject: [PATCH] runtime: Refactor Expression/Statement Evaluators Allow the user to customize the evaluation/execution of expressions/ statements with something that implements functions that perform those tasks. This additional functionality will make it possible for the "classic" evaluator not to waste time checking for the presence of interlopers and give implementers additional customization opportunities. Signed-off-by: Will Hawkins --- Sources/Common/Execution.swift | 167 ++++++++++++++++++++----- Sources/Common/Protocols.swift | 16 +++ Sources/P4Runtime/Common.swift | 64 +--------- Sources/P4Runtime/Expressions.swift | 24 ++-- Sources/P4Runtime/Parser.swift | 8 +- Sources/P4Runtime/Runtime.swift | 2 +- Sources/P4Runtime/Statements.swift | 10 +- Tests/p4rseTests/InterloperTests.swift | 11 +- 8 files changed, 181 insertions(+), 121 deletions(-) diff --git a/Sources/Common/Execution.swift b/Sources/Common/Execution.swift index a5e138f..a4f932a 100644 --- a/Sources/Common/Execution.swift +++ b/Sources/Common/Execution.swift @@ -15,6 +15,137 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . + + +public typealias ExecuteStatementResultHandler = (ControlFlow, ProgramExecution) -> ( + ControlFlow, ProgramExecution +) + +public struct ClassicEvaluator: ProgramExecutionEvaluator { + public func ExecuteStatement( + _ statements: [EvaluatableStatement], handleResult handler: ExecuteStatementResultHandler, + inExecution execution: ProgramExecution, + ) -> (ControlFlow, ProgramExecution) { + + var execution = execution + for s in statements { + let (control_flow, next_execution) = s.evaluate(execution: 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) { + return expression.evaluate(execution: execution) + } +} + +public struct InterloperEvaluator: ProgramExecutionEvaluator { + var statement_interloper: StatementInterloper? + var expression_interloper: ExpressionInterloper? + + public init() {} + + public func getStatementInterloper() -> StatementInterloper? { + return self.statement_interloper + } + + public func setStatementInterloper( + _ interloper: @escaping StatementInterloper + ) -> InterloperEvaluator { + var pe = self + pe.statement_interloper = interloper + return pe + } + + public func getExpressionInterloper() -> ExpressionInterloper? { + return self.expression_interloper + } + + public func setExpressionInterloper( + _ interloper: @escaping ExpressionInterloper + ) -> InterloperEvaluator { + var pe = self + pe.expression_interloper = interloper + return pe + } + + public func ExecuteStatement( + _ statements: [EvaluatableStatement], handleResult handler: ExecuteStatementResultHandler, + inExecution execution: ProgramExecution, + ) -> (ControlFlow, ProgramExecution) { + + var debugger: StatementInterloper? = .none + var hasDebugInterloper = false + if let found_deb = self.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 = self.getExpressionInterloper() { + debugger = found_deb + hasDebugInterloper = true + } + + let (result, execution) = expression.evaluate(execution: execution) + + if hasDebugInterloper { + debugger!(expression, result, execution) + } + + return (result, execution) + + } +} + public typealias StatementInterloper = (EvaluatableStatement, ControlFlow, ProgramExecution) -> Void public typealias ExpressionInterloper = (EvaluatableExpression, Result, ProgramExecution) -> Void @@ -24,24 +155,24 @@ open class ProgramExecution: CustomStringConvertible { var globalValues: VarValueScopes? var error: Error? var debug: DebugLevel = DebugLevel.Error - var statement_interloper: StatementInterloper? - var expression_interloper: ExpressionInterloper? + public let evaluator: ProgramExecutionEvaluator init(copy: ProgramExecution) { self.scopes = copy.scopes self.globalValues = copy.globalValues self.error = copy.error self.debug = copy.debug - self.statement_interloper = copy.statement_interloper - self.expression_interloper = copy.expression_interloper + self.evaluator = copy.evaluator } public init() { globalValues = .none + evaluator = ClassicEvaluator() } - public init(withGlobalValues values: VarValueScopes) { - globalValues = values + public init(_ evaluator: ProgramExecutionEvaluator) { + globalValues = .none + self.evaluator = evaluator } open var description: String { @@ -72,30 +203,6 @@ 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 } diff --git a/Sources/Common/Protocols.swift b/Sources/Common/Protocols.swift index 03a5f2a..2f9c1ad 100644 --- a/Sources/Common/Protocols.swift +++ b/Sources/Common/Protocols.swift @@ -54,3 +54,19 @@ public protocol EvaluatableLValueExpression: EvaluatableExpression { ) -> Result<(VarValueScopes, P4Value)> func check(to: EvaluatableExpression, inScopes scopes: VarTypeScopes) -> Result<()> } + +public protocol ProgramExecutionEvaluator { + func ExecuteStatement( + _ statements: [EvaluatableStatement], handleResult handler: ExecuteStatementResultHandler, + inExecution execution: ProgramExecution, + ) -> (ControlFlow, ProgramExecution); + + func ExecuteStatement( + _ statement: EvaluatableStatement, handleResult handler: ExecuteStatementResultHandler, + inExecution execution: ProgramExecution + ) -> (ControlFlow, ProgramExecution); + + func EvaluateExpression( + _ expression: EvaluatableExpression, inExecution execution: ProgramExecution, + ) -> (Result, ProgramExecution) +} \ No newline at end of file diff --git a/Sources/P4Runtime/Common.swift b/Sources/P4Runtime/Common.swift index de70e3d..8926de9 100644 --- a/Sources/P4Runtime/Common.swift +++ b/Sources/P4Runtime/Common.swift @@ -33,7 +33,7 @@ public func Call( let arg_idx = argument.index let arg_value = argument.argument //let maybe_argument_value = arg_value.evaluate(execution: called_execution) - let maybe_argument_value = EvaluateExpression(arg_value, inExecution: called_execution) + let maybe_argument_value = called_execution.evaluator.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)")), @@ -84,65 +84,3 @@ 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 db6946e..bc4cf51 100644 --- a/Sources/P4Runtime/Expressions.swift +++ b/Sources/P4Runtime/Expressions.swift @@ -30,15 +30,15 @@ extension SelectCaseExpression: EvaluatableExpression { extension SelectExpression: EvaluatableExpression { public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) { - switch EvaluateExpression(self.selector, inExecution: execution) { + switch execution.evaluator.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) = EvaluateExpression( + if case (.Ok(let kse), let updated_execution) = updated_execution.evaluator.EvaluateExpression( sce.key, inExecution: updated_execution), kse.eq(selector_value) { //let result = sce.evaluate(execution: updated_execution) - let result = EvaluateExpression(sce, inExecution: updated_execution) + let result = updated_execution.evaluator.EvaluateExpression(sce, inExecution: updated_execution) return result } } @@ -212,13 +212,13 @@ 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 = EvaluateExpression(self.left, inExecution: updated_execution) + let maybe_evaluated_left = updated_execution.evaluator.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 = EvaluateExpression(self.right, inExecution: updated_execution) + let maybe_evaluated_right = updated_execution.evaluator.EvaluateExpression(self.right, inExecution: updated_execution) guard case (.Ok(let evaluated_right), let updated_execution) = maybe_evaluated_right else { return maybe_evaluated_right } @@ -235,13 +235,13 @@ 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 = EvaluateExpression(self.name, inExecution: updated_execution) + let maybe_name = updated_execution.evaluator.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 = EvaluateExpression(self.indexor, inExecution: updated_execution) + let maybe_indexor = updated_execution.evaluator.EvaluateExpression(self.indexor, inExecution: updated_execution) guard case (.Ok(let indexor), let updated_execution) = maybe_indexor else { return maybe_indexor } @@ -271,7 +271,7 @@ extension ArrayAccessExpression: EvaluatableLValueExpression { let updated_execution = execution //let maybe_value = self.name.evaluate(execution: updated_execution) - let maybe_value = EvaluateExpression(self.name, inExecution: updated_execution) + let maybe_value = updated_execution.evaluator.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()!)")) @@ -282,7 +282,7 @@ extension ArrayAccessExpression: EvaluatableLValueExpression { // Now, get the indexor! //let maybe_indexor_value = self.indexor.evaluate(execution: updated_execution) - let maybe_indexor_value = EvaluateExpression(self.indexor, inExecution: updated_execution) + let maybe_indexor_value = updated_execution.evaluator.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()!)") @@ -349,7 +349,7 @@ extension FieldAccessExpression: EvaluatableExpression { let updated_execution = execution //let maybe_struct = self.strct.evaluate(execution: updated_execution) - let maybe_struct = EvaluateExpression(self.strct, inExecution: updated_execution) + let maybe_struct = updated_execution.evaluator.EvaluateExpression(self.strct, inExecution: updated_execution) guard case (.Ok(let strct), let updated_execution) = maybe_struct else { return maybe_struct } @@ -384,7 +384,7 @@ 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 = EvaluateExpression(self.strct, inExecution: updated_execution) + let maybe_value = updated_execution.evaluator.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()!)")) @@ -454,7 +454,7 @@ extension FieldAccessExpression: EvaluatableLValueExpression { extension KeysetExpression: EvaluatableExpression { public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) { //return self.key.evaluate(execution: execution) - return EvaluateExpression(self.key, inExecution: execution) + return execution.evaluator.EvaluateExpression(self.key, inExecution: execution) } public func type() -> P4Type { diff --git a/Sources/P4Runtime/Parser.swift b/Sources/P4Runtime/Parser.swift index 8223c8c..e03bcb5 100644 --- a/Sources/P4Runtime/Parser.swift +++ b/Sources/P4Runtime/Parser.swift @@ -22,7 +22,7 @@ extension ParserAssignmentStatement: EvaluatableStatement { public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { let updated_execution = execution //let result = self.value.evaluate(execution: updated_execution) - let result = EvaluateExpression(self.value, inExecution: updated_execution) + let result = updated_execution.evaluator.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()!)) } @@ -44,7 +44,7 @@ extension ParserStateDirectTransition: EvaluatableParserState { ) -> (any EvaluatableParserState, Common.ProgramExecution) { var program = program.enter_scope() - let (control_flow, next_execution) = ExecuteStatement( + let (control_flow, next_execution) = program.evaluator.ExecuteStatement( statements, handleResult: { (control_flow, execution) in return (control_flow, execution) @@ -105,7 +105,7 @@ extension ParserStateSelectTransition: EvaluatableParserState { ) -> (any EvaluatableParserState, Common.ProgramExecution) { var program = program.enter_scope() - let (control_flow, next_execution) = ExecuteStatement( + let (control_flow, next_execution) = program.evaluator.ExecuteStatement( statements, handleResult: { (control_flow, execution) in return (control_flow, execution) @@ -123,7 +123,7 @@ extension ParserStateSelectTransition: EvaluatableParserState { } //switch self.selectExpression.evaluate(execution: program) { - switch EvaluateExpression(self.selectExpression, inExecution: program) { + switch program.evaluator.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()) diff --git a/Sources/P4Runtime/Runtime.swift b/Sources/P4Runtime/Runtime.swift index 64e2fd1..fc54dc1 100644 --- a/Sources/P4Runtime/Runtime.swift +++ b/Sources/P4Runtime/Runtime.swift @@ -59,7 +59,7 @@ public struct ParserRuntime: CustomStringConvertible { { let pe = if let initial = initialValues { - ProgramExecution(withGlobalValues: initial) + ProgramExecution().setGlobalValues(initial) } else { ProgramExecution() } diff --git a/Sources/P4Runtime/Statements.swift b/Sources/P4Runtime/Statements.swift index 8bdc051..05ef20d 100644 --- a/Sources/P4Runtime/Statements.swift +++ b/Sources/P4Runtime/Statements.swift @@ -20,7 +20,7 @@ import P4Lang extension BlockStatement: EvaluatableStatement { public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { - return ExecuteStatement( + return execution.evaluator.ExecuteStatement( self.statements, handleResult: { (cf, execution) in switch cf { @@ -43,7 +43,7 @@ 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) = EvaluateExpression( + case (.Ok(let initial_value), let execution) = execution.evaluator.EvaluateExpression( self.initializer, inExecution: execution) else { return ( @@ -61,7 +61,7 @@ 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) = EvaluateExpression( + case (.Ok(let evaluated_condition), let execution) = execution.evaluator.EvaluateExpression( self.condition, inExecution: execution) else { return ( @@ -111,7 +111,7 @@ extension ExpressionStatement: EvaluatableStatement { // Evaluate, there might be side effects! //return switch self.expression.evaluate(execution: execution) { - return switch EvaluateExpression(self.expression, inExecution: execution) { + return switch execution.evaluator.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)) @@ -122,7 +122,7 @@ extension ExpressionStatement: EvaluatableStatement { extension ReturnStatement: EvaluatableStatement { public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { //return switch self.value.evaluate(execution: execution) { - return switch EvaluateExpression(self.value, inExecution: execution) { + return switch execution.evaluator.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/InterloperTests.swift b/Tests/p4rseTests/InterloperTests.swift index f6f3640..da19c87 100644 --- a/Tests/p4rseTests/InterloperTests.swift +++ b/Tests/p4rseTests/InterloperTests.swift @@ -51,11 +51,11 @@ import TreeSitterP4 var statements_executed: [String] = Array() - let pe = ProgramExecution().setStatementInterloper({ (statement, cf, execution) in + let ev = InterloperEvaluator().setStatementInterloper() { (statement, cf, execution) in statements_executed.append("\(statement)") - }) + } - let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: ArgumentList(), inExecution: pe)) + let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: ArgumentList(), inExecution: ProgramExecution(ev))) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept) @@ -90,12 +90,11 @@ import TreeSitterP4 var expressions_evaluated: [String] = Array() - let pe = ProgramExecution().setExpressionInterloper() { expression, result, execution in - print("Expression: \(expression)") + let ev = InterloperEvaluator().setExpressionInterloper() { expression, result, execution in expressions_evaluated.append("\(expression)") } - let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: ArgumentList(), inExecution: pe)) + let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: ArgumentList(), inExecution: ProgramExecution(ev))) #expect(AsInstantiatedParserState(state_result) == P4Lang.accept)