@@ -21,6 +21,13 @@ open class ProgramExecution: CustomStringConvertible {
|
||||
var error: Error?
|
||||
var debug: DebugLevel = DebugLevel.Error
|
||||
|
||||
init(copy: ProgramExecution) {
|
||||
self.scopes = copy.scopes
|
||||
self.initialValues = copy.initialValues
|
||||
self.error = copy.error
|
||||
self.debug = copy.debug
|
||||
}
|
||||
|
||||
public init() {
|
||||
initialValues = .none
|
||||
}
|
||||
@@ -42,7 +49,7 @@ open class ProgramExecution: CustomStringConvertible {
|
||||
}
|
||||
|
||||
public func setError(error: Error) -> ProgramExecution {
|
||||
let npe = self
|
||||
let npe = ProgramExecution(copy: self)
|
||||
npe.error = error
|
||||
return npe
|
||||
}
|
||||
@@ -52,7 +59,7 @@ open class ProgramExecution: CustomStringConvertible {
|
||||
}
|
||||
|
||||
public func setDebugLevel(_ dl: DebugLevel) -> ProgramExecution {
|
||||
let pe = self
|
||||
let pe = ProgramExecution(copy: self)
|
||||
pe.debug = dl
|
||||
return pe
|
||||
}
|
||||
@@ -67,22 +74,22 @@ open class ProgramExecution: CustomStringConvertible {
|
||||
}
|
||||
|
||||
public func enter_scope() -> ProgramExecution {
|
||||
let new_pe = self
|
||||
new_pe.scopes = self.scopes.enter()
|
||||
let new_pe = ProgramExecution(copy: self)
|
||||
new_pe.scopes = new_pe.scopes.enter()
|
||||
|
||||
return new_pe
|
||||
}
|
||||
|
||||
public func exit_scope() -> ProgramExecution {
|
||||
let new_pe = self
|
||||
new_pe.scopes = self.scopes.exit()
|
||||
let new_pe = ProgramExecution(copy: self)
|
||||
new_pe.scopes = new_pe.scopes.exit()
|
||||
|
||||
return new_pe
|
||||
}
|
||||
|
||||
public func declare(identifier: Identifier, withValue value: P4Value) -> ProgramExecution {
|
||||
let new_pe = self
|
||||
let new_scopes = self.scopes.declare(identifier: identifier, withValue: value)
|
||||
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
|
||||
@@ -98,3 +105,12 @@ 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
|
||||
}
|
||||
|
||||
@@ -28,8 +28,10 @@ public protocol EvaluatableStatement {
|
||||
/// Evaluate a statement for a given execution
|
||||
/// - Parameters
|
||||
/// - execution: The execution context in which to evaluate the parser statement
|
||||
/// - Returns: An updated execution after evaluating the parser statement
|
||||
func evaluate(execution: ProgramExecution) -> ProgramExecution
|
||||
/// - Returns: A tuple of
|
||||
/// 1. Whether this statement affects control flow.
|
||||
/// 2. An updated execution after evaluating the parser statement
|
||||
func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution)
|
||||
}
|
||||
|
||||
public protocol P4Type: CustomStringConvertible {
|
||||
|
||||
@@ -0,0 +1,318 @@
|
||||
// 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/>.
|
||||
|
||||
import Common
|
||||
import P4Lang
|
||||
import P4Runtime
|
||||
import SwiftTreeSitter
|
||||
import TreeSitterExtensions
|
||||
import TreeSitterP4
|
||||
|
||||
func parameter_list_compiler(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ParameterList, CompilerContext)> {
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
|
||||
if node.text == ")" {
|
||||
// There are no parameters!
|
||||
return Result.Ok((ParameterList([]), context))
|
||||
}
|
||||
|
||||
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
|
||||
node: node, type: "parameter_list", nice_type_name: "Parameter List")
|
||||
|
||||
var parameters: ParameterList = ParameterList([])
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
if currentChild?.nodeType == "parameter_list" {
|
||||
switch parameter_list_compiler(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (ps, _)):
|
||||
parameters = ps
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
// We may have moved nodes, check/reset currentChild.
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// If this is a ')', we are done.
|
||||
if currentChild?.text == ")" {
|
||||
return Result.Ok((parameters, context))
|
||||
}
|
||||
|
||||
// If this is a comma, we skip it!
|
||||
if currentChild?.text == "," {
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// Otherwise, there should be one parameter left!
|
||||
switch Parameter.Compile(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (vds, updated_context)):
|
||||
return Result.Ok((parameters.addParameter(vds), updated_context))
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
extension ParameterList: Compilable {
|
||||
public typealias T = ParameterList
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ParameterList, CompilerContext)> {
|
||||
|
||||
let parameter_node = node
|
||||
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
|
||||
node: parameter_node, type: "parameters", nice_type_name: "Parameters")
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
|
||||
// Let's eat the '(' before we start ...
|
||||
if parameter_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: parameter_node, withError: "Missing '(' in parameter list component"))
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if parameter_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: parameter_node, withError: "Missing parameter list component"))
|
||||
}
|
||||
let currentChild = parameter_node.child(at: currentChildIdx)
|
||||
|
||||
return parameter_list_compiler(node: currentChild!, withContext: context)
|
||||
}
|
||||
}
|
||||
|
||||
extension Parameter: Compilable {
|
||||
public typealias T = Parameter
|
||||
public static func Compile(
|
||||
node: Node, withContext context: CompilerContext
|
||||
) -> Result<(Parameter, CompilerContext)> {
|
||||
|
||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
||||
node: node, type: "parameter", nice_type_name: "parameter")
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter declaration component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// Annotation?
|
||||
if currentChild!.nodeType == "annotations" {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: currentChild!,
|
||||
withError: "Annotations in parameter declarations are not yet handled"))
|
||||
// Will increment indexes here.
|
||||
}
|
||||
|
||||
// Direction?
|
||||
if currentChild!.nodeType == "direction" {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: currentChild!, withError: "Direction in parameter declarations are not yet handled"
|
||||
))
|
||||
// Will increment indexes here.
|
||||
}
|
||||
|
||||
if currentChild!.nodeType != "typeRef" {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Did not find type name for parameter declaration"))
|
||||
}
|
||||
|
||||
guard
|
||||
case .Ok(let parameter_type) = Types.CompileType(type: currentChild!, withContext: context)
|
||||
else {
|
||||
return Result.Error(
|
||||
Error(withMessage: "Could not parse a P4 type from \(currentChild!.text!)"))
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter declaration component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
if currentChild!.nodeType != "identifier" {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Did not find identifier for parameter statement"))
|
||||
}
|
||||
|
||||
guard
|
||||
case .Ok(let parameter_name) = Identifier.Compile(node: currentChild!, withContext: context)
|
||||
else {
|
||||
return Result.Error(
|
||||
Error(withMessage: "Could not parse a parameter name from \(currentChild!.text!)"))
|
||||
}
|
||||
|
||||
return Result.Ok(
|
||||
(
|
||||
Parameter(
|
||||
identifier: parameter_name, withType: parameter_type),
|
||||
context
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
func argument_list_compiler(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ArgumentList, CompilerContext)> {
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
|
||||
if node.text == ")" {
|
||||
// There are no arguments!
|
||||
return Result.Ok((ArgumentList([]), context))
|
||||
}
|
||||
|
||||
#RequireNodeType<Node, (ArgumentList, CompilerContext)>(
|
||||
node: node, type: "argument_list", nice_type_name: "argument List")
|
||||
|
||||
var arguments: ArgumentList = ArgumentList([])
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing argument list component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
if currentChild?.nodeType == "argument_list" {
|
||||
switch argument_list_compiler(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (ps, _)):
|
||||
arguments = ps
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
// We may have moved nodes, check/reset currentChild.
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing argument list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// If this is a ')', we are done.
|
||||
if currentChild?.text == ")" {
|
||||
return Result.Ok((arguments, context))
|
||||
}
|
||||
|
||||
// If this is a comma, we skip it!
|
||||
if currentChild?.text == "," {
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing argument list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// Otherwise, there should be one argument left!
|
||||
switch Argument.Compile(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (ce, updated_context)):
|
||||
return Result.Ok((arguments.addArgument(Argument(ce, atIndex: arguments.count() + 1)), updated_context))
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
extension ArgumentList: Compilable {
|
||||
public typealias T = ArgumentList
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ArgumentList, CompilerContext)> {
|
||||
|
||||
let argument_node = node
|
||||
#RequireNodeType<Node, (ArgumentList, CompilerContext)>(
|
||||
node: argument_node, type: "arguments", nice_type_name: "arguments")
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
|
||||
// Let's eat the '(' before we start ...
|
||||
if argument_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: argument_node, withError: "Missing '(' in argument list component"))
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if argument_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: argument_node, withError: "Missing argument list component"))
|
||||
}
|
||||
let currentChild = argument_node.child(at: currentChildIdx)
|
||||
|
||||
return argument_list_compiler(node: currentChild!, withContext: context)
|
||||
}
|
||||
}
|
||||
|
||||
extension Argument: Compilable {
|
||||
public typealias T = EvaluatableExpression
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(EvaluatableExpression, CompilerContext)> {
|
||||
let argument_node = node
|
||||
#RequireNodeType<Node, (EvaluatableExpression, CompilerContext)>(
|
||||
node: argument_node, type: "argument", nice_type_name: "argument")
|
||||
|
||||
let expression_node = node.child(at: 0)!
|
||||
|
||||
return switch Expression.Compile(node: expression_node, withContext: context) {
|
||||
case .Ok(let compiled_expression): .Ok((compiled_expression, context))
|
||||
case .Error(let e): .Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -379,185 +379,6 @@ extension P4Lang.Parser: CompilableDeclaration {
|
||||
}
|
||||
}
|
||||
|
||||
func parameter_list_compiler(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ParameterList, CompilerContext)> {
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
|
||||
if node.text == ")" {
|
||||
// There are no parameters!
|
||||
return Result.Ok((ParameterList([]), context))
|
||||
}
|
||||
|
||||
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
|
||||
node: node, type: "parameter_list", nice_type_name: "Parameter List")
|
||||
|
||||
var parameters: ParameterList = ParameterList([])
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
if currentChild?.nodeType == "parameter_list" {
|
||||
switch parameter_list_compiler(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (ps, _)):
|
||||
parameters = ps
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
// We may have moved nodes, check/reset currentChild.
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// If this is a ')', we are done.
|
||||
if currentChild?.text == ")" {
|
||||
return Result.Ok((parameters, context))
|
||||
}
|
||||
|
||||
// If this is a comma, we skip it!
|
||||
if currentChild?.text == "," {
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// Otherwise, there should be one parameter left!
|
||||
switch Parameter.Compile(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (vds, updated_context)):
|
||||
return Result.Ok((parameters.addParameter(vds), updated_context))
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
extension ParameterList: Compilable {
|
||||
public typealias T = ParameterList
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ParameterList, CompilerContext)> {
|
||||
|
||||
let parameter_node = node
|
||||
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
|
||||
node: parameter_node, type: "parameters", nice_type_name: "Parameters")
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
|
||||
// Let's eat the '(' before we start ...
|
||||
if parameter_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: parameter_node, withError: "Missing '(' in parameter list component"))
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if parameter_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: parameter_node, withError: "Missing parameter list component"))
|
||||
}
|
||||
let currentChild = parameter_node.child(at: currentChildIdx)
|
||||
|
||||
return parameter_list_compiler(node: currentChild!, withContext: context)
|
||||
}
|
||||
}
|
||||
|
||||
extension Parameter: Compilable {
|
||||
public typealias T = Parameter
|
||||
public static func Compile(
|
||||
node: Node, withContext context: CompilerContext
|
||||
) -> Result<(Parameter, CompilerContext)> {
|
||||
|
||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
||||
node: node, type: "parameter", nice_type_name: "parameter")
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter declaration component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// Annotation?
|
||||
if currentChild!.nodeType == "annotations" {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: currentChild!,
|
||||
withError: "Annotations in parameter declarations are not yet handled"))
|
||||
// Will increment indexes here.
|
||||
}
|
||||
|
||||
// Direction?
|
||||
if currentChild!.nodeType == "direction" {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: currentChild!, withError: "Direction in parameter declarations are not yet handled"
|
||||
))
|
||||
// Will increment indexes here.
|
||||
}
|
||||
|
||||
if currentChild!.nodeType != "typeRef" {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Did not find type name for parameter declaration"))
|
||||
}
|
||||
|
||||
guard
|
||||
case .Ok(let parameter_type) = Types.CompileType(type: currentChild!, withContext: context)
|
||||
else {
|
||||
return Result.Error(
|
||||
Error(withMessage: "Could not parse a P4 type from \(currentChild!.text!)"))
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter declaration component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
if currentChild!.nodeType != "identifier" {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Did not find identifier for parameter statement"))
|
||||
}
|
||||
|
||||
guard
|
||||
case .Ok(let parameter_name) = Identifier.Compile(node: currentChild!, withContext: context)
|
||||
else {
|
||||
return Result.Error(
|
||||
Error(withMessage: "Could not parse a parameter name from \(currentChild!.text!)"))
|
||||
}
|
||||
|
||||
return Result.Ok(
|
||||
(
|
||||
Parameter(
|
||||
identifier: parameter_name, withType: parameter_type),
|
||||
context
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
extension Control: CompilableDeclaration {
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
|
||||
@@ -163,6 +163,7 @@ struct Expression {
|
||||
let expression_parsers: [CompilableExpression.Type] = [
|
||||
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self,
|
||||
BinaryOperatorExpression.self, ArrayAccessExpression.self, FieldAccessExpression.self,
|
||||
FunctionCall.self
|
||||
]
|
||||
|
||||
for candidate_expression_parser in expression_parsers {
|
||||
@@ -653,3 +654,67 @@ extension ArrayAccessExpression: CompilableLValueExpression {
|
||||
return Result.Ok(array_access_expression)
|
||||
}
|
||||
}
|
||||
|
||||
extension FunctionCall: CompilableExpression {
|
||||
static func compile(
|
||||
node: Node, withContext context: CompilerContext
|
||||
) -> Result<EvaluatableExpression?> {
|
||||
let expression = node.child(at: 0)!
|
||||
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
|
||||
node: expression, type: "function_call")
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
|
||||
if expression.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing function call component"))
|
||||
}
|
||||
|
||||
currentChild = expression.child(at: currentChildIdx)
|
||||
|
||||
let maybe_callee_name = Identifier.Compile(
|
||||
node: currentChild!, withContext: context)
|
||||
guard case .Ok(let callee_name) = maybe_callee_name else {
|
||||
return Result.Error(maybe_callee_name.error()!)
|
||||
}
|
||||
|
||||
let maybe_callee = switch context.types.lookup(identifier: callee_name) {
|
||||
case .Ok(let looked_up): switch looked_up {
|
||||
case let callee as FunctionDeclaration: Result.Ok(callee) // What we found is actually a function declaration
|
||||
default: Result<FunctionDeclaration>.Error(ErrorOnNode(node: currentChild!, withError: "\(callee_name) is not a function"))
|
||||
}
|
||||
case .Error(let e): Result<FunctionDeclaration>.Error(e)
|
||||
}
|
||||
|
||||
guard case .Ok(let callee) = maybe_callee else {
|
||||
return .Error(maybe_callee.error()!)
|
||||
}
|
||||
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if expression.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing function call component"))
|
||||
}
|
||||
currentChild = expression.child(at: currentChildIdx)
|
||||
|
||||
let maybe_argument_list = ArgumentList.Compile(node: currentChild!, withContext: context)
|
||||
|
||||
guard case .Ok((let arguments, _)) = maybe_argument_list else {
|
||||
return .Error(maybe_argument_list.error()!)
|
||||
}
|
||||
|
||||
// Now, compare the arguments with the parameters:
|
||||
|
||||
if case .Error(let e) = arguments.compatible(callee.params) {
|
||||
return .Error(e)
|
||||
}
|
||||
|
||||
// All good!
|
||||
|
||||
return .Ok(FunctionCall(callee, withArguments: arguments))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ public struct Parser {
|
||||
"expressionStatement": ExpressionStatement.self,
|
||||
"variableDeclaration": VariableDeclarationStatement.self,
|
||||
"conditionalStatement": ConditionalStatement.self, "blockStatement": BlockStatement.self,
|
||||
"return_statement": ReturnStatement.self,
|
||||
]
|
||||
guard let parser = statementParsers[statement.nodeType ?? ""] else {
|
||||
return Result.Error(
|
||||
|
||||
@@ -299,3 +299,19 @@ extension ParserAssignmentStatement: CompilableStatement {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
extension ReturnStatement: CompilableStatement {
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(any Common.EvaluatableStatement, CompilerContext)> {
|
||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
||||
node: node, type: "return_statement", nice_type_name: "return statement")
|
||||
|
||||
let expression_node = node.child(at: 1)!
|
||||
|
||||
return switch Expression.Compile(node: expression_node, withContext: context) {
|
||||
case .Ok(let result): .Ok((ReturnStatement(result), context))
|
||||
case .Error(let e): .Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// 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/>.
|
||||
|
||||
import Common
|
||||
|
||||
public struct Parameter: CustomStringConvertible, Equatable {
|
||||
public static func == (lhs: Parameter, rhs: Parameter) -> Bool {
|
||||
return lhs.name == rhs.name && lhs.type.eq(rhs: rhs.type)
|
||||
}
|
||||
|
||||
public var name: Identifier
|
||||
public var type: P4Type
|
||||
|
||||
public init(
|
||||
identifier: Identifier, withType type: P4Type
|
||||
) {
|
||||
self.name = identifier
|
||||
self.type = type
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Parameter: \(self.name) with type \(self.type)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct ParameterList: CustomStringConvertible, Equatable {
|
||||
public static func == (lhs: ParameterList, rhs: ParameterList) -> Bool {
|
||||
if lhs.parameters.count != rhs.parameters.count {
|
||||
return false
|
||||
}
|
||||
|
||||
return 0
|
||||
== zip(lhs.parameters, rhs.parameters).count { (lparam, rparam) in
|
||||
return lparam != rparam
|
||||
}
|
||||
}
|
||||
|
||||
public var parameters: [Parameter]
|
||||
|
||||
public init() {
|
||||
self.parameters = Array()
|
||||
}
|
||||
|
||||
public init(_ parameters: [Parameter]) {
|
||||
self.parameters = parameters
|
||||
}
|
||||
|
||||
public func addParameter(_ parameter: Parameter) -> ParameterList {
|
||||
return ParameterList(self.parameters + [parameter])
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
let parameters = self.parameters.map { parameter in
|
||||
parameter.description
|
||||
}.joined(separator: ";")
|
||||
return "Parameter list: \(parameters)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct ArgumentList {
|
||||
public let arguments: [Argument]
|
||||
|
||||
public init(_ arguments: [Argument]) {
|
||||
self.arguments = arguments
|
||||
}
|
||||
|
||||
public func compatible(_ parameters: ParameterList) -> Result<()> {
|
||||
if self.arguments.count != parameters.parameters.count {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage:
|
||||
"\(self.arguments.count) arguments found but \(parameters.parameters.count) required"))
|
||||
}
|
||||
|
||||
for (arg, param) in zip(self.arguments, parameters.parameters) {
|
||||
let arg_index = arg.index
|
||||
let arg_type = arg.argument.type()
|
||||
if !arg_type.eq(rhs: param.type) {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage:
|
||||
"Argument \(arg_index)'s type (\(arg_type)) is incompatible with the parameter type (\(param.type))"
|
||||
))
|
||||
}
|
||||
}
|
||||
return .Ok(())
|
||||
}
|
||||
|
||||
public func addArgument(_ argument: Argument) -> ArgumentList {
|
||||
return ArgumentList(self.arguments + [argument])
|
||||
}
|
||||
|
||||
public func count() -> Int {
|
||||
return self.arguments.count
|
||||
}
|
||||
}
|
||||
|
||||
public struct Argument {
|
||||
public let index: Int
|
||||
public let argument: EvaluatableExpression
|
||||
|
||||
public init(_ argument: EvaluatableExpression, atIndex index: Int) {
|
||||
self.argument = argument
|
||||
self.index = index
|
||||
}
|
||||
}
|
||||
@@ -19,60 +19,6 @@ import Common
|
||||
|
||||
public struct Declaration {}
|
||||
|
||||
public struct Parameter: CustomStringConvertible, Equatable {
|
||||
public static func == (lhs: Parameter, rhs: Parameter) -> Bool {
|
||||
return lhs.name == rhs.name && lhs.type.eq(rhs: rhs.type)
|
||||
}
|
||||
|
||||
public var name: Identifier
|
||||
public var type: P4Type
|
||||
|
||||
public init(
|
||||
identifier: Identifier, withType type: P4Type
|
||||
) {
|
||||
self.name = identifier
|
||||
self.type = type
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Parameter: \(self.name) with type \(self.type)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct ParameterList: CustomStringConvertible, Equatable {
|
||||
public static func == (lhs: ParameterList, rhs: ParameterList) -> Bool {
|
||||
if lhs.parameters.count != rhs.parameters.count {
|
||||
return false
|
||||
}
|
||||
|
||||
return 0
|
||||
== zip(lhs.parameters, rhs.parameters).count { (lparam, rparam) in
|
||||
return lparam != rparam
|
||||
}
|
||||
}
|
||||
|
||||
public var parameters: [Parameter]
|
||||
|
||||
public init() {
|
||||
self.parameters = Array()
|
||||
}
|
||||
|
||||
public init(_ parameters: [Parameter]) {
|
||||
self.parameters = parameters
|
||||
}
|
||||
|
||||
public func addParameter(_ parameter: Parameter) -> ParameterList {
|
||||
return ParameterList(self.parameters + [parameter])
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
let parameters = self.parameters.map { parameter in
|
||||
parameter.description
|
||||
}.joined(separator: ";")
|
||||
return "Parameter list: \(parameters)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct FunctionDeclaration: P4Type, P4Value {
|
||||
public func type() -> any Common.P4Type {
|
||||
return self
|
||||
|
||||
@@ -201,33 +201,12 @@ public struct FieldAccessExpression {
|
||||
}
|
||||
}
|
||||
|
||||
public struct ArgumentList {
|
||||
public let arguments: [(Int, EvaluatableExpression)]
|
||||
public init(_ arguments: [EvaluatableExpression]) {
|
||||
self.arguments = zip(1..., arguments).map { (idx, argument) in
|
||||
(idx, argument)
|
||||
}
|
||||
}
|
||||
public struct FunctionCall {
|
||||
public let callee: FunctionDeclaration
|
||||
public let arguments: ArgumentList
|
||||
|
||||
public func compatible(_ parameters: ParameterList) -> Result<()> {
|
||||
if self.arguments.count != parameters.parameters.count {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage:
|
||||
"\(self.arguments.count) arguments found but \(parameters.parameters.count) required"))
|
||||
}
|
||||
|
||||
for (arg, param) in zip(self.arguments, parameters.parameters) {
|
||||
let arg_index = arg.0
|
||||
let arg_type = arg.1.type()
|
||||
if !arg_type.eq(rhs: param.type) {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage:
|
||||
"Argument \(arg_index)'s type (\(arg_type)) is incompatible with the parameter type (\(param.type))"
|
||||
))
|
||||
}
|
||||
}
|
||||
return .Ok(())
|
||||
public init(_ callee: FunctionDeclaration, withArguments arguments: ArgumentList) {
|
||||
self.callee = callee
|
||||
self.arguments = arguments
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,3 +55,11 @@ public struct BlockStatement {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct ReturnStatement {
|
||||
public let value: EvaluatableExpression
|
||||
|
||||
public init(_ value: EvaluatableExpression) {
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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/>.
|
||||
|
||||
import Common
|
||||
import P4Lang
|
||||
@@ -382,3 +382,40 @@ extension KeysetExpression: EvaluatableExpression {
|
||||
return self.kse_type()
|
||||
}
|
||||
}
|
||||
|
||||
extension FunctionCall: EvaluatableExpression {
|
||||
public func evaluate(execution: Common.ProgramExecution) -> Common.Result<any Common.P4Value> {
|
||||
|
||||
guard let body = self.callee.body else {
|
||||
return .Error(Error(withMessage: "No body for called function (\(self.callee.name))"))
|
||||
}
|
||||
|
||||
// Put the arguments into scope
|
||||
|
||||
var called_execution = execution.enter_scope()
|
||||
for (parameter, argument) in zip(self.callee.params.parameters, arguments.arguments) {
|
||||
let arg_idx = argument.index
|
||||
let arg_value = argument.argument
|
||||
let maybe_argument_value = arg_value.evaluate(execution: called_execution)
|
||||
guard case .Ok(let argument_value) = maybe_argument_value else {
|
||||
return .Error(Error(withMessage: "Cannot evaluate argument \(arg_idx): \(argument)"))
|
||||
}
|
||||
called_execution = called_execution.declare(identifier: parameter.name, withValue: argument_value)
|
||||
}
|
||||
|
||||
let (control_flow, _) = body.evaluate(execution: called_execution)
|
||||
|
||||
return switch control_flow {
|
||||
case ControlFlow.Return(let value): if let value = value {
|
||||
.Ok(value)
|
||||
} else {
|
||||
.Error(Error(withMessage: "No value returned from called function (\(self.callee.name))"))
|
||||
}
|
||||
default: .Error(Error(withMessage: "No value returned from called function (\(self.callee.name))"))
|
||||
}
|
||||
}
|
||||
|
||||
public func type() -> any Common.P4Type {
|
||||
return self.callee.tipe
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,20 +19,20 @@ import Common
|
||||
import P4Lang
|
||||
|
||||
extension ParserAssignmentStatement: EvaluatableStatement {
|
||||
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
|
||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||
let result = self.value.evaluate(execution: execution)
|
||||
guard case Result.Ok(let value) = result else {
|
||||
return execution.setError(error: result.error()!)
|
||||
return (ControlFlow.Error, execution.setError(error: result.error()!))
|
||||
}
|
||||
|
||||
let maybe_updated_scopes = self.lvalue.set(
|
||||
to: value, inScopes: execution.scopes, duringExecution: execution)
|
||||
guard case Result.Ok(let updated_scopes) = maybe_updated_scopes else {
|
||||
return execution.setError(error: maybe_updated_scopes.error()!)
|
||||
return (ControlFlow.Error, execution.setError(error: maybe_updated_scopes.error()!))
|
||||
}
|
||||
execution.scopes = updated_scopes.0
|
||||
|
||||
return execution
|
||||
return (ControlFlow.Next, execution)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,12 @@ extension ParserStateDirectTransition: EvaluatableParserState {
|
||||
var program = program.enter_scope()
|
||||
|
||||
for statement in statements {
|
||||
program = statement.evaluate(execution: program)
|
||||
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 res = program.scopes.lookup(identifier: get_next_state())
|
||||
|
||||
@@ -93,7 +98,12 @@ extension ParserStateSelectTransition: EvaluatableParserState {
|
||||
|
||||
// First, evaluate the statements.
|
||||
for statement in statements {
|
||||
program = statement.evaluate(execution: program)
|
||||
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 res = self.selectExpression.evaluate(execution: program)
|
||||
@@ -159,8 +169,8 @@ extension Parser: CallableExecution {
|
||||
}
|
||||
|
||||
for (parameter, argument) in zip(self.parameters.parameters, arguments.arguments) {
|
||||
let arg_idx = argument.0
|
||||
let arg_value = argument.1
|
||||
let arg_idx = argument.index
|
||||
let arg_value = argument.argument
|
||||
let maybe_argument_value = arg_value.evaluate(execution: execution)
|
||||
guard case .Ok(let argument_value) = maybe_argument_value else {
|
||||
return (
|
||||
|
||||
@@ -22,12 +22,6 @@ public protocol Execution {
|
||||
func execute(execution: ProgramExecution) -> ProgramExecution
|
||||
}
|
||||
|
||||
public protocol Compilable {
|
||||
associatedtype ToCompile
|
||||
associatedtype Compiled
|
||||
static func compile(_: ToCompile) -> Result<Compiled>
|
||||
}
|
||||
|
||||
public protocol EvaluatableParserState: P4Value {
|
||||
func execute(program: ProgramExecution) -> (EvaluatableParserState, ProgramExecution)
|
||||
func done() -> Bool
|
||||
|
||||
@@ -19,51 +19,99 @@ import Common
|
||||
import P4Lang
|
||||
|
||||
extension BlockStatement: EvaluatableStatement {
|
||||
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
|
||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||
var execution = execution
|
||||
for s in self.statements {
|
||||
execution = s.evaluate(execution: execution)
|
||||
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 execution
|
||||
return (ControlFlow.Next, execution)
|
||||
}
|
||||
}
|
||||
|
||||
extension VariableDeclarationStatement: EvaluatableStatement {
|
||||
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
|
||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||
guard case .Ok(let initial_value) = self.initializer.evaluate(execution: execution) else {
|
||||
return execution.setError(error: Error(withMessage: "Could not evaluate \(self.initializer)"))
|
||||
return (
|
||||
ControlFlow.Error,
|
||||
execution.setError(error: Error(withMessage: "Could not evaluate \(self.initializer)"))
|
||||
)
|
||||
}
|
||||
let new_scopes = execution.scopes.declare(identifier: self.identifier, withValue: initial_value)
|
||||
execution.scopes = new_scopes
|
||||
return execution
|
||||
return (ControlFlow.Next, execution)
|
||||
}
|
||||
}
|
||||
|
||||
extension ConditionalStatement: EvaluatableStatement {
|
||||
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
|
||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||
guard case .Ok(let evaluated_condition) = self.condition.evaluate(execution: execution) else {
|
||||
return execution.setError(error: Error(withMessage: "Could not evaluate \(self.condition)"))
|
||||
return (
|
||||
ControlFlow.Error,
|
||||
execution.setError(error: Error(withMessage: "Could not evaluate \(self.condition)"))
|
||||
)
|
||||
}
|
||||
|
||||
if !evaluated_condition.type().eq(rhs: P4Boolean()) {
|
||||
return execution.setError(error: Error(withMessage: "Condition expression is not a Boolean"))
|
||||
return (
|
||||
ControlFlow.Error,
|
||||
execution.setError(error: Error(withMessage: "Condition expression is not a Boolean"))
|
||||
)
|
||||
}
|
||||
|
||||
if evaluated_condition.eq(rhs: P4BooleanValue.init(withValue: true)) {
|
||||
let execution = execution.enter_scope()
|
||||
var result = self.thenn.evaluate(execution: execution)
|
||||
result = result.exit_scope()
|
||||
return result
|
||||
switch self.thenn.evaluate(execution: execution) {
|
||||
case (ControlFlow.Next, let result): return (ControlFlow.Next, result.exit_scope())
|
||||
case (ControlFlow.Error, let result): return (ControlFlow.Error, result.exit_scope())
|
||||
case (let cf, let result):
|
||||
return (
|
||||
ControlFlow.Next,
|
||||
result.setError(
|
||||
error: Error(withMessage: "Invalid control flow \(cf) in conditional statement"))
|
||||
)
|
||||
}
|
||||
} else if let elss = self.elss {
|
||||
let execution = execution.enter_scope()
|
||||
var result = elss.evaluate(execution: execution)
|
||||
result = result.exit_scope()
|
||||
return result
|
||||
switch elss.evaluate(execution: execution) {
|
||||
case (ControlFlow.Next, let result): return (ControlFlow.Next, result.exit_scope())
|
||||
case (ControlFlow.Error, let result): return (ControlFlow.Error, result.exit_scope())
|
||||
case (let cf, let result):
|
||||
return (
|
||||
ControlFlow.Next,
|
||||
result.setError(
|
||||
error: Error(withMessage: "Invalid control flow \(cf) in conditional statement"))
|
||||
)
|
||||
}
|
||||
}
|
||||
return execution
|
||||
return (ControlFlow.Next, execution)
|
||||
}
|
||||
}
|
||||
|
||||
extension ExpressionStatement: EvaluatableStatement {
|
||||
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
|
||||
return execution
|
||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||
// TODO: Should this do something? Side effects?
|
||||
return (ControlFlow.Next, execution)
|
||||
}
|
||||
}
|
||||
|
||||
extension ReturnStatement: EvaluatableStatement {
|
||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||
return switch self.value.evaluate(execution: execution) {
|
||||
case .Ok(let v): (ControlFlow.Return(v), execution)
|
||||
case .Error(let e): (ControlFlow.Error, execution.setError(error: e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user