@@ -21,6 +21,13 @@ open class ProgramExecution: CustomStringConvertible {
|
|||||||
var error: Error?
|
var error: Error?
|
||||||
var debug: DebugLevel = DebugLevel.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() {
|
public init() {
|
||||||
initialValues = .none
|
initialValues = .none
|
||||||
}
|
}
|
||||||
@@ -42,7 +49,7 @@ open class ProgramExecution: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func setError(error: Error) -> ProgramExecution {
|
public func setError(error: Error) -> ProgramExecution {
|
||||||
let npe = self
|
let npe = ProgramExecution(copy: self)
|
||||||
npe.error = error
|
npe.error = error
|
||||||
return npe
|
return npe
|
||||||
}
|
}
|
||||||
@@ -52,7 +59,7 @@ open class ProgramExecution: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func setDebugLevel(_ dl: DebugLevel) -> ProgramExecution {
|
public func setDebugLevel(_ dl: DebugLevel) -> ProgramExecution {
|
||||||
let pe = self
|
let pe = ProgramExecution(copy: self)
|
||||||
pe.debug = dl
|
pe.debug = dl
|
||||||
return pe
|
return pe
|
||||||
}
|
}
|
||||||
@@ -67,22 +74,22 @@ open class ProgramExecution: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func enter_scope() -> ProgramExecution {
|
public func enter_scope() -> ProgramExecution {
|
||||||
let new_pe = self
|
let new_pe = ProgramExecution(copy: self)
|
||||||
new_pe.scopes = self.scopes.enter()
|
new_pe.scopes = new_pe.scopes.enter()
|
||||||
|
|
||||||
return new_pe
|
return new_pe
|
||||||
}
|
}
|
||||||
|
|
||||||
public func exit_scope() -> ProgramExecution {
|
public func exit_scope() -> ProgramExecution {
|
||||||
let new_pe = self
|
let new_pe = ProgramExecution(copy: self)
|
||||||
new_pe.scopes = self.scopes.exit()
|
new_pe.scopes = new_pe.scopes.exit()
|
||||||
|
|
||||||
return new_pe
|
return new_pe
|
||||||
}
|
}
|
||||||
|
|
||||||
public func declare(identifier: Identifier, withValue value: P4Value) -> ProgramExecution {
|
public func declare(identifier: Identifier, withValue value: P4Value) -> ProgramExecution {
|
||||||
let new_pe = self
|
let new_pe = ProgramExecution(copy: self)
|
||||||
let new_scopes = self.scopes.declare(identifier: identifier, withValue: value)
|
let new_scopes = new_pe.scopes.declare(identifier: identifier, withValue: value)
|
||||||
|
|
||||||
new_pe.scopes = new_scopes
|
new_pe.scopes = new_scopes
|
||||||
return new_pe
|
return new_pe
|
||||||
@@ -98,3 +105,12 @@ public typealias VarValueScope = Scope<P4Value>
|
|||||||
|
|
||||||
/// Scopes that resolves variable identifiers to their values.
|
/// Scopes that resolves variable identifiers to their values.
|
||||||
public typealias VarValueScopes = Scopes<P4Value>
|
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
|
/// Evaluate a statement for a given execution
|
||||||
/// - Parameters
|
/// - Parameters
|
||||||
/// - execution: The execution context in which to evaluate the parser statement
|
/// - execution: The execution context in which to evaluate the parser statement
|
||||||
/// - Returns: An updated execution after evaluating the parser statement
|
/// - Returns: A tuple of
|
||||||
func evaluate(execution: ProgramExecution) -> ProgramExecution
|
/// 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 {
|
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 {
|
extension Control: CompilableDeclaration {
|
||||||
public static func Compile(
|
public static func Compile(
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ struct Expression {
|
|||||||
let expression_parsers: [CompilableExpression.Type] = [
|
let expression_parsers: [CompilableExpression.Type] = [
|
||||||
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self,
|
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self,
|
||||||
BinaryOperatorExpression.self, ArrayAccessExpression.self, FieldAccessExpression.self,
|
BinaryOperatorExpression.self, ArrayAccessExpression.self, FieldAccessExpression.self,
|
||||||
|
FunctionCall.self
|
||||||
]
|
]
|
||||||
|
|
||||||
for candidate_expression_parser in expression_parsers {
|
for candidate_expression_parser in expression_parsers {
|
||||||
@@ -653,3 +654,67 @@ extension ArrayAccessExpression: CompilableLValueExpression {
|
|||||||
return Result.Ok(array_access_expression)
|
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,
|
"expressionStatement": ExpressionStatement.self,
|
||||||
"variableDeclaration": VariableDeclarationStatement.self,
|
"variableDeclaration": VariableDeclarationStatement.self,
|
||||||
"conditionalStatement": ConditionalStatement.self, "blockStatement": BlockStatement.self,
|
"conditionalStatement": ConditionalStatement.self, "blockStatement": BlockStatement.self,
|
||||||
|
"return_statement": ReturnStatement.self,
|
||||||
]
|
]
|
||||||
guard let parser = statementParsers[statement.nodeType ?? ""] else {
|
guard let parser = statementParsers[statement.nodeType ?? ""] else {
|
||||||
return Result.Error(
|
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 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 struct FunctionDeclaration: P4Type, P4Value {
|
||||||
public func type() -> any Common.P4Type {
|
public func type() -> any Common.P4Type {
|
||||||
return self
|
return self
|
||||||
|
|||||||
@@ -201,33 +201,12 @@ public struct FieldAccessExpression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ArgumentList {
|
public struct FunctionCall {
|
||||||
public let arguments: [(Int, EvaluatableExpression)]
|
public let callee: FunctionDeclaration
|
||||||
public init(_ arguments: [EvaluatableExpression]) {
|
public let arguments: ArgumentList
|
||||||
self.arguments = zip(1..., arguments).map { (idx, argument) in
|
|
||||||
(idx, argument)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func compatible(_ parameters: ParameterList) -> Result<()> {
|
public init(_ callee: FunctionDeclaration, withArguments arguments: ArgumentList) {
|
||||||
if self.arguments.count != parameters.parameters.count {
|
self.callee = callee
|
||||||
return .Error(
|
self.arguments = arguments
|
||||||
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(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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()
|
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
|
import P4Lang
|
||||||
|
|
||||||
extension ParserAssignmentStatement: EvaluatableStatement {
|
extension ParserAssignmentStatement: EvaluatableStatement {
|
||||||
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
|
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||||
let result = self.value.evaluate(execution: execution)
|
let result = self.value.evaluate(execution: execution)
|
||||||
guard case Result.Ok(let value) = result else {
|
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(
|
let maybe_updated_scopes = self.lvalue.set(
|
||||||
to: value, inScopes: execution.scopes, duringExecution: execution)
|
to: value, inScopes: execution.scopes, duringExecution: execution)
|
||||||
guard case Result.Ok(let updated_scopes) = maybe_updated_scopes else {
|
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
|
execution.scopes = updated_scopes.0
|
||||||
|
|
||||||
return execution
|
return (ControlFlow.Next, execution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +43,12 @@ extension ParserStateDirectTransition: EvaluatableParserState {
|
|||||||
var program = program.enter_scope()
|
var program = program.enter_scope()
|
||||||
|
|
||||||
for statement in 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 = program.scopes.lookup(identifier: get_next_state())
|
let res = program.scopes.lookup(identifier: get_next_state())
|
||||||
|
|
||||||
@@ -93,7 +98,12 @@ extension ParserStateSelectTransition: EvaluatableParserState {
|
|||||||
|
|
||||||
// First, evaluate the statements.
|
// First, evaluate the statements.
|
||||||
for statement in 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)
|
let res = self.selectExpression.evaluate(execution: program)
|
||||||
@@ -159,8 +169,8 @@ extension Parser: CallableExecution {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (parameter, argument) in zip(self.parameters.parameters, arguments.arguments) {
|
for (parameter, argument) in zip(self.parameters.parameters, arguments.arguments) {
|
||||||
let arg_idx = argument.0
|
let arg_idx = argument.index
|
||||||
let arg_value = argument.1
|
let arg_value = argument.argument
|
||||||
let maybe_argument_value = arg_value.evaluate(execution: execution)
|
let maybe_argument_value = arg_value.evaluate(execution: execution)
|
||||||
guard case .Ok(let argument_value) = maybe_argument_value else {
|
guard case .Ok(let argument_value) = maybe_argument_value else {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -22,12 +22,6 @@ public protocol Execution {
|
|||||||
func execute(execution: ProgramExecution) -> ProgramExecution
|
func execute(execution: ProgramExecution) -> ProgramExecution
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol Compilable {
|
|
||||||
associatedtype ToCompile
|
|
||||||
associatedtype Compiled
|
|
||||||
static func compile(_: ToCompile) -> Result<Compiled>
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol EvaluatableParserState: P4Value {
|
public protocol EvaluatableParserState: P4Value {
|
||||||
func execute(program: ProgramExecution) -> (EvaluatableParserState, ProgramExecution)
|
func execute(program: ProgramExecution) -> (EvaluatableParserState, ProgramExecution)
|
||||||
func done() -> Bool
|
func done() -> Bool
|
||||||
|
|||||||
@@ -19,51 +19,99 @@ import Common
|
|||||||
import P4Lang
|
import P4Lang
|
||||||
|
|
||||||
extension BlockStatement: EvaluatableStatement {
|
extension BlockStatement: EvaluatableStatement {
|
||||||
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
|
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||||
var execution = execution
|
var execution = execution
|
||||||
for s in self.statements {
|
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 {
|
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 {
|
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)
|
let new_scopes = execution.scopes.declare(identifier: self.identifier, withValue: initial_value)
|
||||||
execution.scopes = new_scopes
|
execution.scopes = new_scopes
|
||||||
return execution
|
return (ControlFlow.Next, execution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ConditionalStatement: EvaluatableStatement {
|
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 {
|
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()) {
|
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)) {
|
if evaluated_condition.eq(rhs: P4BooleanValue.init(withValue: true)) {
|
||||||
let execution = execution.enter_scope()
|
let execution = execution.enter_scope()
|
||||||
var result = self.thenn.evaluate(execution: execution)
|
switch self.thenn.evaluate(execution: execution) {
|
||||||
result = result.exit_scope()
|
case (ControlFlow.Next, let result): return (ControlFlow.Next, result.exit_scope())
|
||||||
return result
|
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 {
|
} else if let elss = self.elss {
|
||||||
let execution = execution.enter_scope()
|
let execution = execution.enter_scope()
|
||||||
var result = elss.evaluate(execution: execution)
|
switch elss.evaluate(execution: execution) {
|
||||||
result = result.exit_scope()
|
case (ControlFlow.Next, let result): return (ControlFlow.Next, result.exit_scope())
|
||||||
return result
|
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 {
|
extension ExpressionStatement: EvaluatableStatement {
|
||||||
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
|
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
||||||
return execution
|
// 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 runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
|
||||||
let args = ArgumentList([
|
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))
|
let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: args))
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
||||||
@@ -197,7 +197,7 @@ import TreeSitterP4
|
|||||||
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
|
||||||
let args = ArgumentList([
|
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))
|
let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: args))
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ import TreeSitterP4
|
|||||||
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
|
||||||
let args = ArgumentList([
|
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))
|
let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: args))
|
||||||
// We should be in the accept state.
|
// We should be in the accept state.
|
||||||
@@ -118,7 +118,7 @@ import TreeSitterP4
|
|||||||
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
|
||||||
let args = ArgumentList([
|
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(
|
#expect(
|
||||||
@@ -142,7 +142,7 @@ import TreeSitterP4
|
|||||||
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
|
||||||
let args = ArgumentList([
|
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(
|
#expect(
|
||||||
@@ -164,7 +164,7 @@ import TreeSitterP4
|
|||||||
"""
|
"""
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
||||||
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
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(
|
#expect(
|
||||||
#RequireErrorResult<(ParserState, ProgramExecution)>(
|
#RequireErrorResult<(ParserState, ProgramExecution)>(
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ export default grammar({
|
|||||||
direction: $ => choice($.in, $.out, $.inout),
|
direction: $ => choice($.in, $.out, $.inout),
|
||||||
parameters: $=> seq('(', optional($.parameter_list), ')'),
|
parameters: $=> seq('(', optional($.parameter_list), ')'),
|
||||||
|
|
||||||
|
argument_list: $ => choice($.argument, seq($.argument_list, ',', $.argument)),
|
||||||
|
argument: $ => $.expression,
|
||||||
|
arguments: $=> seq('(', optional($.argument_list), ')'),
|
||||||
|
|
||||||
// Common - Types
|
// Common - Types
|
||||||
typeRef: $ => choice($.baseType, $.type_identifier),
|
typeRef: $ => choice($.baseType, $.type_identifier),
|
||||||
baseType: $ => choice($.bool, $.error, $.string, $.int, $.bit /* omitting "templated" types" */),
|
baseType: $ => choice($.bool, $.error, $.string, $.int, $.bit /* omitting "templated" types" */),
|
||||||
@@ -96,11 +100,12 @@ export default grammar({
|
|||||||
|
|
||||||
// General statements
|
// General statements
|
||||||
statements: $ => repeat1($.statement),
|
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), '}'),
|
blockStatement: $ => seq(optional($.annotations), '{', optional($.statements), '}'),
|
||||||
conditionalStatement: $ => choice(prec(1, seq($.if, '(', $.expression, ')', $.statement)), prec(2, seq($.if, '(', $.expression, ')', $.statement, $.else, $.statement))),
|
conditionalStatement: $ => choice(prec(1, seq($.if, '(', $.expression, ')', $.statement)), prec(2, seq($.if, '(', $.expression, ')', $.statement, $.else, $.statement))),
|
||||||
expressionStatement: $=> seq($.expression, $._semicolon),
|
expressionStatement: $=> seq($.expression, $._semicolon),
|
||||||
assignmentStatement: $=> seq($.expression, $.assignment, $.expression, $._semicolon),
|
assignmentStatement: $=> seq($.expression, $.assignment, $.expression, $._semicolon),
|
||||||
|
return_statement: $=> seq($.return, $.expression, $._semicolon),
|
||||||
|
|
||||||
// Parser statements
|
// Parser statements
|
||||||
parserStatements: $ => repeat1($.parserStatement),
|
parserStatements: $ => repeat1($.parserStatement),
|
||||||
@@ -111,7 +116,7 @@ export default grammar({
|
|||||||
// Expressions
|
// Expressions
|
||||||
expression: $ => choice($.grouped_expression, $.simple_expression),
|
expression: $ => choice($.grouped_expression, $.simple_expression),
|
||||||
grouped_expression: $ => seq('(', $.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),
|
booleanLiteralExpression: $ => choice($.true, $.false),
|
||||||
selectExpression: $ => seq($.select, '(', $.expression, ')', '{', $.selectBody, '}'), // TODO: Should be expression list and not just a single expression
|
selectExpression: $ => seq($.select, '(', $.expression, ')', '{', $.selectBody, '}'), // TODO: Should be expression list and not just a single expression
|
||||||
transitionSelectionExpression: $ => choice($.identifier, $.selectExpression),
|
transitionSelectionExpression: $ => choice($.identifier, $.selectExpression),
|
||||||
@@ -131,6 +136,10 @@ export default grammar({
|
|||||||
arrayAccessExpression: $ => seq($.expression, $.open_bracket, $.expression, $.close_bracket),
|
arrayAccessExpression: $ => seq($.expression, $.open_bracket, $.expression, $.close_bracket),
|
||||||
fieldAccessExpression: $=> prec.left(2, seq($.expression, $.field_access, $.identifier)),
|
fieldAccessExpression: $=> prec.left(2, seq($.expression, $.field_access, $.identifier)),
|
||||||
|
|
||||||
|
// Function call
|
||||||
|
|
||||||
|
function_call: $=> seq($.identifier, $.arguments),
|
||||||
|
|
||||||
// Binary Operations
|
// Binary Operations
|
||||||
binaryEqualOperatorExpression: $ => prec.left(2, seq($.expression, $.double_equal, $.expression)),
|
binaryEqualOperatorExpression: $ => prec.left(2, seq($.expression, $.double_equal, $.expression)),
|
||||||
binaryLessThanOperatorExpression: $ => prec.left(2, seq($.expression, $.less_than, $.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