diff --git a/Sources/Common/Execution.swift b/Sources/Common/Execution.swift
index e6d1541..bbf75d7 100644
--- a/Sources/Common/Execution.swift
+++ b/Sources/Common/Execution.swift
@@ -87,6 +87,12 @@ open class ProgramExecution: CustomStringConvertible {
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)
diff --git a/Sources/Common/P4Types.swift b/Sources/Common/P4Types.swift
index e3a7ea0..fcf3afa 100644
--- a/Sources/Common/P4Types.swift
+++ b/Sources/Common/P4Types.swift
@@ -15,6 +15,13 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+public enum TypeCheckResults: Equatable {
+ case Ok
+ case ReadOnly
+ case WrongDirection
+ case IncompatibleTypes
+}
+
public enum Direction: Equatable, CustomStringConvertible {
case In
case Out
@@ -115,11 +122,11 @@ public struct P4Type: CustomStringConvertible {
}
public func update(removeAttribute attribute: P4TypeAttribute) -> P4Type {
- return P4Type(self._data_type, self._attributes.update(addAttribute: attribute))
+ return P4Type(self._data_type, self._attributes.update(removeAttribute: attribute))
}
public func update(addAttribute attribute: P4TypeAttribute) -> P4Type {
- return P4Type(self._data_type, self._attributes.update(removeAttribute: attribute))
+ return P4Type(self._data_type, self._attributes.update(addAttribute: attribute))
}
public func direction() -> Direction? {
@@ -143,6 +150,37 @@ public struct P4Type: CustomStringConvertible {
&& self.dataType().eq(rhs: rhs.dataType())
}
+ public func assignable() -> TypeCheckResults {
+ if self.readOnly() {
+ return TypeCheckResults.ReadOnly
+ }
+
+ if let direction = direction(),
+ direction == Direction.In
+ {
+ return TypeCheckResults.WrongDirection
+ }
+ return TypeCheckResults.Ok
+ }
+
+ public func assignableFromType(_ rhs: P4Type) -> TypeCheckResults {
+ if !self.dataType().eq(rhs: rhs.dataType()) {
+ return TypeCheckResults.IncompatibleTypes
+ }
+
+ if self.readOnly() {
+ return TypeCheckResults.ReadOnly
+ }
+
+ if let direction = direction(),
+ direction == Direction.In
+ {
+ return TypeCheckResults.WrongDirection
+ }
+
+ return TypeCheckResults.Ok
+ }
+
public static func ReadOnly(_ type: P4DataType) -> P4Type {
return P4Type(type, P4TypeAttributes.ReadOnly())
}
diff --git a/Sources/Common/Protocols.swift b/Sources/Common/Protocols.swift
index bce3be1..03a5f2a 100644
--- a/Sources/Common/Protocols.swift
+++ b/Sources/Common/Protocols.swift
@@ -20,7 +20,7 @@ public protocol EvaluatableExpression {
/// - Parameters
/// - execution: The execution context in which to evaluate the expression
/// - Returns: The value of expression
- func evaluate(execution: ProgramExecution) -> Result
+ func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution)
func type() -> P4Type
}
diff --git a/Sources/P4Compiler/Common.swift b/Sources/P4Compiler/Common.swift
index 2b7d92f..a292182 100644
--- a/Sources/P4Compiler/Common.swift
+++ b/Sources/P4Compiler/Common.swift
@@ -83,8 +83,8 @@ func parameter_list_compiler(
// 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 .Ok(let (parsed_parameter, updated_context)):
+ return Result.Ok((parameters.addParameter(parsed_parameter), updated_context))
case .Error(let e): return Result.Error(e)
}
}
diff --git a/Sources/P4Compiler/Statement.swift b/Sources/P4Compiler/Statement.swift
index 54cf3ce..4ffdebf 100644
--- a/Sources/P4Compiler/Statement.swift
+++ b/Sources/P4Compiler/Statement.swift
@@ -312,7 +312,7 @@ extension ReturnStatement: CompilableStatement {
return switch Expression.Compile(node: expression_node, withContext: context) {
case .Ok(let result):
- if result.type().eq(context.expected_type!) {
+ if result.type().dataType().eq(rhs: context.expected_type!.dataType()) {
.Ok((ReturnStatement(result), context))
} else {
.Error(
diff --git a/Sources/P4Lang/Common.swift b/Sources/P4Lang/Common.swift
index adf0690..7500006 100644
--- a/Sources/P4Lang/Common.swift
+++ b/Sources/P4Lang/Common.swift
@@ -39,7 +39,16 @@ public struct Parameter: CustomStringConvertible, Equatable {
/// Calculate whether the `argument` is compatible with this parameter.
public func compatible(_ argument: Argument) -> Bool {
let arg_type = argument.argument.type()
- return arg_type.eq(self.type)
+
+ // If the parameter is (in)out, then the argument must be an lvalue.
+ if let param_direction = self.type.direction(),
+ param_direction == Direction.In || param_direction == Direction.InOut
+ {
+ if !(argument.argument is EvaluatableLValueExpression) {
+ return false
+ }
+ }
+ return arg_type.dataType().eq(rhs: self.type.dataType())
}
}
diff --git a/Sources/P4Runtime/Common.swift b/Sources/P4Runtime/Common.swift
index 7419abc..d486da3 100644
--- a/Sources/P4Runtime/Common.swift
+++ b/Sources/P4Runtime/Common.swift
@@ -17,3 +17,57 @@
import Common
import P4Lang
+
+public func Call(
+ body: (ProgramExecution) -> (Result, ProgramExecution), withArguments args: ArgumentList,
+ withParameters params: ParameterList, inExecution execution: ProgramExecution
+) -> (Result, ProgramExecution) {
+
+ if case .Error(let e) = args.compatible(params) {
+ return (.Error(e), execution)
+ }
+
+ var called_execution = execution.enter_scope()
+
+ 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)
+ guard case (.Ok(let argument_value), let updated_execution) = maybe_argument_value else {
+ return (
+ .Error(Error(withMessage: "Cannot evaluate argument \(arg_idx): \(argument)")),
+ called_execution.exit_scope()
+ )
+ }
+ called_execution = updated_execution.declare(
+ identifier: parameter.name, withValue: argument_value)
+ }
+
+ let (maybe_call_result, updated_execution) = body(called_execution)
+ guard case .Ok(let call_result) = maybe_call_result else {
+ return (.Error(maybe_call_result.error()!), updated_execution.exit_scope())
+ }
+
+ // Before returning, update the (in)out parameters!
+ var inout_scopes = updated_execution.exit_scope().scopes
+
+ for (parameter, argument) in zip(params.parameters, args.arguments) {
+ if let param_direction = parameter.type.direction(),
+ param_direction == Direction.InOut || param_direction == Direction.Out {
+ // Let's make sure that it is an evaluatable l value!
+ guard let arg_lvalue = argument.argument as? EvaluatableLValueExpression else {
+ return (.Error(Error(withMessage: "(in)out parameter argument is not lvalue")), updated_execution.exit_scope())
+ }
+
+ guard case .Ok(let arg_new_value) = updated_execution.scopes.lookup(identifier: parameter.name) else {
+ return (.Error(Error(withMessage: "Could not get (in)out parameter value from scope")), updated_execution.exit_scope())
+ }
+
+ switch arg_lvalue.set(to: arg_new_value, inScopes: inout_scopes, duringExecution: updated_execution) {
+ case .Ok((let updated_scopes, _)): inout_scopes = updated_scopes
+ case .Error(let e): return (.Error(e), updated_execution.exit_scope())
+ }
+ }
+ }
+ return (.Ok(call_result), updated_execution.replaceScopes(inout_scopes))
+}
\ No newline at end of file
diff --git a/Sources/P4Runtime/Expressions.swift b/Sources/P4Runtime/Expressions.swift
index b09931c..9565eb3 100644
--- a/Sources/P4Runtime/Expressions.swift
+++ b/Sources/P4Runtime/Expressions.swift
@@ -19,8 +19,8 @@ import Common
import P4Lang
extension SelectCaseExpression: EvaluatableExpression {
- public func evaluate(execution: Common.ProgramExecution) -> Common.Result {
- return execution.scopes.lookup(identifier: next_state_identifier)
+ public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) {
+ return (execution.scopes.lookup(identifier: next_state_identifier), execution)
}
public func type() -> P4Type {
@@ -29,19 +29,19 @@ extension SelectCaseExpression: EvaluatableExpression {
}
extension SelectExpression: EvaluatableExpression {
- public func evaluate(execution: Common.ProgramExecution) -> Common.Result {
+ public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) {
switch self.selector.evaluate(execution: execution) {
- case .Ok(let selector_value):
+ case (.Ok(let selector_value), let updated_execution):
for sce in self.case_expressions {
- if case .Ok(let kse) = sce.key.evaluate(execution: execution),
+ if case (.Ok(let kse), let updated_execution) = sce.key.evaluate(execution: updated_execution),
kse.eq(selector_value)
{
- let result = sce.evaluate(execution: execution)
+ let result = sce.evaluate(execution: updated_execution)
return result
}
}
- return .Error(Error(withMessage: "No key matched the selector"))
- case .Error(let e): return .Error(e)
+ return (.Error(Error(withMessage: "No key matched the selector")), updated_execution)
+ case (.Error(let e), let updated_execution): return (.Error(e), updated_execution)
}
}
@@ -56,8 +56,8 @@ extension TypedIdentifier: EvaluatableExpression {
return self.type
}
- public func evaluate(execution: Common.ProgramExecution) -> Result {
- return execution.scopes.lookup(identifier: self)
+ public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) {
+ return (execution.scopes.lookup(identifier: self), execution)
}
}
@@ -81,14 +81,12 @@ extension TypedIdentifier: EvaluatableLValueExpression {
return .Error(Error(withMessage: "Cannot assign to identifier not in scope"))
}
- if !type.eq(to.type()) {
- return .Error(
- Error(
- withMessage:
- "Cannot assign value with type \(to.type()) to identifier \(self) with type \(type)"
- ))
+ return switch type.assignableFromType(to.type()) {
+ case TypeCheckResults.IncompatibleTypes: .Error( Error( withMessage: "Cannot assign value with type \(to.type()) to identifier \(self) with type \(type)"))
+ case TypeCheckResults.ReadOnly: .Error( Error( withMessage: "Cannot assign value with type \(to.type()) to identifier \(self) that is read only"))
+ case TypeCheckResults.WrongDirection: .Error( Error( withMessage: "Cannot assign value with type \(to.type()) to identifier \(self) that is in parameter"))
+ case TypeCheckResults.Ok: .Ok(())
}
- return .Ok(())
}
}
@@ -196,18 +194,19 @@ public func binary_int_math_operator_checker(
}
extension BinaryOperatorExpression: EvaluatableExpression {
- public func evaluate(execution: Common.ProgramExecution) -> Common.Result {
- let maybe_evaluated_left = self.left.evaluate(execution: execution)
- guard case Result.Ok(let evaluated_left) = maybe_evaluated_left else {
+ public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) {
+ let updated_execution = execution
+ let maybe_evaluated_left = self.left.evaluate(execution: 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: execution)
- guard case Result.Ok(let evaluated_right) = maybe_evaluated_right else {
+ let maybe_evaluated_right = self.right.evaluate(execution: updated_execution)
+ guard case (.Ok(let evaluated_right), let updated_execution) = maybe_evaluated_right else {
return maybe_evaluated_right
}
- return Result.Ok(P4Value(self.evaluator.2(evaluated_left, evaluated_right)))
+ return (.Ok(P4Value(self.evaluator.2(evaluated_left, evaluated_right))), updated_execution)
}
public func type() -> P4Type {
@@ -216,27 +215,28 @@ extension BinaryOperatorExpression: EvaluatableExpression {
}
extension ArrayAccessExpression: EvaluatableExpression {
- public func evaluate(execution: Common.ProgramExecution) -> Common.Result {
- let maybe_name = self.name.evaluate(execution: execution)
- guard case Result.Ok(let name) = maybe_name else {
+ public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) {
+ let updated_execution = execution
+ let maybe_name = self.name.evaluate(execution: updated_execution)
+ guard case (.Ok(let name), let updated_execution) = maybe_name else {
return maybe_name
}
- let maybe_indexor = self.indexor.evaluate(execution: execution)
- guard case Result.Ok(let indexor) = maybe_indexor else {
+ let maybe_indexor = self.indexor.evaluate(execution: updated_execution)
+ guard case (.Ok(let indexor), let updated_execution) = maybe_indexor else {
return maybe_indexor
}
guard let indexor_int = indexor.dataValue() as? P4IntValue else {
- return Result.Error(Error(withMessage: "\(indexor) cannot index an array"))
+ return (.Error(Error(withMessage: "\(indexor) cannot index an array")), updated_execution)
}
guard let array = name.dataValue() as? P4ArrayValue else {
- return Result.Error(Error(withMessage: "\(name) does not name an array"))
+ return (.Error(Error(withMessage: "\(name) does not name an array")), updated_execution)
}
let accessed = array.access(indexor_int.access())
- return .Ok(accessed)
+ return (.Ok(accessed), updated_execution)
}
public func type() -> P4Type {
@@ -250,20 +250,20 @@ extension ArrayAccessExpression: EvaluatableLValueExpression {
duringExecution execution: ProgramExecution
) -> Common.Result<(Common.VarValueScopes, P4Value)> {
- let maybe_value = self.name.evaluate(execution: execution)
- guard case .Ok(let value) = maybe_value else {
- return Result.Error(
- Error(withMessage: "\(self.name) cannot be evaluated: \(maybe_value.error()!)"))
+ let updated_execution = execution
+ let maybe_value = self.name.evaluate(execution: 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()!)"))
}
guard let array_value = value.dataValue() as? P4ArrayValue else {
return Result.Error(Error(withMessage: "\(self.name) does not identify an array"))
}
// Now, get the indexor!
- let maybe_indexor_value = self.indexor.evaluate(execution: execution)
- guard case .Ok(let indexor_value) = maybe_indexor_value else {
+ let maybe_indexor_value = self.indexor.evaluate(execution: 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.error()!)"))
+ Error(withMessage: "\(self.indexor) cannot be evaluated: \(maybe_indexor_value.0.error()!)"))
}
guard let indexor_int = indexor_value.dataValue() as? P4IntValue else {
return Result.Error(Error(withMessage: "\(self.indexor) cannot be used to index an array"))
@@ -282,42 +282,63 @@ extension ArrayAccessExpression: EvaluatableLValueExpression {
}
let array_lvalue = self.name as! EvaluatableLValueExpression
- return array_lvalue.set(to: updated_array_value, inScopes: scopes, duringExecution: execution)
+ return array_lvalue.set(to: updated_array_value, inScopes: scopes, duringExecution: updated_execution)
}
public func check(
to: any Common.EvaluatableExpression, inScopes scopes: Common.VarTypeScopes
) -> Common.Result<()> {
- if !self.type.value_type().eq(to.type()) {
- return .Error(
+ return switch self.type.value_type().assignableFromType(to.type()) {
+ case TypeCheckResults.IncompatibleTypes:
+ .Error(
Error(
withMessage:
"Cannot assign value of type \(to.type()) to array with values of type \(self.name.type())"
))
+ case TypeCheckResults.ReadOnly:
+ .Error(
+ Error(
+ withMessage: "Cannot assign value of type \(to.type()) to array \(self) that is read only"
+ ))
+ case TypeCheckResults.WrongDirection:
+ .Error(
+ Error(
+ withMessage:
+ "Cannot assign value of type \(to.type()) to array \(self) that is in parameter"))
+ case TypeCheckResults.Ok:
+ // Now, check the type of the array itself.
+ switch self.name.type().assignable() {
+ case TypeCheckResults.ReadOnly:
+ .Error(Error(withMessage: "Cannot assign to array \(self) that is read only"))
+ case TypeCheckResults.WrongDirection:
+ .Error(Error(withMessage: "Cannot assign to array \(self) that is in parameter"))
+ case TypeCheckResults.Ok: .Ok(())
+ default: .Error(Error(withMessage: "Cannot assign to array \(self)"))
+ }
}
- return .Ok(())
}
}
extension FieldAccessExpression: EvaluatableExpression {
- public func evaluate(execution: Common.ProgramExecution) -> Common.Result {
+ public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) {
- let maybe_struct = self.strct.evaluate(execution: execution)
- guard case Result.Ok(let strct) = maybe_struct else {
+ let updated_execution = execution
+ let maybe_struct = self.strct.evaluate(execution: updated_execution)
+ guard case (.Ok(let strct), let updated_execution) = maybe_struct else {
return maybe_struct
}
guard let struct_strct = strct.dataValue() as? P4StructValue else {
- return Result.Error(Error(withMessage: "\(strct) does not identify a struct"))
+ return (.Error(Error(withMessage: "\(strct) does not identify a struct")), updated_execution)
}
// TODO: Create a default value?
guard let value = struct_strct.get(field: self.field) else {
- return .Error(Error(withMessage: "Missing value"))
+ return (.Error(Error(withMessage: "Missing value")), updated_execution)
}
- return .Ok(value)
+ return (.Ok(value), updated_execution)
}
public func type() -> P4Type {
@@ -335,15 +356,16 @@ extension FieldAccessExpression: EvaluatableLValueExpression {
// where strct_id expands to
// (identifier.field_id1.field_id2...).field_id = new_field_value
+ let updated_execution = execution
// First, evaluate strct_id and make sure that it names a struct.
- let maybe_value = self.strct.evaluate(execution: execution)
- guard case .Ok(let value) = maybe_value else {
- return Result.Error(
- Error(withMessage: "\(self.strct) cannot be evaluated: \(maybe_value.error()!)"))
+ let maybe_value = self.strct.evaluate(execution: 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()!)"))
}
guard let struct_value = value.dataValue() as? P4StructValue else {
- return Result.Error(Error(withMessage: "\(self.strct) does not identify a struct"))
+ return .Error(Error(withMessage: "\(self.strct) does not identify a struct"))
}
// Now we know that struct_id identifies a structure value.
@@ -363,25 +385,47 @@ extension FieldAccessExpression: EvaluatableLValueExpression {
// We use recursion here -- ultimately finding our way to a TypedIdentifier that
// will update the scope. Pretty cool!
let struct_lvalue = self.strct as! EvaluatableLValueExpression
- return struct_lvalue.set(to: new_struct_value, inScopes: scopes, duringExecution: execution)
+ return struct_lvalue.set(to: new_struct_value, inScopes: scopes, duringExecution: updated_execution)
}
public func check(
to: any Common.EvaluatableExpression, inScopes scopes: Common.VarTypeScopes
) -> Common.Result<()> {
-
- if !self.field.type.eq(to.type()) {
- return .Error(
+ return switch self.field.type().assignableFromType(to.type()) {
+ case TypeCheckResults.IncompatibleTypes:
+ .Error(
Error(
withMessage:
- "Cannot assign value of type \(to.type()) to field with type \(self.field.type)"))
+ "Cannot assign value of type \(to.type()) to field \(self.field) of type \(self.type())"
+ ))
+ case TypeCheckResults.ReadOnly:
+ .Error(
+ Error(
+ withMessage:
+ "Cannot assign value of type \(to.type()) to field \(self.field) that is read only"
+ ))
+ case TypeCheckResults.Ok:
+ // Now, check the type of the struct itself.
+ switch self.strct.type().assignable() {
+ case TypeCheckResults.ReadOnly:
+ .Error(
+ Error(
+ withMessage: "Cannot assign to field \(self.field) of \(self.strct) that is read only"))
+ case TypeCheckResults.WrongDirection:
+ .Error(
+ Error(
+ withMessage:
+ "Cannot assign to field \(self.field) of \(self.strct) that is in parameter"))
+ case TypeCheckResults.Ok: .Ok(())
+ default: .Error(Error(withMessage: "Cannot assign to field \(self.field) of \(self.strct)"))
+ }
+ default: .Error(Error(withMessage: "Cannot assign to field \(self.field) of \(self.strct)"))
}
- return .Ok(())
}
}
extension KeysetExpression: EvaluatableExpression {
- public func evaluate(execution: Common.ProgramExecution) -> Result {
+ public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) {
return self.key.evaluate(execution: execution)
}
@@ -391,38 +435,24 @@ extension KeysetExpression: EvaluatableExpression {
}
extension FunctionCall: EvaluatableExpression {
- public func evaluate(execution: Common.ProgramExecution) -> Common.Result {
+ public func evaluate(execution: Common.ProgramExecution) -> (Common.Result, ProgramExecution) {
guard let body = self.callee.body else {
- return .Error(Error(withMessage: "No body for called function (\(self.callee.name))"))
+ return (.Error(Error(withMessage: "No body for called function (\(self.callee.name))")), execution)
}
- // 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)"))
+ let call_body: (ProgramExecution) -> (Result, ProgramExecution) = { (execution: ProgramExecution) in
+ let (control_flow, updated_execution) = body.evaluate(execution: execution)
+ return switch control_flow {
+ case ControlFlow.Return(.some(let value)): (.Ok(value), updated_execution)
+ default:
+ (.Error(Error(withMessage: "No value returned from called function (\(self.callee.name))")), execution)
}
- 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))"))
- }
+ return Call(
+ body: call_body, withArguments: self.arguments, withParameters: self.callee.params,
+ inExecution: execution)
}
public func type() -> P4Type {
@@ -431,7 +461,7 @@ extension FunctionCall: EvaluatableExpression {
}
extension P4Value: EvaluatableExpression {
- public func evaluate(execution: ProgramExecution) -> Result {
- return .Ok(self)
+ public func evaluate(execution: ProgramExecution) -> (Result, ProgramExecution) {
+ return (.Ok(self), execution)
}
}
diff --git a/Sources/P4Runtime/Parser.swift b/Sources/P4Runtime/Parser.swift
index 3d5f4ce..f705c1e 100644
--- a/Sources/P4Runtime/Parser.swift
+++ b/Sources/P4Runtime/Parser.swift
@@ -20,19 +20,20 @@ import P4Lang
extension ParserAssignmentStatement: EvaluatableStatement {
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
- let result = self.value.evaluate(execution: execution)
- guard case Result.Ok(let value) = result else {
- return (ControlFlow.Error, execution.setError(error: result.error()!))
+ let updated_execution = execution
+ let result = self.value.evaluate(execution: updated_execution)
+ guard case (.Ok(let value), let updated_execution) = result else {
+ return (ControlFlow.Error, execution.setError(error: result.0.error()!))
}
let maybe_updated_scopes = self.lvalue.set(
- to: value, inScopes: execution.scopes, duringExecution: execution)
+ to: value, inScopes: execution.scopes, duringExecution: updated_execution)
guard case Result.Ok(let updated_scopes) = maybe_updated_scopes else {
return (ControlFlow.Error, execution.setError(error: maybe_updated_scopes.error()!))
}
execution.scopes = updated_scopes.0
- return (ControlFlow.Next, execution)
+ return (ControlFlow.Next, updated_execution)
}
}
@@ -116,9 +117,8 @@ extension ParserStateSelectTransition: EvaluatableParserState {
}
}
- let res = self.selectExpression.evaluate(execution: program)
-
- if case .Ok(let value) = res {
+ switch self.selectExpression.evaluate(execution: program) {
+ case (.Ok(let value), let program):
if value.type().dataType().eq(rhs: self) {
return (value.dataValue() as! EvaluatableParserState, program.exit_scope())
} else {
@@ -128,10 +128,8 @@ extension ParserStateSelectTransition: EvaluatableParserState {
error: Error(withMessage: "Select transition transitioned to a none state"))
)
}
+ case (.Error(let e), let program): return (self, program.setError(error: e).exit_scope())
}
-
- program = program.setError(error: res.error()!).exit_scope()
- return (self, program.exit_scope())
}
public func done() -> Bool {
@@ -143,7 +141,7 @@ extension ParserStateSelectTransition: EvaluatableParserState {
}
}
-extension Parser: CallableExecution {
+extension Parser: LibraryCallable {
public typealias T = InstantiatedParserState
public func call(
execution: Common.ProgramExecution, arguments: P4Lang.ArgumentList
@@ -178,34 +176,24 @@ extension Parser: CallableExecution {
)
}
- // Now that we are assured that there is a start state,
- // let's set the arguments.
-
- if case .Error(let e) = arguments.compatible(self.parameters) {
- return (
- reject, execution.setError(error: Error(withMessage: "Cannot call parser: \(e)"))
- )
- }
-
- for (parameter, argument) in zip(self.parameters.parameters, arguments.arguments) {
- 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 (
- reject,
- execution.setError(
- error: Error(withMessage: "Cannot evaluate argument \(arg_idx): \(argument)"))
- )
+ let call_body: (ProgramExecution) -> (Result, ProgramExecution) = {
+ (execution: ProgramExecution) in
+ var current_execution = execution
+ // Evaluate until the state is either accept or reject.
+ while !current_state.done() && !current_execution.hasError() {
+ (current_state, current_execution) = current_state.execute(program: current_execution)
}
- execution = execution.declare(identifier: parameter.name, withValue: argument_value)
+ return (.Ok(P4Value(AsInstantiatedParserState(current_state.state()))), current_execution)
}
- // Evaluate until the state is either accept or reject.
- while !current_state.done() && !execution.hasError() {
- (current_state, execution) = current_state.execute(program: execution)
+ return
+ switch Call(
+ body: call_body, withArguments: arguments, withParameters: parameters, inExecution: execution)
+ {
+ case (.Ok(let value), let updated_execution):
+ (value.dataValue() as! InstantiatedParserState, updated_execution)
+ case (.Error(let e), let updated_execution):
+ (reject, updated_execution.setError(error: Error(withMessage: "Cannot call parser: \(e)")))
}
-
- return (AsInstantiatedParserState(current_state.state()), execution.exit_scope())
}
}
diff --git a/Sources/P4Runtime/Protocols.swift b/Sources/P4Runtime/Protocols.swift
index c8088fb..c131ea0 100644
--- a/Sources/P4Runtime/Protocols.swift
+++ b/Sources/P4Runtime/Protocols.swift
@@ -28,7 +28,8 @@ public protocol EvaluatableParserState: P4DataValue {
func state() -> ParserState
}
-public protocol CallableExecution {
+/// Defines an interface for P4 components that can be invoked directly by the p4rse library user
+public protocol LibraryCallable {
associatedtype T
func call(execution: ProgramExecution, arguments: ArgumentList) -> (T, ProgramExecution)
}
diff --git a/Sources/P4Runtime/Statements.swift b/Sources/P4Runtime/Statements.swift
index 0d47ad9..977ba53 100644
--- a/Sources/P4Runtime/Statements.swift
+++ b/Sources/P4Runtime/Statements.swift
@@ -41,7 +41,7 @@ extension BlockStatement: EvaluatableStatement {
extension VariableDeclarationStatement: EvaluatableStatement {
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
- guard case .Ok(let initial_value) = self.initializer.evaluate(execution: execution) else {
+ guard case (.Ok(let initial_value), let execution) = self.initializer.evaluate(execution: execution) else {
return (
ControlFlow.Error,
execution.setError(error: Error(withMessage: "Could not evaluate \(self.initializer)"))
@@ -55,7 +55,7 @@ extension VariableDeclarationStatement: EvaluatableStatement {
extension ConditionalStatement: EvaluatableStatement {
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
- guard case .Ok(let evaluated_condition) = self.condition.evaluate(execution: execution) else {
+ guard case (.Ok(let evaluated_condition), let execution) = self.condition.evaluate(execution: execution) else {
return (
ControlFlow.Error,
execution.setError(error: Error(withMessage: "Could not evaluate \(self.condition)"))
@@ -108,8 +108,8 @@ extension ExpressionStatement: EvaluatableStatement {
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))
+ 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/Declarations.swift b/Tests/p4rseTests/Declarations.swift
index 5a99f20..f99936e 100644
--- a/Tests/p4rseTests/Declarations.swift
+++ b/Tests/p4rseTests/Declarations.swift
@@ -172,6 +172,34 @@ import TreeSitterP4
x = true;
};
"""
- #expect(#RequireOkResult(Program.Compile(simple_parser_declaration)))
+ #expect(
+ #RequireErrorResult(
+ Error(
+ withMessage:
+ "{56, 21}: Failed to parse a statement element: {63, 9}: Failed to parse a statement element: {63, 1}: Cannot assign value with type Boolean to identifier x that is in parameter"
+ ),
+ Program.Compile(simple_parser_declaration))
+ )
+}
+
+@Test func test_function_declaration_with_parameters_and_direction_struct() async throws {
+ let simple_parser_declaration = """
+ struct Testing {
+ bool yesno;
+ int count;
+ };
+ bool functionb(in Testing x, out string y, inout int z) {
+ x.yesno = true;
+ };
+ """
+ #expect(
+ #RequireErrorResult(
+ Error(
+ withMessage:
+ "{113, 27}: Failed to parse a statement element: {120, 15}: Failed to parse a statement element: {120, 7}: Cannot assign to field yesno of x that is in parameter"
+ ),
+ Program.Compile(simple_parser_declaration))
+ )
+
}
diff --git a/Tests/p4rseTests/ExpressionTests/FunctionCall.swift b/Tests/p4rseTests/ExpressionTests/FunctionCall.swift
index 0a675ed..c8a6e37 100644
--- a/Tests/p4rseTests/ExpressionTests/FunctionCall.swift
+++ b/Tests/p4rseTests/ExpressionTests/FunctionCall.swift
@@ -83,6 +83,35 @@ import TreeSitterP4
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
}
+@Test func test_function_call_scoped_name_collision_inout() async throws {
+ let simple_parser_declaration = """
+ bool functionb(inout bool c) {
+ c = true;
+ return c;
+ };
+ parser main_parser() {
+ state start {
+ bool c = false;
+ bool b = functionb(c);
+ transition select (c) {
+ false: reject;
+ true: accept;
+ };
+ }
+ };
+ """
+
+ let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
+ let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
+ let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
+ let (state_result, _) = try! #UseOkResult(runtime.run())
+
+ #expect(parser.states.count() == 1)
+
+ #expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
+}
+
+
@Test func test_function_call_integer_return_value() async throws {
let simple_parser_declaration = """
int functionb(int c) {
diff --git a/Tests/p4rseTests/StructTests.swift b/Tests/p4rseTests/StructTests.swift
index 1840d5e..5da57be 100644
--- a/Tests/p4rseTests/StructTests.swift
+++ b/Tests/p4rseTests/StructTests.swift
@@ -327,7 +327,7 @@ import TreeSitterP4
#expect(
#RequireErrorResult(
Error(
- withMessage: "{49, 13}: Failed to parse a statement element: {49, 8}: Cannot assign value of type Int to field with type Boolean"
+ withMessage: "{49, 13}: Failed to parse a statement element: {49, 8}: Cannot assign value of type Int to field yesno of type Boolean"
),
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
)
@@ -457,7 +457,7 @@ import TreeSitterP4
#expect(
#RequireErrorResult(
Error(
- withMessage: "{49, 20}: Failed to parse a statement element: {49, 11}: Cannot assign value of type Boolean to field with type Int"
+ withMessage: "{49, 20}: Failed to parse a statement element: {49, 11}: Cannot assign value of type Boolean to field count of type Int"
),
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
)