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( .testTarget(
name: "Tests", 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 ### Status
@@ -93,7 +99,7 @@ While coding, it may be useful to leave ourselves notes. Every note is formatted
```Swift ```Swift
// NOTE: note text /// NOTE<: optional note text>
``` ```
where `NOTE` can be: 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> { public func set(index: Int, to: P4Value) -> Result<P4ArrayValue> {
// TODO: Check for OOB /// TODO: Check for OOB
var updated_values = self.value var updated_values = self.value
updated_values[index] = to updated_values[index] = to
return Result.Ok(P4ArrayValue(withType: self.vtype, withValue: updated_values)) return Result.Ok(P4ArrayValue(withType: self.vtype, withValue: updated_values))
@@ -621,7 +621,7 @@ public class P4ArrayValue: P4DataValue {
guard rhs as? P4ArrayValue != nil else { guard rhs as? P4ArrayValue != nil else {
return false return false
} }
// TODO!! /// TODO
return true return true
} }
@@ -629,7 +629,7 @@ public class P4ArrayValue: P4DataValue {
guard rhs as? P4ArrayValue != nil else { guard rhs as? P4ArrayValue != nil else {
return false return false
} }
// TODO!! /// TODO
return true return true
} }
@@ -637,7 +637,7 @@ public class P4ArrayValue: P4DataValue {
guard rhs as? P4ArrayValue != nil else { guard rhs as? P4ArrayValue != nil else {
return false return false
} }
// TODO!! /// TODO
return true return true
} }
@@ -645,7 +645,7 @@ public class P4ArrayValue: P4DataValue {
guard rhs as? P4ArrayValue != nil else { guard rhs as? P4ArrayValue != nil else {
return false return false
} }
// TODO!! /// TODO
return true return true
} }
@@ -653,7 +653,7 @@ public class P4ArrayValue: P4DataValue {
guard rhs as? P4ArrayValue != nil else { guard rhs as? P4ArrayValue != nil else {
return false return false
} }
// TODO!! /// TODO
return true return true
} }
+46 -47
View File
@@ -15,38 +15,47 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
public typealias ExecuteStatementResultHandlerT = (ControlFlow, ProgramExecution) -> (
public typealias ExecuteStatementResultHandler = (ControlFlow, ProgramExecution) -> (
ControlFlow, ProgramExecution ControlFlow, ProgramExecution
) )
public struct ClassicEvaluator: ProgramExecutionEvaluator { public typealias ExecuteStatementT = (EvaluatableStatement, ProgramExecution) -> (
public func ExecuteStatement( ControlFlow, ProgramExecution
_ statements: [EvaluatableStatement], handleResult handler: ExecuteStatementResultHandler, )
inExecution execution: ProgramExecution,
) -> (ControlFlow, ProgramExecution) {
var execution = execution func CanonicalExecuteStatements(
for s in statements { _ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
let (control_flow, next_execution) = s.evaluate(execution: execution) _ executor: ExecuteStatementT
) -> (ControlFlow, ProgramExecution) {
switch handler(control_flow, next_execution) { var execution = execution
case (ControlFlow.Next, let handled_next_execution): execution = handled_next_execution for s in statements {
case (ControlFlow.Return(let value), let handled_next_execution): // Execute the statement with the user-provided statement executor.
return (ControlFlow.Return(value), handled_next_execution) switch executor(s, execution) {
case (let handled_control_flow, let handled_next_execution): // And decide what to do next!
return (handled_control_flow, handled_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)
} }
return (ControlFlow.Next, execution)
} }
return (ControlFlow.Next, execution)
}
public func ExecuteStatement( public struct ClassicEvaluator: ProgramExecutionEvaluator {
_ statement: EvaluatableStatement, handleResult handler: ExecuteStatementResultHandler, public func ExecuteStatements(
inExecution execution: ProgramExecution _ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
_ handler: ExecuteStatementResultHandlerT?
) -> (ControlFlow, ProgramExecution) { ) -> (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( public func EvaluateExpression(
@@ -86,9 +95,9 @@ public struct InterloperEvaluator: ProgramExecutionEvaluator {
return pe return pe
} }
public func ExecuteStatement( public func ExecuteStatements(
_ statements: [EvaluatableStatement], handleResult handler: ExecuteStatementResultHandler, _ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
inExecution execution: ProgramExecution, _ handler: ExecuteStatementResultHandlerT?
) -> (ControlFlow, ProgramExecution) { ) -> (ControlFlow, ProgramExecution) {
var debugger: StatementInterloper? = .none var debugger: StatementInterloper? = .none
@@ -98,30 +107,20 @@ public struct InterloperEvaluator: ProgramExecutionEvaluator {
hasDebugInterloper = true hasDebugInterloper = true
} }
var execution = execution return CanonicalExecuteStatements(statements, inExecution: execution) { statement, execution in
for s in statements { let (cf, value) = statement.evaluate(execution: execution)
let (control_flow, next_execution) = s.evaluate(execution: execution) let (handled_cf, handled_value) =
if let handler = handler {
handler(cf, value)
} else {
(cf, value)
}
if hasDebugInterloper { if hasDebugInterloper {
debugger!(s, control_flow, next_execution) debugger!(statement, handled_cf, handled_value)
}
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)
} }
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( 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> { 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)) return .Ok(P4Value(value, self._type))
} }
+16 -9
View File
@@ -56,17 +56,24 @@ public protocol EvaluatableLValueExpression: EvaluatableExpression {
} }
public protocol ProgramExecutionEvaluator { public protocol ProgramExecutionEvaluator {
func ExecuteStatement( func ExecuteStatements(
_ statements: [EvaluatableStatement], handleResult handler: ExecuteStatementResultHandler, _ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
inExecution execution: ProgramExecution, _ handler: ExecuteStatementResultHandlerT?
) -> (ControlFlow, ProgramExecution); ) -> (ControlFlow, ProgramExecution)
func ExecuteStatement( func ExecuteStatements(
_ statement: EvaluatableStatement, handleResult handler: ExecuteStatementResultHandler, _ statements: [EvaluatableStatement], inExecution execution: ProgramExecution
inExecution execution: ProgramExecution ) -> (ControlFlow, ProgramExecution)
) -> (ControlFlow, ProgramExecution);
func EvaluateExpression( func EvaluateExpression(
_ expression: EvaluatableExpression, inExecution execution: ProgramExecution, _ expression: EvaluatableExpression, inExecution execution: ProgramExecution,
) -> (Result<P4Value>, 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] nodes: N, type: [String], nice_type_names: [String]
) = ) =
#externalMacro(module: "Macros", type: "RequireNodesType") #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") #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 @main
struct P4Macros: CompilerPlugin { struct P4Macros: CompilerPlugin {
var providingMacros: [Macro.Type] = [ var providingMacros: [Macro.Type] = [
RequireResult.self, RequireErrorResult.self, UseOkResult.self, UseErrorResult.self, 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 node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(ParameterList, CompilerContext)> { ) -> Common.Result<(ParameterList, CompilerContext)> {
var currentChildIdx = 0 var walker = Walker(node: node)
var currentChildIdxSafe = 1 var current_node: Node? = .none
var currentChild: Node? = .none
if node.text == ")" { if node.text == ")" {
// There are no parameters! // There are no parameters!
@@ -40,49 +39,45 @@ func parameter_list_compiler(
var parameters: ParameterList = ParameterList([]) var parameters: ParameterList = ParameterList([])
if node.childCount < currentChildIdxSafe { #MustOr(
return Result.Error( result: current_node, thing: walker.getNext(),
ErrorOnNode(node: node, withError: "Missing parameter list component")) or: Result<(ParameterList, CompilerContext)>.Error(
} ErrorOnNode(
node: node, withError: "Missing parameter list component")))
currentChild = node.child(at: currentChildIdx) if current_node?.nodeType == "parameter_list" {
if currentChild?.nodeType == "parameter_list" { switch parameter_list_compiler(node: current_node!, withContext: context) {
switch parameter_list_compiler(node: currentChild!, withContext: context) {
case .Ok(let (ps, _)): case .Ok(let (ps, _)):
parameters = ps parameters = ps
case .Error(let e): return Result.Error(e) case .Error(let e): return Result.Error(e)
} }
walker.next()
currentChildIdx += 1
currentChildIdxSafe += 1
} }
// We may have moved nodes, check/reset currentChild. #MustOr(
if node.childCount < currentChildIdxSafe { result: current_node, thing: walker.getNext(),
return Result.Error( or: Result<(ParameterList, CompilerContext)>.Error(
ErrorOnNode(node: node, withError: "Missing parameter list component")) ErrorOnNode(
} node: node, withError: "Missing parameter list component")))
currentChild = node.child(at: currentChildIdx)
// If this is a ')', we are done. // If this is a ')', we are done.
if currentChild?.text == ")" { if current_node?.text == ")" {
return Result.Ok((parameters, context)) return Result.Ok((parameters, context))
} }
// If this is a comma, we skip it! // If this is a comma, we skip it!
if currentChild?.text == "," { if current_node?.text == "," {
currentChildIdx += 1 walker.next()
currentChildIdxSafe += 1
} }
if node.childCount < currentChildIdxSafe { #MustOr(
return Result.Error( result: current_node, thing: walker.getNext(),
ErrorOnNode(node: node, withError: "Missing parameter list component")) or: Result<(ParameterList, CompilerContext)>.Error(
} ErrorOnNode(
currentChild = node.child(at: currentChildIdx) node: node, withError: "Missing parameter list component")))
// Otherwise, there should be one parameter left! // Otherwise, there should be one parameter left!
switch Parameter.Compile(node: currentChild!, withContext: context) { switch Parameter.Compile(node: current_node!, withContext: context) {
case .Ok(let (parsed_parameter, updated_context)): case .Ok(let (parsed_parameter, updated_context)):
return Result.Ok((parameters.addParameter(parsed_parameter), updated_context)) return Result.Ok((parameters.addParameter(parsed_parameter), updated_context))
case .Error(let e): return Result.Error(e) case .Error(let e): return Result.Error(e)
@@ -99,24 +94,24 @@ extension ParameterList: Compilable {
#RequireNodeType<Node, (ParameterList, CompilerContext)>( #RequireNodeType<Node, (ParameterList, CompilerContext)>(
node: parameter_node, type: "parameters", nice_type_name: "Parameters") node: parameter_node, type: "parameters", nice_type_name: "Parameters")
var currentChildIdx = 0 var walker = Walker(node: parameter_node)
var currentChildIdxSafe = 1
// Let's eat the '(' before we start ... var current_node: Node? = .none
if parameter_node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: parameter_node, withError: "Missing '(' in parameter list component"))
}
currentChildIdx += 1 #MustOr(
currentChildIdxSafe += 1 result: current_node, thing: walker.getNext(),
if parameter_node.childCount < currentChildIdxSafe { or: Result<(ParameterList, CompilerContext)>.Error(
return .Error( ErrorOnNode(
ErrorOnNode(node: parameter_node, withError: "Missing parameter list component")) node: node, withError: "Missing '(' in parameter list component")))
}
let currentChild = parameter_node.child(at: currentChildIdx)
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)>( #RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
node: node, type: "parameter", nice_type_name: "parameter") node: node, type: "parameter", nice_type_name: "parameter")
var currentChildIdx = 0 var walker = Walker(node: node)
var currentChildIdxSafe = 1 var current_node: Node? = .none
var currentChild: Node? = .none
if node.childCount < currentChildIdxSafe { #MustOr(
return .Error( result: current_node, thing: walker.getNext(),
ErrorOnNode(node: node, withError: "Missing parameter declaration component")) or: Result<(Parameter, CompilerContext)>.Error(
} ErrorOnNode(
node: node, withError: "Missing parameter declaration component")))
currentChild = node.child(at: currentChildIdx)
// Annotation? // Annotation?
if currentChild!.nodeType == "annotations" { if current_node!.nodeType == "annotations" {
return .Error( return .Error(
ErrorOnNode( ErrorOnNode(
node: currentChild!, node: current_node!,
withError: "Annotations in parameter declarations are not yet handled")) withError: "Annotations in parameter declarations are not yet handled"))
// Will increment indexes here. // 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 var direction: Direction? = .none
// Direction? // 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 { guard case .Ok((let parsed_direction, _)) = maybe_parsed_direction else {
return .Error(maybe_parsed_direction.error()!) return .Error(maybe_parsed_direction.error()!)
} }
direction = parsed_direction direction = parsed_direction
currentChildIdx += 1 walker.next()
currentChildIdxSafe += 1
} }
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( return Result.Error(
ErrorOnNode( ErrorOnNode(
node: node, withError: "Did not find type name for parameter declaration")) node: node, withError: "Did not find type name for parameter declaration"))
} }
guard 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 { else {
return Result.Error( 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 walker.next()
currentChildIdxSafe += 1 #MustOr(
if node.childCount < currentChildIdxSafe { result: current_node, thing: walker.getNext(),
return .Error( or: Result<(Parameter, CompilerContext)>.Error(
ErrorOnNode(node: node, withError: "Missing parameter declaration component")) ErrorOnNode(
} node: node, withError: "Missing parameter declaration component")))
currentChild = node.child(at: currentChildIdx) if current_node!.nodeType != "identifier" {
if currentChild!.nodeType != "identifier" {
return Result.Error( return Result.Error(
ErrorOnNode( ErrorOnNode(
node: node, withError: "Did not find identifier for parameter statement")) node: node, withError: "Did not find identifier for parameter statement"))
} }
guard 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 { else {
return Result.Error( 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( return Result.Ok(
@@ -239,9 +240,8 @@ func argument_list_compiler(
node: SwiftTreeSitter.Node, withContext context: CompilerContext node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(ArgumentList, CompilerContext)> { ) -> Common.Result<(ArgumentList, CompilerContext)> {
var currentChildIdx = 0 var walker = Walker(node: node)
var currentChildIdxSafe = 1 var current_node: Node? = .none
var currentChild: Node? = .none
if node.text == ")" { if node.text == ")" {
// There are no arguments! // There are no arguments!
@@ -253,49 +253,47 @@ func argument_list_compiler(
var arguments: ArgumentList = ArgumentList([]) var arguments: ArgumentList = ArgumentList([])
if node.childCount < currentChildIdxSafe { #MustOr(
return Result.Error( result: current_node, thing: walker.getNext(),
ErrorOnNode(node: node, withError: "Missing argument list component")) or: Result<(ArgumentList, CompilerContext)>.Error(
} ErrorOnNode(
node: node, withError: "Missing argument list component")))
currentChild = node.child(at: currentChildIdx) if current_node?.nodeType == "argument_list" {
if currentChild?.nodeType == "argument_list" { switch argument_list_compiler(node: current_node!, withContext: context) {
switch argument_list_compiler(node: currentChild!, withContext: context) {
case .Ok(let (ps, _)): case .Ok(let (ps, _)):
arguments = ps arguments = ps
case .Error(let e): return Result.Error(e) case .Error(let e): return Result.Error(e)
} }
currentChildIdx += 1 walker.next()
currentChildIdxSafe += 1
} }
// We may have moved nodes, check/reset currentChild. // We may have moved nodes, check/reset current_node.
if node.childCount < currentChildIdxSafe { #MustOr(
return Result.Error( result: current_node, thing: walker.getNext(),
ErrorOnNode(node: node, withError: "Missing argument list component")) or: Result<(ArgumentList, CompilerContext)>.Error(
} ErrorOnNode(
currentChild = node.child(at: currentChildIdx) node: node, withError: "Missing argument list component")))
// If this is a ')', we are done. // If this is a ')', we are done.
if currentChild?.text == ")" { if current_node?.text == ")" {
return Result.Ok((arguments, context)) return Result.Ok((arguments, context))
} }
// If this is a comma, we skip it! // If this is a comma, we skip it!
if currentChild?.text == "," { if current_node?.text == "," {
currentChildIdx += 1 walker.next()
currentChildIdxSafe += 1
} }
if node.childCount < currentChildIdxSafe { #MustOr(
return Result.Error( result: current_node, thing: walker.getNext(),
ErrorOnNode(node: node, withError: "Missing argument list component")) or: Result<(ArgumentList, CompilerContext)>.Error(
} ErrorOnNode(
currentChild = node.child(at: currentChildIdx) node: node, withError: "Missing argument list component")))
// Otherwise, there should be one argument left! // 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)): case .Ok(let (ce, updated_context)):
return Result.Ok( return Result.Ok(
(arguments.addArgument(Argument(ce, atIndex: arguments.count() + 1)), updated_context)) (arguments.addArgument(Argument(ce, atIndex: arguments.count() + 1)), updated_context))
@@ -313,24 +311,24 @@ extension ArgumentList: Compilable {
#RequireNodeType<Node, (ArgumentList, CompilerContext)>( #RequireNodeType<Node, (ArgumentList, CompilerContext)>(
node: argument_node, type: "arguments", nice_type_name: "arguments") node: argument_node, type: "arguments", nice_type_name: "arguments")
var currentChildIdx = 0 var walker = Walker(node: argument_node)
var currentChildIdxSafe = 1 var current_node: Node? = .none
// Let's eat the '(' before we start ... #MustOr(
if argument_node.childCount < currentChildIdxSafe { result: current_node, thing: walker.getNext(),
return .Error( or: Result<(ArgumentList, CompilerContext)>.Error(
ErrorOnNode(node: argument_node, withError: "Missing '(' in argument list component")) ErrorOnNode(
} node: node, withError: "Missing '(' in argument list component")))
currentChildIdx += 1 walker.next()
currentChildIdxSafe += 1
if argument_node.childCount < currentChildIdxSafe {
return .Error(
ErrorOnNode(node: argument_node, withError: "Missing argument list component"))
}
let currentChild = argument_node.child(at: currentChildIdx)
return argument_list_compiler(node: currentChild!, withContext: context) #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 { public struct CompilerContext {
let instances: VarTypeScopes let instances: VarTypeScopes
let types: TypeTypeScopes let types: TypeTypeScopes
let externs: TypeTypeScopes
let ffis: [P4FFI]
let expected_type: P4Type? let expected_type: P4Type?
let extern_context: Bool
public init(withInstances _instances: VarTypeScopes) { public init() {
instances = _instances instances = VarTypeScopes().enter()
types = TypeTypeScopes() types = TypeTypeScopes().enter()
externs = TypeTypeScopes().enter()
expected_type = .none expected_type = .none
extern_context = false
ffis = Array()
} }
public init(withInstances _instances: VarTypeScopes, withTypes _types: TypeTypeScopes) { public init(withInstances _instances: VarTypeScopes, withTypes _types: TypeTypeScopes) {
instances = _instances instances = _instances
types = _types types = _types
externs = TypeTypeScopes().enter()
expected_type = .none expected_type = .none
extern_context = false
ffis = Array()
} }
public init( public init(
withInstances _instances: VarTypeScopes, withTypes _types: TypeTypeScopes, withInstances _instances: VarTypeScopes, withTypes _types: TypeTypeScopes,
withExpectation expectation: P4Type? withExpectation expectation: P4Type?, withExtern extern: Bool,
withExterns externs: TypeTypeScopes, withFFIs foreigns: [P4FFI]
) { ) {
instances = _instances instances = _instances
types = _types types = _types
expected_type = expectation expected_type = expectation
extern_context = extern
self.externs = externs
ffis = foreigns
} }
/// Update a compiler context /// 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. /// - 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 { public func update(newInstances instances: VarTypeScopes) -> CompilerContext {
return 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 /// 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. /// - 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 { public func update(newTypes types: TypeTypeScopes) -> CompilerContext {
return 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 /// 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. /// - 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 { public func update(newExpectation expectation: P4Type?) -> CompilerContext {
return 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?> { ) -> Result<EvaluatableExpression?> {
let node = node.child(at: 0)! let node = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>( #SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: node, type: "identifier") node: node, type: "identifier")
guard guard
@@ -59,7 +59,7 @@ extension TypedIdentifier: CompilableLValueExpression {
) -> Result<EvaluatableLValueExpression?> { ) -> Result<EvaluatableLValueExpression?> {
let expression = node.child(at: 0)! let expression = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>( #SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: expression, type: "identifier") node: expression, type: "identifier")
let maybe_parsed_expression = TypedIdentifier.compile(node: node, withContext: context) let maybe_parsed_expression = TypedIdentifier.compile(node: node, withContext: context)
@@ -78,7 +78,7 @@ extension P4BooleanValue: CompilableExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableExpression?> { ) -> Result<EvaluatableExpression?> {
let node = node.child(at: 0)! let node = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>( #SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: node, type: "booleanLiteralExpression") node: node, type: "booleanLiteralExpression")
if node.text == "false" { if node.text == "false" {
@@ -97,7 +97,7 @@ extension P4IntValue: CompilableExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableExpression?> { ) -> Result<EvaluatableExpression?> {
let node = node.child(at: 0)! 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!) { if let parsed_int = Int(node.text!) {
return .Ok(P4Value(P4IntValue(withValue: parsed_int))) return .Ok(P4Value(P4IntValue(withValue: parsed_int)))
} else { } else {
@@ -111,7 +111,7 @@ extension P4StringValue: CompilableExpression {
node: SwiftTreeSitter.Node, withContext scopes: CompilerContext node: SwiftTreeSitter.Node, withContext scopes: CompilerContext
) -> Result<EvaluatableExpression?> { ) -> Result<EvaluatableExpression?> {
let node = node.child(at: 0)! let node = node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>( #SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: node, type: "string_literal") node: node, type: "string_literal")
return .Ok(P4Value(P4StringValue(withValue: node.text!))) return .Ok(P4Value(P4StringValue(withValue: node.text!)))
} }
@@ -339,53 +339,48 @@ extension BinaryOperatorExpression: CompilableExpression {
) -> Result<(EvaluatableExpression)?> { ) -> Result<(EvaluatableExpression)?> {
let expression = node.child(at: 0)! let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>( #SkipUnlessNodeType<Node>(
node: expression, type: "binaryOperatorExpression") node: expression, type: "binaryOperatorExpression")
var currentChildIdx = 0 let binary_operator_expression_node = expression.child(at: 0)!
var currentChildIdxSafe = 1 var walker = Walker(node: binary_operator_expression_node)
var currentChild: Node? = .none
if expression.childCount < currentChildIdxSafe { var current_node: Node? = .none
return Result.Error( #MustOr(
ErrorOnNode(node: node, withError: "Malformed binary operator expression")) result: current_node, thing: walker.getNext(),
} or: Result<EvaluatableExpression?>.Error(
currentChild = expression.child(at: currentChildIdx) 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 // swift-format-ignore
#RequireNodesType<Node, EvaluatableExpression?>( #RequireNodesType<Node, EvaluatableExpression?>(
nodes: binary_operator_expression_node, nodes: binary_operator_expression_node,
type: ["binaryEqualOperatorExpression", "binaryLessThanOperatorExpression", "binaryLessThanEqualOperatorExpression", "binaryGreaterThanOperatorExpression", "binaryGreaterThanEqualOperatorExpression", "binaryAndOperatorExpression", "binaryOrOperatorExpression", "binaryAddOperatorExpression", "binarySubtractOperatorExpression", "binaryMultiplyOperatorExpression", "binaryDivideOperatorExpression"], 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"]) 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 { let left_hand_side_raw = current_node!
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!
currentChildIdx = currentChildIdx + 1 walker.next()
currentChildIdxSafe = currentChildIdxSafe + 1 #MustOr(
if binary_operator_expression_node.childCount < currentChildIdxSafe { result: current_node, thing: walker.getNext(),
return Result.Error( or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(node: node, withError: "Missing binary operator for binary operator expression") ErrorOnNode(
) node: node, withError: "Missing binary operator for binary operator expression")))
}
currentChild = binary_operator_expression_node.child(at: currentChildIdx)
currentChildIdx = currentChildIdx + 1 walker.next()
currentChildIdxSafe = currentChildIdxSafe + 1 #MustOr(
if binary_operator_expression_node.childCount < currentChildIdxSafe { result: current_node, thing: walker.getNext(),
return Result.Error( or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(node: node, withError: "Missing binary operator for binary operator expression") ErrorOnNode(
) node: node, withError: "Missing RHS for binary operator expression")))
}
currentChild = binary_operator_expression_node.child(at: currentChildIdx) let right_hand_side_raw = current_node!
let right_hand_side_raw = currentChild!
let maybe_left_hand_side = Expression.Compile(node: left_hand_side_raw, withContext: context) 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 { guard case Result.Ok(let left_hand_side) = maybe_left_hand_side else {
@@ -465,54 +460,48 @@ extension BinaryOperatorExpression: CompilableExpression {
extension ArrayAccessExpression: CompilableExpression { extension ArrayAccessExpression: CompilableExpression {
static func compile( static func compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(any Common.EvaluatableExpression)?> { ) -> Common.Result<EvaluatableExpression?> {
let expression = node.child(at: 0)! let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>( #SkipUnlessNodeType<Node>(
node: expression, type: "arrayAccessExpression") node: expression, type: "arrayAccessExpression")
let array_access_expression_node = expression let array_access_expression_node = expression
var currentChildIdx = 0 var walker = Walker(node: array_access_expression_node)
var currentChildIdxSafe = 1 var current_node: Node? = .none
var currentChild: Node? = .none
// What is the "name" of the array? #MustOr(
if array_access_expression_node.childCount < currentChildIdxSafe { result: current_node, thing: walker.getNext(),
return Result.Error( or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(node: node, withError: "Malformed array access expression")) ErrorOnNode(
} node: node, withError: "Malformed array access expression")))
currentChild = expression.child(at: currentChildIdx)
#RequireNodeType<Node, EvaluatableExpression?>( #RequireNodeType<Node, EvaluatableExpression?>(
node: currentChild!, type: "expression", node: current_node!, type: "expression",
nice_type_name: "array identifier expression") nice_type_name: "array identifier expression")
let array_access_identifier_node = currentChild! let array_access_identifier_node = current_node!
// Check for the [ walker.next()
currentChildIdx = currentChildIdx + 1 #MustOr(
currentChildIdxSafe = currentChildIdxSafe + 1 result: current_node, thing: walker.getNext(),
if array_access_expression_node.childCount < currentChildIdxSafe { or: Result<EvaluatableExpression?>.Error(
return Result.Error( ErrorOnNode(
ErrorOnNode(node: node, withError: "Missing [ for array access expression") node: node, withError: "Missing [ for array access expression")))
)
}
// What is the indexor of the array? walker.next()
currentChildIdx = currentChildIdx + 1
currentChildIdxSafe = currentChildIdxSafe + 1 #MustOr(
if array_access_expression_node.childCount < currentChildIdxSafe { result: current_node, thing: walker.getNext(),
return Result.Error( or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(node: node, withError: "Missing indexor expression for array access expression") ErrorOnNode(
) node: node, withError: "Missing indexor expression for array access expression")))
}
currentChild = array_access_expression_node.child(at: currentChildIdx)
#RequireNodeType<Node, EvaluatableExpression?>( #RequireNodeType<Node, EvaluatableExpression?>(
node: currentChild!, type: "expression", node: current_node!, type: "expression",
nice_type_name: "array indexor 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( let maybe_array_identifier = Expression.Compile(
node: array_access_identifier_node, withContext: context) node: array_access_identifier_node, withContext: context)
@@ -547,51 +536,44 @@ extension FieldAccessExpression: CompilableExpression {
) -> Common.Result<(any Common.EvaluatableExpression)?> { ) -> Common.Result<(any Common.EvaluatableExpression)?> {
let expression = node.child(at: 0)! let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>( #SkipUnlessNodeType<Node>(
node: expression, type: "fieldAccessExpression") node: expression, type: "fieldAccessExpression")
let field_access_expression_node = expression let field_access_expression_node = expression
var currentChildIdx = 0 var walker = Walker(node: field_access_expression_node)
var currentChildIdxSafe = 1 var current_node: Node? = .none
var currentChild: Node? = .none
// What is the "name" of the struct? #MustOr(
if field_access_expression_node.childCount < currentChildIdxSafe { result: current_node, thing: walker.getNext(),
return Result.Error( or: Result<EvaluatableExpression?>.Error(
ErrorOnNode(node: node, withError: "Malformed field access expression")) ErrorOnNode(
} node: node, withError: "Malformed field access expression")))
currentChild = expression.child(at: currentChildIdx)
#RequireNodeType<Node, EvaluatableExpression?>( #RequireNodeType<Node, EvaluatableExpression?>(
node: currentChild!, type: "expression", node: current_node!, type: "expression",
nice_type_name: "struct identifier expression") nice_type_name: "struct identifier expression")
let struct_identifier_node = currentChild! let struct_identifier_node = current_node!
// Check for the . walker.next()
currentChildIdx = currentChildIdx + 1 #MustOr(
currentChildIdxSafe = currentChildIdxSafe + 1 result: current_node, thing: walker.getNext(),
if field_access_expression_node.childCount < currentChildIdxSafe { or: Result<EvaluatableExpression?>.Error(
return Result.Error( ErrorOnNode(
ErrorOnNode(node: node, withError: "Missing . for field access expression") node: node, withError: "Missing . for field access expression")))
)
}
// What is the field of the struct? walker.next()
currentChildIdx = currentChildIdx + 1 #MustOr(
currentChildIdxSafe = currentChildIdxSafe + 1 result: current_node, thing: walker.getNext(),
if field_access_expression_node.childCount < currentChildIdxSafe { or: Result<EvaluatableExpression?>.Error(
return Result.Error( ErrorOnNode(
ErrorOnNode(node: node, withError: "Missing field name for field access expression") node: node, withError: "Missing field name for field access expression")))
)
}
currentChild = field_access_expression_node.child(at: currentChildIdx)
#RequireNodeType<Node, EvaluatableExpression?>( #RequireNodeType<Node, EvaluatableExpression?>(
node: currentChild!, type: "identifier", node: current_node!, type: "identifier",
nice_type_name: "field name") 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. // Make sure that the identifier really identifies a struct.
let maybe_struct_identifier = Expression.Compile( let maybe_struct_identifier = Expression.Compile(
@@ -633,7 +615,7 @@ extension FieldAccessExpression: CompilableLValueExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableLValueExpression?> { ) -> Result<EvaluatableLValueExpression?> {
let expression = node.child(at: 0)! let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>( #SkipUnlessNodeType<Node>(
node: expression, type: "fieldAccessExpression") node: expression, type: "fieldAccessExpression")
let maybe_parsed_expression = FieldAccessExpression.compile(node: node, withContext: context) let maybe_parsed_expression = FieldAccessExpression.compile(node: node, withContext: context)
@@ -652,7 +634,7 @@ extension ArrayAccessExpression: CompilableLValueExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableLValueExpression?> { ) -> Result<EvaluatableLValueExpression?> {
let expression = node.child(at: 0)! let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>( #SkipUnlessNodeType<Node>(
node: expression, type: "arrayAccessExpression") node: expression, type: "arrayAccessExpression")
let maybe_parsed_expression = ArrayAccessExpression.compile(node: node, withContext: context) let maybe_parsed_expression = ArrayAccessExpression.compile(node: node, withContext: context)
@@ -670,52 +652,68 @@ extension FunctionCall: CompilableExpression {
static func compile( static func compile(
node: Node, withContext context: CompilerContext node: Node, withContext context: CompilerContext
) -> Result<EvaluatableExpression?> { ) -> Result<EvaluatableExpression?> {
let expression = node.child(at: 0)! let expression = node.child(at: 0)!
#SkipUnlessNodeType<Node, EvaluatableExpression?>( #SkipUnlessNodeType<Node>(
node: expression, type: "function_call") node: expression, type: "function_call")
var currentChildIdx = 0 var walker = Walker(node: expression)
var currentChildIdxSafe = 1 var current_node: Node? = .none
var currentChild: Node? = .none
if expression.childCount < currentChildIdxSafe { #MustOr(
return Result.Error( result: current_node, thing: walker.getNext(),
ErrorOnNode(node: node, withError: "Missing function call component")) or: Result<EvaluatableExpression?>.Error(
} ErrorOnNode(
node: node, withError: "Missing function call component")))
currentChild = expression.child(at: currentChildIdx)
let maybe_callee_name = Identifier.Compile( 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 { guard case .Ok(let callee_name) = maybe_callee_name else {
return Result.Error(maybe_callee_name.error()!) return Result.Error(maybe_callee_name.error()!)
} }
let maybe_callee = var maybe_callee: Result<(FunctionDeclaration?, Declaration?)> =
switch context.types.lookup(identifier: callee_name) { switch context.types.lookup(identifier: callee_name) {
case .Ok(let looked_up): case .Ok(let looked_up):
switch 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: default:
Result<FunctionDeclaration>.Error( Result<(FunctionDeclaration?, Declaration?)>.Error(
ErrorOnNode(node: currentChild!, withError: "\(callee_name) is not a function")) 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 { guard case .Ok(let callee) = maybe_callee else {
return .Error(maybe_callee.error()!) return .Error(maybe_callee.error()!)
} }
currentChildIdx += 1 walker.next()
currentChildIdxSafe += 1
if expression.childCount < currentChildIdxSafe {
return Result.Error(
ErrorOnNode(node: node, withError: "Missing function call component"))
}
currentChild = expression.child(at: currentChildIdx)
let maybe_argument_list = ArgumentList.Compile(node: currentChild!, withContext: context) #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 { guard case .Ok((let arguments, _)) = maybe_argument_list else {
return .Error(maybe_argument_list.error()!) return .Error(maybe_argument_list.error()!)
@@ -723,12 +721,35 @@ extension FunctionCall: CompilableExpression {
// Now, compare the arguments with the parameters: // 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) return .Error(e)
} }
// All good! // 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( static func Compile(
node: Node, withContext context: CompilerContext node: Node, withContext context: CompilerContext
) -> Result<(InstantiatedParserState, CompilerContext)> { ) -> Result<(InstantiatedParserState, CompilerContext)> {
var walker = Walker(node: node)
var currentChildIdx = 0 var current_node: Node? = .none
var currentChildIdxSafe = 1
var currentChild: Node? = .none
guard let node_type = node.nodeType, guard let node_type = node.nodeType,
node_type == "parserState" node_type == "parserState"
@@ -192,50 +190,50 @@ public struct Parser {
ErrorOnNode(node: node, withError: "Did not find a parser state declaration")) ErrorOnNode(node: node, withError: "Did not find a parser state declaration"))
} }
if node.childCount < currentChildIdxSafe { #MustOr(
return Result.Error( result: current_node, thing: walker.getNext(),
ErrorOnNode(node: node, withError: "Missing elements in parser state declaration")) or: Result<(InstantiatedParserState, CompilerContext)>.Error(
} ErrorOnNode(
node: node, withError: "Missing elements in parser state declaration")))
currentChild = node.child(at: currentChildIdx) if current_node!.nodeType == "annotations" {
if currentChild!.nodeType == "annotations" {
return Result.Error( return Result.Error(
ErrorOnNode( 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. // Would increment here.
} }
// Skip the keyword state // Skip the keyword state
currentChildIdx += 1 walker.next()
currentChildIdxSafe += 1 #MustOr(
if node.childCount < currentChildIdxSafe { result: current_node, thing: walker.getNext(),
return Result.Error( or: Result<(InstantiatedParserState, CompilerContext)>.Error(
ErrorOnNode(node: node, withError: "Missing elements in parser state declaration")) ErrorOnNode(
} node: node, withError: "Missing elements in parser state declaration")))
currentChild = node.child(at: currentChildIdx)
let maybe_state_identifier = Identifier.Compile( 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 { guard case Result.Ok(let state_identifier) = maybe_state_identifier else {
return Result.Error(maybe_state_identifier.error()!) return Result.Error(maybe_state_identifier.error()!)
} }
walker.next()
// Skip the '{' // Skip the '{'
currentChildIdx += 2 walker.next()
currentChildIdxSafe += 2 #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 parse_errs: [Error] = Array()
var current_context = context var current_context = context
var parsed_s: [EvaluatableStatement] = Array() var parsed_s: [EvaluatableStatement] = Array()
if node.childCount < currentChildIdxSafe { if current_node!.nodeType == "parserStatements" {
return Result.Error(ErrorOnNode(node: node, withError: "Missing body of state declaration"))
}
currentChild = node.child(at: currentChildIdx)
if currentChild!.nodeType == "parserStatements" {
switch Statements.Compile( switch Statements.Compile(
node: currentChild!, withContext: current_context) node: current_node!, withContext: current_context)
{ {
case .Ok(let (state_statements, updated_context)): case .Ok(let (state_statements, updated_context)):
parsed_s = state_statements parsed_s = state_statements
@@ -243,8 +241,7 @@ public struct Parser {
case .Error(let error): case .Error(let error):
parse_errs.append(error) parse_errs.append(error)
} }
currentChildIdx += 1 walker.next()
currentChildIdxSafe += 1
} }
if !parse_errs.isEmpty { if !parse_errs.isEmpty {
@@ -255,13 +252,14 @@ public struct Parser {
}.joined(separator: ";"))) }.joined(separator: ";")))
} }
if node.childCount < currentChildIdxSafe { #MustOr(
return Result.Error( result: current_node, thing: walker.getNext(),
ErrorOnNode(node: node, withError: "Missing transition statement of state declaration")) or: Result<(InstantiatedParserState, CompilerContext)>.Error(
} ErrorOnNode(
currentChild = node.child(at: currentChildIdx) node: node, withError: "Missing transition statement of state declaration")))
return TransitionStatement.Compile( return TransitionStatement.Compile(
node: currentChild!, forState: state_identifier, withStatements: parsed_s, node: current_node!, forState: state_identifier, withStatements: parsed_s,
withContext: current_context) withContext: current_context)
} }
} }
@@ -277,7 +275,7 @@ public struct Parser {
var error: Error? = .none var error: Error? = .none
var current_context = context var current_context = context
// TODO: Assert that there is only one. /// TODO: Assert that there is only one.
node.enumerateNamedChildren { parser_state in node.enumerateNamedChildren { parser_state in
if parser_state.nodeType != "parserState" { if parser_state.nodeType != "parserState" {
return return
+15 -5
View File
@@ -24,18 +24,19 @@ import TreeSitterP4
public struct Program { public struct Program {
public static func Compile(_ source: String) -> Result<P4Lang.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( public static func Compile(
_ source: String, withGlobalInstances globalInstances: VarTypeScopes _ source: String, withGlobalInstances globalInstances: VarTypeScopes
) -> Result<P4Lang.Program> { ) -> Result<P4Lang.Program> {
return Program.Compile(source, withGlobalInstances: globalInstances, withGlobalTypes: .none) return Program.Compile(
source, withGlobalInstances: globalInstances, withGlobalTypes: .none, withFFIs: [])
} }
public static func Compile( public static func Compile(
_ source: String, withGlobalInstances globalInstances: VarTypeScopes?, _ source: String, withGlobalInstances globalInstances: VarTypeScopes?,
withGlobalTypes globalTypes: TypeTypeScopes? withGlobalTypes globalTypes: TypeTypeScopes?, withFFIs ffis: [P4FFI] = Array()
) -> Result<P4Lang.Program> { ) -> Result<P4Lang.Program> {
let maybe_parser = ConfigureP4Parser() let maybe_parser = ConfigureP4Parser()
@@ -54,8 +55,10 @@ public struct Program {
var program = P4Lang.Program() var program = P4Lang.Program()
// Set up a context for parsing. // Set up a context for parsing.
var compilation_context = CompilerContext( var compilation_context = CompilerContext()
withInstances: VarTypeScopes().enter(), withTypes: TypeTypeScopes().enter())
// Add our FFIs
compilation_context = compilation_context.update(newFFIs: ffis)
var errors: [Error] = Array() var errors: [Error] = Array()
@@ -120,6 +123,13 @@ public struct Program {
compilation_context.types.map { (_, v) in compilation_context.types.map { (_, v) in
v 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) return Result.Ok(program)
} }
} }
+5 -1
View File
@@ -39,9 +39,13 @@ public protocol CompilableType {
} }
public protocol CompilableDeclaration { 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( static func Compile(
node: Node, withContext context: CompilerContext node: Node, withContext context: CompilerContext
) -> Result<(P4DataType, CompilerContext)?> ) -> Result<(Declaration, CompilerContext)?>
} }
public protocol Compilable<T> { public protocol Compilable<T> {
+25 -28
View File
@@ -29,34 +29,32 @@ extension BlockStatement: CompilableStatement {
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>( #RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
node: node, type: "blockStatement", nice_type_name: "block statement") node: node, type: "blockStatement", nice_type_name: "block statement")
var currentChildIdx = 0 var walker = Walker(node: node)
var currentChildIdxSafe = 1 var current_node: Node? = .none
var currentChild: 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( 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 statements: [EvaluatableStatement] = Array()
var parse_err: Error? = .none var parse_err: Error? = .none
var current_context = context var current_context = context
if node.childCount < currentChildIdxSafe { walker.next()
return Result.Error( #MustOr(
ErrorOnNode(node: node, withError: "Malformed block statement")) result: current_node, thing: walker.getNext(),
} or: Result<(EvaluatableStatement, CompilerContext)>.Error(
currentChild = node.child(at: currentChildIdx) ErrorOnNode(node: node, withError: "Malformed block statement")))
if currentChild!.nodeType == "statements" {
if current_node!.nodeType == "statements" {
switch Parser.Statements.Compile( switch Parser.Statements.Compile(
node: currentChild!, withContext: current_context) node: current_node!, withContext: current_context)
{ {
case .Ok(let (parsed_statements, updated_context)): case .Ok(let (parsed_statements, updated_context)):
current_context = updated_context current_context = updated_context
@@ -65,22 +63,21 @@ extension BlockStatement: CompilableStatement {
parse_err = error parse_err = error
} }
currentChildIdx += 1 walker.next()
currentChildIdxSafe += 1
} }
if let err = parse_err { if let err = parse_err {
return .Error(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( 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"))
} }
return .Ok((BlockStatement(statements), current_context)) 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 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 { public var description: String {
return "Action: " return "Action: "
+ "\(self.name) with parameters \(self.params) and body \(String(describing: self.body))" + "\(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 var name: Identifier
public init( public init(
named name: Identifier, withParameters parameters: ParameterList, named name: Identifier = Identifier(name: ""), withParameters parameters: ParameterList = ParameterList([]),
withBody body: BlockStatement? withBody body: BlockStatement? = .none
) { ) {
self.name = name self.name = name
self.params = parameters self.params = parameters
@@ -88,15 +138,24 @@ public struct TableKeys: CustomStringConvertible {
} }
} }
/// TODO public struct TableActionsProperty: CustomStringConvertible {
public struct TableActions { public let actions: [TypedIdentifier]
public init() {} 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 { public struct TablePropertyList: CustomStringConvertible {
let actions: TableActions let actions: TableActionsProperty
let keys: TableKeys let keys: TableKeys
public init(withActions actions: TableActions, withKeys keys: TableKeys) { public init(withActions actions: TableActionsProperty, withKeys keys: TableKeys) {
self.actions = actions self.actions = actions
self.keys = keys self.keys = keys
} }
@@ -208,7 +267,8 @@ public struct Control: P4DataType, P4DataValue, Equatable, CustomStringConvertib
withParameters: ParameterList(), withParameters: ParameterList(),
withTable: Table( withTable: Table(
withName: Identifier(name: "empty"), withName: Identifier(name: "empty"),
withPropertyList: TablePropertyList(withActions: TableActions(), withKeys: TableKeys())), withPropertyList: TablePropertyList(
withActions: TableActionsProperty(), withKeys: TableKeys())),
withActions: Actions(withActions: []), withApply: ApplyStatement()) withActions: Actions(withActions: []), withApply: ApplyStatement())
} }
+42 -3
View File
@@ -17,7 +17,46 @@
import Common 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 struct FunctionDeclaration: P4DataType, P4DataValue {
public func type() -> any Common.P4DataType { 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)" 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 params: ParameterList
public var name: Identifier public var name: Identifier
public var tipe: P4Type public var tipe: P4Type
public init( public init(
named name: Identifier, ofType type: P4Type, withParameters parameters: ParameterList, named name: Identifier, ofType type: P4Type, withParameters parameters: ParameterList,
withBody body: EvaluatableStatement? withBody body: BlockStatement?
) { ) {
self.name = name self.name = name
self.tipe = type self.tipe = type
+11 -2
View File
@@ -128,11 +128,20 @@ public struct FieldAccessExpression {
} }
public struct FunctionCall { public struct FunctionCall {
public let callee: FunctionDeclaration public let callee: (FunctionDeclaration?, P4FFI?)
public let arguments: ArgumentList public let arguments: ArgumentList
public let return_type: P4DataType
public init(_ callee: FunctionDeclaration, withArguments arguments: ArgumentList) { public init(_ callee: FunctionDeclaration, withArguments arguments: ArgumentList) {
self.callee = callee self.callee = (callee, .none)
self.arguments = arguments 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 struct Program {
public var types: [P4DataType] = Array() public var types: [P4DataType] = Array()
public var externs: [P4DataType] = Array()
public var instances: [P4Type] = Array() public var instances: [P4Type] = Array()
/// Type of closure for filtering results from ``Program/InstancesWithTypes(_:)`` /// 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(_:)`` /// 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. /// Retrieve global instances in the compiled P4 program.
public func InstancesWithTypes() -> [P4Type] { public func InstancesWithTypes() -> [P4Type] {
@@ -51,7 +52,7 @@ public struct Program {
/// ///
/// @Snippet(path: "use-program-instanceswithtypes", slice: "include") /// @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 return self.instances.filter { instance in
filter(instance) filter(instance)
} }
@@ -74,12 +75,35 @@ public struct Program {
/// ///
/// @Snippet(path: "use-program-typeswithtypes", slice: "include") /// @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 return self.types.filter { instance in
filter(instance) 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 /// Find the program's main parser
/// ///
/// Note: For now, the main parser is expected to be named 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_idx = argument.index
let arg_value = argument.argument let arg_value = argument.argument
//let maybe_argument_value = arg_value.evaluate(execution: called_execution) //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 { guard case (.Ok(let argument_value), let updated_execution) = maybe_argument_value else {
return ( return (
.Error(Error(withMessage: "Cannot evaluate argument \(arg_idx): \(argument)")), .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)) 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) { switch execution.evaluator.EvaluateExpression(self.selector, inExecution: execution) {
case (.Ok(let selector_value), let updated_execution): case (.Ok(let selector_value), let updated_execution):
for sce in self.case_expressions { for sce in self.case_expressions {
if case (.Ok(let kse), let updated_execution) = updated_execution.evaluator.EvaluateExpression( if case (.Ok(let kse), let updated_execution) = updated_execution.evaluator
sce.key, inExecution: updated_execution), .EvaluateExpression(
sce.key, inExecution: updated_execution),
kse.eq(selector_value) kse.eq(selector_value)
{ {
//let result = sce.evaluate(execution: updated_execution) //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 return result
} }
} }
@@ -212,13 +214,15 @@ extension BinaryOperatorExpression: EvaluatableExpression {
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) { public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
let updated_execution = execution let updated_execution = execution
//let maybe_evaluated_left = self.left.evaluate(execution: updated_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 { guard case (.Ok(let evaluated_left), let updated_execution) = maybe_evaluated_left else {
return maybe_evaluated_left return maybe_evaluated_left
} }
//let maybe_evaluated_right = self.right.evaluate(execution: updated_execution) //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 { guard case (.Ok(let evaluated_right), let updated_execution) = maybe_evaluated_right else {
return maybe_evaluated_right return maybe_evaluated_right
} }
@@ -235,13 +239,15 @@ extension ArrayAccessExpression: EvaluatableExpression {
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) { public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
let updated_execution = execution let updated_execution = execution
//let maybe_name = self.name.evaluate(execution: updated_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 { guard case (.Ok(let name), let updated_execution) = maybe_name else {
return maybe_name return maybe_name
} }
//let maybe_indexor = self.indexor.evaluate(execution: updated_execution) //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 { guard case (.Ok(let indexor), let updated_execution) = maybe_indexor else {
return maybe_indexor return maybe_indexor
} }
@@ -271,7 +277,8 @@ extension ArrayAccessExpression: EvaluatableLValueExpression {
let updated_execution = execution let updated_execution = execution
//let maybe_value = self.name.evaluate(execution: updated_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 { guard case (.Ok(let value), let updated_execution) = maybe_value else {
return .Error( return .Error(
Error(withMessage: "\(self.name) cannot be evaluated: \(maybe_value.0.error()!)")) Error(withMessage: "\(self.name) cannot be evaluated: \(maybe_value.0.error()!)"))
@@ -282,7 +289,8 @@ extension ArrayAccessExpression: EvaluatableLValueExpression {
// Now, get the indexor! // Now, get the indexor!
//let maybe_indexor_value = self.indexor.evaluate(execution: updated_execution) //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 { guard case (.Ok(let indexor_value), let updated_execution) = maybe_indexor_value else {
return Result.Error( return Result.Error(
Error(withMessage: "\(self.indexor) cannot be evaluated: \(maybe_indexor_value.0.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 updated_execution = execution
//let maybe_struct = self.strct.evaluate(execution: updated_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 { guard case (.Ok(let strct), let updated_execution) = maybe_struct else {
return maybe_struct return maybe_struct
} }
@@ -358,7 +367,7 @@ extension FieldAccessExpression: EvaluatableExpression {
return (.Error(Error(withMessage: "\(strct) does not identify a struct")), updated_execution) return (.Error(Error(withMessage: "\(strct) does not identify a struct")), updated_execution)
} }
// TODO: Create a default value? /// TODO: Create a default value?
guard let value = struct_strct.get(field: self.field) else { guard let value = struct_strct.get(field: self.field) else {
return (.Error(Error(withMessage: "Missing value")), updated_execution) return (.Error(Error(withMessage: "Missing value")), updated_execution)
} }
@@ -384,7 +393,8 @@ extension FieldAccessExpression: EvaluatableLValueExpression {
let updated_execution = execution let updated_execution = execution
// First, evaluate strct_id and make sure that it names a struct. // First, evaluate strct_id and make sure that it names a struct.
//let maybe_value = self.strct.evaluate(execution: updated_execution) //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 { guard case (.Ok(let value), let updated_execution) = maybe_value else {
return .Error( return .Error(
Error(withMessage: "\(self.strct) cannot be evaluated: \(maybe_value.0.error()!)")) Error(withMessage: "\(self.strct) cannot be evaluated: \(maybe_value.0.error()!)"))
@@ -467,33 +477,55 @@ extension FunctionCall: EvaluatableExpression {
execution: Common.ProgramExecution execution: Common.ProgramExecution
) -> (Common.Result<P4Value>, ProgramExecution) { ) -> (Common.Result<P4Value>, ProgramExecution) {
guard let body = self.callee.body else { let body_params:
return ( Result<((ProgramExecution) -> (ControlFlow, ProgramExecution), ParameterList)> =
.Error(Error(withMessage: "No body for called function (\(self.callee.name))")), execution 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) = { let call_body: (ProgramExecution) -> (Result<P4Value>, ProgramExecution) = {
(execution: ProgramExecution) in (execution: ProgramExecution) in
let (control_flow, updated_execution) = body.evaluate(execution: execution) let (control_flow, updated_execution) = body.0(execution)
return switch control_flow { return switch control_flow {
case ControlFlow.Return(.some(let value)): (.Ok(value), updated_execution) case ControlFlow.Return(.some(let value)): (.Ok(value), updated_execution)
default: default:
( (
.Error( .Error(
Error(withMessage: "No value returned from called function (\(self.callee.name))")), Error(withMessage: "No value returned from called function")),
execution execution
) )
} }
} }
return Call( return Call(
body: call_body, withArguments: self.arguments, withParameters: self.callee.params, body: call_body, withArguments: self.arguments, withParameters: body.1,
inExecution: execution) inExecution: execution)
} }
public func type() -> P4Type { 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) { public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
let updated_execution = execution let updated_execution = execution
//let result = self.value.evaluate(execution: updated_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 { guard case (.Ok(let value), let updated_execution) = result else {
return (ControlFlow.Error, execution.setError(error: result.0.error()!)) return (ControlFlow.Error, execution.setError(error: result.0.error()!))
} }
@@ -44,11 +45,8 @@ extension ParserStateDirectTransition: EvaluatableParserState {
) -> (any EvaluatableParserState, Common.ProgramExecution) { ) -> (any EvaluatableParserState, Common.ProgramExecution) {
var program = program.enter_scope() var program = program.enter_scope()
let (control_flow, next_execution) = program.evaluator.ExecuteStatement( let (control_flow, next_execution) = program.evaluator.ExecuteStatements(
statements, statements, inExecution: program)
handleResult: { (control_flow, execution) in
return (control_flow, execution)
}, inExecution: program)
switch control_flow { switch control_flow {
case .Next: program = next_execution case .Next: program = next_execution
@@ -105,12 +103,8 @@ extension ParserStateSelectTransition: EvaluatableParserState {
) -> (any EvaluatableParserState, Common.ProgramExecution) { ) -> (any EvaluatableParserState, Common.ProgramExecution) {
var program = program.enter_scope() var program = program.enter_scope()
let (control_flow, next_execution) = program.evaluator.ExecuteStatement( let (control_flow, next_execution) = program.evaluator.ExecuteStatements(
statements, statements, inExecution: program)
handleResult: { (control_flow, execution) in
return (control_flow, execution)
}, inExecution: program)
switch control_flow { switch control_flow {
case .Next: program = next_execution case .Next: program = next_execution
case .Error: return (reject, next_execution.exit_scope()) case .Error: return (reject, next_execution.exit_scope())
@@ -150,7 +144,7 @@ extension ParserStateSelectTransition: EvaluatableParserState {
extension Parser: LibraryCallable { extension Parser: LibraryCallable {
public typealias T = InstantiatedParserState public typealias T = InstantiatedParserState
public func call( public func call(
execution: Common.ProgramExecution, arguments: P4Lang.ArgumentList execution: Common.ProgramExecution, arguments: ArgumentList
) -> (P4Lang.InstantiatedParserState, Common.ProgramExecution) { ) -> (P4Lang.InstantiatedParserState, Common.ProgramExecution) {
var execution = execution.enter_scope() var execution = execution.enter_scope()
+15 -16
View File
@@ -20,22 +20,21 @@ import P4Lang
extension BlockStatement: EvaluatableStatement { extension BlockStatement: EvaluatableStatement {
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) { public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
return execution.evaluator.ExecuteStatement( return execution.evaluator.ExecuteStatements(
self.statements, self.statements, inExecution: execution
handleResult: { (cf, execution) in ) { (cf, execution) in
switch cf { switch cf {
case ControlFlow.Return(let value): return (ControlFlow.Return(value), execution) case ControlFlow.Return(let value): return (ControlFlow.Return(value), execution)
case ControlFlow.Next: return (cf, execution) case ControlFlow.Next: return (cf, execution)
case ControlFlow.Error: return (ControlFlow.Error, execution) case ControlFlow.Error: return (ControlFlow.Error, execution)
default: default:
return ( return (
ControlFlow.Error, ControlFlow.Error,
execution.setError( execution.setError(
error: Error(withMessage: "Invalid control flow \(cf) in block statement")) error: Error(withMessage: "Invalid control flow \(cf) in block statement"))
) )
} }
}, }
inExecution: execution)
} }
} }
+169
View File
@@ -89,6 +89,113 @@ import P4Lang
#expect(program.InstancesWithTypes(filter).count == 2) #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 { @Test func test_simple_control_declaration_with_parameters() async throws {
let simple_parser_declaration = """ let simple_parser_declaration = """
control simple(bool x, bool y) { control simple(bool x, bool y) {
@@ -107,6 +214,36 @@ import P4Lang
#expect(#RequireOkResult(Program.Compile(simple_parser_declaration))) #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 { @Test func test_simple_control_declaration_with_action_using_parameter() async throws {
let simple_parser_declaration = """ let simple_parser_declaration = """
control simple(bool x, bool y) { control simple(bool x, bool y) {
@@ -150,4 +287,36 @@ import P4Lang
), ),
Program.Compile(simple_parser_declaration)) 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( #RequireErrorResult(
Error( Error(
withMessage: 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)) Program.Compile(simple_parser_declaration))
) )
@@ -196,7 +196,7 @@ import TreeSitterP4
#RequireErrorResult( #RequireErrorResult(
Error( Error(
withMessage: 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)) 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)>( #RequireErrorResult<(EvaluatableStatement, CompilerContext)>(
Error(withMessage: "{2, 154}: Did not find assignment statement"), Error(withMessage: "{2, 154}: Did not find assignment statement"),
ParserAssignmentStatement.Compile( // Note: Calling ParserAssignmentStatement compilation directly. 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 { @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), instantiation: $ => seq($.typeRef, '(', optional($.parameter_list), ')', $.identifier),
// Declarations // 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), type_declaration: $=> choice($.struct_declaration),
struct_declaration: $ => seq($.struct, $.identifier, '{', optional($.struct_declaration_fields), '}'), struct_declaration: $ => seq($.struct, $.identifier, '{', optional($.struct_declaration_fields), '}'),
struct_declaration_fields: $=> repeat1(seq($.variableDeclaration)), 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. // 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), 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_property_list: $ => repeat1(choice($.table_keys, $.table_actions)),
table_keys: $=> seq($.key, '=', '{', repeat($.table_key_entry), '}'), table_keys: $=> seq($.key, '=', '{', repeat($.table_key_entry), '}'),
table_key_entry: $=> seq($.keysetExpression, ':', $.table_key_match_type, $._semicolon), 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 // match types
table_key_match_type: $ => choice($.exact), // support exact only for now. 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) (identifier)
(parameters) (parameters)
(statement (blockStatement
(blockStatement (statements
(statements (statement
(statement (variableDeclaration
(variableDeclaration (typeRef
(typeRef (baseType
(baseType (string)
(string)
)
) )
(identifier)
) )
(identifier)
) )
) )
) )
@@ -340,18 +338,16 @@ bool functionb(bool a, int b) {
) )
) )
) )
(statement (blockStatement
(blockStatement (statements
(statements (statement
(statement (variableDeclaration
(variableDeclaration (typeRef
(typeRef (baseType
(baseType (string)
(string)
)
) )
(identifier)
) )
(identifier)
) )
) )
) )
@@ -416,17 +412,78 @@ bool functionb(in bool a, out int b, inout string c) {
) )
) )
) )
(statement (blockStatement
(blockStatement (statements
(statements (statement
(statement (variableDeclaration
(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 (typeRef
(baseType (baseType
(string) (string)
) )
) )
(identifier) (identifier)
) )
) )
) )
+7 -9
View File
@@ -43,15 +43,13 @@ int fun() {
) )
(identifier) (identifier)
(parameters) (parameters)
(statement (blockStatement
(blockStatement (statements
(statements (statement
(statement (return_statement
(return_statement (expression
(expression (simple_expression
(simple_expression (integer)
(integer)
)
) )
) )
) )