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