compiler, runtime, common: Support (in)out Parameters

When a function is called, if there is an (in)out parameter,
make sure that updated values are propogated to the calling
function.

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
Will Hawkins
2026-04-16 06:58:45 -04:00
parent 94086c8e17
commit 82c125e4d1
14 changed files with 322 additions and 139 deletions
+6
View File
@@ -87,6 +87,12 @@ open class ProgramExecution: CustomStringConvertible {
return new_pe 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 { public func declare(identifier: Identifier, withValue value: P4Value) -> ProgramExecution {
let new_pe = ProgramExecution(copy: self) let new_pe = ProgramExecution(copy: self)
let new_scopes = new_pe.scopes.declare(identifier: identifier, withValue: value) let new_scopes = new_pe.scopes.declare(identifier: identifier, withValue: value)
+40 -2
View File
@@ -15,6 +15,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
public enum TypeCheckResults: Equatable {
case Ok
case ReadOnly
case WrongDirection
case IncompatibleTypes
}
public enum Direction: Equatable, CustomStringConvertible { public enum Direction: Equatable, CustomStringConvertible {
case In case In
case Out case Out
@@ -115,11 +122,11 @@ public struct P4Type: CustomStringConvertible {
} }
public func update(removeAttribute attribute: P4TypeAttribute) -> P4Type { 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 { 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? { public func direction() -> Direction? {
@@ -143,6 +150,37 @@ public struct P4Type: CustomStringConvertible {
&& self.dataType().eq(rhs: rhs.dataType()) && 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 { public static func ReadOnly(_ type: P4DataType) -> P4Type {
return P4Type(type, P4TypeAttributes.ReadOnly()) return P4Type(type, P4TypeAttributes.ReadOnly())
} }
+1 -1
View File
@@ -20,7 +20,7 @@ public protocol EvaluatableExpression {
/// - Parameters /// - Parameters
/// - execution: The execution context in which to evaluate the expression /// - execution: The execution context in which to evaluate the expression
/// - Returns: The value of expression /// - Returns: The value of expression
func evaluate(execution: ProgramExecution) -> Result<P4Value> func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution)
func type() -> P4Type func type() -> P4Type
} }
+2 -2
View File
@@ -83,8 +83,8 @@ func parameter_list_compiler(
// Otherwise, there should be one parameter left! // Otherwise, there should be one parameter left!
switch Parameter.Compile(node: currentChild!, withContext: context) { switch Parameter.Compile(node: currentChild!, withContext: context) {
case .Ok(let (vds, updated_context)): case .Ok(let (parsed_parameter, updated_context)):
return Result.Ok((parameters.addParameter(vds), updated_context)) return Result.Ok((parameters.addParameter(parsed_parameter), updated_context))
case .Error(let e): return Result.Error(e) case .Error(let e): return Result.Error(e)
} }
} }
+1 -1
View File
@@ -312,7 +312,7 @@ extension ReturnStatement: CompilableStatement {
return switch Expression.Compile(node: expression_node, withContext: context) { return switch Expression.Compile(node: expression_node, withContext: context) {
case .Ok(let result): 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)) .Ok((ReturnStatement(result), context))
} else { } else {
.Error( .Error(
+10 -1
View File
@@ -39,7 +39,16 @@ public struct Parameter: CustomStringConvertible, Equatable {
/// Calculate whether the `argument` is compatible with this parameter. /// Calculate whether the `argument` is compatible with this parameter.
public func compatible(_ argument: Argument) -> Bool { public func compatible(_ argument: Argument) -> Bool {
let arg_type = argument.argument.type() 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())
} }
} }
+54
View File
@@ -17,3 +17,57 @@
import Common import Common
import P4Lang import P4Lang
public func Call<T>(
body: (ProgramExecution) -> (Result<T>, ProgramExecution), withArguments args: ArgumentList,
withParameters params: ParameterList, inExecution execution: ProgramExecution
) -> (Result<T>, 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))
}
+117 -87
View File
@@ -19,8 +19,8 @@ import Common
import P4Lang import P4Lang
extension SelectCaseExpression: EvaluatableExpression { extension SelectCaseExpression: EvaluatableExpression {
public func evaluate(execution: Common.ProgramExecution) -> Common.Result<P4Value> { public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
return execution.scopes.lookup(identifier: next_state_identifier) return (execution.scopes.lookup(identifier: next_state_identifier), execution)
} }
public func type() -> P4Type { public func type() -> P4Type {
@@ -29,19 +29,19 @@ extension SelectCaseExpression: EvaluatableExpression {
} }
extension SelectExpression: EvaluatableExpression { extension SelectExpression: EvaluatableExpression {
public func evaluate(execution: Common.ProgramExecution) -> Common.Result<P4Value> { public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
switch self.selector.evaluate(execution: execution) { 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 { 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) kse.eq(selector_value)
{ {
let result = sce.evaluate(execution: execution) let result = sce.evaluate(execution: updated_execution)
return result return result
} }
} }
return .Error(Error(withMessage: "No key matched the selector")) return (.Error(Error(withMessage: "No key matched the selector")), updated_execution)
case .Error(let e): return .Error(e) case (.Error(let e), let updated_execution): return (.Error(e), updated_execution)
} }
} }
@@ -56,8 +56,8 @@ extension TypedIdentifier: EvaluatableExpression {
return self.type return self.type
} }
public func evaluate(execution: Common.ProgramExecution) -> Result<P4Value> { public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
return execution.scopes.lookup(identifier: self) 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")) return .Error(Error(withMessage: "Cannot assign to identifier not in scope"))
} }
if !type.eq(to.type()) { return switch type.assignableFromType(to.type()) {
return .Error( case TypeCheckResults.IncompatibleTypes: .Error( Error( withMessage: "Cannot assign value with type \(to.type()) to identifier \(self) with type \(type)"))
Error( case TypeCheckResults.ReadOnly: .Error( Error( withMessage: "Cannot assign value with type \(to.type()) to identifier \(self) that is read only"))
withMessage: case TypeCheckResults.WrongDirection: .Error( Error( withMessage: "Cannot assign value with type \(to.type()) to identifier \(self) that is in parameter"))
"Cannot assign value with type \(to.type()) to identifier \(self) with type \(type)" case TypeCheckResults.Ok: .Ok(())
))
} }
return .Ok(())
} }
} }
@@ -196,18 +194,19 @@ public func binary_int_math_operator_checker(
} }
extension BinaryOperatorExpression: EvaluatableExpression { extension BinaryOperatorExpression: EvaluatableExpression {
public func evaluate(execution: Common.ProgramExecution) -> Common.Result<P4Value> { public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
let maybe_evaluated_left = self.left.evaluate(execution: execution) let updated_execution = execution
guard case Result.Ok(let evaluated_left) = maybe_evaluated_left else { 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 return maybe_evaluated_left
} }
let maybe_evaluated_right = self.right.evaluate(execution: execution) let maybe_evaluated_right = self.right.evaluate(execution: updated_execution)
guard case Result.Ok(let evaluated_right) = maybe_evaluated_right else { guard case (.Ok(let evaluated_right), let updated_execution) = maybe_evaluated_right else {
return maybe_evaluated_right 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 { public func type() -> P4Type {
@@ -216,27 +215,28 @@ extension BinaryOperatorExpression: EvaluatableExpression {
} }
extension ArrayAccessExpression: EvaluatableExpression { extension ArrayAccessExpression: EvaluatableExpression {
public func evaluate(execution: Common.ProgramExecution) -> Common.Result<P4Value> { public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
let maybe_name = self.name.evaluate(execution: execution) let updated_execution = execution
guard case Result.Ok(let name) = maybe_name else { let maybe_name = self.name.evaluate(execution: updated_execution)
guard case (.Ok(let name), let updated_execution) = maybe_name else {
return maybe_name return maybe_name
} }
let maybe_indexor = self.indexor.evaluate(execution: execution) let maybe_indexor = self.indexor.evaluate(execution: updated_execution)
guard case Result.Ok(let indexor) = maybe_indexor else { guard case (.Ok(let indexor), let updated_execution) = maybe_indexor else {
return maybe_indexor return maybe_indexor
} }
guard let indexor_int = indexor.dataValue() as? P4IntValue else { 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 { 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()) let accessed = array.access(indexor_int.access())
return .Ok(accessed) return (.Ok(accessed), updated_execution)
} }
public func type() -> P4Type { public func type() -> P4Type {
@@ -250,20 +250,20 @@ extension ArrayAccessExpression: EvaluatableLValueExpression {
duringExecution execution: ProgramExecution duringExecution execution: ProgramExecution
) -> Common.Result<(Common.VarValueScopes, P4Value)> { ) -> Common.Result<(Common.VarValueScopes, P4Value)> {
let maybe_value = self.name.evaluate(execution: execution) let updated_execution = execution
guard case .Ok(let value) = maybe_value else { let maybe_value = self.name.evaluate(execution: updated_execution)
return Result.Error( guard case (.Ok(let value), let updated_execution) = maybe_value else {
Error(withMessage: "\(self.name) cannot be evaluated: \(maybe_value.error()!)")) return .Error(Error(withMessage: "\(self.name) cannot be evaluated: \(maybe_value.0.error()!)"))
} }
guard let array_value = value.dataValue() as? P4ArrayValue else { guard let array_value = value.dataValue() as? P4ArrayValue else {
return Result.Error(Error(withMessage: "\(self.name) does not identify an array")) return Result.Error(Error(withMessage: "\(self.name) does not identify an array"))
} }
// Now, get the indexor! // Now, get the indexor!
let maybe_indexor_value = self.indexor.evaluate(execution: execution) let maybe_indexor_value = self.indexor.evaluate(execution: updated_execution)
guard case .Ok(let indexor_value) = maybe_indexor_value else { guard case (.Ok(let indexor_value), let updated_execution) = maybe_indexor_value else {
return Result.Error( 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 { guard let indexor_int = indexor_value.dataValue() as? P4IntValue else {
return Result.Error(Error(withMessage: "\(self.indexor) cannot be used to index an array")) 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 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( public func check(
to: any Common.EvaluatableExpression, inScopes scopes: Common.VarTypeScopes to: any Common.EvaluatableExpression, inScopes scopes: Common.VarTypeScopes
) -> Common.Result<()> { ) -> Common.Result<()> {
if !self.type.value_type().eq(to.type()) { return switch self.type.value_type().assignableFromType(to.type()) {
return .Error( case TypeCheckResults.IncompatibleTypes:
.Error(
Error( Error(
withMessage: withMessage:
"Cannot assign value of type \(to.type()) to array with values of type \(self.name.type())" "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 { extension FieldAccessExpression: EvaluatableExpression {
public func evaluate(execution: Common.ProgramExecution) -> Common.Result<P4Value> { public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
let maybe_struct = self.strct.evaluate(execution: execution) let updated_execution = execution
guard case Result.Ok(let strct) = maybe_struct else { let maybe_struct = self.strct.evaluate(execution: updated_execution)
guard case (.Ok(let strct), let updated_execution) = maybe_struct else {
return maybe_struct return maybe_struct
} }
guard let struct_strct = strct.dataValue() as? P4StructValue else { 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? // TODO: Create a default value?
guard let value = struct_strct.get(field: self.field) else { 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 { public func type() -> P4Type {
@@ -335,15 +356,16 @@ extension FieldAccessExpression: EvaluatableLValueExpression {
// where strct_id expands to // where strct_id expands to
// (identifier.field_id1.field_id2...).field_id = new_field_value // (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. // First, evaluate strct_id and make sure that it names a struct.
let maybe_value = self.strct.evaluate(execution: execution) let maybe_value = self.strct.evaluate(execution: updated_execution)
guard case .Ok(let value) = maybe_value else { guard case (.Ok(let value), let updated_execution) = maybe_value else {
return Result.Error( return .Error(
Error(withMessage: "\(self.strct) cannot be evaluated: \(maybe_value.error()!)")) Error(withMessage: "\(self.strct) cannot be evaluated: \(maybe_value.0.error()!)"))
} }
guard let struct_value = value.dataValue() as? P4StructValue else { 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. // 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 // We use recursion here -- ultimately finding our way to a TypedIdentifier that
// will update the scope. Pretty cool! // will update the scope. Pretty cool!
let struct_lvalue = self.strct as! EvaluatableLValueExpression 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( public func check(
to: any Common.EvaluatableExpression, inScopes scopes: Common.VarTypeScopes to: any Common.EvaluatableExpression, inScopes scopes: Common.VarTypeScopes
) -> Common.Result<()> { ) -> Common.Result<()> {
return switch self.field.type().assignableFromType(to.type()) {
if !self.field.type.eq(to.type()) { case TypeCheckResults.IncompatibleTypes:
return .Error( .Error(
Error( Error(
withMessage: 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 { extension KeysetExpression: EvaluatableExpression {
public func evaluate(execution: Common.ProgramExecution) -> Result<P4Value> { public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
return self.key.evaluate(execution: execution) return self.key.evaluate(execution: execution)
} }
@@ -391,47 +435,33 @@ extension KeysetExpression: EvaluatableExpression {
} }
extension FunctionCall: EvaluatableExpression { extension FunctionCall: EvaluatableExpression {
public func evaluate(execution: Common.ProgramExecution) -> Common.Result<P4Value> { public func evaluate(execution: Common.ProgramExecution) -> (Common.Result<P4Value>, ProgramExecution) {
guard let body = self.callee.body else { 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 let call_body: (ProgramExecution) -> (Result<P4Value>, ProgramExecution) = { (execution: ProgramExecution) in
let (control_flow, updated_execution) = body.evaluate(execution: execution)
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 { return switch control_flow {
case ControlFlow.Return(let value): case ControlFlow.Return(.some(let value)): (.Ok(value), updated_execution)
if let value = value {
.Ok(value)
} else {
.Error(Error(withMessage: "No value returned from called function (\(self.callee.name))"))
}
default: default:
.Error(Error(withMessage: "No value returned from called function (\(self.callee.name))")) (.Error(Error(withMessage: "No value returned from called function (\(self.callee.name))")), execution)
} }
} }
return Call(
body: call_body, withArguments: self.arguments, withParameters: self.callee.params,
inExecution: execution)
}
public func type() -> P4Type { public func type() -> P4Type {
return self.callee.tipe return self.callee.tipe
} }
} }
extension P4Value: EvaluatableExpression { extension P4Value: EvaluatableExpression {
public func evaluate(execution: ProgramExecution) -> Result<P4Value> { public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
return .Ok(self) return (.Ok(self), execution)
} }
} }
+26 -38
View File
@@ -20,19 +20,20 @@ import P4Lang
extension ParserAssignmentStatement: EvaluatableStatement { extension ParserAssignmentStatement: EvaluatableStatement {
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
let result = self.value.evaluate(execution: execution) let updated_execution = execution
guard case Result.Ok(let value) = result else { let result = self.value.evaluate(execution: updated_execution)
return (ControlFlow.Error, execution.setError(error: result.error()!)) 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( 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 { guard case Result.Ok(let updated_scopes) = maybe_updated_scopes else {
return (ControlFlow.Error, execution.setError(error: maybe_updated_scopes.error()!)) return (ControlFlow.Error, execution.setError(error: maybe_updated_scopes.error()!))
} }
execution.scopes = updated_scopes.0 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) switch self.selectExpression.evaluate(execution: program) {
case (.Ok(let value), let program):
if case .Ok(let value) = res {
if value.type().dataType().eq(rhs: self) { if value.type().dataType().eq(rhs: self) {
return (value.dataValue() as! EvaluatableParserState, program.exit_scope()) return (value.dataValue() as! EvaluatableParserState, program.exit_scope())
} else { } else {
@@ -128,10 +128,8 @@ extension ParserStateSelectTransition: EvaluatableParserState {
error: Error(withMessage: "Select transition transitioned to a none state")) 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 { public func done() -> Bool {
@@ -143,7 +141,7 @@ extension ParserStateSelectTransition: EvaluatableParserState {
} }
} }
extension Parser: CallableExecution { extension Parser: LibraryCallable {
public typealias T = InstantiatedParserState public typealias T = InstantiatedParserState
public func call( public func call(
execution: Common.ProgramExecution, arguments: P4Lang.ArgumentList 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 call_body: (ProgramExecution) -> (Result<P4Value>, ProgramExecution) = {
// let's set the arguments. (execution: ProgramExecution) in
var current_execution = execution
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)"))
)
}
execution = execution.declare(identifier: parameter.name, withValue: argument_value)
}
// Evaluate until the state is either accept or reject. // Evaluate until the state is either accept or reject.
while !current_state.done() && !execution.hasError() { while !current_state.done() && !current_execution.hasError() {
(current_state, execution) = current_state.execute(program: execution) (current_state, current_execution) = current_state.execute(program: current_execution)
}
return (.Ok(P4Value(AsInstantiatedParserState(current_state.state()))), current_execution)
} }
return (AsInstantiatedParserState(current_state.state()), execution.exit_scope()) 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)")))
}
} }
} }
+2 -1
View File
@@ -28,7 +28,8 @@ public protocol EvaluatableParserState: P4DataValue {
func state() -> ParserState func state() -> ParserState
} }
public protocol CallableExecution<T> { /// Defines an interface for P4 components that can be invoked directly by the p4rse library user
public protocol LibraryCallable<T> {
associatedtype T associatedtype T
func call(execution: ProgramExecution, arguments: ArgumentList) -> (T, ProgramExecution) func call(execution: ProgramExecution, arguments: ArgumentList) -> (T, ProgramExecution)
} }
+4 -4
View File
@@ -41,7 +41,7 @@ extension BlockStatement: EvaluatableStatement {
extension VariableDeclarationStatement: EvaluatableStatement { extension VariableDeclarationStatement: EvaluatableStatement {
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { 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 ( return (
ControlFlow.Error, ControlFlow.Error,
execution.setError(error: Error(withMessage: "Could not evaluate \(self.initializer)")) execution.setError(error: Error(withMessage: "Could not evaluate \(self.initializer)"))
@@ -55,7 +55,7 @@ extension VariableDeclarationStatement: EvaluatableStatement {
extension ConditionalStatement: EvaluatableStatement { extension ConditionalStatement: EvaluatableStatement {
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { 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 ( return (
ControlFlow.Error, ControlFlow.Error,
execution.setError(error: Error(withMessage: "Could not evaluate \(self.condition)")) execution.setError(error: Error(withMessage: "Could not evaluate \(self.condition)"))
@@ -108,8 +108,8 @@ extension ExpressionStatement: EvaluatableStatement {
extension ReturnStatement: EvaluatableStatement { extension ReturnStatement: EvaluatableStatement {
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
return switch self.value.evaluate(execution: execution) { return switch self.value.evaluate(execution: execution) {
case .Ok(let v): (ControlFlow.Return(v), execution) case (.Ok(let v), let execution): (ControlFlow.Return(v), execution)
case .Error(let e): (ControlFlow.Error, execution.setError(error: e)) case (.Error(let e), let execution): (ControlFlow.Error, execution.setError(error: e))
} }
} }
} }
+29 -1
View File
@@ -172,6 +172,34 @@ import TreeSitterP4
x = true; 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))
)
} }
@@ -83,6 +83,35 @@ import TreeSitterP4
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject) #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 { @Test func test_function_call_integer_return_value() async throws {
let simple_parser_declaration = """ let simple_parser_declaration = """
int functionb(int c) { int functionb(int c) {
+2 -2
View File
@@ -327,7 +327,7 @@ import TreeSitterP4
#expect( #expect(
#RequireErrorResult( #RequireErrorResult(
Error( 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)) Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
) )
@@ -457,7 +457,7 @@ import TreeSitterP4
#expect( #expect(
#RequireErrorResult( #RequireErrorResult(
Error( 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)) Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
) )