Files
gp4/Sources/Common/Execution.swift
T
Will Hawkins a24571222b 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 <hawkinsw@obs.cr>
2026-04-20 16:28:07 -04:00

269 lines
7.9 KiB
Swift

// 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 <https://www.gnu.org/licenses/>.
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<P4Value>, 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<P4Value>, 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<P4Value>, ProgramExecution)
-> Void
open class ProgramExecution: CustomStringConvertible {
public var scopes: VarValueScopes = VarValueScopes()
var globalValues: VarValueScopes?
var error: Error?
var debug: DebugLevel = DebugLevel.Error
public let evaluator: ProgramExecutionEvaluator
init(copy: ProgramExecution) {
self.scopes = copy.scopes
self.globalValues = copy.globalValues
self.error = copy.error
self.debug = copy.debug
self.evaluator = copy.evaluator
}
public init() {
globalValues = .none
evaluator = ClassicEvaluator()
}
public init(_ evaluator: ProgramExecutionEvaluator) {
globalValues = .none
self.evaluator = evaluator
}
open var description: String {
return "Runtime:\nScopes: \(scopes)"
}
public func hasError() -> Bool {
return self.error != nil
}
public func getError() -> Error? {
return self.error
}
public func setError(error: Error) -> ProgramExecution {
let npe = ProgramExecution(copy: self)
npe.error = error
return npe
}
public func getDebugLevel() -> DebugLevel {
return self.debug
}
public func setDebugLevel(_ dl: DebugLevel) -> ProgramExecution {
let pe = ProgramExecution(copy: self)
pe.debug = dl
return pe
}
open func isDone() -> Bool {
return false
}
open func setDone() -> ProgramExecution {
// For a bare ProgramExecution, setDone is a noop.
return self
}
public func enter_scope() -> ProgramExecution {
let new_pe = ProgramExecution(copy: self)
new_pe.scopes = new_pe.scopes.enter()
return new_pe
}
public func exit_scope() -> ProgramExecution {
let new_pe = ProgramExecution(copy: self)
new_pe.scopes = new_pe.scopes.exit()
return new_pe
}
public func replaceScopes(_ new_scopes: VarValueScopes) -> ProgramExecution {
let new_pe = ProgramExecution(copy: self)
new_pe.scopes = new_scopes
return new_pe
}
public func declare(identifier: Identifier, withValue value: P4Value) -> ProgramExecution {
let new_pe = ProgramExecution(copy: self)
let new_scopes = new_pe.scopes.declare(identifier: identifier, withValue: value)
new_pe.scopes = new_scopes
return new_pe
}
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.
public typealias VarValueScope = Scope<P4Value>
/// Scopes that resolves variable identifiers to their values.
public typealias VarValueScopes = Scopes<P4Value>
/// Indicate the control flow result of a particular statement.
public enum ControlFlow {
case Next
case Continue
case Break
case Return(P4Value?)
case Error
}