@@ -21,6 +21,13 @@ open class ProgramExecution: CustomStringConvertible {
|
||||
var error: Error?
|
||||
var debug: DebugLevel = DebugLevel.Error
|
||||
|
||||
init(copy: ProgramExecution) {
|
||||
self.scopes = copy.scopes
|
||||
self.initialValues = copy.initialValues
|
||||
self.error = copy.error
|
||||
self.debug = copy.debug
|
||||
}
|
||||
|
||||
public init() {
|
||||
initialValues = .none
|
||||
}
|
||||
@@ -42,7 +49,7 @@ open class ProgramExecution: CustomStringConvertible {
|
||||
}
|
||||
|
||||
public func setError(error: Error) -> ProgramExecution {
|
||||
let npe = self
|
||||
let npe = ProgramExecution(copy: self)
|
||||
npe.error = error
|
||||
return npe
|
||||
}
|
||||
@@ -52,7 +59,7 @@ open class ProgramExecution: CustomStringConvertible {
|
||||
}
|
||||
|
||||
public func setDebugLevel(_ dl: DebugLevel) -> ProgramExecution {
|
||||
let pe = self
|
||||
let pe = ProgramExecution(copy: self)
|
||||
pe.debug = dl
|
||||
return pe
|
||||
}
|
||||
@@ -67,22 +74,22 @@ open class ProgramExecution: CustomStringConvertible {
|
||||
}
|
||||
|
||||
public func enter_scope() -> ProgramExecution {
|
||||
let new_pe = self
|
||||
new_pe.scopes = self.scopes.enter()
|
||||
let new_pe = ProgramExecution(copy: self)
|
||||
new_pe.scopes = new_pe.scopes.enter()
|
||||
|
||||
return new_pe
|
||||
}
|
||||
|
||||
public func exit_scope() -> ProgramExecution {
|
||||
let new_pe = self
|
||||
new_pe.scopes = self.scopes.exit()
|
||||
let new_pe = ProgramExecution(copy: self)
|
||||
new_pe.scopes = new_pe.scopes.exit()
|
||||
|
||||
return new_pe
|
||||
}
|
||||
|
||||
public func declare(identifier: Identifier, withValue value: P4Value) -> ProgramExecution {
|
||||
let new_pe = self
|
||||
let new_scopes = self.scopes.declare(identifier: identifier, withValue: value)
|
||||
let new_pe = ProgramExecution(copy: self)
|
||||
let new_scopes = new_pe.scopes.declare(identifier: identifier, withValue: value)
|
||||
|
||||
new_pe.scopes = new_scopes
|
||||
return new_pe
|
||||
@@ -98,3 +105,12 @@ public typealias VarValueScope = Scope<P4Value>
|
||||
|
||||
/// Scopes that resolves variable identifiers to their values.
|
||||
public typealias VarValueScopes = Scopes<P4Value>
|
||||
|
||||
/// Indicate the control flow result of a particular statement.
|
||||
public enum ControlFlow {
|
||||
case Next
|
||||
case Continue
|
||||
case Break
|
||||
case Return(P4Value?)
|
||||
case Error
|
||||
}
|
||||
|
||||
@@ -28,8 +28,10 @@ public protocol EvaluatableStatement {
|
||||
/// Evaluate a statement for a given execution
|
||||
/// - Parameters
|
||||
/// - execution: The execution context in which to evaluate the parser statement
|
||||
/// - Returns: An updated execution after evaluating the parser statement
|
||||
func evaluate(execution: ProgramExecution) -> ProgramExecution
|
||||
/// - Returns: A tuple of
|
||||
/// 1. Whether this statement affects control flow.
|
||||
/// 2. An updated execution after evaluating the parser statement
|
||||
func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution)
|
||||
}
|
||||
|
||||
public protocol P4Type: CustomStringConvertible {
|
||||
|
||||
@@ -0,0 +1,318 @@
|
||||
// p4rse, Copyright 2026, Will Hawkins
|
||||
//
|
||||
// This file is part of p4rse.
|
||||
//
|
||||
// This file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Common
|
||||
import P4Lang
|
||||
import P4Runtime
|
||||
import SwiftTreeSitter
|
||||
import TreeSitterExtensions
|
||||
import TreeSitterP4
|
||||
|
||||
func parameter_list_compiler(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ParameterList, CompilerContext)> {
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
|
||||
if node.text == ")" {
|
||||
// There are no parameters!
|
||||
return Result.Ok((ParameterList([]), context))
|
||||
}
|
||||
|
||||
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
|
||||
node: node, type: "parameter_list", nice_type_name: "Parameter List")
|
||||
|
||||
var parameters: ParameterList = ParameterList([])
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
if currentChild?.nodeType == "parameter_list" {
|
||||
switch parameter_list_compiler(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (ps, _)):
|
||||
parameters = ps
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
// We may have moved nodes, check/reset currentChild.
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// If this is a ')', we are done.
|
||||
if currentChild?.text == ")" {
|
||||
return Result.Ok((parameters, context))
|
||||
}
|
||||
|
||||
// If this is a comma, we skip it!
|
||||
if currentChild?.text == "," {
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// Otherwise, there should be one parameter left!
|
||||
switch Parameter.Compile(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (vds, updated_context)):
|
||||
return Result.Ok((parameters.addParameter(vds), updated_context))
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
extension ParameterList: Compilable {
|
||||
public typealias T = ParameterList
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ParameterList, CompilerContext)> {
|
||||
|
||||
let parameter_node = node
|
||||
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
|
||||
node: parameter_node, type: "parameters", nice_type_name: "Parameters")
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
|
||||
// Let's eat the '(' before we start ...
|
||||
if parameter_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: parameter_node, withError: "Missing '(' in parameter list component"))
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if parameter_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: parameter_node, withError: "Missing parameter list component"))
|
||||
}
|
||||
let currentChild = parameter_node.child(at: currentChildIdx)
|
||||
|
||||
return parameter_list_compiler(node: currentChild!, withContext: context)
|
||||
}
|
||||
}
|
||||
|
||||
extension Parameter: Compilable {
|
||||
public typealias T = Parameter
|
||||
public static func Compile(
|
||||
node: Node, withContext context: CompilerContext
|
||||
) -> Result<(Parameter, CompilerContext)> {
|
||||
|
||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
||||
node: node, type: "parameter", nice_type_name: "parameter")
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter declaration component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// Annotation?
|
||||
if currentChild!.nodeType == "annotations" {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: currentChild!,
|
||||
withError: "Annotations in parameter declarations are not yet handled"))
|
||||
// Will increment indexes here.
|
||||
}
|
||||
|
||||
// Direction?
|
||||
if currentChild!.nodeType == "direction" {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: currentChild!, withError: "Direction in parameter declarations are not yet handled"
|
||||
))
|
||||
// Will increment indexes here.
|
||||
}
|
||||
|
||||
if currentChild!.nodeType != "typeRef" {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Did not find type name for parameter declaration"))
|
||||
}
|
||||
|
||||
guard
|
||||
case .Ok(let parameter_type) = Types.CompileType(type: currentChild!, withContext: context)
|
||||
else {
|
||||
return Result.Error(
|
||||
Error(withMessage: "Could not parse a P4 type from \(currentChild!.text!)"))
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter declaration component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
if currentChild!.nodeType != "identifier" {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Did not find identifier for parameter statement"))
|
||||
}
|
||||
|
||||
guard
|
||||
case .Ok(let parameter_name) = Identifier.Compile(node: currentChild!, withContext: context)
|
||||
else {
|
||||
return Result.Error(
|
||||
Error(withMessage: "Could not parse a parameter name from \(currentChild!.text!)"))
|
||||
}
|
||||
|
||||
return Result.Ok(
|
||||
(
|
||||
Parameter(
|
||||
identifier: parameter_name, withType: parameter_type),
|
||||
context
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
func argument_list_compiler(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ArgumentList, CompilerContext)> {
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
|
||||
if node.text == ")" {
|
||||
// There are no arguments!
|
||||
return Result.Ok((ArgumentList([]), context))
|
||||
}
|
||||
|
||||
#RequireNodeType<Node, (ArgumentList, CompilerContext)>(
|
||||
node: node, type: "argument_list", nice_type_name: "argument List")
|
||||
|
||||
var arguments: ArgumentList = ArgumentList([])
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing argument list component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
if currentChild?.nodeType == "argument_list" {
|
||||
switch argument_list_compiler(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (ps, _)):
|
||||
arguments = ps
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
// We may have moved nodes, check/reset currentChild.
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing argument list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// If this is a ')', we are done.
|
||||
if currentChild?.text == ")" {
|
||||
return Result.Ok((arguments, context))
|
||||
}
|
||||
|
||||
// If this is a comma, we skip it!
|
||||
if currentChild?.text == "," {
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing argument list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// Otherwise, there should be one argument left!
|
||||
switch Argument.Compile(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (ce, updated_context)):
|
||||
return Result.Ok((arguments.addArgument(Argument(ce, atIndex: arguments.count() + 1)), updated_context))
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
extension ArgumentList: Compilable {
|
||||
public typealias T = ArgumentList
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ArgumentList, CompilerContext)> {
|
||||
|
||||
let argument_node = node
|
||||
#RequireNodeType<Node, (ArgumentList, CompilerContext)>(
|
||||
node: argument_node, type: "arguments", nice_type_name: "arguments")
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
|
||||
// Let's eat the '(' before we start ...
|
||||
if argument_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: argument_node, withError: "Missing '(' in argument list component"))
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if argument_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: argument_node, withError: "Missing argument list component"))
|
||||
}
|
||||
let currentChild = argument_node.child(at: currentChildIdx)
|
||||
|
||||
return argument_list_compiler(node: currentChild!, withContext: context)
|
||||
}
|
||||
}
|
||||
|
||||
extension Argument: Compilable {
|
||||
public typealias T = EvaluatableExpression
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(EvaluatableExpression, CompilerContext)> {
|
||||
let argument_node = node
|
||||
#RequireNodeType<Node, (EvaluatableExpression, CompilerContext)>(
|
||||
node: argument_node, type: "argument", nice_type_name: "argument")
|
||||
|
||||
let expression_node = node.child(at: 0)!
|
||||
|
||||
return switch Expression.Compile(node: expression_node, withContext: context) {
|
||||
case .Ok(let compiled_expression): .Ok((compiled_expression, context))
|
||||
case .Error(let e): .Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -379,185 +379,6 @@ extension P4Lang.Parser: CompilableDeclaration {
|
||||
}
|
||||
}
|
||||
|
||||
func parameter_list_compiler(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ParameterList, CompilerContext)> {
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
|
||||
if node.text == ")" {
|
||||
// There are no parameters!
|
||||
return Result.Ok((ParameterList([]), context))
|
||||
}
|
||||
|
||||
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
|
||||
node: node, type: "parameter_list", nice_type_name: "Parameter List")
|
||||
|
||||
var parameters: ParameterList = ParameterList([])
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
if currentChild?.nodeType == "parameter_list" {
|
||||
switch parameter_list_compiler(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (ps, _)):
|
||||
parameters = ps
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
// We may have moved nodes, check/reset currentChild.
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// If this is a ')', we are done.
|
||||
if currentChild?.text == ")" {
|
||||
return Result.Ok((parameters, context))
|
||||
}
|
||||
|
||||
// If this is a comma, we skip it!
|
||||
if currentChild?.text == "," {
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// Otherwise, there should be one parameter left!
|
||||
switch Parameter.Compile(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (vds, updated_context)):
|
||||
return Result.Ok((parameters.addParameter(vds), updated_context))
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
extension ParameterList: Compilable {
|
||||
public typealias T = ParameterList
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ParameterList, CompilerContext)> {
|
||||
|
||||
let parameter_node = node
|
||||
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
|
||||
node: parameter_node, type: "parameters", nice_type_name: "Parameters")
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
|
||||
// Let's eat the '(' before we start ...
|
||||
if parameter_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: parameter_node, withError: "Missing '(' in parameter list component"))
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if parameter_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: parameter_node, withError: "Missing parameter list component"))
|
||||
}
|
||||
let currentChild = parameter_node.child(at: currentChildIdx)
|
||||
|
||||
return parameter_list_compiler(node: currentChild!, withContext: context)
|
||||
}
|
||||
}
|
||||
|
||||
extension Parameter: Compilable {
|
||||
public typealias T = Parameter
|
||||
public static func Compile(
|
||||
node: Node, withContext context: CompilerContext
|
||||
) -> Result<(Parameter, CompilerContext)> {
|
||||
|
||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
||||
node: node, type: "parameter", nice_type_name: "parameter")
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter declaration component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// Annotation?
|
||||
if currentChild!.nodeType == "annotations" {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: currentChild!,
|
||||
withError: "Annotations in parameter declarations are not yet handled"))
|
||||
// Will increment indexes here.
|
||||
}
|
||||
|
||||
// Direction?
|
||||
if currentChild!.nodeType == "direction" {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: currentChild!, withError: "Direction in parameter declarations are not yet handled"
|
||||
))
|
||||
// Will increment indexes here.
|
||||
}
|
||||
|
||||
if currentChild!.nodeType != "typeRef" {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Did not find type name for parameter declaration"))
|
||||
}
|
||||
|
||||
guard
|
||||
case .Ok(let parameter_type) = Types.CompileType(type: currentChild!, withContext: context)
|
||||
else {
|
||||
return Result.Error(
|
||||
Error(withMessage: "Could not parse a P4 type from \(currentChild!.text!)"))
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter declaration component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
if currentChild!.nodeType != "identifier" {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Did not find identifier for parameter statement"))
|
||||
}
|
||||
|
||||
guard
|
||||
case .Ok(let parameter_name) = Identifier.Compile(node: currentChild!, withContext: context)
|
||||
else {
|
||||
return Result.Error(
|
||||
Error(withMessage: "Could not parse a parameter name from \(currentChild!.text!)"))
|
||||
}
|
||||
|
||||
return Result.Ok(
|
||||
(
|
||||
Parameter(
|
||||
identifier: parameter_name, withType: parameter_type),
|
||||
context
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
extension Control: CompilableDeclaration {
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
|
||||
@@ -163,6 +163,7 @@ struct Expression {
|
||||
let expression_parsers: [CompilableExpression.Type] = [
|
||||
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self,
|
||||
BinaryOperatorExpression.self, ArrayAccessExpression.self, FieldAccessExpression.self,
|
||||
FunctionCall.self
|
||||
]
|
||||
|
||||
for candidate_expression_parser in expression_parsers {
|
||||
@@ -653,3 +654,67 @@ extension ArrayAccessExpression: CompilableLValueExpression {
|
||||
return Result.Ok(array_access_expression)
|
||||
}
|
||||
}
|
||||
|
||||
extension FunctionCall: CompilableExpression {
|
||||
static func compile(
|
||||
node: Node, withContext context: CompilerContext
|
||||
) -> Result<EvaluatableExpression?> {
|
||||
let expression = node.child(at: 0)!
|
||||
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
|
||||
node: expression, type: "function_call")
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
|
||||
if expression.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing function call component"))
|
||||
}
|
||||
|
||||
currentChild = expression.child(at: currentChildIdx)
|
||||
|
||||
let maybe_callee_name = Identifier.Compile(
|
||||
node: currentChild!, withContext: context)
|
||||
guard case .Ok(let callee_name) = maybe_callee_name else {
|
||||
return Result.Error(maybe_callee_name.error()!)
|
||||
}
|
||||
|
||||
let maybe_callee = switch context.types.lookup(identifier: callee_name) {
|
||||
case .Ok(let looked_up): switch looked_up {
|
||||
case let callee as FunctionDeclaration: Result.Ok(callee) // What we found is actually a function declaration
|
||||
default: Result<FunctionDeclaration>.Error(ErrorOnNode(node: currentChild!, withError: "\(callee_name) is not a function"))
|
||||
}
|
||||
case .Error(let e): Result<FunctionDeclaration>.Error(e)
|
||||
}
|
||||
|
||||
guard case .Ok(let callee) = maybe_callee else {
|
||||
return .Error(maybe_callee.error()!)
|
||||
}
|
||||
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if expression.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing function call component"))
|
||||
}
|
||||
currentChild = expression.child(at: currentChildIdx)
|
||||
|
||||
let maybe_argument_list = ArgumentList.Compile(node: currentChild!, withContext: context)
|
||||
|
||||
guard case .Ok((let arguments, _)) = maybe_argument_list else {
|
||||
return .Error(maybe_argument_list.error()!)
|
||||
}
|
||||
|
||||
// Now, compare the arguments with the parameters:
|
||||
|
||||
if case .Error(let e) = arguments.compatible(callee.params) {
|
||||
return .Error(e)
|
||||
}
|
||||
|
||||
// All good!
|
||||
|
||||
return .Ok(FunctionCall(callee, withArguments: arguments))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ public struct Parser {
|
||||
"expressionStatement": ExpressionStatement.self,
|
||||
"variableDeclaration": VariableDeclarationStatement.self,
|
||||
"conditionalStatement": ConditionalStatement.self, "blockStatement": BlockStatement.self,
|
||||
"return_statement": ReturnStatement.self,
|
||||
]
|
||||
guard let parser = statementParsers[statement.nodeType ?? ""] else {
|
||||
return Result.Error(
|
||||
|
||||
@@ -299,3 +299,19 @@ extension ParserAssignmentStatement: CompilableStatement {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
extension ReturnStatement: CompilableStatement {
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(any Common.EvaluatableStatement, CompilerContext)> {
|
||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
||||
node: node, type: "return_statement", nice_type_name: "return statement")
|
||||
|
||||
let expression_node = node.child(at: 1)!
|
||||
|
||||
return switch Expression.Compile(node: expression_node, withContext: context) {
|
||||
case .Ok(let result): .Ok((ReturnStatement(result), context))
|
||||
case .Error(let e): .Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// p4rse, Copyright 2026, Will Hawkins
|
||||
//
|
||||
// This file is part of p4rse.
|
||||
//
|
||||
// This file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Common
|
||||
|
||||
public struct Parameter: CustomStringConvertible, Equatable {
|
||||
public static func == (lhs: Parameter, rhs: Parameter) -> Bool {
|
||||
return lhs.name == rhs.name && lhs.type.eq(rhs: rhs.type)
|
||||
}
|
||||
|
||||
public var name: Identifier
|
||||
public var type: P4Type
|
||||
|
||||
public init(
|
||||
identifier: Identifier, withType type: P4Type
|
||||
) {
|
||||
self.name = identifier
|
||||
self.type = type
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Parameter: \(self.name) with type \(self.type)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct ParameterList: CustomStringConvertible, Equatable {
|
||||
public static func == (lhs: ParameterList, rhs: ParameterList) -> Bool {
|
||||
if lhs.parameters.count != rhs.parameters.count {
|
||||
return false
|
||||
}
|
||||
|
||||
return 0
|
||||
== zip(lhs.parameters, rhs.parameters).count { (lparam, rparam) in
|
||||
return lparam != rparam
|
||||
}
|
||||
}
|
||||
|
||||
public var parameters: [Parameter]
|
||||
|
||||
public init() {
|
||||
self.parameters = Array()
|
||||
}
|
||||
|
||||
public init(_ parameters: [Parameter]) {
|
||||
self.parameters = parameters
|
||||
}
|
||||
|
||||
public func addParameter(_ parameter: Parameter) -> ParameterList {
|
||||
return ParameterList(self.parameters + [parameter])
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
let parameters = self.parameters.map { parameter in
|
||||
parameter.description
|
||||
}.joined(separator: ";")
|
||||
return "Parameter list: \(parameters)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct ArgumentList {
|
||||
public let arguments: [Argument]
|
||||
|
||||
public init(_ arguments: [Argument]) {
|
||||
self.arguments = arguments
|
||||
}
|
||||
|
||||
public func compatible(_ parameters: ParameterList) -> Result<()> {
|
||||
if self.arguments.count != parameters.parameters.count {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage:
|
||||
"\(self.arguments.count) arguments found but \(parameters.parameters.count) required"))
|
||||
}
|
||||
|
||||
for (arg, param) in zip(self.arguments, parameters.parameters) {
|
||||
let arg_index = arg.index
|
||||
let arg_type = arg.argument.type()
|
||||
if !arg_type.eq(rhs: param.type) {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage:
|
||||
"Argument \(arg_index)'s type (\(arg_type)) is incompatible with the parameter type (\(param.type))"
|
||||
))
|
||||
}
|
||||
}
|
||||
return .Ok(())
|
||||
}
|
||||
|
||||
public func addArgument(_ argument: Argument) -> ArgumentList {
|
||||
return ArgumentList(self.arguments + [argument])
|
||||
}
|
||||
|
||||
public func count() -> Int {
|
||||
return self.arguments.count
|
||||
}
|
||||
}
|
||||
|
||||
public struct Argument {
|
||||
public let index: Int
|
||||
public let argument: EvaluatableExpression
|
||||
|
||||
public init(_ argument: EvaluatableExpression, atIndex index: Int) {
|
||||
self.argument = argument
|
||||
self.index = index
|
||||
}
|
||||
}
|
||||
@@ -19,60 +19,6 @@ import Common
|
||||
|
||||
public struct Declaration {}
|
||||
|
||||
public struct Parameter: CustomStringConvertible, Equatable {
|
||||
public static func == (lhs: Parameter, rhs: Parameter) -> Bool {
|
||||
return lhs.name == rhs.name && lhs.type.eq(rhs: rhs.type)
|
||||
}
|
||||
|
||||
public var name: Identifier
|
||||
public var type: P4Type
|
||||
|
||||
public init(
|
||||
identifier: Identifier, withType type: P4Type
|
||||
) {
|
||||
self.name = identifier
|
||||
self.type = type
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Parameter: \(self.name) with type \(self.type)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct ParameterList: CustomStringConvertible, Equatable {
|
||||
public static func == (lhs: ParameterList, rhs: ParameterList) -> Bool {
|
||||
if lhs.parameters.count != rhs.parameters.count {
|
||||
return false
|
||||
}
|
||||
|
||||
return 0
|
||||
== zip(lhs.parameters, rhs.parameters).count { (lparam, rparam) in
|
||||
return lparam != rparam
|
||||
}
|
||||
}
|
||||
|
||||
public var parameters: [Parameter]
|
||||
|
||||
public init() {
|
||||
self.parameters = Array()
|
||||
}
|
||||
|
||||
public init(_ parameters: [Parameter]) {
|
||||
self.parameters = parameters
|
||||
}
|
||||
|
||||
public func addParameter(_ parameter: Parameter) -> ParameterList {
|
||||
return ParameterList(self.parameters + [parameter])
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
let parameters = self.parameters.map { parameter in
|
||||
parameter.description
|
||||
}.joined(separator: ";")
|
||||
return "Parameter list: \(parameters)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct FunctionDeclaration: P4Type, P4Value {
|
||||
public func type() -> any Common.P4Type {
|
||||
return self
|
||||
|
||||
@@ -201,33 +201,12 @@ public struct FieldAccessExpression {
|
||||
}
|
||||
}
|
||||
|
||||
public struct ArgumentList {
|
||||
public let arguments: [(Int, EvaluatableExpression)]
|
||||
public init(_ arguments: [EvaluatableExpression]) {
|
||||
self.arguments = zip(1..., arguments).map { (idx, argument) in
|
||||
(idx, argument)
|
||||
}
|
||||
}
|
||||
public struct FunctionCall {
|
||||
public let callee: FunctionDeclaration
|
||||
public let arguments: ArgumentList
|
||||
|
||||
public func compatible(_ parameters: ParameterList) -> Result<()> {
|
||||
if self.arguments.count != parameters.parameters.count {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage:
|
||||
"\(self.arguments.count) arguments found but \(parameters.parameters.count) required"))
|
||||
}
|
||||
|
||||
for (arg, param) in zip(self.arguments, parameters.parameters) {
|
||||
let arg_index = arg.0
|
||||
let arg_type = arg.1.type()
|
||||
if !arg_type.eq(rhs: param.type) {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage:
|
||||
"Argument \(arg_index)'s type (\(arg_type)) is incompatible with the parameter type (\(param.type))"
|
||||
))
|
||||
}
|
||||
}
|
||||
return .Ok(())
|
||||
public init(_ callee: FunctionDeclaration, withArguments arguments: ArgumentList) {
|
||||
self.callee = callee
|
||||
self.arguments = arguments
|
||||
}
|
||||
}
|
||||
@@ -55,3 +55,11 @@ public struct BlockStatement {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct ReturnStatement {
|
||||
public let value: EvaluatableExpression
|
||||
|
||||
public init(_ value: EvaluatableExpression) {
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// p4rse, Copyright 2026, Will Hawkins
|
||||
//
|
||||
// This file is part of p4rse.
|
||||
//
|
||||
// This file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Common
|
||||
import P4Lang
|
||||
@@ -382,3 +382,40 @@ extension KeysetExpression: EvaluatableExpression {
|
||||
return self.kse_type()
|
||||
}
|
||||
}
|
||||
|
||||
extension FunctionCall: EvaluatableExpression {
|
||||
public func evaluate(execution: Common.ProgramExecution) -> Common.Result<any Common.P4Value> {
|
||||
|
||||
guard let body = self.callee.body else {
|
||||
return .Error(Error(withMessage: "No body for called function (\(self.callee.name))"))
|
||||
}
|
||||
|
||||
// Put the arguments into scope
|
||||
|
||||
var called_execution = execution.enter_scope()
|
||||
for (parameter, argument) in zip(self.callee.params.parameters, arguments.arguments) {
|
||||
let arg_idx = argument.index
|
||||
let arg_value = argument.argument
|
||||
let maybe_argument_value = arg_value.evaluate(execution: called_execution)
|
||||
guard case .Ok(let argument_value) = maybe_argument_value else {
|
||||
return .Error(Error(withMessage: "Cannot evaluate argument \(arg_idx): \(argument)"))
|
||||
}
|
||||
called_execution = called_execution.declare(identifier: parameter.name, withValue: argument_value)
|
||||
}
|
||||
|
||||
let (control_flow, _) = body.evaluate(execution: called_execution)
|
||||
|
||||
return switch control_flow {
|
||||
case ControlFlow.Return(let value): if let value = value {
|
||||
.Ok(value)
|
||||
} else {
|
||||
.Error(Error(withMessage: "No value returned from called function (\(self.callee.name))"))
|
||||
}
|
||||
default: .Error(Error(withMessage: "No value returned from called function (\(self.callee.name))"))
|
||||
}
|
||||
}
|
||||
|
||||
public func type() -> any Common.P4Type {
|
||||
return self.callee.tipe
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,20 +19,20 @@ import Common
|
||||
import P4Lang
|
||||
|
||||
extension ParserAssignmentStatement: EvaluatableStatement {
|
||||
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
|
||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||
let result = self.value.evaluate(execution: execution)
|
||||
guard case Result.Ok(let value) = result else {
|
||||
return execution.setError(error: result.error()!)
|
||||
return (ControlFlow.Error, execution.setError(error: result.error()!))
|
||||
}
|
||||
|
||||
let maybe_updated_scopes = self.lvalue.set(
|
||||
to: value, inScopes: execution.scopes, duringExecution: execution)
|
||||
guard case Result.Ok(let updated_scopes) = maybe_updated_scopes else {
|
||||
return execution.setError(error: maybe_updated_scopes.error()!)
|
||||
return (ControlFlow.Error, execution.setError(error: maybe_updated_scopes.error()!))
|
||||
}
|
||||
execution.scopes = updated_scopes.0
|
||||
|
||||
return execution
|
||||
return (ControlFlow.Next, execution)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,12 @@ extension ParserStateDirectTransition: EvaluatableParserState {
|
||||
var program = program.enter_scope()
|
||||
|
||||
for statement in statements {
|
||||
program = statement.evaluate(execution: program)
|
||||
let (control_flow, next_program) = statement.evaluate(execution: program)
|
||||
switch control_flow {
|
||||
case .Next: program = next_program // Ok!
|
||||
case .Error: return (reject, next_program)
|
||||
default: return (reject, next_program.setError(error: Error(withMessage: "Invalid control flow (\(control_flow) in parser)")))
|
||||
}
|
||||
}
|
||||
let res = program.scopes.lookup(identifier: get_next_state())
|
||||
|
||||
@@ -93,7 +98,12 @@ extension ParserStateSelectTransition: EvaluatableParserState {
|
||||
|
||||
// First, evaluate the statements.
|
||||
for statement in statements {
|
||||
program = statement.evaluate(execution: program)
|
||||
let (control_flow, next_program) = statement.evaluate(execution: program)
|
||||
switch control_flow {
|
||||
case .Next: program = next_program // Ok!
|
||||
case .Error: return (reject, next_program)
|
||||
default: return (reject, next_program.setError(error: Error(withMessage: "Invalid control flow (\(control_flow) in parser)")))
|
||||
}
|
||||
}
|
||||
|
||||
let res = self.selectExpression.evaluate(execution: program)
|
||||
@@ -159,8 +169,8 @@ extension Parser: CallableExecution {
|
||||
}
|
||||
|
||||
for (parameter, argument) in zip(self.parameters.parameters, arguments.arguments) {
|
||||
let arg_idx = argument.0
|
||||
let arg_value = argument.1
|
||||
let arg_idx = argument.index
|
||||
let arg_value = argument.argument
|
||||
let maybe_argument_value = arg_value.evaluate(execution: execution)
|
||||
guard case .Ok(let argument_value) = maybe_argument_value else {
|
||||
return (
|
||||
|
||||
@@ -22,12 +22,6 @@ public protocol Execution {
|
||||
func execute(execution: ProgramExecution) -> ProgramExecution
|
||||
}
|
||||
|
||||
public protocol Compilable {
|
||||
associatedtype ToCompile
|
||||
associatedtype Compiled
|
||||
static func compile(_: ToCompile) -> Result<Compiled>
|
||||
}
|
||||
|
||||
public protocol EvaluatableParserState: P4Value {
|
||||
func execute(program: ProgramExecution) -> (EvaluatableParserState, ProgramExecution)
|
||||
func done() -> Bool
|
||||
|
||||
@@ -19,51 +19,99 @@ import Common
|
||||
import P4Lang
|
||||
|
||||
extension BlockStatement: EvaluatableStatement {
|
||||
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
|
||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||
var execution = execution
|
||||
for s in self.statements {
|
||||
execution = s.evaluate(execution: execution)
|
||||
let (control_flow, next_execution) = s.evaluate(execution: execution)
|
||||
switch control_flow {
|
||||
case ControlFlow.Return(let value): return (ControlFlow.Return(value), next_execution)
|
||||
case ControlFlow.Next: execution = next_execution
|
||||
case ControlFlow.Error: return (ControlFlow.Error, next_execution)
|
||||
default:
|
||||
return (
|
||||
ControlFlow.Next,
|
||||
next_execution.setError(
|
||||
error: Error(withMessage: "Invalid control flow \(control_flow) in block statement"))
|
||||
)
|
||||
}
|
||||
return execution
|
||||
}
|
||||
return (ControlFlow.Next, execution)
|
||||
}
|
||||
}
|
||||
|
||||
extension VariableDeclarationStatement: EvaluatableStatement {
|
||||
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
|
||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||
guard case .Ok(let initial_value) = self.initializer.evaluate(execution: execution) else {
|
||||
return execution.setError(error: Error(withMessage: "Could not evaluate \(self.initializer)"))
|
||||
return (
|
||||
ControlFlow.Error,
|
||||
execution.setError(error: Error(withMessage: "Could not evaluate \(self.initializer)"))
|
||||
)
|
||||
}
|
||||
let new_scopes = execution.scopes.declare(identifier: self.identifier, withValue: initial_value)
|
||||
execution.scopes = new_scopes
|
||||
return execution
|
||||
return (ControlFlow.Next, execution)
|
||||
}
|
||||
}
|
||||
|
||||
extension ConditionalStatement: EvaluatableStatement {
|
||||
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
|
||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||
guard case .Ok(let evaluated_condition) = self.condition.evaluate(execution: execution) else {
|
||||
return execution.setError(error: Error(withMessage: "Could not evaluate \(self.condition)"))
|
||||
return (
|
||||
ControlFlow.Error,
|
||||
execution.setError(error: Error(withMessage: "Could not evaluate \(self.condition)"))
|
||||
)
|
||||
}
|
||||
|
||||
if !evaluated_condition.type().eq(rhs: P4Boolean()) {
|
||||
return execution.setError(error: Error(withMessage: "Condition expression is not a Boolean"))
|
||||
return (
|
||||
ControlFlow.Error,
|
||||
execution.setError(error: Error(withMessage: "Condition expression is not a Boolean"))
|
||||
)
|
||||
}
|
||||
|
||||
if evaluated_condition.eq(rhs: P4BooleanValue.init(withValue: true)) {
|
||||
let execution = execution.enter_scope()
|
||||
var result = self.thenn.evaluate(execution: execution)
|
||||
result = result.exit_scope()
|
||||
return result
|
||||
switch self.thenn.evaluate(execution: execution) {
|
||||
case (ControlFlow.Next, let result): return (ControlFlow.Next, result.exit_scope())
|
||||
case (ControlFlow.Error, let result): return (ControlFlow.Error, result.exit_scope())
|
||||
case (let cf, let result):
|
||||
return (
|
||||
ControlFlow.Next,
|
||||
result.setError(
|
||||
error: Error(withMessage: "Invalid control flow \(cf) in conditional statement"))
|
||||
)
|
||||
}
|
||||
} else if let elss = self.elss {
|
||||
let execution = execution.enter_scope()
|
||||
var result = elss.evaluate(execution: execution)
|
||||
result = result.exit_scope()
|
||||
return result
|
||||
switch elss.evaluate(execution: execution) {
|
||||
case (ControlFlow.Next, let result): return (ControlFlow.Next, result.exit_scope())
|
||||
case (ControlFlow.Error, let result): return (ControlFlow.Error, result.exit_scope())
|
||||
case (let cf, let result):
|
||||
return (
|
||||
ControlFlow.Next,
|
||||
result.setError(
|
||||
error: Error(withMessage: "Invalid control flow \(cf) in conditional statement"))
|
||||
)
|
||||
}
|
||||
return execution
|
||||
}
|
||||
return (ControlFlow.Next, execution)
|
||||
}
|
||||
}
|
||||
|
||||
extension ExpressionStatement: EvaluatableStatement {
|
||||
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
|
||||
return execution
|
||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||
// TODO: Should this do something? Side effects?
|
||||
return (ControlFlow.Next, execution)
|
||||
}
|
||||
}
|
||||
|
||||
extension ReturnStatement: EvaluatableStatement {
|
||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||
return switch self.value.evaluate(execution: execution) {
|
||||
case .Ok(let v): (ControlFlow.Return(v), execution)
|
||||
case .Error(let e): (ControlFlow.Error, execution.setError(error: e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
// p4rse, Copyright 2026, Will Hawkins
|
||||
//
|
||||
// This file is part of p4rse.
|
||||
//
|
||||
// This file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Common
|
||||
import Foundation
|
||||
import Macros
|
||||
import P4Lang
|
||||
import P4Runtime
|
||||
import SwiftTreeSitter
|
||||
import Testing
|
||||
import TreeSitter
|
||||
import TreeSitterP4
|
||||
|
||||
@testable import P4Compiler
|
||||
|
||||
@Test func test_function_call_scoped_name_collision() async throws {
|
||||
let simple_parser_declaration = """
|
||||
bool functionb(bool c) {
|
||||
return c;
|
||||
};
|
||||
parser main_parser() {
|
||||
state start {
|
||||
int c = 5;
|
||||
bool b = functionb(true);
|
||||
transition select (b) {
|
||||
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_scoped_name_collision2() async throws {
|
||||
// Test whether the assignment to c leaks out of the function call scope.
|
||||
let simple_parser_declaration = """
|
||||
bool functionb(bool c) {
|
||||
c = true;
|
||||
return c;
|
||||
};
|
||||
parser main_parser() {
|
||||
state start {
|
||||
bool c = false;
|
||||
bool b = functionb(true);
|
||||
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.reject)
|
||||
}
|
||||
|
||||
@Test func test_function_call_integer_return_value() async throws {
|
||||
let simple_parser_declaration = """
|
||||
int functionb(int c) {
|
||||
return c;
|
||||
};
|
||||
parser main_parser() {
|
||||
state start {
|
||||
int c = 5;
|
||||
transition select (5 == functionb(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_value2() async throws {
|
||||
let simple_parser_declaration = """
|
||||
int functionb(int c) {
|
||||
return c;
|
||||
};
|
||||
parser main_parser() {
|
||||
state start {
|
||||
int c = 5;
|
||||
transition select (4 == functionb(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.reject)
|
||||
}
|
||||
@@ -176,7 +176,7 @@ import TreeSitterP4
|
||||
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||
|
||||
let args = ArgumentList([
|
||||
P4BooleanValue(withValue: false), P4StringValue(withValue: "Testing"), P4IntValue(withValue: 5),
|
||||
Argument(P4BooleanValue(withValue: false), atIndex: 1), Argument(P4StringValue(withValue: "Testing"), atIndex: 2), Argument(P4IntValue(withValue: 5), atIndex: 3),
|
||||
])
|
||||
let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: args))
|
||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
||||
@@ -197,7 +197,7 @@ import TreeSitterP4
|
||||
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||
|
||||
let args = ArgumentList([
|
||||
P4BooleanValue(withValue: false), P4StringValue(withValue: "Testing"), P4IntValue(withValue: 5),
|
||||
Argument(P4BooleanValue(withValue: false), atIndex: 1), Argument(P4StringValue(withValue: "Testing"), atIndex: 2), Argument(P4IntValue(withValue: 5), atIndex: 3),
|
||||
])
|
||||
let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: args))
|
||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
||||
|
||||
@@ -96,7 +96,7 @@ import TreeSitterP4
|
||||
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||
|
||||
let args = ArgumentList([
|
||||
P4BooleanValue(withValue: true), P4StringValue(withValue: "Testing"), P4IntValue(withValue: 5),
|
||||
Argument(P4BooleanValue(withValue: true), atIndex: 1), Argument(P4StringValue(withValue: "Testing"), atIndex: 2), Argument(P4IntValue(withValue: 5), atIndex: 3),
|
||||
])
|
||||
let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: args))
|
||||
// We should be in the accept state.
|
||||
@@ -118,7 +118,7 @@ import TreeSitterP4
|
||||
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||
|
||||
let args = ArgumentList([
|
||||
P4BooleanValue(withValue: true), P4BooleanValue(withValue: false), P4IntValue(withValue: 5),
|
||||
Argument(P4BooleanValue(withValue: true), atIndex: 1), Argument(P4BooleanValue(withValue: false), atIndex: 2), Argument(P4IntValue(withValue: 5), atIndex: 3),
|
||||
])
|
||||
|
||||
#expect(
|
||||
@@ -142,7 +142,7 @@ import TreeSitterP4
|
||||
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||
|
||||
let args = ArgumentList([
|
||||
P4IntValue(withValue: 5), P4StringValue(withValue: "Testing"), P4IntValue(withValue: 5),
|
||||
Argument(P4IntValue(withValue: 5), atIndex: 1), Argument(P4StringValue(withValue: "Testing"), atIndex: 2), Argument(P4IntValue(withValue: 5), atIndex: 3),
|
||||
])
|
||||
|
||||
#expect(
|
||||
@@ -164,7 +164,7 @@ import TreeSitterP4
|
||||
"""
|
||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
||||
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||
let args = ArgumentList([P4BooleanValue(withValue: true)])
|
||||
let args = ArgumentList([Argument(P4BooleanValue(withValue: true), atIndex: 0)])
|
||||
|
||||
#expect(
|
||||
#RequireErrorResult<(ParserState, ProgramExecution)>(
|
||||
|
||||
@@ -34,6 +34,10 @@ export default grammar({
|
||||
direction: $ => choice($.in, $.out, $.inout),
|
||||
parameters: $=> seq('(', optional($.parameter_list), ')'),
|
||||
|
||||
argument_list: $ => choice($.argument, seq($.argument_list, ',', $.argument)),
|
||||
argument: $ => $.expression,
|
||||
arguments: $=> seq('(', optional($.argument_list), ')'),
|
||||
|
||||
// Common - Types
|
||||
typeRef: $ => choice($.baseType, $.type_identifier),
|
||||
baseType: $ => choice($.bool, $.error, $.string, $.int, $.bit /* omitting "templated" types" */),
|
||||
@@ -96,11 +100,12 @@ export default grammar({
|
||||
|
||||
// General statements
|
||||
statements: $ => repeat1($.statement),
|
||||
statement: $ => choice($.conditionalStatement, $.blockStatement, $.expressionStatement, $.assignmentStatement, $.variableDeclaration),// Limited, so far.
|
||||
statement: $ => choice($.conditionalStatement, $.blockStatement, $.expressionStatement, $.assignmentStatement, $.variableDeclaration, $.return_statement),// Limited, so far.
|
||||
blockStatement: $ => seq(optional($.annotations), '{', optional($.statements), '}'),
|
||||
conditionalStatement: $ => choice(prec(1, seq($.if, '(', $.expression, ')', $.statement)), prec(2, seq($.if, '(', $.expression, ')', $.statement, $.else, $.statement))),
|
||||
expressionStatement: $=> seq($.expression, $._semicolon),
|
||||
assignmentStatement: $=> seq($.expression, $.assignment, $.expression, $._semicolon),
|
||||
return_statement: $=> seq($.return, $.expression, $._semicolon),
|
||||
|
||||
// Parser statements
|
||||
parserStatements: $ => repeat1($.parserStatement),
|
||||
@@ -111,7 +116,7 @@ export default grammar({
|
||||
// Expressions
|
||||
expression: $ => choice($.grouped_expression, $.simple_expression),
|
||||
grouped_expression: $ => seq('(', $.expression, ')'),
|
||||
simple_expression: $ => choice($.identifier, $.integer, $.booleanLiteralExpression, $.string_literal, $.binaryOperatorExpression, $.arrayAccessExpression, $.fieldAccessExpression), // Very limited.
|
||||
simple_expression: $ => choice($.identifier, $.integer, $.booleanLiteralExpression, $.string_literal, $.binaryOperatorExpression, $.arrayAccessExpression, $.fieldAccessExpression, $.function_call), // Very limited.
|
||||
booleanLiteralExpression: $ => choice($.true, $.false),
|
||||
selectExpression: $ => seq($.select, '(', $.expression, ')', '{', $.selectBody, '}'), // TODO: Should be expression list and not just a single expression
|
||||
transitionSelectionExpression: $ => choice($.identifier, $.selectExpression),
|
||||
@@ -131,6 +136,10 @@ export default grammar({
|
||||
arrayAccessExpression: $ => seq($.expression, $.open_bracket, $.expression, $.close_bracket),
|
||||
fieldAccessExpression: $=> prec.left(2, seq($.expression, $.field_access, $.identifier)),
|
||||
|
||||
// Function call
|
||||
|
||||
function_call: $=> seq($.identifier, $.arguments),
|
||||
|
||||
// Binary Operations
|
||||
binaryEqualOperatorExpression: $ => prec.left(2, seq($.expression, $.double_equal, $.expression)),
|
||||
binaryLessThanOperatorExpression: $ => prec.left(2, seq($.expression, $.less_than, $.expression)),
|
||||
|
||||
@@ -306,3 +306,131 @@ parser simple() {
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
=========================
|
||||
Simple Function Call
|
||||
=========================
|
||||
parser simple() {
|
||||
state start {
|
||||
func();
|
||||
transition accept;
|
||||
}
|
||||
};
|
||||
|
||||
---
|
||||
(p4program
|
||||
(declaration
|
||||
(parserDeclaration
|
||||
(parserType
|
||||
(parser)
|
||||
(identifier)
|
||||
(parameters)
|
||||
)
|
||||
(parserStates
|
||||
(parserState
|
||||
(state)
|
||||
(identifier)
|
||||
(parserStatements
|
||||
(parserStatement
|
||||
(expressionStatement
|
||||
(expression
|
||||
(simple_expression
|
||||
(function_call
|
||||
(identifier)
|
||||
(arguments)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(parserTransitionStatement
|
||||
(transition)
|
||||
(transitionSelectionExpression
|
||||
(identifier)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
=========================
|
||||
Simple Function Call (With Arguments)
|
||||
=========================
|
||||
parser simple() {
|
||||
state start {
|
||||
func(1, true, variable);
|
||||
transition accept;
|
||||
}
|
||||
};
|
||||
|
||||
---
|
||||
(p4program
|
||||
(declaration
|
||||
(parserDeclaration
|
||||
(parserType
|
||||
(parser)
|
||||
(identifier)
|
||||
(parameters)
|
||||
)
|
||||
(parserStates
|
||||
(parserState
|
||||
(state)
|
||||
(identifier)
|
||||
(parserStatements
|
||||
(parserStatement
|
||||
(expressionStatement
|
||||
(expression
|
||||
(simple_expression
|
||||
(function_call
|
||||
(identifier)
|
||||
(arguments
|
||||
(argument_list
|
||||
(argument_list
|
||||
(argument_list
|
||||
(argument
|
||||
(expression
|
||||
(simple_expression
|
||||
(integer)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(argument
|
||||
(expression
|
||||
(simple_expression
|
||||
(booleanLiteralExpression
|
||||
(true)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(argument
|
||||
(expression
|
||||
(simple_expression
|
||||
(identifier)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(parserTransitionStatement
|
||||
(transition)
|
||||
(transitionSelectionExpression
|
||||
(identifier)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user