Compare commits

..

10 Commits

Author SHA1 Message Date
Will Hawkins b97aa1af72 parser, compiler: Support Table Actions
Continuous Integration / Grammar Tests (push) Has been cancelled
Continuous Integration / Library Tests (push) Has been cancelled
Continuous Integration / Library Format Tests (push) Has been cancelled
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-04-30 05:38:29 -04:00
Will Hawkins 833979a5c9 Make Formatter Happy
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-04-27 09:15:31 -04:00
Will Hawkins 75da49ba7e documentation: Rename Project (2)
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-04-27 09:10:47 -04:00
Will Hawkins 0012963361 documentation: Rename Project
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-04-27 09:08:07 -04:00
Will Hawkins 8c0c16ed87 documentation: Update Note Protocol
Using /// rather than simply //.

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-04-27 09:03:59 -04:00
Will Hawkins 0f0662709e compiler, testing: Build System for Compilation
The compilation code was written as a precursor for implementation
with macros. The updates in this commit make the switch.

There is still plenty to do:

1. Comment Walker.
2. Comment Macros.

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-04-27 08:39:40 -04:00
Will Hawkins f2bd53ce5f compiler, runtime, common: (Initial) Support For extern Declarations
Especially FFI

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-04-23 06:07:54 -04:00
Will Hawkins 74fead1eba grammar: Parse externs
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-04-23 06:07:54 -04:00
Will Hawkins a2d6aa0e28 runtime: Refactor ExecutionEvaluator
Move as much of the common functionality of executing a block
of statements into a common area as possible.

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-04-23 06:07:54 -04:00
Will Hawkins 99d3d2bace Make Formatter Happy
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-04-20 16:29:59 -04:00
38 changed files with 1945 additions and 960 deletions
+2 -1
View File
@@ -75,7 +75,8 @@ let package = Package(
),
.testTarget(
name: "Tests",
dependencies: ["P4Compiler", "P4Runtime", "P4Lang", "Macros", "TreeSitterExtensions", "Common"]
dependencies: ["P4Compiler", "P4Runtime", "P4Lang", "Macros", "TreeSitterExtensions", "Common"],
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")],
),
],
)
+9 -3
View File
@@ -1,6 +1,12 @@
## P4RSE: P4 Runtime In Swift Environment
## P4CE: P4 Continuous Evolution
_P4RSE_ (yes, the acronym is a bit forced) is a P4 parser and runtime written in Swift. The project was started as a means to better learn Swift.
_P4CE_[^pronounce] is a P4 parser and runtime written in Swift that supports an _evolved_ version of P4.
[^pronounce]: The acronym is pronounced "p force".
### Evolved How?
Coming soon.
### Status
@@ -93,7 +99,7 @@ While coding, it may be useful to leave ourselves notes. Every note is formatted
```Swift
// NOTE: note text
/// NOTE<: optional note text>
```
where `NOTE` can be:
+134
View File
@@ -0,0 +1,134 @@
// 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/>.
public struct Parameter: CustomStringConvertible, Equatable {
public static func == (lhs: Parameter, rhs: Parameter) -> Bool {
return lhs.name == rhs.name && lhs.type.eq(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)"
}
/// Calculate whether the `argument` is compatible with this parameter.
public func compatible(_ argument: Argument) -> Bool {
let arg_type = argument.argument.type()
// If the parameter is (in)out, then the argument must be an lvalue.
if let param_direction = self.type.direction(),
param_direction == Direction.In || param_direction == Direction.InOut
{
if !(argument.argument is EvaluatableLValueExpression) {
return false
}
}
return arg_type.dataType().eq(rhs: self.type.dataType())
}
}
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 !param.compatible(arg) {
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
}
}
+6 -6
View File
@@ -611,7 +611,7 @@ public class P4ArrayValue: P4DataValue {
}
public func set(index: Int, to: P4Value) -> Result<P4ArrayValue> {
// TODO: Check for OOB
/// TODO: Check for OOB
var updated_values = self.value
updated_values[index] = to
return Result.Ok(P4ArrayValue(withType: self.vtype, withValue: updated_values))
@@ -621,7 +621,7 @@ public class P4ArrayValue: P4DataValue {
guard rhs as? P4ArrayValue != nil else {
return false
}
// TODO!!
/// TODO
return true
}
@@ -629,7 +629,7 @@ public class P4ArrayValue: P4DataValue {
guard rhs as? P4ArrayValue != nil else {
return false
}
// TODO!!
/// TODO
return true
}
@@ -637,7 +637,7 @@ public class P4ArrayValue: P4DataValue {
guard rhs as? P4ArrayValue != nil else {
return false
}
// TODO!!
/// TODO
return true
}
@@ -645,7 +645,7 @@ public class P4ArrayValue: P4DataValue {
guard rhs as? P4ArrayValue != nil else {
return false
}
// TODO!!
/// TODO
return true
}
@@ -653,7 +653,7 @@ public class P4ArrayValue: P4DataValue {
guard rhs as? P4ArrayValue != nil else {
return false
}
// TODO!!
/// TODO
return true
}
+46 -47
View File
@@ -15,38 +15,47 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
public typealias ExecuteStatementResultHandler = (ControlFlow, ProgramExecution) -> (
public typealias ExecuteStatementResultHandlerT = (ControlFlow, ProgramExecution) -> (
ControlFlow, ProgramExecution
)
public struct ClassicEvaluator: ProgramExecutionEvaluator {
public func ExecuteStatement(
_ statements: [EvaluatableStatement], handleResult handler: ExecuteStatementResultHandler,
inExecution execution: ProgramExecution,
) -> (ControlFlow, ProgramExecution) {
public typealias ExecuteStatementT = (EvaluatableStatement, ProgramExecution) -> (
ControlFlow, ProgramExecution
)
var execution = execution
for s in statements {
let (control_flow, next_execution) = s.evaluate(execution: execution)
switch handler(control_flow, next_execution) {
case (ControlFlow.Next, let handled_next_execution): execution = handled_next_execution
case (ControlFlow.Return(let value), let handled_next_execution):
return (ControlFlow.Return(value), handled_next_execution)
case (let handled_control_flow, let handled_next_execution):
return (handled_control_flow, handled_next_execution)
}
func CanonicalExecuteStatements(
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
_ executor: ExecuteStatementT
) -> (ControlFlow, ProgramExecution) {
var execution = execution
for s in statements {
// Execute the statement with the user-provided statement executor.
switch executor(s, execution) {
// And decide what to do next!
case (ControlFlow.Next, let handled_next_execution): execution = handled_next_execution
case (ControlFlow.Return(let value), let handled_next_execution):
return (ControlFlow.Return(value), handled_next_execution)
case (let handled_control_flow, let handled_next_execution):
return (handled_control_flow, handled_next_execution)
}
return (ControlFlow.Next, execution)
}
return (ControlFlow.Next, execution)
}
public func ExecuteStatement(
_ statement: EvaluatableStatement, handleResult handler: ExecuteStatementResultHandler,
inExecution execution: ProgramExecution
public struct ClassicEvaluator: ProgramExecutionEvaluator {
public func ExecuteStatements(
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
_ handler: ExecuteStatementResultHandlerT?
) -> (ControlFlow, ProgramExecution) {
return ExecuteStatement([statement], handleResult: handler, inExecution: execution)
return CanonicalExecuteStatements(statements, inExecution: execution) { statement, execution in
let (cf, value) = statement.evaluate(execution: execution)
// Apply the user-specified handler before continuing.
guard let handler = handler else {
return (cf, value)
}
return handler(cf, value)
}
}
public func EvaluateExpression(
@@ -86,9 +95,9 @@ public struct InterloperEvaluator: ProgramExecutionEvaluator {
return pe
}
public func ExecuteStatement(
_ statements: [EvaluatableStatement], handleResult handler: ExecuteStatementResultHandler,
inExecution execution: ProgramExecution,
public func ExecuteStatements(
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
_ handler: ExecuteStatementResultHandlerT?
) -> (ControlFlow, ProgramExecution) {
var debugger: StatementInterloper? = .none
@@ -98,30 +107,20 @@ public struct InterloperEvaluator: ProgramExecutionEvaluator {
hasDebugInterloper = true
}
var execution = execution
for s in statements {
let (control_flow, next_execution) = s.evaluate(execution: execution)
return CanonicalExecuteStatements(statements, inExecution: execution) { statement, execution in
let (cf, value) = statement.evaluate(execution: execution)
let (handled_cf, handled_value) =
if let handler = handler {
handler(cf, value)
} else {
(cf, value)
}
if hasDebugInterloper {
debugger!(s, control_flow, next_execution)
}
switch handler(control_flow, next_execution) {
case (ControlFlow.Next, let handled_next_execution): execution = handled_next_execution
case (ControlFlow.Return(let value), let handled_next_execution):
return (ControlFlow.Return(value), handled_next_execution)
case (let handled_control_flow, let handled_next_execution):
return (handled_control_flow, handled_next_execution)
debugger!(statement, handled_cf, handled_value)
}
return (handled_cf, handled_value)
}
return (ControlFlow.Next, execution)
}
public func ExecuteStatement(
_ statement: EvaluatableStatement, handleResult handler: ExecuteStatementResultHandler,
inExecution execution: ProgramExecution
) -> (ControlFlow, ProgramExecution) {
return ExecuteStatement([statement], handleResult: handler, inExecution: execution)
}
public func EvaluateExpression(
+22
View File
@@ -0,0 +1,22 @@
// 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/>.
public protocol P4FFI {
func execute(execution: ProgramExecution) -> (ControlFlow, ProgramExecution)
func type() -> P4Type
func parameters() -> ParameterList
}
+1 -1
View File
@@ -204,7 +204,7 @@ public struct P4Value: CustomStringConvertible {
}
public func update(withNewValue value: P4DataValue) -> Result<P4Value> {
// TODO: Check that the types match.
/// TODO: Check that the types match.
return .Ok(P4Value(value, self._type))
}
+16 -9
View File
@@ -56,17 +56,24 @@ public protocol EvaluatableLValueExpression: EvaluatableExpression {
}
public protocol ProgramExecutionEvaluator {
func ExecuteStatement(
_ statements: [EvaluatableStatement], handleResult handler: ExecuteStatementResultHandler,
inExecution execution: ProgramExecution,
) -> (ControlFlow, ProgramExecution);
func ExecuteStatements(
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
_ handler: ExecuteStatementResultHandlerT?
) -> (ControlFlow, ProgramExecution)
func ExecuteStatement(
_ statement: EvaluatableStatement, handleResult handler: ExecuteStatementResultHandler,
inExecution execution: ProgramExecution
) -> (ControlFlow, ProgramExecution);
func ExecuteStatements(
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution
) -> (ControlFlow, ProgramExecution)
func EvaluateExpression(
_ expression: EvaluatableExpression, inExecution execution: ProgramExecution,
) -> (Result<P4Value>, ProgramExecution)
}
}
extension ProgramExecutionEvaluator {
public func ExecuteStatements(
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution
) -> (ControlFlow, ProgramExecution) {
return ExecuteStatements(statements, inExecution: execution, .none)
}
}
+4 -1
View File
@@ -148,5 +148,8 @@ public func Map<T, U>(input: T, block: (T) -> U) -> U {
nodes: N, type: [String], nice_type_names: [String]
) =
#externalMacro(module: "Macros", type: "RequireNodesType")
@freestanding(codeItem) public macro SkipUnlessNodeType<N, T>(node: N, type: String) =
@freestanding(codeItem) public macro SkipUnlessNodeType<N>(node: N, type: String) =
#externalMacro(module: "Macros", type: "SkipUnlessNodeType")
@freestanding(codeItem) public macro MustOr<E, N>(result: E, thing: E?, or: N) =
#externalMacro(module: "Macros", type: "MustOr")
+29 -1
View File
@@ -225,10 +225,38 @@ public struct SkipUnlessNodeType: CodeItemMacro {
}
}
public struct MustOr: CodeItemMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax] {
let arguments = node.arguments.indices
var arg_index = arguments.startIndex
let result = node.arguments[arg_index].expression
arg_index = arguments.index(after: arg_index)
let thing = node.arguments[arg_index].expression
arg_index = arguments.index(after: arg_index)
let or = node.arguments[arg_index].expression
return [
CodeBlockItemSyntax(
"""
if let __thing = \(thing) {
\(result) = __thing
} else {
return \(or)
}
""")
]
}
}
@main
struct P4Macros: CompilerPlugin {
var providingMacros: [Macro.Type] = [
RequireResult.self, RequireErrorResult.self, UseOkResult.self, UseErrorResult.self,
RequireNodeType.self, SkipUnlessNodeType.self, RequireNodesType.self,
RequireNodeType.self, SkipUnlessNodeType.self, RequireNodesType.self, MustOr.self,
]
}
+115 -117
View File
@@ -26,9 +26,8 @@ func parameter_list_compiler(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(ParameterList, CompilerContext)> {
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: node)
var current_node: Node? = .none
if node.text == ")" {
// There are no parameters!
@@ -40,49 +39,45 @@ func parameter_list_compiler(
var parameters: ParameterList = ParameterList([])
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing parameter list component"))
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParameterList, CompilerContext)>.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) {
if current_node?.nodeType == "parameter_list" {
switch parameter_list_compiler(node: current_node!, withContext: context) {
case .Ok(let (ps, _)):
parameters = ps
case .Error(let e): return Result.Error(e)
}
currentChildIdx += 1
currentChildIdxSafe += 1
walker.next()
}
// 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)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParameterList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter list component")))
// If this is a ')', we are done.
if currentChild?.text == ")" {
if current_node?.text == ")" {
return Result.Ok((parameters, context))
}
// If this is a comma, we skip it!
if currentChild?.text == "," {
currentChildIdx += 1
currentChildIdxSafe += 1
if current_node?.text == "," {
walker.next()
}
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing parameter list component"))
}
currentChild = node.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParameterList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter list component")))
// Otherwise, there should be one parameter left!
switch Parameter.Compile(node: currentChild!, withContext: context) {
switch Parameter.Compile(node: current_node!, withContext: context) {
case .Ok(let (parsed_parameter, updated_context)):
return Result.Ok((parameters.addParameter(parsed_parameter), updated_context))
case .Error(let e): return Result.Error(e)
@@ -99,24 +94,24 @@ extension ParameterList: Compilable {
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
node: parameter_node, type: "parameters", nice_type_name: "Parameters")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var walker = Walker(node: parameter_node)
// Let's eat the '(' before we start ...
if parameter_node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: parameter_node, withError: "Missing '(' in parameter list component"))
}
var current_node: Node? = .none
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)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParameterList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing '(' in parameter list component")))
return parameter_list_compiler(node: currentChild!, withContext: context)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParameterList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter list component")))
return parameter_list_compiler(node: current_node!, withContext: context)
}
}
@@ -153,74 +148,80 @@ extension Parameter: Compilable {
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
node: node, type: "parameter", nice_type_name: "parameter")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: node)
var current_node: Node? = .none
if node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: node, withError: "Missing parameter declaration component"))
}
currentChild = node.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Parameter, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter declaration component")))
// Annotation?
if currentChild!.nodeType == "annotations" {
if current_node!.nodeType == "annotations" {
return .Error(
ErrorOnNode(
node: currentChild!,
node: current_node!,
withError: "Annotations in parameter declarations are not yet handled"))
// Will increment indexes here.
}
currentChild = node.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Parameter, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter declaration component")))
var direction: Direction? = .none
// Direction?
if currentChild!.nodeType == "direction" {
if current_node!.nodeType == "direction" {
let maybe_parsed_direction = Direction.Compile(node: currentChild!, withContext: context)
let maybe_parsed_direction = Direction.Compile(node: current_node!, withContext: context)
guard case .Ok((let parsed_direction, _)) = maybe_parsed_direction else {
return .Error(maybe_parsed_direction.error()!)
}
direction = parsed_direction
currentChildIdx += 1
currentChildIdxSafe += 1
walker.next()
}
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType != "typeRef" {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Parameter, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter declaration component")))
if current_node!.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)
case .Ok(let parameter_type) = Types.CompileType(type: current_node!, withContext: context)
else {
return Result.Error(
Error(withMessage: "Could not parse a P4 type from \(currentChild!.text!)"))
Error(withMessage: "Could not parse a P4 type from \(current_node!.text!)"))
}
currentChildIdx += 1
currentChildIdxSafe += 1
if node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: node, withError: "Missing parameter declaration component"))
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Parameter, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing parameter declaration component")))
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType != "identifier" {
if current_node!.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)
case .Ok(let parameter_name) = Identifier.Compile(node: current_node!, withContext: context)
else {
return Result.Error(
Error(withMessage: "Could not parse a parameter name from \(currentChild!.text!)"))
Error(withMessage: "Could not parse a parameter name from \(current_node!.text!)"))
}
return Result.Ok(
@@ -239,9 +240,8 @@ func argument_list_compiler(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(ArgumentList, CompilerContext)> {
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: node)
var current_node: Node? = .none
if node.text == ")" {
// There are no arguments!
@@ -253,49 +253,47 @@ func argument_list_compiler(
var arguments: ArgumentList = ArgumentList([])
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing argument list component"))
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ArgumentList, CompilerContext)>.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) {
if current_node?.nodeType == "argument_list" {
switch argument_list_compiler(node: current_node!, withContext: context) {
case .Ok(let (ps, _)):
arguments = ps
case .Error(let e): return Result.Error(e)
}
currentChildIdx += 1
currentChildIdxSafe += 1
walker.next()
}
// 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)
// We may have moved nodes, check/reset current_node.
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ArgumentList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing argument list component")))
// If this is a ')', we are done.
if currentChild?.text == ")" {
if current_node?.text == ")" {
return Result.Ok((arguments, context))
}
// If this is a comma, we skip it!
if currentChild?.text == "," {
currentChildIdx += 1
currentChildIdxSafe += 1
if current_node?.text == "," {
walker.next()
}
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing argument list component"))
}
currentChild = node.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ArgumentList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing argument list component")))
// Otherwise, there should be one argument left!
switch Argument.Compile(node: currentChild!, withContext: context) {
switch Argument.Compile(node: current_node!, withContext: context) {
case .Ok(let (ce, updated_context)):
return Result.Ok(
(arguments.addArgument(Argument(ce, atIndex: arguments.count() + 1)), updated_context))
@@ -313,24 +311,24 @@ extension ArgumentList: Compilable {
#RequireNodeType<Node, (ArgumentList, CompilerContext)>(
node: argument_node, type: "arguments", nice_type_name: "arguments")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var walker = Walker(node: argument_node)
var current_node: Node? = .none
// Let's eat the '(' before we start ...
if argument_node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: argument_node, withError: "Missing '(' in argument list component"))
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ArgumentList, CompilerContext)>.Error(
ErrorOnNode(
node: 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)
walker.next()
return argument_list_compiler(node: currentChild!, withContext: context)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ArgumentList, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing argument list component")))
return argument_list_compiler(node: current_node!, withContext: context)
}
}
+64 -13
View File
@@ -52,60 +52,111 @@ public func ErrorOnNode(node: Node, withError error: String) -> Error {
public struct CompilerContext {
let instances: VarTypeScopes
let types: TypeTypeScopes
let externs: TypeTypeScopes
let ffis: [P4FFI]
let expected_type: P4Type?
let extern_context: Bool
public init(withInstances _instances: VarTypeScopes) {
instances = _instances
types = TypeTypeScopes()
public init() {
instances = VarTypeScopes().enter()
types = TypeTypeScopes().enter()
externs = TypeTypeScopes().enter()
expected_type = .none
extern_context = false
ffis = Array()
}
public init(withInstances _instances: VarTypeScopes, withTypes _types: TypeTypeScopes) {
instances = _instances
types = _types
externs = TypeTypeScopes().enter()
expected_type = .none
extern_context = false
ffis = Array()
}
public init(
withInstances _instances: VarTypeScopes, withTypes _types: TypeTypeScopes,
withExpectation expectation: P4Type?
withExpectation expectation: P4Type?, withExtern extern: Bool,
withExterns externs: TypeTypeScopes, withFFIs foreigns: [P4FFI]
) {
instances = _instances
types = _types
expected_type = expectation
extern_context = extern
self.externs = externs
ffis = foreigns
}
/// Update a compiler context
///
/// Create a new compiler context based on the current with the same types and new names.
/// Create a new compiler context based on the current but new instances.
///
/// - Parameter instances: a ``VarTypeScopes`` with the updated instances for the newly created compiler context.
/// - Returns: A new compiler context based on the current with the same types and new names.
/// - Returns: A new compiler context based on the current but new instances.
public func update(newInstances instances: VarTypeScopes) -> CompilerContext {
return CompilerContext(
withInstances: instances, withTypes: self.types, withExpectation: self.expected_type)
withInstances: instances, withTypes: self.types, withExpectation: self.expected_type,
withExtern: self.extern_context, withExterns: self.externs, withFFIs: self.ffis)
}
/// Update a compiler context
///
/// Create a new compiler context based on the current with the same names and new types.
/// Create a new compiler context based on the current but new types.
///
/// - Parameter types: a ``TypeTypeScopes`` with the updated types for the newly created compiler context.
/// - Returns: A new compiler context based on the current with the same names and new types.
/// - Returns: A new compiler context based on the current but new types.
public func update(newTypes types: TypeTypeScopes) -> CompilerContext {
return CompilerContext(
withInstances: self.instances, withTypes: types, withExpectation: self.expected_type)
withInstances: self.instances, withTypes: types, withExpectation: self.expected_type,
withExtern: self.extern_context, withExterns: self.externs, withFFIs: self.ffis)
}
/// Update a compiler context
///
/// Create a new compiler context based on the current with the same names and types but new expected type.
/// Create a new compiler context based on the current but new expected type.
///
/// - Parameter expectation: a ``P4Type?`` to (re)set the type the compiler is expecting.
/// - Returns: A new compiler context based on the current with the same names and types but new expected type.
/// - Returns: A new compiler context based on the current but new expected type.
public func update(newExpectation expectation: P4Type?) -> CompilerContext {
return CompilerContext(
withInstances: self.instances, withTypes: self.types, withExpectation: expectation)
withInstances: self.instances, withTypes: self.types, withExpectation: expectation,
withExtern: self.extern_context, withExterns: self.externs, withFFIs: self.ffis)
}
/// Update a compiler context
///
/// Create a new compiler context based on the current but new extern context value.
///
/// - Parameter extern: a ``Bool`` to (re)set whether the compiler is compiling in an extern context.
/// - Returns: A new compiler context based on the current but new extern context value.
public func update(newExtern extern: Bool) -> CompilerContext {
return CompilerContext(
withInstances: self.instances, withTypes: self.types, withExpectation: self.expected_type,
withExtern: extern, withExterns: self.externs, withFFIs: self.ffis)
}
/// Update a compiler context
///
/// Create a new compiler context based on the current but new externs.
///
/// - Parameter externs: a ``TypeTypeScopes`` to (re)set the list of extern-al declarations.
/// - Returns: A new compiler context based on the current but new list of external-al declarations.
public func update(newExterns externs: TypeTypeScopes) -> CompilerContext {
return CompilerContext(
withInstances: self.instances, withTypes: self.types, withExpectation: self.expected_type,
withExtern: self.extern_context, withExterns: externs, withFFIs: self.ffis)
}
/// Update a compiler context
///
/// Create a new compiler context based on the current but new FFIs.
///
/// - Parameter foreigns: an array of ``P4FFI`` to (re)set the list of foreign functions.
/// - Returns: A new compiler context based on the current but with a new list of foreign functions.
public func update(newFFIs foreigns: [P4FFI]) -> CompilerContext {
return CompilerContext(
withInstances: self.instances, withTypes: self.types, withExpectation: self.expected_type,
withExtern: self.extern_context, withExterns: externs, withFFIs: foreigns)
}
}
File diff suppressed because it is too large Load Diff
+152 -131
View File
@@ -39,7 +39,7 @@ extension TypedIdentifier: CompilableExpression {
) -> Result<EvaluatableExpression?> {
let node = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: node, type: "identifier")
guard
@@ -59,7 +59,7 @@ extension TypedIdentifier: CompilableLValueExpression {
) -> Result<EvaluatableLValueExpression?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: expression, type: "identifier")
let maybe_parsed_expression = TypedIdentifier.compile(node: node, withContext: context)
@@ -78,7 +78,7 @@ extension P4BooleanValue: CompilableExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableExpression?> {
let node = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: node, type: "booleanLiteralExpression")
if node.text == "false" {
@@ -97,7 +97,7 @@ extension P4IntValue: CompilableExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableExpression?> {
let node = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(node: node, type: "integer")
#SkipUnlessNodeType<SwiftTreeSitter.Node>(node: node, type: "integer")
if let parsed_int = Int(node.text!) {
return .Ok(P4Value(P4IntValue(withValue: parsed_int)))
} else {
@@ -111,7 +111,7 @@ extension P4StringValue: CompilableExpression {
node: SwiftTreeSitter.Node, withContext scopes: CompilerContext
) -> Result<EvaluatableExpression?> {
let node = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: node, type: "string_literal")
return .Ok(P4Value(P4StringValue(withValue: node.text!)))
}
@@ -339,53 +339,48 @@ extension BinaryOperatorExpression: CompilableExpression {
) -> Result<(EvaluatableExpression)?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
#SkipUnlessNodeType<Node>(
node: expression, type: "binaryOperatorExpression")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
let binary_operator_expression_node = expression.child(at: 0)!
var walker = Walker(node: binary_operator_expression_node)
if expression.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Malformed binary operator expression"))
}
currentChild = expression.child(at: currentChildIdx)
var current_node: Node? = .none
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Malformed binary operator expression")))
let binary_operator_expression_node = currentChild!
// TODO: This macro cannot handle new lines in the arrays
/// TODO: This macro cannot handle new lines in the arrays
// swift-format-ignore
#RequireNodesType<Node, EvaluatableExpression?>(
nodes: binary_operator_expression_node,
type: ["binaryEqualOperatorExpression", "binaryLessThanOperatorExpression", "binaryLessThanEqualOperatorExpression", "binaryGreaterThanOperatorExpression", "binaryGreaterThanEqualOperatorExpression", "binaryAndOperatorExpression", "binaryOrOperatorExpression", "binaryAddOperatorExpression", "binarySubtractOperatorExpression", "binaryMultiplyOperatorExpression", "binaryDivideOperatorExpression"],
nice_type_names: [ "binary equal operator", "binary less than operator", "binary less than or equal to operator", "binary greater than operator", "binary greater than or equal to operator", "binary and operator", "binary or operator", "binary add operator", "binary subtract operator", "binary multiply operator", "binary divide operator"])
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing LHS for binary operator expression")))
if binary_operator_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing LHS for binary operator expression"))
}
currentChild = binary_operator_expression_node.child(at: currentChildIdx)
let left_hand_side_raw = currentChild!
let left_hand_side_raw = current_node!
currentChildIdx = currentChildIdx + 1
currentChildIdxSafe = currentChildIdxSafe + 1
if binary_operator_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing binary operator for binary operator expression")
)
}
currentChild = binary_operator_expression_node.child(at: currentChildIdx)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing binary operator for binary operator expression")))
currentChildIdx = currentChildIdx + 1
currentChildIdxSafe = currentChildIdxSafe + 1
if binary_operator_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing binary operator for binary operator expression")
)
}
currentChild = binary_operator_expression_node.child(at: currentChildIdx)
let right_hand_side_raw = currentChild!
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing RHS for binary operator expression")))
let right_hand_side_raw = current_node!
let maybe_left_hand_side = Expression.Compile(node: left_hand_side_raw, withContext: context)
guard case Result.Ok(let left_hand_side) = maybe_left_hand_side else {
@@ -465,54 +460,48 @@ extension BinaryOperatorExpression: CompilableExpression {
extension ArrayAccessExpression: CompilableExpression {
static func compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(any Common.EvaluatableExpression)?> {
) -> Common.Result<EvaluatableExpression?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
#SkipUnlessNodeType<Node>(
node: expression, type: "arrayAccessExpression")
let array_access_expression_node = expression
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: array_access_expression_node)
var current_node: Node? = .none
// What is the "name" of the array?
if array_access_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Malformed array access expression"))
}
currentChild = expression.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Malformed array access expression")))
#RequireNodeType<Node, EvaluatableExpression?>(
node: currentChild!, type: "expression",
node: current_node!, type: "expression",
nice_type_name: "array identifier expression")
let array_access_identifier_node = currentChild!
let array_access_identifier_node = current_node!
// Check for the [
currentChildIdx = currentChildIdx + 1
currentChildIdxSafe = currentChildIdxSafe + 1
if array_access_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing [ for array access expression")
)
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing [ for array access expression")))
// What is the indexor of the array?
currentChildIdx = currentChildIdx + 1
currentChildIdxSafe = currentChildIdxSafe + 1
if array_access_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing indexor expression for array access expression")
)
}
currentChild = array_access_expression_node.child(at: currentChildIdx)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing indexor expression for array access expression")))
#RequireNodeType<Node, EvaluatableExpression?>(
node: currentChild!, type: "expression",
node: current_node!, type: "expression",
nice_type_name: "array indexor expression")
let array_access_indexor_node = currentChild!
let array_access_indexor_node = current_node!
let maybe_array_identifier = Expression.Compile(
node: array_access_identifier_node, withContext: context)
@@ -547,51 +536,44 @@ extension FieldAccessExpression: CompilableExpression {
) -> Common.Result<(any Common.EvaluatableExpression)?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
#SkipUnlessNodeType<Node>(
node: expression, type: "fieldAccessExpression")
let field_access_expression_node = expression
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: field_access_expression_node)
var current_node: Node? = .none
// What is the "name" of the struct?
if field_access_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Malformed field access expression"))
}
currentChild = expression.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Malformed field access expression")))
#RequireNodeType<Node, EvaluatableExpression?>(
node: currentChild!, type: "expression",
node: current_node!, type: "expression",
nice_type_name: "struct identifier expression")
let struct_identifier_node = currentChild!
let struct_identifier_node = current_node!
// Check for the .
currentChildIdx = currentChildIdx + 1
currentChildIdxSafe = currentChildIdxSafe + 1
if field_access_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing . for field access expression")
)
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing . for field access expression")))
// What is the field of the struct?
currentChildIdx = currentChildIdx + 1
currentChildIdxSafe = currentChildIdxSafe + 1
if field_access_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing field name for field access expression")
)
}
currentChild = field_access_expression_node.child(at: currentChildIdx)
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing field name for field access expression")))
#RequireNodeType<Node, EvaluatableExpression?>(
node: currentChild!, type: "identifier",
node: current_node!, type: "identifier",
nice_type_name: "field name")
let field_name_node = currentChild!
let field_name_node = current_node!
// Make sure that the identifier really identifies a struct.
let maybe_struct_identifier = Expression.Compile(
@@ -633,7 +615,7 @@ extension FieldAccessExpression: CompilableLValueExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableLValueExpression?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
#SkipUnlessNodeType<Node>(
node: expression, type: "fieldAccessExpression")
let maybe_parsed_expression = FieldAccessExpression.compile(node: node, withContext: context)
@@ -652,7 +634,7 @@ extension ArrayAccessExpression: CompilableLValueExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableLValueExpression?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
#SkipUnlessNodeType<Node>(
node: expression, type: "arrayAccessExpression")
let maybe_parsed_expression = ArrayAccessExpression.compile(node: node, withContext: context)
@@ -670,52 +652,68 @@ extension FunctionCall: CompilableExpression {
static func compile(
node: Node, withContext context: CompilerContext
) -> Result<EvaluatableExpression?> {
let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
#SkipUnlessNodeType<Node>(
node: expression, type: "function_call")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: expression)
var current_node: Node? = .none
if expression.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing function call component"))
}
currentChild = expression.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing function call component")))
let maybe_callee_name = Identifier.Compile(
node: currentChild!, withContext: context)
node: current_node!, withContext: context)
guard case .Ok(let callee_name) = maybe_callee_name else {
return Result.Error(maybe_callee_name.error()!)
}
let maybe_callee =
var maybe_callee: Result<(FunctionDeclaration?, Declaration?)> =
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
case let callee as FunctionDeclaration:
Result<(FunctionDeclaration?, Declaration?)>.Ok((callee, .none)) // What we found is actually a function declaration
default:
Result<FunctionDeclaration>.Error(
ErrorOnNode(node: currentChild!, withError: "\(callee_name) is not a function"))
Result<(FunctionDeclaration?, Declaration?)>.Error(
ErrorOnNode(node: current_node!, withError: "\(callee_name) is not a function"))
}
case .Error(let e): Result<FunctionDeclaration>.Error(e)
case .Error(let e): Result<(FunctionDeclaration?, Declaration?)>.Error(e)
}
maybe_callee =
if case .Error(let e) = maybe_callee {
switch context.externs.lookup(identifier: callee_name) {
case .Ok(let callee as Declaration):
// Now, make sure that it is a function declaration!
switch callee.identifier.type.dataType() {
case is FunctionDeclaration: Result.Ok((.none, callee))
default:
.Error(ErrorOnNode(node: current_node!, withError: "\(callee_name) is not a function"))
}
default: .Error(e)
}
} else {
maybe_callee
}
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)
walker.next()
let maybe_argument_list = ArgumentList.Compile(node: currentChild!, withContext: context)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(
node: node, withError: "Missing function call component")))
let maybe_argument_list = ArgumentList.Compile(node: current_node!, withContext: context)
guard case .Ok((let arguments, _)) = maybe_argument_list else {
return .Error(maybe_argument_list.error()!)
@@ -723,12 +721,35 @@ extension FunctionCall: CompilableExpression {
// Now, compare the arguments with the parameters:
if case .Error(let e) = arguments.compatible(callee.params) {
let params =
switch callee {
case (.some(let callee), .none): Optional<ParameterList>.some(callee.params)
case (.none, .some(let callee)):
Optional<ParameterList>.some((callee.ffi!.type().dataType() as! FunctionDeclaration).params)
default: Optional<ParameterList>.none
}
guard case .some(let params) = params else {
return Result.Error(
ErrorOnNode(
node: node,
withError: "Could not lookup the parameters for the called function (\(callee_name))"))
}
if case .Error(let e) = arguments.compatible(params) {
return .Error(e)
}
// All good!
return .Ok(FunctionCall(callee, withArguments: arguments))
return switch callee {
case (.some(let callee), .none): .Ok(FunctionCall(callee, withArguments: arguments))
case (.none, .some(let callee)): .Ok(FunctionCall(callee.ffi!, withArguments: arguments))
default:
Result.Error(
ErrorOnNode(
node: node, withError: "Unexpected error occurred calling function named (\(callee_name))"
))
}
}
}
+34 -36
View File
@@ -179,11 +179,9 @@ public struct Parser {
static func Compile(
node: Node, withContext context: CompilerContext
) -> Result<(InstantiatedParserState, CompilerContext)> {
var walker = Walker(node: node)
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var current_node: Node? = .none
guard let node_type = node.nodeType,
node_type == "parserState"
@@ -192,50 +190,50 @@ public struct Parser {
ErrorOnNode(node: node, withError: "Did not find a parser state declaration"))
}
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing elements in parser state declaration"))
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing elements in parser state declaration")))
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType == "annotations" {
if current_node!.nodeType == "annotations" {
return Result.Error(
ErrorOnNode(
node: currentChild!, withError: "Annotations in parser state are not yet handled."))
node: current_node!, withError: "Annotations in parser state are not yet handled."))
// Would increment here.
}
// Skip the keyword state
currentChildIdx += 1
currentChildIdxSafe += 1
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing elements in parser state declaration"))
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing elements in parser state declaration")))
currentChild = node.child(at: currentChildIdx)
let maybe_state_identifier = Identifier.Compile(
node: currentChild!, withContext: context)
node: current_node!, withContext: context)
guard case Result.Ok(let state_identifier) = maybe_state_identifier else {
return Result.Error(maybe_state_identifier.error()!)
}
walker.next()
// Skip the '{'
currentChildIdx += 2
currentChildIdxSafe += 2
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing body of state declaration")))
var parse_errs: [Error] = Array()
var current_context = context
var parsed_s: [EvaluatableStatement] = Array()
if node.childCount < currentChildIdxSafe {
return Result.Error(ErrorOnNode(node: node, withError: "Missing body of state declaration"))
}
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType == "parserStatements" {
if current_node!.nodeType == "parserStatements" {
switch Statements.Compile(
node: currentChild!, withContext: current_context)
node: current_node!, withContext: current_context)
{
case .Ok(let (state_statements, updated_context)):
parsed_s = state_statements
@@ -243,8 +241,7 @@ public struct Parser {
case .Error(let error):
parse_errs.append(error)
}
currentChildIdx += 1
currentChildIdxSafe += 1
walker.next()
}
if !parse_errs.isEmpty {
@@ -255,13 +252,14 @@ public struct Parser {
}.joined(separator: ";")))
}
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing transition statement of state declaration"))
}
currentChild = node.child(at: currentChildIdx)
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
ErrorOnNode(
node: node, withError: "Missing transition statement of state declaration")))
return TransitionStatement.Compile(
node: currentChild!, forState: state_identifier, withStatements: parsed_s,
node: current_node!, forState: state_identifier, withStatements: parsed_s,
withContext: current_context)
}
}
@@ -277,7 +275,7 @@ public struct Parser {
var error: Error? = .none
var current_context = context
// TODO: Assert that there is only one.
/// TODO: Assert that there is only one.
node.enumerateNamedChildren { parser_state in
if parser_state.nodeType != "parserState" {
return
+15 -5
View File
@@ -24,18 +24,19 @@ import TreeSitterP4
public struct Program {
public static func Compile(_ source: String) -> Result<P4Lang.Program> {
return Program.Compile(source, withGlobalInstances: .none, withGlobalTypes: .none)
return Program.Compile(source, withGlobalInstances: .none, withGlobalTypes: .none, withFFIs: [])
}
public static func Compile(
_ source: String, withGlobalInstances globalInstances: VarTypeScopes
) -> Result<P4Lang.Program> {
return Program.Compile(source, withGlobalInstances: globalInstances, withGlobalTypes: .none)
return Program.Compile(
source, withGlobalInstances: globalInstances, withGlobalTypes: .none, withFFIs: [])
}
public static func Compile(
_ source: String, withGlobalInstances globalInstances: VarTypeScopes?,
withGlobalTypes globalTypes: TypeTypeScopes?
withGlobalTypes globalTypes: TypeTypeScopes?, withFFIs ffis: [P4FFI] = Array()
) -> Result<P4Lang.Program> {
let maybe_parser = ConfigureP4Parser()
@@ -54,8 +55,10 @@ public struct Program {
var program = P4Lang.Program()
// Set up a context for parsing.
var compilation_context = CompilerContext(
withInstances: VarTypeScopes().enter(), withTypes: TypeTypeScopes().enter())
var compilation_context = CompilerContext()
// Add our FFIs
compilation_context = compilation_context.update(newFFIs: ffis)
var errors: [Error] = Array()
@@ -120,6 +123,13 @@ public struct Program {
compilation_context.types.map { (_, v) in
v
})
// Any of the extern types that are in the top-level scope should go into the program!
program.externs = Array(
compilation_context.externs.map { (_, v) in
v
})
return Result.Ok(program)
}
}
+5 -1
View File
@@ -39,9 +39,13 @@ public protocol CompilableType {
}
public protocol CompilableDeclaration {
/// Info
///
/// Extensions should update the context with the newly declared item _unless_
/// they are in an extern context (``CompilerContext.extern_context``).
static func Compile(
node: Node, withContext context: CompilerContext
) -> Result<(P4DataType, CompilerContext)?>
) -> Result<(Declaration, CompilerContext)?>
}
public protocol Compilable<T> {
+25 -28
View File
@@ -29,34 +29,32 @@ extension BlockStatement: CompilableStatement {
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
node: node, type: "blockStatement", nice_type_name: "block statement")
var currentChildIdx = 0
var currentChildIdxSafe = 1
var currentChild: Node? = .none
var walker = Walker(node: node)
var current_node: Node? = .none
if node.childCount < currentChildIdxSafe {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(EvaluatableStatement, CompilerContext)>.Error(
ErrorOnNode(node: node, withError: "Malformed block statement")))
if current_node!.nodeType != "{" {
return Result.Error(
ErrorOnNode(node: node, withError: "Malformed block statement"))
ErrorOnNode(node: current_node!, withError: "Missing { on block statement"))
}
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType != "{" {
return Result.Error(
ErrorOnNode(node: currentChild!, withError: "Missing { on block statement"))
}
currentChildIdx += 1
currentChildIdxSafe += 1
var statements: [EvaluatableStatement] = Array()
var parse_err: Error? = .none
var current_context = context
if node.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Malformed block statement"))
}
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType == "statements" {
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(EvaluatableStatement, CompilerContext)>.Error(
ErrorOnNode(node: node, withError: "Malformed block statement")))
if current_node!.nodeType == "statements" {
switch Parser.Statements.Compile(
node: currentChild!, withContext: current_context)
node: current_node!, withContext: current_context)
{
case .Ok(let (parsed_statements, updated_context)):
current_context = updated_context
@@ -65,22 +63,21 @@ extension BlockStatement: CompilableStatement {
parse_err = error
}
currentChildIdx += 1
currentChildIdxSafe += 1
walker.next()
}
if let err = parse_err {
return .Error(err)
}
if node.childCount < currentChildIdxSafe {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(EvaluatableStatement, CompilerContext)>.Error(
ErrorOnNode(node: node, withError: "Malformed block statement")))
if current_node!.nodeType != "}" {
return Result.Error(
ErrorOnNode(node: node, withError: "Malformed block statement"))
}
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType != "}" {
return Result.Error(
ErrorOnNode(node: currentChild!, withError: "Missing } on block statement"))
ErrorOnNode(node: current_node!, withError: "Missing } on block statement"))
}
return .Ok((BlockStatement(statements), current_context))
+71
View File
@@ -0,0 +1,71 @@
// 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 SwiftTreeSitter
import TreeSitterP4
public struct Walker {
var currentChildIdx: Int
let childCount: Int
let node: Node
public init(node: Node) {
self.currentChildIdx = 0
self.childCount = node.childCount
self.node = node
}
public mutating func next() {
self.currentChildIdx += 1
}
public func getNext() -> Node? {
// If it is safe, then return the node!
if self.currentChildIdx < self.childCount {
return self.node.child(at: self.currentChildIdx)!
}
return .none
}
public func overUntil(n: Int, todo: (Node) -> Result<()>) -> Result<()> {
for currentChildIdx in currentChildIdx..<n {
let currentChild = node.child(at: currentChildIdx)!
if case Result.Error(let e) = todo(currentChild) {
return Result<()>.Error(e)
}
}
return Result.Ok(())
}
public func try_map<T>(n: Int, onlyNamed: Bool = false, todo: (Node) -> Result<T>) -> ([T], [Error]) {
var errors: [Error] = Array()
var results: [T] = Array()
for currentChildIdx in currentChildIdx..<n {
let currentChild = node.child(at: currentChildIdx)!
if onlyNamed && !currentChild.isNamed {
continue
}
switch todo(currentChild) {
case .Ok(let r): results.append(r)
case .Error(let e): errors.append(e)
}
}
return (results, errors)
}
}
-136
View File
@@ -1,136 +0,0 @@
// 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.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)"
}
/// Calculate whether the `argument` is compatible with this parameter.
public func compatible(_ argument: Argument) -> Bool {
let arg_type = argument.argument.type()
// If the parameter is (in)out, then the argument must be an lvalue.
if let param_direction = self.type.direction(),
param_direction == Direction.In || param_direction == Direction.InOut
{
if !(argument.argument is EvaluatableLValueExpression) {
return false
}
}
return arg_type.dataType().eq(rhs: self.type.dataType())
}
}
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 !param.compatible(arg) {
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
}
}
+69 -9
View File
@@ -17,7 +17,57 @@
import Common
public struct Action: CustomStringConvertible {
public struct Action: CustomStringConvertible, P4DataType, P4DataValue {
public func type() -> any Common.P4DataType {
return self
}
public func eq(rhs: any Common.P4DataValue) -> Bool {
return switch rhs {
case let arhs as Action: self.name == arhs.name
default: false
}
}
public func eq(rhs: any Common.P4DataType) -> Bool {
return switch rhs {
case is Action: true
default: false
}
}
public func lt(rhs: any Common.P4DataValue) -> Bool {
switch rhs {
case let arhs as Action: return self.name < arhs.name
default: return false
}
}
public func lte(rhs: any Common.P4DataValue) -> Bool {
switch rhs {
case let arhs as Action: return self.name <= arhs.name
default: return false
}
}
public func gt(rhs: any Common.P4DataValue) -> Bool {
switch rhs {
case let arhs as Action: return self.name > arhs.name
default: return false
}
}
public func gte(rhs: any Common.P4DataValue) -> Bool {
switch rhs {
case let arhs as Action: return self.name >= arhs.name
default: return false
}
}
public func def() -> any Common.P4DataValue {
return Action()
}
public var description: String {
return "Action: "
+ "\(self.name) with parameters \(self.params) and body \(String(describing: self.body))"
@@ -28,8 +78,8 @@ public struct Action: CustomStringConvertible {
public var name: Identifier
public init(
named name: Identifier, withParameters parameters: ParameterList,
withBody body: BlockStatement?
named name: Identifier = Identifier(name: ""), withParameters parameters: ParameterList = ParameterList([]),
withBody body: BlockStatement? = .none
) {
self.name = name
self.params = parameters
@@ -88,15 +138,24 @@ public struct TableKeys: CustomStringConvertible {
}
}
/// TODO
public struct TableActions {
public init() {}
public struct TableActionsProperty: CustomStringConvertible {
public let actions: [TypedIdentifier]
public init(_ actions: [TypedIdentifier] = []) {
self.actions = actions
}
public var description: String {
return "Table Actions: "
+ self.actions.map { action in
return action.description
}.joined(separator: ";")
}
}
public struct TablePropertyList: CustomStringConvertible {
let actions: TableActions
let actions: TableActionsProperty
let keys: TableKeys
public init(withActions actions: TableActions, withKeys keys: TableKeys) {
public init(withActions actions: TableActionsProperty, withKeys keys: TableKeys) {
self.actions = actions
self.keys = keys
}
@@ -208,7 +267,8 @@ public struct Control: P4DataType, P4DataValue, Equatable, CustomStringConvertib
withParameters: ParameterList(),
withTable: Table(
withName: Identifier(name: "empty"),
withPropertyList: TablePropertyList(withActions: TableActions(), withKeys: TableKeys())),
withPropertyList: TablePropertyList(
withActions: TableActionsProperty(), withKeys: TableKeys())),
withActions: Actions(withActions: []), withApply: ApplyStatement())
}
+42 -3
View File
@@ -17,7 +17,46 @@
import Common
public struct Declaration {}
public struct Declaration: P4DataType {
public let identifier: TypedIdentifier
public let extern: Bool
public let ffi: P4FFI?
public init(_ id: TypedIdentifier) {
identifier = id
ffi = .none
self.extern = false
}
public init(extern: Declaration, ffi: P4FFI) {
identifier = extern.identifier
self.ffi = ffi
self.extern = true
}
public func eq(rhs: any Common.P4DataType) -> Bool {
return switch rhs {
case let rrhs as Declaration:
self.identifier.type.dataType().eq(rhs: rrhs.identifier.type.dataType())
&& self.extern == rrhs.extern
default: false
}
}
public func def() -> any Common.P4DataValue {
/// TODO: Is a default of the extern'd type the right way to go?
return self.identifier.type.dataType().def()
}
public func type() -> any Common.P4DataType {
return self
}
public var description: String {
return "Extern \(self.identifier)"
}
}
public struct ExternDeclaration {}
public struct FunctionDeclaration: P4DataType, P4DataValue {
public func type() -> any Common.P4DataType {
@@ -79,14 +118,14 @@ public struct FunctionDeclaration: P4DataType, P4DataValue {
return "Function named \(self.name) that returns \(self.tipe) with parameters \(self.params)"
}
public var body: EvaluatableStatement?
public var body: BlockStatement?
public var params: ParameterList
public var name: Identifier
public var tipe: P4Type
public init(
named name: Identifier, ofType type: P4Type, withParameters parameters: ParameterList,
withBody body: EvaluatableStatement?
withBody body: BlockStatement?
) {
self.name = name
self.tipe = type
+11 -2
View File
@@ -128,11 +128,20 @@ public struct FieldAccessExpression {
}
public struct FunctionCall {
public let callee: FunctionDeclaration
public let callee: (FunctionDeclaration?, P4FFI?)
public let arguments: ArgumentList
public let return_type: P4DataType
public init(_ callee: FunctionDeclaration, withArguments arguments: ArgumentList) {
self.callee = callee
self.callee = (callee, .none)
self.arguments = arguments
self.return_type = callee.tipe.dataType()
}
public init(_ callee: P4FFI, withArguments arguments: ArgumentList) {
self.callee = (.none, callee)
self.arguments = arguments
/// ASSUME: That the FFI has been checked and the type is always a function declaration.
self.return_type = (callee.type().dataType() as! FunctionDeclaration).tipe.dataType()
}
}
+18
View File
@@ -0,0 +1,18 @@
// 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
+28 -4
View File
@@ -27,12 +27,13 @@ public struct ExpressionStatement {
public struct Program {
public var types: [P4DataType] = Array()
public var externs: [P4DataType] = Array()
public var instances: [P4Type] = Array()
/// Type of closure for filtering results from ``Program/InstancesWithTypes(_:)``
public typealias AttributedTypeFilter = (P4Type) -> Bool
public typealias TypeFilter = (P4Type) -> Bool
/// Type of closure for filtering results from ``Program/TypesWithTypes(_:)``
public typealias TypeFilter = (P4DataType) -> Bool
public typealias DataTypeFilter = (P4DataType) -> Bool
/// Retrieve global instances in the compiled P4 program.
public func InstancesWithTypes() -> [P4Type] {
@@ -51,7 +52,7 @@ public struct Program {
///
/// @Snippet(path: "use-program-instanceswithtypes", slice: "include")
///
public func InstancesWithTypes(_ filter: AttributedTypeFilter) -> [P4Type] {
public func InstancesWithTypes(_ filter: TypeFilter) -> [P4Type] {
return self.instances.filter { instance in
filter(instance)
}
@@ -74,12 +75,35 @@ public struct Program {
///
/// @Snippet(path: "use-program-typeswithtypes", slice: "include")
///
public func TypesWithTypes(_ filter: TypeFilter) -> [P4DataType] {
public func TypesWithTypes(_ filter: DataTypeFilter) -> [P4DataType] {
return self.types.filter { instance in
filter(instance)
}
}
/// Retrieve extern types in the compiled P4 program.
public func Externs() -> [P4DataType] {
return self.externs
}
/// Retrieve extern types declared in the compiled P4 program.
///
/// Use the given filter to select which of the extern types
/// declared in the compiled P4 program to retrieve.
///
/// If the compiled P4 program (from the source in the
/// string `p4_program_with_struct_decl`) has two extern structs declared and
/// you only want to select the one named `agg`, you could
/// use a filter like
///
/// @Snippet(path: "use-program-typeswithtypes", slice: "include")
///
public func Externs(_ filter: DataTypeFilter) -> [P4DataType] {
return self.externs.filter { instance in
filter(instance)
}
}
/// Find the program's main parser
///
/// Note: For now, the main parser is expected to be named main_parser.
+2 -2
View File
@@ -33,7 +33,8 @@ public func Call<T>(
let arg_idx = argument.index
let arg_value = argument.argument
//let maybe_argument_value = arg_value.evaluate(execution: called_execution)
let maybe_argument_value = called_execution.evaluator.EvaluateExpression(arg_value, inExecution: called_execution)
let maybe_argument_value = called_execution.evaluator.EvaluateExpression(
arg_value, inExecution: called_execution)
guard case (.Ok(let argument_value), let updated_execution) = maybe_argument_value else {
return (
.Error(Error(withMessage: "Cannot evaluate argument \(arg_idx): \(argument)")),
@@ -83,4 +84,3 @@ public func Call<T>(
}
return (.Ok(call_result), updated_execution.replaceScopes(inout_scopes))
}
+52 -20
View File
@@ -33,12 +33,14 @@ extension SelectExpression: EvaluatableExpression {
switch execution.evaluator.EvaluateExpression(self.selector, inExecution: execution) {
case (.Ok(let selector_value), let updated_execution):
for sce in self.case_expressions {
if case (.Ok(let kse), let updated_execution) = updated_execution.evaluator.EvaluateExpression(
sce.key, inExecution: updated_execution),
if case (.Ok(let kse), let updated_execution) = updated_execution.evaluator
.EvaluateExpression(
sce.key, inExecution: updated_execution),
kse.eq(selector_value)
{
//let result = sce.evaluate(execution: updated_execution)
let result = updated_execution.evaluator.EvaluateExpression(sce, inExecution: updated_execution)
let result = updated_execution.evaluator.EvaluateExpression(
sce, inExecution: updated_execution)
return result
}
}
@@ -212,13 +214,15 @@ extension BinaryOperatorExpression: EvaluatableExpression {
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
let updated_execution = execution
//let maybe_evaluated_left = self.left.evaluate(execution: updated_execution)
let maybe_evaluated_left = updated_execution.evaluator.EvaluateExpression(self.left, inExecution: updated_execution)
let maybe_evaluated_left = updated_execution.evaluator.EvaluateExpression(
self.left, inExecution: updated_execution)
guard case (.Ok(let evaluated_left), let updated_execution) = maybe_evaluated_left else {
return maybe_evaluated_left
}
//let maybe_evaluated_right = self.right.evaluate(execution: updated_execution)
let maybe_evaluated_right = updated_execution.evaluator.EvaluateExpression(self.right, inExecution: updated_execution)
let maybe_evaluated_right = updated_execution.evaluator.EvaluateExpression(
self.right, inExecution: updated_execution)
guard case (.Ok(let evaluated_right), let updated_execution) = maybe_evaluated_right else {
return maybe_evaluated_right
}
@@ -235,13 +239,15 @@ extension ArrayAccessExpression: EvaluatableExpression {
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
let updated_execution = execution
//let maybe_name = self.name.evaluate(execution: updated_execution)
let maybe_name = updated_execution.evaluator.EvaluateExpression(self.name, inExecution: updated_execution)
let maybe_name = updated_execution.evaluator.EvaluateExpression(
self.name, inExecution: updated_execution)
guard case (.Ok(let name), let updated_execution) = maybe_name else {
return maybe_name
}
//let maybe_indexor = self.indexor.evaluate(execution: updated_execution)
let maybe_indexor = updated_execution.evaluator.EvaluateExpression(self.indexor, inExecution: updated_execution)
let maybe_indexor = updated_execution.evaluator.EvaluateExpression(
self.indexor, inExecution: updated_execution)
guard case (.Ok(let indexor), let updated_execution) = maybe_indexor else {
return maybe_indexor
}
@@ -271,7 +277,8 @@ extension ArrayAccessExpression: EvaluatableLValueExpression {
let updated_execution = execution
//let maybe_value = self.name.evaluate(execution: updated_execution)
let maybe_value = updated_execution.evaluator.EvaluateExpression(self.name, inExecution: updated_execution)
let maybe_value = updated_execution.evaluator.EvaluateExpression(
self.name, inExecution: updated_execution)
guard case (.Ok(let value), let updated_execution) = maybe_value else {
return .Error(
Error(withMessage: "\(self.name) cannot be evaluated: \(maybe_value.0.error()!)"))
@@ -282,7 +289,8 @@ extension ArrayAccessExpression: EvaluatableLValueExpression {
// Now, get the indexor!
//let maybe_indexor_value = self.indexor.evaluate(execution: updated_execution)
let maybe_indexor_value = updated_execution.evaluator.EvaluateExpression(self.indexor, inExecution: updated_execution)
let maybe_indexor_value = updated_execution.evaluator.EvaluateExpression(
self.indexor, inExecution: updated_execution)
guard case (.Ok(let indexor_value), let updated_execution) = maybe_indexor_value else {
return Result.Error(
Error(withMessage: "\(self.indexor) cannot be evaluated: \(maybe_indexor_value.0.error()!)")
@@ -349,7 +357,8 @@ extension FieldAccessExpression: EvaluatableExpression {
let updated_execution = execution
//let maybe_struct = self.strct.evaluate(execution: updated_execution)
let maybe_struct = updated_execution.evaluator.EvaluateExpression(self.strct, inExecution: updated_execution)
let maybe_struct = updated_execution.evaluator.EvaluateExpression(
self.strct, inExecution: updated_execution)
guard case (.Ok(let strct), let updated_execution) = maybe_struct else {
return maybe_struct
}
@@ -358,7 +367,7 @@ extension FieldAccessExpression: EvaluatableExpression {
return (.Error(Error(withMessage: "\(strct) does not identify a struct")), updated_execution)
}
// TODO: Create a default value?
/// TODO: Create a default value?
guard let value = struct_strct.get(field: self.field) else {
return (.Error(Error(withMessage: "Missing value")), updated_execution)
}
@@ -384,7 +393,8 @@ extension FieldAccessExpression: EvaluatableLValueExpression {
let updated_execution = execution
// First, evaluate strct_id and make sure that it names a struct.
//let maybe_value = self.strct.evaluate(execution: updated_execution)
let maybe_value = updated_execution.evaluator.EvaluateExpression(self.strct, inExecution: updated_execution)
let maybe_value = updated_execution.evaluator.EvaluateExpression(
self.strct, inExecution: updated_execution)
guard case (.Ok(let value), let updated_execution) = maybe_value else {
return .Error(
Error(withMessage: "\(self.strct) cannot be evaluated: \(maybe_value.0.error()!)"))
@@ -467,33 +477,55 @@ extension FunctionCall: EvaluatableExpression {
execution: Common.ProgramExecution
) -> (Common.Result<P4Value>, ProgramExecution) {
guard let body = self.callee.body else {
return (
.Error(Error(withMessage: "No body for called function (\(self.callee.name))")), execution
)
let body_params:
Result<((ProgramExecution) -> (ControlFlow, ProgramExecution), ParameterList)> =
switch self.callee {
case (.some(let callee), .none):
switch callee.body {
case .some(let body):
.Ok(
(
{ (execution: ProgramExecution) -> (ControlFlow, ProgramExecution) in
return body.evaluate(execution: execution)
}, callee.params
))
case .none: .Error(Error(withMessage: "No body for called function (\(callee.name))"))
}
case (.none, .some(let callee)):
.Ok(
(
{ (execution: ProgramExecution) -> (ControlFlow, ProgramExecution) in
return callee.execute(execution: execution)
}, callee.parameters()
))
default: .Error(Error(withMessage: "No callee found for function call"))
}
guard case .Ok(let body) = body_params else {
return (.Error(body_params.error()!), execution)
}
let call_body: (ProgramExecution) -> (Result<P4Value>, ProgramExecution) = {
(execution: ProgramExecution) in
let (control_flow, updated_execution) = body.evaluate(execution: execution)
let (control_flow, updated_execution) = body.0(execution)
return switch control_flow {
case ControlFlow.Return(.some(let value)): (.Ok(value), updated_execution)
default:
(
.Error(
Error(withMessage: "No value returned from called function (\(self.callee.name))")),
Error(withMessage: "No value returned from called function")),
execution
)
}
}
return Call(
body: call_body, withArguments: self.arguments, withParameters: self.callee.params,
body: call_body, withArguments: self.arguments, withParameters: body.1,
inExecution: execution)
}
public func type() -> P4Type {
return self.callee.tipe
return P4Type(self.return_type)
}
}
+7 -13
View File
@@ -22,7 +22,8 @@ extension ParserAssignmentStatement: EvaluatableStatement {
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
let updated_execution = execution
//let result = self.value.evaluate(execution: updated_execution)
let result = updated_execution.evaluator.EvaluateExpression(self.value, inExecution: updated_execution)
let result = updated_execution.evaluator.EvaluateExpression(
self.value, inExecution: updated_execution)
guard case (.Ok(let value), let updated_execution) = result else {
return (ControlFlow.Error, execution.setError(error: result.0.error()!))
}
@@ -44,11 +45,8 @@ extension ParserStateDirectTransition: EvaluatableParserState {
) -> (any EvaluatableParserState, Common.ProgramExecution) {
var program = program.enter_scope()
let (control_flow, next_execution) = program.evaluator.ExecuteStatement(
statements,
handleResult: { (control_flow, execution) in
return (control_flow, execution)
}, inExecution: program)
let (control_flow, next_execution) = program.evaluator.ExecuteStatements(
statements, inExecution: program)
switch control_flow {
case .Next: program = next_execution
@@ -105,12 +103,8 @@ extension ParserStateSelectTransition: EvaluatableParserState {
) -> (any EvaluatableParserState, Common.ProgramExecution) {
var program = program.enter_scope()
let (control_flow, next_execution) = program.evaluator.ExecuteStatement(
statements,
handleResult: { (control_flow, execution) in
return (control_flow, execution)
}, inExecution: program)
let (control_flow, next_execution) = program.evaluator.ExecuteStatements(
statements, inExecution: program)
switch control_flow {
case .Next: program = next_execution
case .Error: return (reject, next_execution.exit_scope())
@@ -150,7 +144,7 @@ extension ParserStateSelectTransition: EvaluatableParserState {
extension Parser: LibraryCallable {
public typealias T = InstantiatedParserState
public func call(
execution: Common.ProgramExecution, arguments: P4Lang.ArgumentList
execution: Common.ProgramExecution, arguments: ArgumentList
) -> (P4Lang.InstantiatedParserState, Common.ProgramExecution) {
var execution = execution.enter_scope()
+15 -16
View File
@@ -20,22 +20,21 @@ import P4Lang
extension BlockStatement: EvaluatableStatement {
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
return execution.evaluator.ExecuteStatement(
self.statements,
handleResult: { (cf, execution) in
switch cf {
case ControlFlow.Return(let value): return (ControlFlow.Return(value), execution)
case ControlFlow.Next: return (cf, execution)
case ControlFlow.Error: return (ControlFlow.Error, execution)
default:
return (
ControlFlow.Error,
execution.setError(
error: Error(withMessage: "Invalid control flow \(cf) in block statement"))
)
}
},
inExecution: execution)
return execution.evaluator.ExecuteStatements(
self.statements, inExecution: execution
) { (cf, execution) in
switch cf {
case ControlFlow.Return(let value): return (ControlFlow.Return(value), execution)
case ControlFlow.Next: return (cf, execution)
case ControlFlow.Error: return (ControlFlow.Error, execution)
default:
return (
ControlFlow.Error,
execution.setError(
error: Error(withMessage: "Invalid control flow \(cf) in block statement"))
)
}
}
}
}
+169
View File
@@ -89,6 +89,113 @@ import P4Lang
#expect(program.InstancesWithTypes(filter).count == 2)
}
@Test func test_simple_control_declaration_with_actions() async throws {
let simple_parser_declaration = """
control simple() {
action a() {
}
table t {
key = {
true: exact;
}
actions = {
a;
}
}
apply {
}
};
"""
let x = { (tipe: P4Type) -> Bool in
switch tipe.dataType() {
case let c as Control: c.name == "simple"
default: false
}
}
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
#expect(program.InstancesWithTypes(x).count == 1)
}
@Test func test_simple_control_declaration_with_misnamed_actions() async throws {
let simple_parser_declaration = """
control simple() {
action a() {
}
table t {
key = {
true: exact;
}
actions = {
b;
}
}
apply {
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage: "{54, 63}: Error(s) parsing property list: {91, 26}: Error(s) parsing table actions: Cannot find b in lexical scope."
),
Program.Compile(simple_parser_declaration))
)
}
@Test func test_simple_control_declaration_with_misnamed_actions2() async throws {
let simple_parser_declaration = """
control simple() {
action a() {
}
table t {
key = {
true: exact;
}
actions = {
a;
b;
}
}
apply {
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage: "{54, 72}: Error(s) parsing property list: {91, 35}: Error(s) parsing table actions: Cannot find b in lexical scope."
),
Program.Compile(simple_parser_declaration))
)
}
@Test func test_simple_control_declaration_with_mistyped_actions() async throws {
let simple_parser_declaration = """
bool a() {
return true;
};
control simple() {
table t {
key = {
true: exact;
}
actions = {
a;
}
}
apply {
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage: "{64, 63}: Error(s) parsing property list: {101, 26}: Error(s) parsing table actions: {101, 26}: a does not name an action"
),
Program.Compile(simple_parser_declaration))
)
}
@Test func test_simple_control_declaration_with_parameters() async throws {
let simple_parser_declaration = """
control simple(bool x, bool y) {
@@ -107,6 +214,36 @@ import P4Lang
#expect(#RequireOkResult(Program.Compile(simple_parser_declaration)))
}
@Test func test_simple_control_declaration_with_multiple_tables() async throws {
let simple_parser_declaration = """
control simple(bool x, bool y, bool a, bool b) {
action a() {
}
table t {
key = {
x: exact;
y: exact;
}
}
table u {
key = {
a: exact;
b: exact;
}
}
apply {
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage: "{0, 215}: More than one table in control declaration"
),
Program.Compile(simple_parser_declaration))
)
}
@Test func test_simple_control_declaration_with_action_using_parameter() async throws {
let simple_parser_declaration = """
control simple(bool x, bool y) {
@@ -150,4 +287,36 @@ import P4Lang
),
Program.Compile(simple_parser_declaration))
)
}
@Test func test_simple_control_declaration_with_element_after_apply() async throws {
let simple_parser_declaration = """
control simple(bool x, bool y) {
action a(int z) {
z = false;
}
table t {
key = {
x: exact;
y: exact;
}
}
apply {
}
table x {
key = {
x: exact;
y: exact;
}
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"Could not compile the P4 program"
),
Program.Compile(simple_parser_declaration))
)
}
+2 -2
View File
@@ -176,7 +176,7 @@ import TreeSitterP4
#RequireErrorResult(
Error(
withMessage:
"{56, 21}: Failed to parse a statement element: {63, 9}: Failed to parse a statement element: {63, 1}: Cannot assign value with type Boolean to identifier x that is in parameter"
"{63, 9}: Failed to parse a statement element: {63, 1}: Cannot assign value with type Boolean to identifier x that is in parameter"
),
Program.Compile(simple_parser_declaration))
)
@@ -196,7 +196,7 @@ import TreeSitterP4
#RequireErrorResult(
Error(
withMessage:
"{113, 27}: Failed to parse a statement element: {120, 15}: Failed to parse a statement element: {120, 7}: Cannot assign to field yesno of x that is in parameter"
"{120, 15}: Failed to parse a statement element: {120, 7}: Cannot assign to field yesno of x that is in parameter"
),
Program.Compile(simple_parser_declaration))
)
+124
View File
@@ -0,0 +1,124 @@
// 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
public struct Return5: P4FFI {
public func execute(execution: Common.ProgramExecution) -> (
Common.ControlFlow, Common.ProgramExecution
) {
return (ControlFlow.Return(P4Value(P4IntValue(withValue: 5))), execution)
}
public func parameters() -> ParameterList {
return ParameterList()
}
public func type() -> Common.P4Type {
return P4Type(
FunctionDeclaration(
named: Identifier(name: "externally"), ofType: P4Type(P4Int()),
withParameters: ParameterList(), withBody: .none?))
}
public init() {}
}
public struct Return6: P4FFI {
public func execute(execution: Common.ProgramExecution) -> (
Common.ControlFlow, Common.ProgramExecution
) {
return (ControlFlow.Return(P4Value(P4IntValue(withValue: 6))), execution)
}
public func parameters() -> ParameterList {
return ParameterList()
}
public func type() -> Common.P4Type {
return P4Type(
FunctionDeclaration(
named: Identifier(name: "externally"), ofType: P4Type(P4Int()),
withParameters: ParameterList(), withBody: .none?))
}
public init() {}
}
@Test func test_extern_function_declaration() async throws {
let simple_parser_declaration = """
extern int externally();
parser main_parser() {
state start {
int t = externally();
transition select (t == 5) {
true: accept;
false: reject;
};
}
};
"""
let externally = Return5()
let program = try! #UseOkResult(
Program.Compile(
simple_parser_declaration, withGlobalInstances: .none, withGlobalTypes: .none,
withFFIs: [externally]))
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
let (state_result, _) = try! #UseOkResult(runtime.run())
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
}
@Test func test_extern_function_declaration2() async throws {
let simple_parser_declaration = """
extern int externally();
parser main_parser() {
state start {
int t = externally();
transition select (t == 5) {
true: accept;
false: reject;
};
}
};
"""
let externally = Return6()
let program = try! #UseOkResult(
Program.Compile(
simple_parser_declaration, withGlobalInstances: .none, withGlobalTypes: .none,
withFFIs: [externally]))
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
let (state_result, _) = try! #UseOkResult(runtime.run())
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
}
+1 -1
View File
@@ -98,7 +98,7 @@ import P4Lang
#RequireErrorResult<(EvaluatableStatement, CompilerContext)>(
Error(withMessage: "{2, 154}: Did not find assignment statement"),
ParserAssignmentStatement.Compile( // Note: Calling ParserAssignmentStatement compilation directly.
node: result.rootNode!, withContext: CompilerContext(withInstances: VarTypeScopes()))))
node: result.rootNode!, withContext: CompilerContext())))
}
@Test func test_simple_compiler_parser_with_parameters() async throws {
@@ -0,0 +1,51 @@
// 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 Macros
func wrapper_test_mustor() -> Int {
let x: Int? = 2
var i = 0
#MustOr(result: i, thing: x, or: 1)
return i
}
func wrapper_test_mustor_none() -> Int {
let x: Int? = .none
var i = 0
#MustOr(result: i, thing: x, or: 1)
return i
}
@Test func test_mustor() async throws {
#expect(wrapper_test_mustor() == 2)
}
@Test func test_mustor_none() async throws {
#expect(wrapper_test_mustor_none() == 1)
}
+6 -3
View File
@@ -68,13 +68,16 @@ export default grammar({
instantiation: $ => seq($.typeRef, '(', optional($.parameter_list), ')', $.identifier),
// Declarations
declaration: $ => seq(choice($.parserDeclaration, $.parserTypeDeclaration, $.type_declaration, $.function_declaration, $.control_declaration)),
declaration: $ => seq(choice($.parserDeclaration, $.parserTypeDeclaration, $.type_declaration, $.function_declaration, $.control_declaration, $.extern_declaration)),
type_declaration: $=> choice($.struct_declaration),
struct_declaration: $ => seq($.struct, $.identifier, '{', optional($.struct_declaration_fields), '}'),
struct_declaration_fields: $=> repeat1(seq($.variableDeclaration)),
function_declaration: $=> seq($.typeRef, $.identifier, $.parameters, $.statement),
extern_declaration: $=> seq($.extern, $.declaration),
// The body is truly optional only in the extern case. This check is handled by the compiler.
function_declaration: $=> seq($.typeRef, $.identifier, $.parameters, optional($.blockStatement)),
// Make separate productions for the parser type and the parser type declaration because the latter can have type parameters.
parserTypeDeclaration: $ => seq(optional($.annotations), $.parser, field('parser_name', $.identifier), optional($.typeParameters), $.parameters),
@@ -91,7 +94,7 @@ export default grammar({
table_property_list: $ => repeat1(choice($.table_keys, $.table_actions)),
table_keys: $=> seq($.key, '=', '{', repeat($.table_key_entry), '}'),
table_key_entry: $=> seq($.keysetExpression, ':', $.table_key_match_type, $._semicolon),
table_actions: $=> seq($.actions, '=', '{', optional(repeat1($.identifier)), '}'),
table_actions: $=> seq($.actions, '=', '{', optional(repeat1(seq($.identifier, $._semicolon))), '}'),
// match types
table_key_match_type: $ => choice($.exact), // support exact only for now.
+73
View File
@@ -54,4 +54,77 @@ control simple() {
)
)
)
=========================
Simple Control Declaration With Actions
=========================
control simple() {
action a() {
}
action b() {
}
table t {
key = {
x: exact;
}
actions = {
a;
b;
}
}
apply {
}
};
---
(p4program
(declaration
(control_declaration
(control)
(identifier)
(parameters)
(action_declaration
(action)
(identifier)
(parameters)
(blockStatement)
)
(action_declaration
(action)
(identifier)
(parameters)
(blockStatement)
)
(table_declaration
(table)
(identifier)
(table_property_list
(table_keys
(key)
(table_key_entry
(keysetExpression
(expression
(simple_expression
(identifier)
)
)
)
(table_key_match_type
(exact)
)
)
)
(table_actions
(actions)
(identifier)
(identifier)
)
)
)
(apply_statement
(apply)
(blockStatement)
)
)
)
)
+83 -26
View File
@@ -282,18 +282,16 @@ bool functionb() {
)
(identifier)
(parameters)
(statement
(blockStatement
(statements
(statement
(variableDeclaration
(typeRef
(baseType
(string)
)
(blockStatement
(statements
(statement
(variableDeclaration
(typeRef
(baseType
(string)
)
(identifier)
)
(identifier)
)
)
)
@@ -340,18 +338,16 @@ bool functionb(bool a, int b) {
)
)
)
(statement
(blockStatement
(statements
(statement
(variableDeclaration
(typeRef
(baseType
(string)
)
(blockStatement
(statements
(statement
(variableDeclaration
(typeRef
(baseType
(string)
)
(identifier)
)
(identifier)
)
)
)
@@ -416,17 +412,78 @@ bool functionb(in bool a, out int b, inout string c) {
)
)
)
(statement
(blockStatement
(statements
(statement
(variableDeclaration
(blockStatement
(statements
(statement
(variableDeclaration
(typeRef
(baseType
(string)
)
)
(identifier)
)
)
)
)
)
)
)
=========================
Extern Function Declaration
=========================
extern bool functionb(in bool a, out int b, inout string c);
---
(p4program
(declaration
(extern_declaration
(extern)
(declaration
(function_declaration
(typeRef
(baseType
(bool)
)
)
(identifier)
(parameters
(parameter_list
(parameter_list
(parameter_list
(parameter
(direction
(in)
)
(typeRef
(baseType
(bool)
)
)
(identifier)
)
)
(parameter
(direction
(out)
)
(typeRef
(baseType
(int)
)
)
(identifier)
)
)
(parameter
(direction
(inout)
)
(typeRef
(baseType
(string)
)
)
(identifier)
(identifier)
)
)
)
+7 -9
View File
@@ -43,15 +43,13 @@ int fun() {
)
(identifier)
(parameters)
(statement
(blockStatement
(statements
(statement
(return_statement
(expression
(simple_expression
(integer)
)
(blockStatement
(statements
(statement
(return_statement
(expression
(simple_expression
(integer)
)
)
)