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:
@@ -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)
|
||||
|
||||
@@ -15,6 +15,13 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
public 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())
|
||||
}
|
||||
|
||||
@@ -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<P4Value>
|
||||
func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution)
|
||||
func type() -> P4Type
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,3 +17,57 @@
|
||||
|
||||
import Common
|
||||
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))
|
||||
}
|
||||
@@ -19,8 +19,8 @@ import Common
|
||||
import P4Lang
|
||||
|
||||
extension SelectCaseExpression: EvaluatableExpression {
|
||||
public func evaluate(execution: Common.ProgramExecution) -> Common.Result<P4Value> {
|
||||
return execution.scopes.lookup(identifier: next_state_identifier)
|
||||
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, 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<P4Value> {
|
||||
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, 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<P4Value> {
|
||||
return execution.scopes.lookup(identifier: self)
|
||||
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, 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<P4Value> {
|
||||
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<P4Value>, 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<P4Value> {
|
||||
let maybe_name = self.name.evaluate(execution: execution)
|
||||
guard case Result.Ok(let name) = maybe_name else {
|
||||
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, 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<P4Value> {
|
||||
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, 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<P4Value> {
|
||||
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, 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<P4Value> {
|
||||
public func evaluate(execution: Common.ProgramExecution) -> (Common.Result<P4Value>, 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<P4Value>, 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<P4Value> {
|
||||
return .Ok(self)
|
||||
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
|
||||
return (.Ok(self), execution)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<P4Value>, 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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@ public protocol EvaluatableParserState: P4DataValue {
|
||||
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
|
||||
func call(execution: ProgramExecution, arguments: ArgumentList) -> (T, ProgramExecution)
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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))
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user