Compare commits
24 Commits
041009a22e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ef6b07b54a | |||
| aa12974dd6 | |||
| d22776b018 | |||
| d7022725ed | |||
| 6c23cf7458 | |||
| e17533dfc8 | |||
| fe88e447a9 | |||
| 3769a78b92 | |||
| b9ff228362 | |||
| 6908d9a91d | |||
| 6882a32858 | |||
| 3ee1eab2f8 | |||
| d2797e1acc | |||
| 4f6de341cc | |||
| 297288e2b0 | |||
| 44e93e4cda | |||
| 18461a9215 | |||
| ecc38096b8 | |||
| c8d4d4fc65 | |||
| 294f76acd4 | |||
| 61d8f601e8 | |||
| 925f20a13b | |||
| 97a672bd6d | |||
| bc51b4e280 |
+4
-4
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "ef9c8ff40b986db3a591fe3c50d79e4fe2a9e11749d84a72fbe97babc9ba3683",
|
"originHash" : "87d6d60d6a77921d74305adbe82f77e088a6e06d44cb3db30aa4d25c864181fd",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "swift-argument-parser",
|
"identity" : "swift-argument-parser",
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/apple/swift-docc-plugin",
|
"location" : "https://github.com/apple/swift-docc-plugin",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "3e4f133a77e644a5812911a0513aeb7288b07d06",
|
"revision" : "647c708be89f834fa6a6d4945442793a77ddf5b6",
|
||||||
"version" : "1.4.5"
|
"version" : "1.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
"location" : "https://github.com/tree-sitter/swift-tree-sitter",
|
"location" : "https://github.com/tree-sitter/swift-tree-sitter",
|
||||||
"state" : {
|
"state" : {
|
||||||
"branch" : "main",
|
"branch" : "main",
|
||||||
"revision" : "769f4770fe8197e2f4ce810375e9b21398ae36d5"
|
"revision" : "f97df585296977d8fcaf644cbde567151d1367b8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+13
-29
@@ -10,24 +10,16 @@ let package = Package(
|
|||||||
products: [
|
products: [
|
||||||
// Products define the executables and libraries a package produces, making them visible to other packages.
|
// Products define the executables and libraries a package produces, making them visible to other packages.
|
||||||
.library(
|
.library(
|
||||||
name: "P4Compiler",
|
name: "P4Parser",
|
||||||
targets: ["P4Compiler"]
|
targets: ["P4Parser"]
|
||||||
),
|
),
|
||||||
.library(
|
.library(
|
||||||
name: "Common",
|
name: "Common",
|
||||||
targets: ["Common"]
|
targets: ["Common"]
|
||||||
),
|
),
|
||||||
.library(
|
.library(
|
||||||
name: "P4Lang",
|
name: "P4CodeGen",
|
||||||
targets: ["P4Lang"]
|
targets: ["P4CodeGen"]
|
||||||
),
|
|
||||||
.library(
|
|
||||||
name: "P4Runtime",
|
|
||||||
targets: ["P4Runtime"]
|
|
||||||
),
|
|
||||||
.executable(
|
|
||||||
name: "p4ce",
|
|
||||||
targets: ["Cli"]
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
@@ -37,6 +29,7 @@ let package = Package(
|
|||||||
.package(url: "https://github.com/swiftlang/swift-syntax", from: "602.0.0"),
|
.package(url: "https://github.com/swiftlang/swift-syntax", from: "602.0.0"),
|
||||||
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
|
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
|
||||||
.package(url: "https://github.com/apple/swift-system", from: "1.6.1"),
|
.package(url: "https://github.com/apple/swift-system", from: "1.6.1"),
|
||||||
|
//.package(url: "https://github.com/apple/swift-protobuf.git", from: "1.27.0"),
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.macro(
|
.macro(
|
||||||
@@ -47,15 +40,13 @@ let package = Package(
|
|||||||
],
|
],
|
||||||
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")]),
|
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")]),
|
||||||
.target(
|
.target(
|
||||||
name: "P4Compiler",
|
name: "P4Parser",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.product(name: "SwiftTreeSitter", package: "swift-tree-sitter"),
|
.product(name: "SwiftTreeSitter", package: "swift-tree-sitter"),
|
||||||
.product(name: "SwiftTreeSitterLayer", package: "swift-tree-sitter"),
|
.product(name: "SwiftTreeSitterLayer", package: "swift-tree-sitter"),
|
||||||
.product(name: "TreeSitterP4", package: "tree-sitter-p4"),
|
.product(name: "TreeSitterP4", package: "tree-sitter-p4"),
|
||||||
.target(name: "TreeSitterExtensions"),
|
.target(name: "TreeSitterExtensions"),
|
||||||
.target(name: "Common"),
|
.target(name: "Common"),
|
||||||
.target(name: "P4Lang"),
|
|
||||||
.target(name: "P4Runtime"),
|
|
||||||
],
|
],
|
||||||
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")],
|
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")],
|
||||||
),
|
),
|
||||||
@@ -71,32 +62,25 @@ let package = Package(
|
|||||||
dependencies: [
|
dependencies: [
|
||||||
"Macros",
|
"Macros",
|
||||||
.product(name: "SystemPackage", package: "swift-system"),
|
.product(name: "SystemPackage", package: "swift-system"),
|
||||||
|
.product(name: "SwiftTreeSitter", package: "swift-tree-sitter"),
|
||||||
|
|
||||||
],
|
],
|
||||||
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")],
|
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")],
|
||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "P4Lang",
|
name: "P4CodeGen",
|
||||||
dependencies: ["Common"]
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "P4Runtime",
|
|
||||||
dependencies: ["P4Lang", "Common"]
|
|
||||||
),
|
|
||||||
.executableTarget(
|
|
||||||
name: "Cli",
|
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"Common", "P4Lang", "P4Compiler", "P4Runtime", "Macros",
|
"P4Parser", "Common",
|
||||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
//.product(name: "SwiftProtobuf", package: "swift-protobuf"),
|
||||||
],
|
]
|
||||||
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")],
|
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "Tests",
|
name: "Tests",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"P4Compiler", "P4Runtime", "P4Lang", "Macros", "TreeSitterExtensions", "Common",
|
"P4Parser", "P4CodeGen", "Macros", "TreeSitterExtensions", "Common",
|
||||||
.product(name: "SystemPackage", package: "swift-system"),
|
.product(name: "SystemPackage", package: "swift-system"),
|
||||||
],
|
],
|
||||||
|
//swiftSettings: [.enableExperimentalFeature("CodeItemMacros"), .unsafeFlags(["-Xfrontend", "-dump-macro-expansions"])],
|
||||||
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")],
|
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,50 +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
|
|
||||||
import Foundation
|
|
||||||
import Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
import P4Lang
|
|
||||||
import P4Compiler
|
|
||||||
|
|
||||||
let p4_program_with_control_decl = """
|
|
||||||
control simple() {
|
|
||||||
action a() {
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
true: exact;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
// snippet.include
|
|
||||||
let flter = { (tipe: P4QualifiedType) -> Bool in
|
|
||||||
switch tipe.baseType(){
|
|
||||||
case let c as Control: c.name == "simple"
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if case .Ok(let program) = Program.Compile(p4_program_with_control_decl) {
|
|
||||||
print(program.InstancesWithTypes(flter))
|
|
||||||
}
|
|
||||||
@@ -1,44 +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
|
|
||||||
import Foundation
|
|
||||||
import Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
import P4Lang
|
|
||||||
import P4Compiler
|
|
||||||
|
|
||||||
let p4_program_with_struct_decl = """
|
|
||||||
struct agg {
|
|
||||||
int x;
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
// snippet.include
|
|
||||||
let flter = { (tipe: P4Type) -> Bool in
|
|
||||||
switch tipe {
|
|
||||||
case let c as P4Struct: c.name == "agg"
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if case .Ok(let program) = Program.Compile(p4_program_with_struct_decl) {
|
|
||||||
print(program.TypesWithTypes(flter))
|
|
||||||
}
|
|
||||||
|
|
||||||
+11
-9
@@ -94,7 +94,7 @@ extension Cli {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let maybe_program = Program.Compile(source.getSource())
|
let maybe_program = SpecialCompilers.ProgramCompiler.Compile(source.getSource())
|
||||||
guard case .Ok(_) = maybe_program else {
|
guard case .Ok(_) = maybe_program else {
|
||||||
let feedback = CompilationFeedback(source, [maybe_program.error()!], formatter)
|
let feedback = CompilationFeedback(source, [maybe_program.error()!], formatter)
|
||||||
print(feedback.feedback)
|
print(feedback.feedback)
|
||||||
@@ -134,20 +134,22 @@ extension Cli {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let maybe_program = Program.Compile(source.getSource())
|
let maybe_program = SpecialCompilers.ProgramCompiler.Compile(source.getSource())
|
||||||
guard case .Ok(let program) = maybe_program else {
|
guard case .Ok(let program) = maybe_program else {
|
||||||
print(ErrorWithLabel("Compiler Error", maybe_source.error()!).format(formatter))
|
print(ErrorWithLabel("Compiler Error", maybe_source.error()!).format(formatter))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let maybe_codegen = P4Runtime.CodeGenerator().codeGen(program)
|
/*
|
||||||
guard case .Ok(let codegen) = maybe_codegen else {
|
let maybe_codegen = P4Runtime.CodeGenerator().codeGen(program)
|
||||||
let formatter = FormatterAnsi()
|
guard case .Ok(let codegen) = maybe_codegen else {
|
||||||
print(ErrorWithLabel("Code Generation Error", maybe_codegen.error()!).format(formatter))
|
let formatter = FormatterAnsi()
|
||||||
return
|
print(ErrorWithLabel("Code Generation Error", maybe_codegen.error()!).format(formatter))
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
|
||||||
print("\(codegen.getGeneratedCode())")
|
print("\(codegen.getGeneratedCode())")
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,134 +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/>.
|
|
||||||
|
|
||||||
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: P4QualifiedType
|
|
||||||
|
|
||||||
public init(
|
|
||||||
identifier: Identifier, withType type: P4QualifiedType
|
|
||||||
) {
|
|
||||||
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.baseType().eq(rhs: self.type.baseType())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +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/>.
|
|
||||||
|
|
||||||
/// A scope that resolves variable identifiers to their types.
|
|
||||||
public typealias VarTypeScope = Scope<P4QualifiedType>
|
|
||||||
|
|
||||||
/// Scopes that resolve variable identifiers to their types.
|
|
||||||
public typealias VarTypeScopes = Scopes<P4QualifiedType>
|
|
||||||
|
|
||||||
/// A scope that resolves type identifiers to their types.
|
|
||||||
public typealias TypeTypeScope = Scope<P4Type>
|
|
||||||
|
|
||||||
/// Scopes that resolve type identifiers to their types.
|
|
||||||
public typealias TypeTypeScopes = Scopes<P4Type>
|
|
||||||
@@ -55,8 +55,17 @@ public struct ErrorWithLocation: Errorable, Equatable, CustomStringConvertible {
|
|||||||
|
|
||||||
let prior_snipped = prior.trimmingPrefix(["\n"])
|
let prior_snipped = prior.trimmingPrefix(["\n"])
|
||||||
let after_snipped = after.prefix { $0 != "\n" }
|
let after_snipped = after.prefix { $0 != "\n" }
|
||||||
|
let include_list = fp.reversed().map {
|
||||||
|
let at =
|
||||||
|
if let whence = $0.whence {
|
||||||
|
" included at position \(whence) in"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
return $0.path.string + at
|
||||||
|
}.joined(separator: " ")
|
||||||
return formatter.formatWithStyle("Error: ", Style(StyleColor.Red))
|
return formatter.formatWithStyle("Error: ", Style(StyleColor.Red))
|
||||||
+ "In \(fp), there was an error: \n..." + prior_snipped
|
+ "In \(include_list), there was an error: \n..." + prior_snipped
|
||||||
+ formatter.formatWithStyle(source, Style(.none, [StyleFormat.Underline])) + after_snipped
|
+ formatter.formatWithStyle(source, Style(.none, [StyleFormat.Underline])) + after_snipped
|
||||||
+ "...\n"
|
+ "...\n"
|
||||||
+ self._msg
|
+ self._msg
|
||||||
|
|||||||
@@ -1,267 +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/>.
|
|
||||||
|
|
||||||
public typealias ExecuteStatementResultHandlerT = (ControlFlow, ProgramExecution) -> (
|
|
||||||
ControlFlow, ProgramExecution
|
|
||||||
)
|
|
||||||
|
|
||||||
public typealias ExecuteStatementT = (EvaluatableStatement, ProgramExecution) -> (
|
|
||||||
ControlFlow, ProgramExecution
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct ClassicEvaluator: ProgramExecutionEvaluator {
|
|
||||||
public func ExecuteStatements(
|
|
||||||
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
|
|
||||||
_ handler: ExecuteStatementResultHandlerT?
|
|
||||||
) -> (ControlFlow, ProgramExecution) {
|
|
||||||
|
|
||||||
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(
|
|
||||||
_ expression: EvaluatableExpression, inExecution execution: ProgramExecution,
|
|
||||||
) -> (Result<P4Value>, ProgramExecution) {
|
|
||||||
return expression.evaluate(execution: execution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct InterloperEvaluator: ProgramExecutionEvaluator {
|
|
||||||
var statement_interloper: StatementInterloper?
|
|
||||||
var expression_interloper: ExpressionInterloper?
|
|
||||||
|
|
||||||
public init() {}
|
|
||||||
|
|
||||||
public func getStatementInterloper() -> StatementInterloper? {
|
|
||||||
return self.statement_interloper
|
|
||||||
}
|
|
||||||
|
|
||||||
public func setStatementInterloper(
|
|
||||||
_ interloper: @escaping StatementInterloper
|
|
||||||
) -> InterloperEvaluator {
|
|
||||||
var pe = self
|
|
||||||
pe.statement_interloper = interloper
|
|
||||||
return pe
|
|
||||||
}
|
|
||||||
|
|
||||||
public func getExpressionInterloper() -> ExpressionInterloper? {
|
|
||||||
return self.expression_interloper
|
|
||||||
}
|
|
||||||
|
|
||||||
public func setExpressionInterloper(
|
|
||||||
_ interloper: @escaping ExpressionInterloper
|
|
||||||
) -> InterloperEvaluator {
|
|
||||||
var pe = self
|
|
||||||
pe.expression_interloper = interloper
|
|
||||||
return pe
|
|
||||||
}
|
|
||||||
|
|
||||||
public func ExecuteStatements(
|
|
||||||
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
|
|
||||||
_ handler: ExecuteStatementResultHandlerT?
|
|
||||||
) -> (ControlFlow, ProgramExecution) {
|
|
||||||
|
|
||||||
var debugger: StatementInterloper? = .none
|
|
||||||
var hasDebugInterloper = false
|
|
||||||
if let found_deb = self.getStatementInterloper() {
|
|
||||||
debugger = found_deb
|
|
||||||
hasDebugInterloper = true
|
|
||||||
}
|
|
||||||
|
|
||||||
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!(statement, handled_cf, handled_value)
|
|
||||||
}
|
|
||||||
return (handled_cf, handled_value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func EvaluateExpression(
|
|
||||||
_ expression: EvaluatableExpression, inExecution execution: ProgramExecution,
|
|
||||||
) -> (Result<P4Value>, ProgramExecution) {
|
|
||||||
|
|
||||||
var debugger: ExpressionInterloper? = .none
|
|
||||||
var hasDebugInterloper = false
|
|
||||||
if let found_deb = self.getExpressionInterloper() {
|
|
||||||
debugger = found_deb
|
|
||||||
hasDebugInterloper = true
|
|
||||||
}
|
|
||||||
|
|
||||||
let (result, execution) = expression.evaluate(execution: execution)
|
|
||||||
|
|
||||||
if hasDebugInterloper {
|
|
||||||
debugger!(expression, result, execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (result, execution)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public typealias StatementInterloper = (EvaluatableStatement, ControlFlow, ProgramExecution) -> Void
|
|
||||||
public typealias ExpressionInterloper = (EvaluatableExpression, Result<P4Value>, ProgramExecution)
|
|
||||||
-> Void
|
|
||||||
|
|
||||||
open class ProgramExecution: CustomStringConvertible {
|
|
||||||
public var scopes: VarValueScopes = VarValueScopes()
|
|
||||||
var globalValues: VarValueScopes?
|
|
||||||
var error: (any Errorable)?
|
|
||||||
var debug: DebugLevel = DebugLevel.Error
|
|
||||||
public let evaluator: ProgramExecutionEvaluator
|
|
||||||
|
|
||||||
init(copy: ProgramExecution) {
|
|
||||||
self.scopes = copy.scopes
|
|
||||||
self.globalValues = copy.globalValues
|
|
||||||
self.error = copy.error
|
|
||||||
self.debug = copy.debug
|
|
||||||
self.evaluator = copy.evaluator
|
|
||||||
}
|
|
||||||
|
|
||||||
public init() {
|
|
||||||
globalValues = .none
|
|
||||||
evaluator = ClassicEvaluator()
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(_ evaluator: ProgramExecutionEvaluator) {
|
|
||||||
globalValues = .none
|
|
||||||
self.evaluator = evaluator
|
|
||||||
}
|
|
||||||
|
|
||||||
open var description: String {
|
|
||||||
return "Runtime:\nScopes: \(scopes)"
|
|
||||||
}
|
|
||||||
|
|
||||||
public func hasError() -> Bool {
|
|
||||||
return self.error != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
public func getError() -> (any Errorable)? {
|
|
||||||
return self.error
|
|
||||||
}
|
|
||||||
|
|
||||||
public func setError(error: any Errorable) -> ProgramExecution {
|
|
||||||
let npe = ProgramExecution(copy: self)
|
|
||||||
npe.error = error
|
|
||||||
return npe
|
|
||||||
}
|
|
||||||
|
|
||||||
public func getDebugLevel() -> DebugLevel {
|
|
||||||
return self.debug
|
|
||||||
}
|
|
||||||
|
|
||||||
public func setDebugLevel(_ dl: DebugLevel) -> ProgramExecution {
|
|
||||||
let pe = ProgramExecution(copy: self)
|
|
||||||
pe.debug = dl
|
|
||||||
return pe
|
|
||||||
}
|
|
||||||
|
|
||||||
open func isDone() -> Bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
open func setDone() -> ProgramExecution {
|
|
||||||
// For a bare ProgramExecution, setDone is a noop.
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
public func enter_scope() -> ProgramExecution {
|
|
||||||
let new_pe = ProgramExecution(copy: self)
|
|
||||||
new_pe.scopes = new_pe.scopes.enter()
|
|
||||||
|
|
||||||
return new_pe
|
|
||||||
}
|
|
||||||
|
|
||||||
public func exit_scope() -> ProgramExecution {
|
|
||||||
let new_pe = ProgramExecution(copy: self)
|
|
||||||
new_pe.scopes = new_pe.scopes.exit()
|
|
||||||
|
|
||||||
return new_pe
|
|
||||||
}
|
|
||||||
|
|
||||||
public func replaceScopes(_ new_scopes: VarValueScopes) -> ProgramExecution {
|
|
||||||
let new_pe = ProgramExecution(copy: self)
|
|
||||||
new_pe.scopes = new_scopes
|
|
||||||
return new_pe
|
|
||||||
}
|
|
||||||
|
|
||||||
public func declare(identifier: Identifier, withValue value: P4Value) -> ProgramExecution {
|
|
||||||
let new_pe = ProgramExecution(copy: self)
|
|
||||||
let new_scopes = new_pe.scopes.declare(identifier: identifier, withValue: value)
|
|
||||||
|
|
||||||
new_pe.scopes = new_scopes
|
|
||||||
return new_pe
|
|
||||||
}
|
|
||||||
|
|
||||||
public func getGlobalValues() -> VarValueScopes {
|
|
||||||
return self.globalValues ?? VarValueScopes()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func setGlobalValues(_ global_values: VarValueScopes) -> ProgramExecution {
|
|
||||||
let new_pe = ProgramExecution(copy: self)
|
|
||||||
new_pe.globalValues = global_values
|
|
||||||
return new_pe
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A scope that resolves variable identifiers to their values.
|
|
||||||
public typealias VarValueScope = Scope<P4Value>
|
|
||||||
|
|
||||||
/// Scopes that resolves variable identifiers to their values.
|
|
||||||
public typealias VarValueScopes = Scopes<P4Value>
|
|
||||||
|
|
||||||
/// Indicate the control flow result of a particular statement.
|
|
||||||
public enum ControlFlow {
|
|
||||||
case Next
|
|
||||||
case Continue
|
|
||||||
case Break
|
|
||||||
case Return(P4Value?)
|
|
||||||
case Error
|
|
||||||
}
|
|
||||||
@@ -1,22 +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/>.
|
|
||||||
|
|
||||||
public protocol P4FFI {
|
|
||||||
func execute(execution: ProgramExecution) -> (ControlFlow, ProgramExecution)
|
|
||||||
func type() -> P4QualifiedType
|
|
||||||
func parameters() -> ParameterList
|
|
||||||
}
|
|
||||||
@@ -15,28 +15,16 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
public protocol EvaluatableExpression {
|
|
||||||
/// Evaluate an expression for a given execution
|
|
||||||
/// - Parameters
|
|
||||||
/// - execution: The execution context in which to evaluate the expression
|
|
||||||
/// - Returns: The value of expression
|
|
||||||
func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution)
|
|
||||||
func type() -> P4QualifiedType
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol EvaluatableStatement {
|
|
||||||
/// Evaluate a statement for a given execution
|
|
||||||
/// - Parameters
|
|
||||||
/// - execution: The execution context in which to evaluate the parser statement
|
|
||||||
/// - Returns: A tuple of
|
|
||||||
/// 1. Whether this statement affects control flow.
|
|
||||||
/// 2. An updated execution after evaluating the parser statement
|
|
||||||
func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution)
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol P4Type: CustomStringConvertible {
|
public protocol P4Type: CustomStringConvertible {
|
||||||
func eq(rhs: any P4Type) -> Bool
|
func eq(rhs: any P4Type) -> Bool
|
||||||
func def() -> P4DataValue?
|
func def() -> P4DataValue?
|
||||||
|
func instantiable() -> Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
extension P4Type {
|
||||||
|
public func instantiable() -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol P4DataValue: CustomStringConvertible {
|
public protocol P4DataValue: CustomStringConvertible {
|
||||||
@@ -48,28 +36,6 @@ public protocol P4DataValue: CustomStringConvertible {
|
|||||||
func gte(rhs: P4DataValue) -> Bool
|
func gte(rhs: P4DataValue) -> Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol EvaluatableLValueExpression: EvaluatableExpression {
|
|
||||||
func set(
|
|
||||||
to: P4Value, inScopes scopes: VarValueScopes, duringExecution execution: ProgramExecution
|
|
||||||
) -> Result<(VarValueScopes, P4Value)>
|
|
||||||
func check(to: EvaluatableExpression, inScopes scopes: VarTypeScopes) -> Result<()>
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol ProgramExecutionEvaluator {
|
|
||||||
func ExecuteStatements(
|
|
||||||
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
|
|
||||||
_ handler: ExecuteStatementResultHandlerT?
|
|
||||||
) -> (ControlFlow, ProgramExecution)
|
|
||||||
|
|
||||||
func ExecuteStatements(
|
|
||||||
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution
|
|
||||||
) -> (ControlFlow, ProgramExecution)
|
|
||||||
|
|
||||||
func EvaluateExpression(
|
|
||||||
_ expression: EvaluatableExpression, inExecution execution: ProgramExecution,
|
|
||||||
) -> (Result<P4Value>, ProgramExecution)
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol Errorable: CustomStringConvertible {
|
public protocol Errorable: CustomStringConvertible {
|
||||||
func format(_ formatter: Formattable) -> String
|
func format(_ formatter: Formattable) -> String
|
||||||
func format(_ formatter: Formattable, _ sc: SourceCode) -> String
|
func format(_ formatter: Formattable, _ sc: SourceCode) -> String
|
||||||
@@ -91,10 +57,14 @@ public protocol Formattable {
|
|||||||
func formatWithStyle(_ value: String, _ style: Style) -> String
|
func formatWithStyle(_ value: String, _ style: Style) -> String
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ProgramExecutionEvaluator {
|
public protocol P4Statement {
|
||||||
public func ExecuteStatements(
|
}
|
||||||
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution
|
|
||||||
) -> (ControlFlow, ProgramExecution) {
|
public protocol P4Expression {
|
||||||
return ExecuteStatements(statements, inExecution: execution, .none)
|
}
|
||||||
}
|
|
||||||
|
extension P4Expression {
|
||||||
|
}
|
||||||
|
|
||||||
|
extension P4Expression {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,14 +101,21 @@ public struct SourceManager {
|
|||||||
public struct FileSourceLocation: Equatable, CustomStringConvertible {
|
public struct FileSourceLocation: Equatable, CustomStringConvertible {
|
||||||
let location: SourceLocation
|
let location: SourceLocation
|
||||||
let path: FilePath
|
let path: FilePath
|
||||||
|
let whence: Int?
|
||||||
let nested: [FileSourceLocation]
|
let nested: [FileSourceLocation]
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
_ location: SourceLocation, _ path: FilePath, _ nested: [FileSourceLocation] = Array()
|
_ location: SourceLocation, _ path: FilePath, _ whence: Int? = .none,
|
||||||
|
_ nested: [FileSourceLocation] = Array()
|
||||||
) {
|
) {
|
||||||
self.location = location
|
self.location = location
|
||||||
self.path = path
|
self.path = path
|
||||||
self.nested = nested
|
self.nested = nested
|
||||||
|
self.whence = whence
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(whence: Int) -> FileSourceLocation {
|
||||||
|
return FileSourceLocation(self.location, self.path, whence, self.nested)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getLocation() -> SourceLocation {
|
public func getLocation() -> SourceLocation {
|
||||||
@@ -130,7 +137,7 @@ public struct FileSourceLocation: Equatable, CustomStringConvertible {
|
|||||||
}).joined(separator: ",") + ")"
|
}).joined(separator: ",") + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
public func pathForLocation(_ location: Int) -> FilePath? {
|
public func pathForLocation(_ location: Int) -> [FileSourceLocation]? {
|
||||||
|
|
||||||
let queried_location = SourceLocation(location, 1)
|
let queried_location = SourceLocation(location, 1)
|
||||||
if !self.location.contains(queried_location) {
|
if !self.location.contains(queried_location) {
|
||||||
@@ -139,11 +146,11 @@ public struct FileSourceLocation: Equatable, CustomStringConvertible {
|
|||||||
|
|
||||||
for nested in self.nested {
|
for nested in self.nested {
|
||||||
if nested.location.contains(queried_location) {
|
if nested.location.contains(queried_location) {
|
||||||
return nested.pathForLocation(location)
|
return [self] + nested.pathForLocation(location)!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.getPath()
|
return [self]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,8 +219,8 @@ public struct SourceCode {
|
|||||||
|
|
||||||
public func getSourceSnippet(
|
public func getSourceSnippet(
|
||||||
location: SourceLocation, context: Int = 0
|
location: SourceLocation, context: Int = 0
|
||||||
) -> (FilePath, String, String, String)? {
|
) -> ([FileSourceLocation], String, String, String)? {
|
||||||
guard let path = self.pathForLocation(location.range.lowerBound) else {
|
guard let paths = self.pathForLocation(location.range.lowerBound) else {
|
||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
let lower = String.UTF8View.Index(utf16Offset: location.range.lowerBound, in: self.code)
|
let lower = String.UTF8View.Index(utf16Offset: location.range.lowerBound, in: self.code)
|
||||||
@@ -234,14 +241,14 @@ public struct SourceCode {
|
|||||||
let result = String(self.code.utf16[lower..<upper])!
|
let result = String(self.code.utf16[lower..<upper])!
|
||||||
let prior = String(self.code.utf16[prior_start..<lower])!
|
let prior = String(self.code.utf16[prior_start..<lower])!
|
||||||
let after = String(self.code.utf16[upper...after_end])!
|
let after = String(self.code.utf16[upper...after_end])!
|
||||||
return (path, result, prior, after)
|
return (paths, result, prior, after)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getLocations() -> FileSourceLocation {
|
public func getLocations() -> FileSourceLocation {
|
||||||
return self.locations
|
return self.locations
|
||||||
}
|
}
|
||||||
|
|
||||||
public func pathForLocation(_ location: Int) -> FilePath? {
|
public func pathForLocation(_ location: Int) -> [FileSourceLocation]? {
|
||||||
return self.locations.pathForLocation(location)
|
return self.locations.pathForLocation(location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,42 +270,40 @@ func do_preprocess(
|
|||||||
"Could not open \(file) for preprocessing"))
|
"Could not open \(file) for preprocessing"))
|
||||||
}
|
}
|
||||||
|
|
||||||
var contents = try String.init(
|
let orig = try String.init(
|
||||||
contentsOf: URL(filePath: included_path.string), encoding: String.defaultCStringEncoding)
|
contentsOf: URL(filePath: included_path.string), encoding: String.defaultCStringEncoding)
|
||||||
var changed = true
|
var expanded = ""
|
||||||
|
var oloc = orig.startIndex
|
||||||
|
|
||||||
while changed {
|
for match in orig.matches(of: re) {
|
||||||
changed = false
|
|
||||||
for match in contents.matches(of: re) {
|
|
||||||
|
|
||||||
let before = contents[..<match.range.lowerBound]
|
if oloc != match.range.lowerBound {
|
||||||
let after = contents[match.range.upperBound...]
|
expanded += String(orig[oloc..<match.range.lowerBound])
|
||||||
|
}
|
||||||
|
oloc = match.range.upperBound
|
||||||
|
|
||||||
// By calling ourselves recursively, the include being processed will
|
// By calling ourselves recursively, the include being processed will
|
||||||
// be _completely_ expanded (including any nested includes).
|
// be _completely_ expanded (including any nested includes).
|
||||||
switch do_preprocess(FilePath("\(match.1)"), manager, starting + before.count) {
|
switch do_preprocess(FilePath("\(match.1)"), manager, starting + expanded.count) {
|
||||||
case .Ok((let location, let expanded)):
|
case .Ok((let location, let recursively_expanded)):
|
||||||
// Recombine what was before and after the include being processed
|
// Recombine what was before and after the include being processed
|
||||||
// with the expanded text.
|
// with the expanded text.
|
||||||
contents = before + expanded + after
|
expanded += recursively_expanded
|
||||||
// Remember the location (and those it has nested) that were found in
|
// Remember the location (and those it has nested) that were found in
|
||||||
// the expanded text.
|
// the expanded text.
|
||||||
locations.append(location)
|
locations.append(location.update(whence: match.range.lowerBound.utf16Offset(in: orig)))
|
||||||
case .Error(let e):
|
case .Error(let e):
|
||||||
return .Error(e)
|
return .Error(e)
|
||||||
}
|
|
||||||
|
|
||||||
// Only process one at a time.
|
|
||||||
changed = true
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure that we got it all!
|
||||||
|
expanded += orig[oloc...]
|
||||||
|
|
||||||
return .Ok(
|
return .Ok(
|
||||||
(
|
(
|
||||||
FileSourceLocation(
|
FileSourceLocation(
|
||||||
SourceLocation(starting..<(starting + contents.count)), file, locations),
|
SourceLocation(starting..<(starting + expanded.count)), file, .none, locations), expanded
|
||||||
contents
|
|
||||||
))
|
))
|
||||||
|
|
||||||
} catch (let e) {
|
} catch (let e) {
|
||||||
|
|||||||
@@ -439,11 +439,32 @@ public struct CliTestDeclarationMacro: PeerMacro, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum DeriveParsableStatement: MemberMacro {
|
||||||
|
public static func expansion(
|
||||||
|
of: AttributeSyntax, providingMembersOf type: some DeclGroupSyntax, conformingTo: [TypeSyntax],
|
||||||
|
in: some MacroExpansionContext
|
||||||
|
) throws -> [DeclSyntax] {
|
||||||
|
|
||||||
|
let implementation = DeclSyntax(
|
||||||
|
"""
|
||||||
|
public static func ParseStatement(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Statement> {
|
||||||
|
return switch Parse(node: node, withContext: context) {
|
||||||
|
case .Ok(let res): .Ok(res)
|
||||||
|
case .Error(let e): .Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
return [implementation]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct P4Macros: CompilerPlugin {
|
struct P4Macros: CompilerPlugin {
|
||||||
var providingMacros: [Macro.Type] = [
|
var providingMacros: [Macro.Type] = [
|
||||||
RequireResult.self, RequireErrorResult.self, UseOkResult.self, UseErrorResult.self,
|
RequireResult.self, RequireErrorResult.self, UseOkResult.self, UseErrorResult.self,
|
||||||
RequireNodeType.self, SkipUnlessNodeType.self, SkipUnlessNodesTypes.self, RequireNodesType.self,
|
RequireNodeType.self, SkipUnlessNodeType.self, SkipUnlessNodesTypes.self, RequireNodesType.self,
|
||||||
MustOr.self, CliTestDeclarationMacro.self,
|
MustOr.self, CliTestDeclarationMacro.self, DeriveParsableStatement.self,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,256 @@
|
|||||||
|
// 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 P4Parser
|
||||||
|
|
||||||
|
public struct CSTTextSerializer {
|
||||||
|
public init() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct CSTTextSerializerContext {
|
||||||
|
public let serialized: String
|
||||||
|
public let indents: Int
|
||||||
|
|
||||||
|
public init(_ serialized: String = "", _ indents: Int = 0) {
|
||||||
|
self.serialized = serialized
|
||||||
|
self.indents = indents
|
||||||
|
}
|
||||||
|
|
||||||
|
static func produceIndent(_ indent: Int, _ marker: String) -> String {
|
||||||
|
return repeatElement(marker, count: indent).joined()
|
||||||
|
}
|
||||||
|
public func append(_ a: String) -> CSTTextSerializerContext {
|
||||||
|
return CSTTextSerializerContext(
|
||||||
|
self.serialized + Self.produceIndent(self.indents, "\t") + a + "\n", self.indents)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func indent() -> CSTTextSerializerContext {
|
||||||
|
return CSTTextSerializerContext(self.serialized, self.indents + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func unindent() -> CSTTextSerializerContext {
|
||||||
|
return CSTTextSerializerContext(self.serialized, self.indents - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CSTTextSerializer: CSTVisitor<CSTTextSerializerContext> {
|
||||||
|
public func visit(
|
||||||
|
node: P4Parser.CST.KeysetExpression, driver: P4Parser.CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
var context = context.append("Keyset Expression:").indent()
|
||||||
|
return .Ok(context.unindent())
|
||||||
|
if case CST.KeysetExpression.Value(let x) = node {
|
||||||
|
switch driver.visit(x, visitor: self, context: context) {
|
||||||
|
case .Ok(let c): context = c
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .Ok(context.unindent())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: P4Parser.CST.SelectCaseExpression, driver: P4Parser.CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
var context = context.append("Case Expression:").indent()
|
||||||
|
switch driver.visit(node.key, visitor: self, context: context) {
|
||||||
|
case .Ok(let c): context = c
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
context = context.append("Next State:").indent()
|
||||||
|
switch driver.visit(node.next_state_identifier, visitor: self, context: context) {
|
||||||
|
case .Ok(let c): context = c
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
context = context.unindent()
|
||||||
|
return .Ok(context.unindent())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: P4Parser.CST.SelectExpression, driver: P4Parser.CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
var context = context.append("Select Expression:").indent()
|
||||||
|
context = context.append("Selector:").indent()
|
||||||
|
switch driver.visit(node.selector, visitor: self, context: context) {
|
||||||
|
case .Ok(let c): context = c
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
context = context.unindent()
|
||||||
|
context = context.append("Case Expressions:").indent()
|
||||||
|
for ce in node.case_expressions {
|
||||||
|
switch driver.visit(ce, visitor: self, context: context) {
|
||||||
|
case .Ok(let c): context = c
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context = context.unindent()
|
||||||
|
|
||||||
|
return .Ok(context.unindent())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: P4Parser.CST.Statements, driver: P4Parser.CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
var context = context
|
||||||
|
for s in node.statements {
|
||||||
|
switch driver.visit(s, visitor: self, context: context) {
|
||||||
|
case .Ok(let c): context = c
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: P4Parser.CST.ExpressionStatement, driver: P4Parser.CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
var context = context.append("Expression Statement:").indent()
|
||||||
|
switch driver.visit(node.expression, visitor: self, context: context) {
|
||||||
|
case .Ok(let c): context = c
|
||||||
|
case .Error(let e):
|
||||||
|
return .Error(e)
|
||||||
|
}
|
||||||
|
return .Ok(context.unindent())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: P4Parser.CST.Control, driver: P4Parser.CSTVisitorDriver, context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
return .Ok(context.append("Control Declaration"))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: P4Parser.CST.ExternDeclaration, driver: P4Parser.CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
return .Ok(context.append("Extern Declaration"))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: P4Parser.CST.FunctionDeclaration, driver: P4Parser.CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
return .Ok(context.append("Function Declaration"))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: P4Parser.CST.StructDeclaration, driver: P4Parser.CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
return .Ok(context.append("Struct Declaration"))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: P4Parser.CST.VariableDeclarationStatement, driver: P4Parser.CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
return .Ok(context.append("Variable Declaration Statement"))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: CST.BinaryOperatorExpression, driver: CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
return .Ok(context.append("Binary Operator Expression"))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: CST.Literal, driver: CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
return .Ok(context.append("Literal Expression"))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: CST.Identifier, driver: CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
return .Ok(context.append("Identifier: \(node.id)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: CST.Parser, driver: CSTVisitorDriver, context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
var context = context.append("Parser Expression")
|
||||||
|
context = context.indent()
|
||||||
|
for s in node.states.states {
|
||||||
|
switch driver.visit(s, visitor: self, context: context) {
|
||||||
|
case .Ok(let c): context = c
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .Ok(context.unindent())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: CST.ParserStateDirectTransition, driver: CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
var context = context.append("State: Direct Transition").indent()
|
||||||
|
context = context.append("Statements:")
|
||||||
|
context = context.indent()
|
||||||
|
if let statements = node.statements {
|
||||||
|
switch driver.visit(statements, visitor: self, context: context) {
|
||||||
|
case .Ok(let c): context = c
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context = context.unindent()
|
||||||
|
context = context.append("Next State:").indent()
|
||||||
|
switch driver.visit(node.next_state_identifier!, visitor: self, context: context) {
|
||||||
|
case .Ok(let c): context = c
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
context = context.unindent()
|
||||||
|
return .Ok(context.unindent())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: CST.ParserStateNoTransition, driver: CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
return .Ok(context.append("State: No Transition"))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func visit(
|
||||||
|
node: CST.ParserStateSelectTransition, driver: CSTVisitorDriver,
|
||||||
|
context: CSTTextSerializerContext
|
||||||
|
) -> Common.Result<CSTTextSerializerContext> {
|
||||||
|
|
||||||
|
var context = context.append("State: Select Transition").indent()
|
||||||
|
if let statements = node.statements {
|
||||||
|
context = context.indent()
|
||||||
|
switch driver.visit(statements, visitor: self, context: context) {
|
||||||
|
case .Ok(let c): context = c
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
context = context.unindent()
|
||||||
|
}
|
||||||
|
switch driver.visit(node.te, visitor: self, context: context) {
|
||||||
|
case .Ok(let c): context = c
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(context.unindent())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,389 +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
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import TreeSitterExtensions
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
func parameter_list_compiler(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Common.Result<(ParameterList, CompilerContext)> {
|
|
||||||
|
|
||||||
var walker = Walker(node: node)
|
|
||||||
var current_node: Node? = .none
|
|
||||||
|
|
||||||
if node.text == ")" {
|
|
||||||
// There are no parameters!
|
|
||||||
return Result.Ok((ParameterList([]), context))
|
|
||||||
}
|
|
||||||
|
|
||||||
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
|
|
||||||
node: node, type: "parameter_list", nice_type_name: "Parameter List")
|
|
||||||
|
|
||||||
var parameters: ParameterList = ParameterList([])
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(ParameterList, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Missing parameter list component")))
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
walker.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(ParameterList, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Missing parameter list component")))
|
|
||||||
|
|
||||||
// If this is a ')', we are done.
|
|
||||||
if current_node?.text == ")" {
|
|
||||||
return Result.Ok((parameters, context))
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a comma, we skip it!
|
|
||||||
if current_node?.text == "," {
|
|
||||||
walker.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(ParameterList, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Missing parameter list component")))
|
|
||||||
|
|
||||||
// Otherwise, there should be one parameter left!
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ParameterList: Compilable {
|
|
||||||
public typealias T = ParameterList
|
|
||||||
public static func Compile(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Common.Result<(ParameterList, CompilerContext)> {
|
|
||||||
|
|
||||||
let parameter_node = node
|
|
||||||
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
|
|
||||||
node: parameter_node, type: "parameters", nice_type_name: "Parameters")
|
|
||||||
|
|
||||||
var walker = Walker(node: parameter_node)
|
|
||||||
|
|
||||||
var current_node: Node? = .none
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(ParameterList, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing '(' in parameter list component")))
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(ParameterList, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Missing parameter list component")))
|
|
||||||
|
|
||||||
return parameter_list_compiler(node: current_node!, withContext: context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Direction: Compilable {
|
|
||||||
public typealias T = Direction
|
|
||||||
public static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<(Direction, CompilerContext)> {
|
|
||||||
let direction_node = node
|
|
||||||
#RequireNodeType<Node, (Direction, CompilerContext)>(
|
|
||||||
node: direction_node, type: "direction", nice_type_name: "direction")
|
|
||||||
let directions = [
|
|
||||||
"in": Direction.In,
|
|
||||||
"out": Direction.Out,
|
|
||||||
"inout": Direction.InOut,
|
|
||||||
]
|
|
||||||
|
|
||||||
guard let parsed_direction = directions[direction_node.text!] else {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: direction_node.toSourceLocation(),
|
|
||||||
withError: "\(direction_node.text!) is not a valid direction"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Ok((parsed_direction, context))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Parameter: Compilable {
|
|
||||||
public typealias T = Parameter
|
|
||||||
public static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<(Parameter, CompilerContext)> {
|
|
||||||
|
|
||||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
|
||||||
node: node, type: "parameter", nice_type_name: "parameter")
|
|
||||||
|
|
||||||
var walker = Walker(node: node)
|
|
||||||
var current_node: Node? = .none
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(Parameter, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing parameter declaration component")))
|
|
||||||
|
|
||||||
// Annotation?
|
|
||||||
if current_node!.nodeType == "annotations" {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: current_node!.toSourceLocation(),
|
|
||||||
withError: "Annotations in parameter declarations are not yet handled"))
|
|
||||||
// Will increment indexes here.
|
|
||||||
}
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(Parameter, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing parameter declaration component")))
|
|
||||||
|
|
||||||
var direction: Direction? = .none
|
|
||||||
// Direction?
|
|
||||||
if current_node!.nodeType == "direction" {
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(Parameter, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing parameter declaration component")))
|
|
||||||
|
|
||||||
if current_node!.nodeType != "typeRef" {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Did not find type name for parameter declaration"))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard
|
|
||||||
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 \(current_node!.text!)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(Parameter, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing parameter declaration component")))
|
|
||||||
|
|
||||||
if current_node!.nodeType != "identifier" {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Did not find identifier for parameter statement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard
|
|
||||||
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 \(current_node!.text!)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Ok(
|
|
||||||
(
|
|
||||||
Parameter(
|
|
||||||
identifier: parameter_name,
|
|
||||||
withType: direction != nil
|
|
||||||
? parameter_type.update(addAttribute: P4TypeQualifier.Direction(direction!))
|
|
||||||
: parameter_type),
|
|
||||||
context
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func argument_list_compiler(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Common.Result<(ArgumentList, CompilerContext)> {
|
|
||||||
|
|
||||||
var walker = Walker(node: node)
|
|
||||||
var current_node: Node? = .none
|
|
||||||
|
|
||||||
if node.text == ")" {
|
|
||||||
// There are no arguments!
|
|
||||||
return Result.Ok((ArgumentList([]), context))
|
|
||||||
}
|
|
||||||
|
|
||||||
#RequireNodeType<Node, (ArgumentList, CompilerContext)>(
|
|
||||||
node: node, type: "argument_list", nice_type_name: "argument List")
|
|
||||||
|
|
||||||
var arguments: ArgumentList = ArgumentList([])
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(ArgumentList, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Missing argument list component")))
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
// We may have moved nodes, check/reset current_node.
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(ArgumentList, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Missing argument list component")))
|
|
||||||
|
|
||||||
// If this is a ')', we are done.
|
|
||||||
if current_node?.text == ")" {
|
|
||||||
return Result.Ok((arguments, context))
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a comma, we skip it!
|
|
||||||
if current_node?.text == "," {
|
|
||||||
walker.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(ArgumentList, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Missing argument list component")))
|
|
||||||
|
|
||||||
// Otherwise, there should be one argument left!
|
|
||||||
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))
|
|
||||||
case .Error(let e): return Result.Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ArgumentList: Compilable {
|
|
||||||
public typealias T = ArgumentList
|
|
||||||
public static func Compile(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Common.Result<(ArgumentList, CompilerContext)> {
|
|
||||||
|
|
||||||
let argument_node = node
|
|
||||||
#RequireNodeType<Node, (ArgumentList, CompilerContext)>(
|
|
||||||
node: argument_node, type: "arguments", nice_type_name: "arguments")
|
|
||||||
|
|
||||||
var walker = Walker(node: argument_node)
|
|
||||||
var current_node: Node? = .none
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(ArgumentList, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing '(' in argument list component")))
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(ArgumentList, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Missing argument list component")))
|
|
||||||
|
|
||||||
return argument_list_compiler(node: current_node!, withContext: context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Argument: Compilable {
|
|
||||||
public typealias T = EvaluatableExpression
|
|
||||||
public static func Compile(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Common.Result<(EvaluatableExpression, CompilerContext)> {
|
|
||||||
let argument_node = node
|
|
||||||
#RequireNodeType<Node, (EvaluatableExpression, CompilerContext)>(
|
|
||||||
node: argument_node, type: "argument", nice_type_name: "argument")
|
|
||||||
|
|
||||||
let expression_node = node.child(at: 0)!
|
|
||||||
|
|
||||||
return switch Expression.Compile(node: expression_node, withContext: context) {
|
|
||||||
case .Ok(let compiled_expression): .Ok((compiled_expression, context))
|
|
||||||
case .Error(let e): .Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ContainsInvalidStatements(
|
|
||||||
statement: EvaluatableStatement, invalids: [EvaluatableStatement.Type]
|
|
||||||
) -> Bool {
|
|
||||||
for es in invalids {
|
|
||||||
if type(of: statement) == es {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ContainsInvalidStatements(block: BlockStatement, invalids: [EvaluatableStatement.Type]) -> Bool
|
|
||||||
{
|
|
||||||
return block.statements.contains { statement in
|
|
||||||
for es in invalids {
|
|
||||||
if type(of: statement) == es {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Node {
|
|
||||||
public func toSourceLocation() -> SourceLocation {
|
|
||||||
return SourceLocation(self.range.location, self.range.length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,158 +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
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import TreeSitterExtensions
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
let p4lang = Language(tree_sitter_p4())
|
|
||||||
|
|
||||||
public func ConfigureP4Parser() -> Result<SwiftTreeSitter.Parser> {
|
|
||||||
let p = SwiftTreeSitter.Parser.init()
|
|
||||||
|
|
||||||
do {
|
|
||||||
try p.setLanguage(p4lang)
|
|
||||||
} catch {
|
|
||||||
return Result.Error(Error(withMessage: "Could not configure the P4 parser"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Ok(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Context for compilation
|
|
||||||
///
|
|
||||||
/// It contains (at least) three important pieces of information:
|
|
||||||
/// 1. Instances: A ``VarTypeScopes`` that contains information about instantiated objects
|
|
||||||
/// (and their types) in scope
|
|
||||||
/// 1. Types: A ``TypeTypeScopes`` that contains information about declared types in scope.
|
|
||||||
/// 1. Expected Type: In certain situations, to typecheck an element of a P4 program requires
|
|
||||||
/// knowledge of an expected type. For instance, when compiling a return statement, the
|
|
||||||
/// compiler must know the return type of the function to type check.
|
|
||||||
public struct CompilerContext {
|
|
||||||
let instances: VarTypeScopes
|
|
||||||
let types: TypeTypeScopes
|
|
||||||
let externs: TypeTypeScopes
|
|
||||||
let ffis: [P4FFI]
|
|
||||||
let expected_type: P4QualifiedType?
|
|
||||||
let extern_context: Bool
|
|
||||||
|
|
||||||
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: P4QualifiedType?, 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 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 but new instances.
|
|
||||||
public func update(newInstances instances: VarTypeScopes) -> CompilerContext {
|
|
||||||
return CompilerContext(
|
|
||||||
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 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 but new types.
|
|
||||||
public func update(newTypes types: TypeTypeScopes) -> CompilerContext {
|
|
||||||
return CompilerContext(
|
|
||||||
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 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 but new expected type.
|
|
||||||
public func update(newExpectation expectation: P4QualifiedType?) -> CompilerContext {
|
|
||||||
return CompilerContext(
|
|
||||||
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
@@ -1,821 +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
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
protocol CompilableExpression {
|
|
||||||
static func compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<EvaluatableExpression?>
|
|
||||||
}
|
|
||||||
|
|
||||||
protocol CompilableLValueExpression {
|
|
||||||
static func compile_as_lvalue(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<EvaluatableLValueExpression?>
|
|
||||||
}
|
|
||||||
|
|
||||||
extension TypedIdentifier: CompilableExpression {
|
|
||||||
static func compile(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Result<EvaluatableExpression?> {
|
|
||||||
|
|
||||||
let node = node.child(at: 0)!
|
|
||||||
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
|
|
||||||
node: node, type: "identifier")
|
|
||||||
|
|
||||||
guard
|
|
||||||
case Result.Ok(let type) = context.instances.lookup(
|
|
||||||
identifier: Common.Identifier(name: node.text!))
|
|
||||||
else {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Cannot find \(node.text!) in scope"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Ok(TypedIdentifier(name: node.text!, withType: type))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension TypedIdentifier: CompilableLValueExpression {
|
|
||||||
static func compile_as_lvalue(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Result<EvaluatableLValueExpression?> {
|
|
||||||
|
|
||||||
let expression = node.child(at: 0)!
|
|
||||||
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
|
|
||||||
node: expression, type: "identifier")
|
|
||||||
|
|
||||||
let maybe_parsed_expression = TypedIdentifier.compile(node: node, withContext: context)
|
|
||||||
guard case .Ok(let maybe_typed_identifier) = maybe_parsed_expression else {
|
|
||||||
return .Error(maybe_parsed_expression.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
let typed_identifier_expression = maybe_typed_identifier as! TypedIdentifier
|
|
||||||
|
|
||||||
return Result.Ok(typed_identifier_expression)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension P4BooleanValue: CompilableExpression {
|
|
||||||
static func compile(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Result<EvaluatableExpression?> {
|
|
||||||
let node = node.child(at: 0)!
|
|
||||||
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
|
|
||||||
node: node, type: "booleanLiteralExpression")
|
|
||||||
|
|
||||||
if node.text == "false" {
|
|
||||||
return .Ok(P4Value(P4BooleanValue(withValue: false)))
|
|
||||||
} else if node.text == "true" {
|
|
||||||
return .Ok(P4Value(P4BooleanValue(withValue: true)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Failed to parse boolean literal: \(node.text!)"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension P4IntValue: CompilableExpression {
|
|
||||||
static func compile(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Result<EvaluatableExpression?> {
|
|
||||||
let node = node.child(at: 0)!
|
|
||||||
|
|
||||||
#SkipUnlessNodesTypes<SwiftTreeSitter.Node>(
|
|
||||||
node: node, types: ["integer", "integer_elaborated"])
|
|
||||||
|
|
||||||
var bit_width: BitWidth = BitWidth.Infinite
|
|
||||||
let value_source: String
|
|
||||||
if node.nodeType == "integer_elaborated" {
|
|
||||||
let re = /([0-9]+)([ws])([\-0-9]+)/
|
|
||||||
let integer_components = node.text!.matches(of: re)
|
|
||||||
|
|
||||||
if integer_components.isEmpty || integer_components.count > 1 {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Failed to parse elaborated integer: \(node.text!)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let width_source = "\(integer_components[0].1)"
|
|
||||||
guard let width = Int(width_source) else {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Failed to parse width from elaborated integer: \(width_source)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TODO: Handle signed vs. unsigned.
|
|
||||||
|
|
||||||
bit_width = BitWidth.Width(width)
|
|
||||||
value_source = "\(integer_components[0].3)"
|
|
||||||
} else {
|
|
||||||
value_source = node.text!
|
|
||||||
}
|
|
||||||
|
|
||||||
if let parsed_int = Int(value_source) {
|
|
||||||
return .Ok(P4Value(P4IntValue(withValue: parsed_int, andWidth: bit_width)))
|
|
||||||
} else {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Failed to parse integer: \(node.text!)"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension P4StringValue: CompilableExpression {
|
|
||||||
static func compile(
|
|
||||||
node: SwiftTreeSitter.Node, withContext scopes: CompilerContext
|
|
||||||
) -> Result<EvaluatableExpression?> {
|
|
||||||
let node = node.child(at: 0)!
|
|
||||||
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
|
|
||||||
node: node, type: "string_literal")
|
|
||||||
return .Ok(P4Value(P4StringValue(withValue: node.text!)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension KeysetExpression: CompilableExpression {
|
|
||||||
static func compile(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Common.Result<(any Common.EvaluatableExpression)?> {
|
|
||||||
let keyset_expression_node = node.child(at: 0)!
|
|
||||||
|
|
||||||
#RequireNodesType<Node, EvaluatableExpression>(
|
|
||||||
nodes: keyset_expression_node, type: ["expression", "default_keyset"],
|
|
||||||
nice_type_names: ["expression", "default keyset"])
|
|
||||||
|
|
||||||
// If there is a default keyset, that's easy!
|
|
||||||
if keyset_expression_node.nodeType == "default_keyset" {
|
|
||||||
return .Ok(KeysetExpression(P4Value(P4SetDefaultValue(withType: context.expected_type!))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile the expression:
|
|
||||||
let maybe_compiled_set_expression = Expression.Compile(
|
|
||||||
node: keyset_expression_node, withContext: context)
|
|
||||||
guard case .Ok(let compiled_expression) = maybe_compiled_set_expression else {
|
|
||||||
return .Error(maybe_compiled_set_expression.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Ok(KeysetExpression(compiled_expression))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Expression {
|
|
||||||
public static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<EvaluatableExpression> {
|
|
||||||
#RequireNodeType<Node, EvaluatableExpression>(
|
|
||||||
node: node, type: "expression", nice_type_name: "expression")
|
|
||||||
|
|
||||||
let expression_node = node.child(at: 0)!
|
|
||||||
#RequireNodesType<Node, EvaluatableExpression>(
|
|
||||||
nodes: expression_node, type: ["grouped_expression", "simple_expression"],
|
|
||||||
nice_type_names: ["grouped expression", "simple expression"])
|
|
||||||
|
|
||||||
// If this is a grouped expression, recurse!
|
|
||||||
if expression_node.nodeType == "grouped_expression" {
|
|
||||||
return Expression.Compile(node: expression_node.child(at: 1)!, withContext: context)
|
|
||||||
}
|
|
||||||
|
|
||||||
let expression_parsers: [CompilableExpression.Type] = [
|
|
||||||
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self,
|
|
||||||
BinaryOperatorExpression.self, ArrayAccessExpression.self, FieldAccessExpression.self,
|
|
||||||
FunctionCall.self,
|
|
||||||
]
|
|
||||||
|
|
||||||
for candidate_expression_parser in expression_parsers {
|
|
||||||
switch candidate_expression_parser.compile(
|
|
||||||
node: expression_node, withContext: context)
|
|
||||||
{
|
|
||||||
case .Ok(.some(let parsed)): return .Ok(parsed)
|
|
||||||
case .Error(let e): return .Error(e)
|
|
||||||
default: continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Error(Error(withMessage: "\(node.range): Could not parse into expression"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LValue {
|
|
||||||
public static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<EvaluatableLValueExpression> {
|
|
||||||
#RequireNodeType<Node, EvaluatableExpression>(
|
|
||||||
node: node, type: "expression", nice_type_name: "expression")
|
|
||||||
|
|
||||||
let expression_node = node.child(at: 0)!
|
|
||||||
#RequireNodesType<Node, EvaluatableExpression>(
|
|
||||||
nodes: expression_node, type: ["grouped_expression", "simple_expression"],
|
|
||||||
nice_type_names: ["grouped expression", "simple expression"])
|
|
||||||
|
|
||||||
// If this is a grouped expression, recurse!
|
|
||||||
if expression_node.nodeType == "grouped_expression" {
|
|
||||||
return LValue.Compile(node: expression_node.child(at: 1)!, withContext: context)
|
|
||||||
}
|
|
||||||
|
|
||||||
let lvalue_parsers: [CompilableLValueExpression.Type] = [
|
|
||||||
TypedIdentifier.self, FieldAccessExpression.self, ArrayAccessExpression.self,
|
|
||||||
]
|
|
||||||
|
|
||||||
for candidate_lvalue_parser in lvalue_parsers {
|
|
||||||
switch candidate_lvalue_parser.compile_as_lvalue(
|
|
||||||
node: expression_node, withContext: context)
|
|
||||||
{
|
|
||||||
case .Ok(.some(let parsed)): return .Ok(parsed)
|
|
||||||
case .Error(let e): return .Error(e)
|
|
||||||
default: continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Error(Error(withMessage: "\(node.range): Could not parse into expression"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Identifier {
|
|
||||||
public static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<Common.Identifier> {
|
|
||||||
return if let node_text_value = node.text {
|
|
||||||
.Ok(Common.Identifier(name: node_text_value))
|
|
||||||
} else {
|
|
||||||
.Error(Error(withMessage: "Could not parse an identifier from \(node)"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SelectExpression: CompilableExpression {
|
|
||||||
static func compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<EvaluatableExpression?> {
|
|
||||||
#RequireNodeType<Node, (SelectExpression, CompilerContext)>(
|
|
||||||
node: node, type: "selectExpression", nice_type_name: "parser select expression")
|
|
||||||
|
|
||||||
guard let selector_node = node.child(at: 2),
|
|
||||||
selector_node.nodeType == "expression"
|
|
||||||
else {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Could not find selector expression"))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let select_body_node = node.child(at: 5),
|
|
||||||
select_body_node.nodeType == "selectBody"
|
|
||||||
else {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Could not find select expression body"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_selector = Expression.Compile(node: selector_node, withContext: context)
|
|
||||||
guard case .Ok(let selector) = maybe_selector else {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: selector_node.toSourceLocation(),
|
|
||||||
withError:
|
|
||||||
"Could not parse transition select expression selector expression: \(maybe_selector.error()!)"
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
var sces: [SelectCaseExpression] = Array()
|
|
||||||
var sces_errors: (any Errorable)? = .none
|
|
||||||
|
|
||||||
select_body_node.enumerateNamedChildren { current_node in
|
|
||||||
let maybe_parsed_cse = SelectCaseExpression.compile(
|
|
||||||
node: current_node, withContext: context.update(newExpectation: selector.type()))
|
|
||||||
switch maybe_parsed_cse {
|
|
||||||
case .Ok(let parsed_cse): sces.append(parsed_cse as! SelectCaseExpression)
|
|
||||||
case .Error(let e):
|
|
||||||
sces_errors =
|
|
||||||
if let sces_errors = sces_errors {
|
|
||||||
sces_errors.append(error: Error(withMessage: "\(maybe_parsed_cse.error()!)"))
|
|
||||||
} else {
|
|
||||||
e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let sces_errors = sces_errors {
|
|
||||||
return .Error(ErrorWithLabel("Error(s) parsing select cases", sces_errors))
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Ok(
|
|
||||||
SelectExpression(withSelector: selector, withSelectCaseExpressions: sces),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SelectCaseExpression: CompilableExpression {
|
|
||||||
static func compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<EvaluatableExpression?> {
|
|
||||||
if node.nodeType != "selectCase" {
|
|
||||||
return Result.Error(Error(withMessage: "Expected select case not found"))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let keysetexpression_node = node.child(at: 0),
|
|
||||||
keysetexpression_node.nodeType == "keysetExpression"
|
|
||||||
else {
|
|
||||||
return Result.Error(Error(withMessage: "Missing keyset expression in select case"))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let targetstate_node = node.child(at: 2),
|
|
||||||
targetstate_node.nodeType == "identifier"
|
|
||||||
else {
|
|
||||||
return Result.Error(Error(withMessage: "Missing target state in select case"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_parsed_keysetexpression = KeysetExpression.compile(
|
|
||||||
node: keysetexpression_node, withContext: context)
|
|
||||||
guard case Result.Ok(let maybe_keysetexpression) = maybe_parsed_keysetexpression else {
|
|
||||||
return Result.Error(maybe_parsed_keysetexpression.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let maybe_keysetexpression = maybe_keysetexpression else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: keysetexpression_node.toSourceLocation(),
|
|
||||||
withError: "Missing expected keyset expression"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let keysetexpression = maybe_keysetexpression as! KeysetExpression
|
|
||||||
|
|
||||||
if case .Error(let e) = keysetexpression.compatible(type: context.expected_type!) {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: keysetexpression_node.toSourceLocation(), withError: e.msg()))
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_parsed_targetstate = Identifier.Compile(
|
|
||||||
node: targetstate_node, withContext: context)
|
|
||||||
guard case .Ok(let targetstate) = maybe_parsed_targetstate else {
|
|
||||||
return Result.Error(maybe_parsed_targetstate.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Ok(
|
|
||||||
SelectCaseExpression(
|
|
||||||
withKey: keysetexpression, withNextState: targetstate)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension BinaryOperatorExpression: CompilableExpression {
|
|
||||||
static func compile(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Result<(EvaluatableExpression)?> {
|
|
||||||
let expression = node.child(at: 0)!
|
|
||||||
|
|
||||||
#SkipUnlessNodeType<Node>(
|
|
||||||
node: expression, type: "binaryOperatorExpression")
|
|
||||||
|
|
||||||
let binary_operator_expression_node = expression.child(at: 0)!
|
|
||||||
var walker = Walker(node: binary_operator_expression_node)
|
|
||||||
|
|
||||||
var current_node: Node? = .none
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<EvaluatableExpression?>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Malformed binary operator expression"
|
|
||||||
)))
|
|
||||||
|
|
||||||
/// 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(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing LHS for binary operator expression")))
|
|
||||||
|
|
||||||
let left_hand_side_raw = current_node!
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<EvaluatableExpression?>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing binary operator for binary operator expression")))
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<EvaluatableExpression?>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
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 {
|
|
||||||
return Result.Error(maybe_left_hand_side.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_right_hand_side = Expression.Compile(node: right_hand_side_raw, withContext: context)
|
|
||||||
guard case Result.Ok(let right_hand_side) = maybe_right_hand_side else {
|
|
||||||
return Result.Error(maybe_right_hand_side.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
let evaluators:
|
|
||||||
[String: (String, P4QualifiedType, BinaryOperatorChecker?, BinaryOperatorEvaluator)] = [
|
|
||||||
"binaryEqualOperatorExpression": (
|
|
||||||
"Binary Equal", P4QualifiedType(P4Boolean()), Optional<BinaryOperatorChecker>.none,
|
|
||||||
binary_equal_operator_evaluator
|
|
||||||
),
|
|
||||||
"binaryLessThanOperatorExpression": (
|
|
||||||
"Binary Less Than", P4QualifiedType(P4Boolean()), Optional<BinaryOperatorChecker>.none,
|
|
||||||
binary_lt_operator_evaluator
|
|
||||||
),
|
|
||||||
"binaryLessThanEqualOperatorExpression": (
|
|
||||||
"Binary Less Than Or Equal", P4QualifiedType(P4Boolean()),
|
|
||||||
Optional<BinaryOperatorChecker>.none,
|
|
||||||
binary_lte_operator_evaluator
|
|
||||||
),
|
|
||||||
"binaryGreaterThanOperatorExpression": (
|
|
||||||
"Binary Greater Than", P4QualifiedType(P4Boolean()), Optional<BinaryOperatorChecker>.none,
|
|
||||||
binary_gt_operator_evaluator
|
|
||||||
),
|
|
||||||
"binaryGreaterThanEqualOperatorExpression": (
|
|
||||||
"Binary Greater Than Or Equal", P4QualifiedType(P4Boolean()),
|
|
||||||
Optional<BinaryOperatorChecker>.none,
|
|
||||||
binary_gte_operator_evaluator
|
|
||||||
),
|
|
||||||
"binaryAndOperatorExpression": (
|
|
||||||
"Binary Or", P4QualifiedType(P4Boolean()), binary_and_or_operator_checker,
|
|
||||||
binary_and_operator_evaluator
|
|
||||||
),
|
|
||||||
"binaryOrOperatorExpression": (
|
|
||||||
"Binary And", P4QualifiedType(P4Boolean()), binary_and_or_operator_checker,
|
|
||||||
binary_or_operator_evaluator
|
|
||||||
),
|
|
||||||
"binaryAddOperatorExpression": (
|
|
||||||
"Binary Add", P4QualifiedType(P4Int()), binary_int_math_operator_checker,
|
|
||||||
binary_add_operator_evaluator
|
|
||||||
),
|
|
||||||
"binarySubtractOperatorExpression": (
|
|
||||||
"Binary Subtract", P4QualifiedType(P4Int()), binary_int_math_operator_checker,
|
|
||||||
binary_subtract_operator_evaluator
|
|
||||||
),
|
|
||||||
"binaryMultiplyOperatorExpression": (
|
|
||||||
"Binary Multiply", P4QualifiedType(P4Int()), binary_int_math_operator_checker,
|
|
||||||
binary_multiply_operator_evaluator
|
|
||||||
),
|
|
||||||
"binaryDivideOperatorExpression": (
|
|
||||||
"Binary Divide", P4QualifiedType(P4Int()), binary_int_math_operator_checker,
|
|
||||||
binary_divide_operator_evaluator
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
guard let selected_evaluator = evaluators[binary_operator_expression_node.nodeType!] else {
|
|
||||||
return Result.Error(
|
|
||||||
Error(withMessage: "No evaluator for \(binary_operator_expression_node.nodeType!)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if let checker = selected_evaluator.2,
|
|
||||||
case .Error(let e) = checker(left_hand_side, right_hand_side)
|
|
||||||
{
|
|
||||||
return Result.Error(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Ok(
|
|
||||||
BinaryOperatorExpression(
|
|
||||||
withEvaluator: (selected_evaluator.0, selected_evaluator.1, selected_evaluator.3),
|
|
||||||
withLhs: left_hand_side, withRhs: right_hand_side))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ArrayAccessExpression: CompilableExpression {
|
|
||||||
static func compile(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Common.Result<EvaluatableExpression?> {
|
|
||||||
let expression = node.child(at: 0)!
|
|
||||||
|
|
||||||
#SkipUnlessNodeType<Node>(
|
|
||||||
node: expression, type: "arrayAccessExpression")
|
|
||||||
|
|
||||||
let array_access_expression_node = expression
|
|
||||||
|
|
||||||
var walker = Walker(node: array_access_expression_node)
|
|
||||||
var current_node: Node? = .none
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<EvaluatableExpression?>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Malformed array access expression")))
|
|
||||||
|
|
||||||
#RequireNodeType<Node, EvaluatableExpression?>(
|
|
||||||
node: current_node!, type: "expression",
|
|
||||||
nice_type_name: "array identifier expression")
|
|
||||||
let array_access_identifier_node = current_node!
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<EvaluatableExpression?>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing [ for array access expression")))
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<EvaluatableExpression?>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing indexor expression for array access expression")))
|
|
||||||
|
|
||||||
#RequireNodeType<Node, EvaluatableExpression?>(
|
|
||||||
node: current_node!, type: "expression",
|
|
||||||
nice_type_name: "array indexor expression")
|
|
||||||
|
|
||||||
let array_access_indexor_node = current_node!
|
|
||||||
|
|
||||||
let maybe_array_identifier = Expression.Compile(
|
|
||||||
node: array_access_identifier_node, withContext: context)
|
|
||||||
guard case Result.Ok(let array_identifier) = maybe_array_identifier else {
|
|
||||||
return Result.Error(maybe_array_identifier.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_array_type = array_identifier.type()
|
|
||||||
guard let array_type = maybe_array_type.baseType() as? P4Array else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: array_access_identifier_node.toSourceLocation(),
|
|
||||||
withError: "\(array_identifier) does not name an array type")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_array_indexor = Expression.Compile(
|
|
||||||
node: array_access_indexor_node, withContext: context)
|
|
||||||
guard case Result.Ok(let array_indexor) = maybe_array_indexor else {
|
|
||||||
return Result.Error(maybe_array_indexor.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Ok(
|
|
||||||
ArrayAccessExpression(
|
|
||||||
withName: array_identifier, withType: array_type, withIndexor: array_indexor))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FieldAccessExpression: CompilableExpression {
|
|
||||||
static func compile(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Common.Result<(any Common.EvaluatableExpression)?> {
|
|
||||||
let expression = node.child(at: 0)!
|
|
||||||
|
|
||||||
#SkipUnlessNodeType<Node>(
|
|
||||||
node: expression, type: "fieldAccessExpression")
|
|
||||||
|
|
||||||
let field_access_expression_node = expression
|
|
||||||
|
|
||||||
var walker = Walker(node: field_access_expression_node)
|
|
||||||
var current_node: Node? = .none
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<EvaluatableExpression?>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Malformed field access expression")))
|
|
||||||
|
|
||||||
#RequireNodeType<Node, EvaluatableExpression?>(
|
|
||||||
node: current_node!, type: "expression",
|
|
||||||
nice_type_name: "struct identifier expression")
|
|
||||||
let struct_identifier_node = current_node!
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<EvaluatableExpression?>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing . for field access expression")))
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<EvaluatableExpression?>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing field name for field access expression")))
|
|
||||||
|
|
||||||
#RequireNodeType<Node, EvaluatableExpression?>(
|
|
||||||
node: current_node!, type: "identifier",
|
|
||||||
nice_type_name: "field name")
|
|
||||||
|
|
||||||
let field_name_node = current_node!
|
|
||||||
|
|
||||||
// Make sure that the identifier really identifies a struct.
|
|
||||||
let maybe_struct_identifier = Expression.Compile(
|
|
||||||
node: struct_identifier_node, withContext: context)
|
|
||||||
guard case Result.Ok(let struct_identifier) = maybe_struct_identifier else {
|
|
||||||
return Result.Error(maybe_struct_identifier.error()!)
|
|
||||||
}
|
|
||||||
guard let struct_type = struct_identifier.type().baseType() as? P4Struct else {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: struct_identifier_node.toSourceLocation(),
|
|
||||||
withError: "\(struct_identifier_node.text!) does not have struct type"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_field_name = Identifier.Compile(
|
|
||||||
node: field_name_node, withContext: context)
|
|
||||||
guard case Result.Ok(let field_name) = maybe_field_name else {
|
|
||||||
return Result.Error(maybe_field_name.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that the field is valid for the struct type.
|
|
||||||
let maybe_field_type = struct_type.fields.get_field_type(field_name)
|
|
||||||
guard let field_type = maybe_field_type else {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: field_name_node.toSourceLocation(),
|
|
||||||
withError: "\(field_name) is not a valid field for struct with type \(struct_type)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Ok(
|
|
||||||
FieldAccessExpression(
|
|
||||||
withStruct: struct_identifier,
|
|
||||||
withField: P4StructFieldIdentifier(id: field_name, withType: field_type)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FieldAccessExpression: CompilableLValueExpression {
|
|
||||||
static func compile_as_lvalue(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Result<EvaluatableLValueExpression?> {
|
|
||||||
let expression = node.child(at: 0)!
|
|
||||||
#SkipUnlessNodeType<Node>(
|
|
||||||
node: expression, type: "fieldAccessExpression")
|
|
||||||
|
|
||||||
let maybe_parsed_expression = FieldAccessExpression.compile(node: node, withContext: context)
|
|
||||||
guard case .Ok(let maybe_field_access_expression) = maybe_parsed_expression else {
|
|
||||||
return .Error(maybe_parsed_expression.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
let field_access_expression = maybe_field_access_expression as! FieldAccessExpression
|
|
||||||
|
|
||||||
return Result.Ok(field_access_expression)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ArrayAccessExpression: CompilableLValueExpression {
|
|
||||||
static func compile_as_lvalue(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Result<EvaluatableLValueExpression?> {
|
|
||||||
let expression = node.child(at: 0)!
|
|
||||||
#SkipUnlessNodeType<Node>(
|
|
||||||
node: expression, type: "arrayAccessExpression")
|
|
||||||
|
|
||||||
let maybe_parsed_expression = ArrayAccessExpression.compile(node: node, withContext: context)
|
|
||||||
guard case .Ok(let maybe_array_access_expression) = maybe_parsed_expression else {
|
|
||||||
return .Error(maybe_parsed_expression.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
let array_access_expression = maybe_array_access_expression as! ArrayAccessExpression
|
|
||||||
|
|
||||||
return Result.Ok(array_access_expression)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FunctionCall: CompilableExpression {
|
|
||||||
static func compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<EvaluatableExpression?> {
|
|
||||||
|
|
||||||
let expression = node.child(at: 0)!
|
|
||||||
#SkipUnlessNodeType<Node>(
|
|
||||||
node: expression, type: "function_call")
|
|
||||||
|
|
||||||
var walker = Walker(node: expression)
|
|
||||||
var current_node: Node? = .none
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<EvaluatableExpression?>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Missing function call component")))
|
|
||||||
|
|
||||||
let maybe_callee_name = Identifier.Compile(
|
|
||||||
node: current_node!, withContext: context)
|
|
||||||
guard case .Ok(let callee_name) = maybe_callee_name else {
|
|
||||||
return Result.Error(maybe_callee_name.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
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<(FunctionDeclaration?, Declaration?)>.Ok((callee, .none)) // What we found is actually a function declaration
|
|
||||||
default:
|
|
||||||
Result<(FunctionDeclaration?, Declaration?)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: current_node!.toSourceLocation(),
|
|
||||||
withError: "\(callee_name) is not a function"))
|
|
||||||
}
|
|
||||||
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.baseType() {
|
|
||||||
case is FunctionDeclaration: Result.Ok((.none, callee))
|
|
||||||
default:
|
|
||||||
.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: current_node!.toSourceLocation(),
|
|
||||||
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()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<EvaluatableExpression?>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), 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()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now, compare the arguments with the parameters:
|
|
||||||
|
|
||||||
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().baseType() as! FunctionDeclaration).params)
|
|
||||||
default: Optional<ParameterList>.none
|
|
||||||
}
|
|
||||||
|
|
||||||
guard case .some(let params) = params else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
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 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(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Unexpected error occurred calling function named (\(callee_name))"
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,321 +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
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import TreeSitterExtensions
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
public struct Parser {
|
|
||||||
public struct LocalElements {
|
|
||||||
static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<(EvaluatableStatement, CompilerContext)> {
|
|
||||||
let localElementsParsers: [String: CompilableStatement.Type] = [
|
|
||||||
"variableDeclaration": VariableDeclarationStatement.self
|
|
||||||
]
|
|
||||||
|
|
||||||
guard let parser = localElementsParsers[node.nodeType ?? ""] else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Unparseable statement type (\(node.nodeType ?? "Unknown Statement Type"))"))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch parser.Compile(node: node, withContext: context) {
|
|
||||||
case Result.Ok(let (parsed, parsed_updated_scopes)):
|
|
||||||
return Result.Ok((parsed, parsed_updated_scopes))
|
|
||||||
case Result.Error(let e):
|
|
||||||
return Result.Error(Error(withMessage: "Failed to parse local element: \(e)"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Statement {
|
|
||||||
static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<(EvaluatableStatement, CompilerContext)> {
|
|
||||||
if node.nodeType != "parserStatement" && node.nodeType != "statement" {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Missing expected parser statement")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let statement = node.child(at: 0)!
|
|
||||||
|
|
||||||
let statementParsers: [String: CompilableStatement.Type] = [
|
|
||||||
"assignmentStatement": ParserAssignmentStatement.self,
|
|
||||||
"expressionStatement": ExpressionStatement.self,
|
|
||||||
"variableDeclaration": VariableDeclarationStatement.self,
|
|
||||||
"conditionalStatement": ConditionalStatement.self, "blockStatement": BlockStatement.self,
|
|
||||||
"return_statement": ReturnStatement.self,
|
|
||||||
]
|
|
||||||
guard let parser = statementParsers[statement.nodeType ?? ""] else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: statement.toSourceLocation(),
|
|
||||||
withError:
|
|
||||||
"Unparseable statement type (\(statement.nodeType ?? "Unknown Statement Type"))"))
|
|
||||||
}
|
|
||||||
switch parser.Compile(node: statement, withContext: context) {
|
|
||||||
case Result.Ok(let (parsed, updated_context)):
|
|
||||||
return .Ok((parsed, updated_context))
|
|
||||||
case Result.Error(let e):
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Failed to parse a statement element: \(e)"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct TransitionStatement {
|
|
||||||
static func Compile(
|
|
||||||
node: Node, forState state_identifier: Common.Identifier,
|
|
||||||
withStatements stmts: [EvaluatableStatement], withContext context: CompilerContext
|
|
||||||
) -> Result<(InstantiatedParserState, CompilerContext)> {
|
|
||||||
|
|
||||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
|
||||||
node: node, type: "parserTransitionStatement", nice_type_name: "parser transition statement"
|
|
||||||
)
|
|
||||||
|
|
||||||
guard let tse_node = node.child(at: 1),
|
|
||||||
tse_node.nodeType! == "transitionSelectionExpression"
|
|
||||||
else {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Could not find transition select expression"))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let next_node = tse_node.child(at: 0) else {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Could not find the next token in a transition selection expression"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the next node is an identifier, we have the simple form ...
|
|
||||||
if next_node.nodeType == "identifier" {
|
|
||||||
let maybe_parsed_next_state = Identifier.Compile(
|
|
||||||
node: next_node, withContext: context)
|
|
||||||
if case .Ok(let next_state) = maybe_parsed_next_state {
|
|
||||||
return .Ok(
|
|
||||||
(
|
|
||||||
ParserStateDirectTransition(
|
|
||||||
name: state_identifier, withStatements: stmts, withNextState: next_state), context
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
return .Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Could not parse the next state in a transition statement: \(maybe_parsed_next_state.error()!)"
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We know that the next node is a select expression.
|
|
||||||
return
|
|
||||||
switch SelectExpression.compile(node: next_node, withContext: context)
|
|
||||||
{
|
|
||||||
case .Ok(let tse):
|
|
||||||
.Ok(
|
|
||||||
(
|
|
||||||
ParserStateSelectTransition(
|
|
||||||
name: state_identifier, withStatements: stmts,
|
|
||||||
withTransitioniExpression: tse as! SelectExpression), context
|
|
||||||
))
|
|
||||||
case .Error(let e): .Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Statements {
|
|
||||||
static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<([EvaluatableStatement], CompilerContext)> {
|
|
||||||
if node.nodeType != "statements" && node.nodeType != "parserStatements" {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Did not find expected statements"))
|
|
||||||
}
|
|
||||||
|
|
||||||
var errors: (any Errorable)? = .none
|
|
||||||
var current_context = context
|
|
||||||
var parsed_s: [EvaluatableStatement] = Array()
|
|
||||||
|
|
||||||
node.enumerateNamedChildren { node in
|
|
||||||
switch Statement.Compile(
|
|
||||||
node: node, withContext: current_context)
|
|
||||||
{
|
|
||||||
case .Ok((let parsed_statement, let updated_context)):
|
|
||||||
current_context = updated_context
|
|
||||||
parsed_s.append(parsed_statement)
|
|
||||||
case .Error(let e):
|
|
||||||
errors =
|
|
||||||
if let errors = errors {
|
|
||||||
errors.append(error: e)
|
|
||||||
} else {
|
|
||||||
e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let errors = errors {
|
|
||||||
return .Error(errors)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Ok((parsed_s, current_context))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct State {
|
|
||||||
static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<(InstantiatedParserState, CompilerContext)> {
|
|
||||||
var walker = Walker(node: node)
|
|
||||||
|
|
||||||
var current_node: Node? = .none
|
|
||||||
|
|
||||||
guard let node_type = node.nodeType,
|
|
||||||
node_type == "parserState"
|
|
||||||
else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Did not find a parser state declaration"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing elements in parser state declaration")))
|
|
||||||
|
|
||||||
if current_node!.nodeType == "annotations" {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: current_node!.toSourceLocation(),
|
|
||||||
withError: "Annotations in parser state are not yet handled."))
|
|
||||||
|
|
||||||
// Would increment here.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip the keyword state
|
|
||||||
walker.next()
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing elements in parser state declaration")))
|
|
||||||
|
|
||||||
let maybe_state_identifier = Identifier.Compile(
|
|
||||||
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 '{'
|
|
||||||
walker.next()
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Missing body of state declaration")
|
|
||||||
))
|
|
||||||
|
|
||||||
var errors: (any Errorable)? = .none
|
|
||||||
var current_context = context
|
|
||||||
var parsed_s: [EvaluatableStatement] = Array()
|
|
||||||
|
|
||||||
if current_node!.nodeType == "parserStatements" {
|
|
||||||
switch Statements.Compile(
|
|
||||||
node: current_node!, withContext: current_context)
|
|
||||||
{
|
|
||||||
case .Ok(let (state_statements, updated_context)):
|
|
||||||
parsed_s = state_statements
|
|
||||||
current_context = updated_context
|
|
||||||
case .Error(let error):
|
|
||||||
errors =
|
|
||||||
if let errors = errors {
|
|
||||||
errors.append(error: error)
|
|
||||||
} else {
|
|
||||||
error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
walker.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
if let errors = errors {
|
|
||||||
return .Error(errors)
|
|
||||||
}
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing transition statement of state declaration")))
|
|
||||||
|
|
||||||
return TransitionStatement.Compile(
|
|
||||||
node: current_node!, forState: state_identifier, withStatements: parsed_s,
|
|
||||||
withContext: current_context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func Compile(
|
|
||||||
withName name: Common.Identifier, withParameters parameters: ParameterList, node: Node,
|
|
||||||
withContext context: CompilerContext
|
|
||||||
) -> Result<(P4Lang.Parser, CompilerContext)> {
|
|
||||||
|
|
||||||
var parser = P4Lang.Parser(withName: name, withParameters: parameters)
|
|
||||||
|
|
||||||
// Build a state from each one listed.
|
|
||||||
var error: (any Errorable)? = .none
|
|
||||||
|
|
||||||
var current_context = context
|
|
||||||
/// TODO: Assert that there is only one.
|
|
||||||
node.enumerateNamedChildren { parser_state in
|
|
||||||
if parser_state.nodeType != "parserState" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse a state in a nested scope.
|
|
||||||
switch Parser.State.Compile(
|
|
||||||
node: parser_state,
|
|
||||||
withContext: context.update(newInstances: current_context.instances.enter()))
|
|
||||||
{
|
|
||||||
case Result.Ok(let (state, updated_context)):
|
|
||||||
parser.states = parser.states.append(state: state)
|
|
||||||
current_context = updated_context
|
|
||||||
case Result.Error(let e): error = e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
return .Error(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Ok((parser, current_context))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,143 +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
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import TreeSitterExtensions
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
public struct Program {
|
|
||||||
public static func Compile(_ source: String) -> Result<P4Lang.Program> {
|
|
||||||
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, withFFIs: [])
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func Compile(
|
|
||||||
_ source: String, withGlobalInstances globalInstances: VarTypeScopes?,
|
|
||||||
withGlobalTypes globalTypes: TypeTypeScopes?, withFFIs ffis: [P4FFI] = Array()
|
|
||||||
) -> Result<P4Lang.Program> {
|
|
||||||
|
|
||||||
let maybe_parser = ConfigureP4Parser()
|
|
||||||
guard case .Ok(let p) = maybe_parser else {
|
|
||||||
return .Error(maybe_parser.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = p.parse(source)
|
|
||||||
guard let tree = result,
|
|
||||||
!tree.isError(lang: p4lang),
|
|
||||||
!tree.containsMissing(lang: p4lang)
|
|
||||||
else {
|
|
||||||
return Result.Error(Error(withMessage: "Could not compile the P4 program"))
|
|
||||||
}
|
|
||||||
|
|
||||||
var program = P4Lang.Program()
|
|
||||||
|
|
||||||
// Set up a context for parsing.
|
|
||||||
var compilation_context = CompilerContext()
|
|
||||||
|
|
||||||
// Add our FFIs
|
|
||||||
compilation_context = compilation_context.update(newFFIs: ffis)
|
|
||||||
|
|
||||||
var errors: (any Errorable)? = .none
|
|
||||||
|
|
||||||
// If the caller gave any global instances, add them here.
|
|
||||||
if let globalInstances = globalInstances {
|
|
||||||
compilation_context = compilation_context.update(newInstances: globalInstances)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the caller gave any global types, add them here.
|
|
||||||
if let globalTypes = globalTypes {
|
|
||||||
compilation_context = compilation_context.update(newTypes: globalTypes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to parse all top-level declarations.
|
|
||||||
result?.rootNode?.enumerateNamedChildren { (declaration_node: Node) in
|
|
||||||
let specific_declaration_node = declaration_node.child(at: 0)!
|
|
||||||
|
|
||||||
let declaration_parsers: [CompilableDeclaration.Type] = [
|
|
||||||
Declaration.self, P4Lang.Parser.self,
|
|
||||||
]
|
|
||||||
var found_parser = false
|
|
||||||
|
|
||||||
for parser in declaration_parsers {
|
|
||||||
switch parser.Compile(node: specific_declaration_node, withContext: compilation_context) {
|
|
||||||
case .Ok(.none): {}()
|
|
||||||
case .Ok(.some((_, let updated_context))):
|
|
||||||
found_parser = true
|
|
||||||
compilation_context = updated_context
|
|
||||||
break
|
|
||||||
case .Error(let e):
|
|
||||||
found_parser = true
|
|
||||||
errors =
|
|
||||||
if let errors = errors {
|
|
||||||
errors.append(error: e)
|
|
||||||
} else {
|
|
||||||
e
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If none of the declaration parsers chose to parse, that's an error, too!
|
|
||||||
if !found_parser {
|
|
||||||
|
|
||||||
let no_parser_error = ErrorWithLocation(
|
|
||||||
sourceLocation: specific_declaration_node.toSourceLocation(),
|
|
||||||
withError: "Could not find parser for declaration node"
|
|
||||||
)
|
|
||||||
errors =
|
|
||||||
if let errors = errors {
|
|
||||||
errors.append(error: no_parser_error)
|
|
||||||
} else {
|
|
||||||
no_parser_error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let errors = errors {
|
|
||||||
return .Error(errors)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any of the instances that are in the top-level scope should go into the program!
|
|
||||||
program.instances = Array(
|
|
||||||
compilation_context.instances.map { (_, v) in
|
|
||||||
v
|
|
||||||
})
|
|
||||||
|
|
||||||
// Any of the types that are in the top-level scope should go into the program!
|
|
||||||
program.types = Array(
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +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
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import TreeSitterExtensions
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
public protocol CompilableStatement {
|
|
||||||
static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<(EvaluatableStatement, CompilerContext)>
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol CompilableValue {
|
|
||||||
static func CompileValue(withValue value: String) -> Result<P4DataValue>
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol CompilableType {
|
|
||||||
static func CompileType(
|
|
||||||
type: SwiftTreeSitter.Node, withContext: CompilerContext
|
|
||||||
) -> Result<P4Type?>
|
|
||||||
}
|
|
||||||
|
|
||||||
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<(Declaration, CompilerContext)?>
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol Compilable<T> {
|
|
||||||
associatedtype T
|
|
||||||
static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<(T, CompilerContext)>
|
|
||||||
}
|
|
||||||
@@ -1,376 +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
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import TreeSitterExtensions
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
extension BlockStatement: CompilableStatement {
|
|
||||||
public static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<(EvaluatableStatement, CompilerContext)> {
|
|
||||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
|
||||||
node: node, type: "blockStatement", nice_type_name: "block statement")
|
|
||||||
|
|
||||||
var walker = Walker(node: node)
|
|
||||||
var current_node: Node? = .none
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(EvaluatableStatement, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Malformed block statement")))
|
|
||||||
|
|
||||||
if current_node!.nodeType != "{" {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: current_node!.toSourceLocation(),
|
|
||||||
withError: "Missing { on block statement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
var statements: [EvaluatableStatement] = Array()
|
|
||||||
var parse_err: (any Errorable)? = .none
|
|
||||||
var current_context = context
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(EvaluatableStatement, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Malformed block statement")))
|
|
||||||
|
|
||||||
if current_node!.nodeType == "statements" {
|
|
||||||
switch Parser.Statements.Compile(
|
|
||||||
node: current_node!, withContext: current_context)
|
|
||||||
{
|
|
||||||
case .Ok(let (parsed_statements, updated_context)):
|
|
||||||
current_context = updated_context
|
|
||||||
statements = parsed_statements
|
|
||||||
case .Error(let error):
|
|
||||||
parse_err = error
|
|
||||||
}
|
|
||||||
|
|
||||||
walker.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
if let err = parse_err {
|
|
||||||
return .Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: current_node, thing: walker.getNext(),
|
|
||||||
or: Result<(EvaluatableStatement, CompilerContext)>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "Malformed block statement")))
|
|
||||||
|
|
||||||
if current_node!.nodeType != "}" {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: current_node!.toSourceLocation(),
|
|
||||||
withError: "Missing } on block statement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Ok((BlockStatement(statements), current_context))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ConditionalStatement: CompilableStatement {
|
|
||||||
public static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<(EvaluatableStatement, CompilerContext)> {
|
|
||||||
|
|
||||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
|
||||||
node: node, type: "conditionalStatement", nice_type_name: "conditional statement")
|
|
||||||
|
|
||||||
let maybe_condition_expression = node.child(at: 2)
|
|
||||||
guard let condition_expression = maybe_condition_expression,
|
|
||||||
condition_expression.nodeType == "expression"
|
|
||||||
else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Did not find condition for conditional statement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_thens = node.child(at: 4)
|
|
||||||
guard let thens = maybe_thens,
|
|
||||||
thens.nodeType == "statement"
|
|
||||||
else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Did not find then statement block for conditional statement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard
|
|
||||||
case .Ok(let condition) = Expression.Compile(
|
|
||||||
node: condition_expression, withContext: context)
|
|
||||||
else {
|
|
||||||
return Result.Error(
|
|
||||||
Error(withMessage: "Could not parse a conditional expression in a conditional statement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard
|
|
||||||
case .Ok((let thenns, _)) = Parser.Statement.Compile(
|
|
||||||
node: thens, withContext: context)
|
|
||||||
else {
|
|
||||||
return Result.Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Could not parse the then block in a conditional statement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let optional_elss: Result<(any EvaluatableStatement, CompilerContext)>? =
|
|
||||||
if let elss = node.child(at: 6) {
|
|
||||||
.some(
|
|
||||||
Parser.Statement.Compile(
|
|
||||||
node: elss, withContext: context))
|
|
||||||
} else {
|
|
||||||
.none
|
|
||||||
}
|
|
||||||
|
|
||||||
if let parsed_elss = optional_elss {
|
|
||||||
guard
|
|
||||||
case .Ok((let elss, _)) = parsed_elss
|
|
||||||
else {
|
|
||||||
return Result.Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Could not parse the else block in a conditional statement"))
|
|
||||||
}
|
|
||||||
return .Ok(
|
|
||||||
(ConditionalStatement(condition: condition, withThen: thenns, andElse: elss), context))
|
|
||||||
}
|
|
||||||
return .Ok((ConditionalStatement(condition: condition, withThen: thenns), context))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension VariableDeclarationStatement: CompilableStatement {
|
|
||||||
public static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<(EvaluatableStatement, CompilerContext)> {
|
|
||||||
|
|
||||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
|
||||||
node: node, type: "variableDeclaration", nice_type_name: "variable declaration statement")
|
|
||||||
|
|
||||||
let maybe_typeref = node.child(at: 0)
|
|
||||||
guard let typeref = maybe_typeref,
|
|
||||||
typeref.nodeType == "typeRef"
|
|
||||||
else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Did not find type name for variable declaration statement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_variablename = node.child(at: 1)
|
|
||||||
guard let variablename = maybe_variablename,
|
|
||||||
variablename.nodeType == "identifier"
|
|
||||||
else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Did not find identifier name for variable declaration statement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_rvalue = node.childCount > 3 ? node.child(at: 3) : .none
|
|
||||||
|
|
||||||
guard
|
|
||||||
case .Ok(let parsed_variablename) = Identifier.Compile(
|
|
||||||
node: variablename, withContext: context)
|
|
||||||
else {
|
|
||||||
return Result.Error(
|
|
||||||
Error(withMessage: "Could not parse variable name"))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard case .Ok(let declaration_p4_type) = Types.CompileType(type: typeref, withContext: context)
|
|
||||||
else {
|
|
||||||
return Result.Error(
|
|
||||||
Error(withMessage: "Could not parse a P4 type from \(typeref.text!)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
var initializer: EvaluatableExpression? = .none
|
|
||||||
|
|
||||||
// If there is an initializer, it must be an expression.
|
|
||||||
if let initializer_expression = maybe_rvalue {
|
|
||||||
guard initializer_expression.nodeType == "expression" else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "initial value for declaration statement is not an expression"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_parsed_rvalue = Expression.Compile(
|
|
||||||
node: initializer_expression, withContext: context)
|
|
||||||
guard
|
|
||||||
case .Ok(let parsed_rvalue) = maybe_parsed_rvalue
|
|
||||||
else {
|
|
||||||
return .Error(maybe_parsed_rvalue.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
if parsed_rvalue.type().eq(declaration_p4_type) {
|
|
||||||
initializer = parsed_rvalue
|
|
||||||
} else {
|
|
||||||
return Result.Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Cannot initialize \(parsed_variablename) (with type \(declaration_p4_type)) from expression with type \(parsed_rvalue.type())"
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is no initializer, then it must be defaultable.
|
|
||||||
|
|
||||||
if initializer == nil {
|
|
||||||
initializer = declaration_p4_type.def()
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let initializer = initializer else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(), withError: "No initializer for declaration"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Ok(
|
|
||||||
(
|
|
||||||
VariableDeclarationStatement(
|
|
||||||
identifier: parsed_variablename, withInitializer: initializer),
|
|
||||||
// Context with updated names to include the newly declared name.
|
|
||||||
context.update(
|
|
||||||
newInstances: context.instances.declare(
|
|
||||||
identifier: parsed_variablename, withValue: declaration_p4_type))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ExpressionStatement: CompilableStatement {
|
|
||||||
public static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<(EvaluatableStatement, CompilerContext)> {
|
|
||||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
|
||||||
node: node, type: "expressionStatement", nice_type_name: "expression statement")
|
|
||||||
|
|
||||||
let expression_node = node.child(at: 0)!
|
|
||||||
|
|
||||||
return switch Expression.Compile(node: expression_node, withContext: context) {
|
|
||||||
case .Ok(let expression): .Ok((ExpressionStatement(expression), context))
|
|
||||||
case .Error(let e): .Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ParserAssignmentStatement: CompilableStatement {
|
|
||||||
public static func Compile(
|
|
||||||
node: Node, withContext context: CompilerContext
|
|
||||||
) -> Result<(EvaluatableStatement, CompilerContext)> {
|
|
||||||
|
|
||||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
|
||||||
node: node, type: "assignmentStatement", nice_type_name: "assignment statement")
|
|
||||||
|
|
||||||
guard let lvalue_node = node.child(at: 0),
|
|
||||||
lvalue_node.nodeType == "expression"
|
|
||||||
else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing lvalue in assignment statement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let rvalue_node = node.child(at: 2),
|
|
||||||
rvalue_node.nodeType == "expression"
|
|
||||||
else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError: "Missing rvalue in assignment statement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_parsed_rvalue = Expression.Compile(
|
|
||||||
node: rvalue_node, withContext: context)
|
|
||||||
guard case Result.Ok(let rvalue) = maybe_parsed_rvalue else {
|
|
||||||
return Result.Error(maybe_parsed_rvalue.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_parsed_lvalue = LValue.Compile(node: lvalue_node, withContext: context)
|
|
||||||
guard case .Ok(let lvalue_identifier) = maybe_parsed_lvalue else {
|
|
||||||
return Result.Error(maybe_parsed_lvalue.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
let check_result = lvalue_identifier.check(to: rvalue, inScopes: context.instances)
|
|
||||||
guard case .Ok(_) = check_result else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: lvalue_node.toSourceLocation(),
|
|
||||||
withError: "\(check_result.error()!)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Ok(
|
|
||||||
(
|
|
||||||
ParserAssignmentStatement(
|
|
||||||
withLValue: lvalue_identifier,
|
|
||||||
withValue: rvalue
|
|
||||||
), context
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ReturnStatement: CompilableStatement {
|
|
||||||
public static func Compile(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Common.Result<(any Common.EvaluatableStatement, CompilerContext)> {
|
|
||||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
|
||||||
node: node, type: "return_statement", nice_type_name: "return statement")
|
|
||||||
|
|
||||||
let expression_node = node.child(at: 1)!
|
|
||||||
|
|
||||||
return switch Expression.Compile(node: expression_node, withContext: context) {
|
|
||||||
case .Ok(let result):
|
|
||||||
if result.type().baseType().eq(rhs: context.expected_type!.baseType()) {
|
|
||||||
.Ok((ReturnStatement(result), context))
|
|
||||||
} else {
|
|
||||||
.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: node.toSourceLocation(),
|
|
||||||
withError:
|
|
||||||
"Type of expression in return statement (\(result.type())) is not compatible with function return type (\(context.expected_type!))"
|
|
||||||
))
|
|
||||||
}
|
|
||||||
case .Error(let e): .Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ApplyStatement: CompilableStatement {
|
|
||||||
public static func Compile(
|
|
||||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Common.Result<(any Common.EvaluatableStatement, CompilerContext)> {
|
|
||||||
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
|
|
||||||
node: node, type: "apply_statement", nice_type_name: "apply statement")
|
|
||||||
|
|
||||||
let expression_node = node.child(at: 1)!
|
|
||||||
|
|
||||||
return switch BlockStatement.Compile(node: expression_node, withContext: context) {
|
|
||||||
case .Ok((let statement, let updated_context)):
|
|
||||||
.Ok((ApplyStatement(statement as! BlockStatement), updated_context))
|
|
||||||
case .Error(let e): .Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +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
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import TreeSitterExtensions
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
extension P4Boolean: CompilableType {
|
|
||||||
public static func CompileType(
|
|
||||||
type: SwiftTreeSitter.Node, withContext: CompilerContext
|
|
||||||
) -> Common.Result<(any Common.P4Type)?> {
|
|
||||||
return type.text == "bool" ? .Ok(P4Boolean()) : .Ok(.none)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension P4Int: CompilableType {
|
|
||||||
public static func CompileType(
|
|
||||||
type: SwiftTreeSitter.Node, withContext: CompilerContext
|
|
||||||
) -> Common.Result<(any Common.P4Type)?> {
|
|
||||||
|
|
||||||
// Drill down, as appropriate.
|
|
||||||
let base_type_node = type.child(at: 0)!
|
|
||||||
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
|
|
||||||
node: base_type_node, type: "baseType")
|
|
||||||
|
|
||||||
let type_node = base_type_node.child(at: 0)!
|
|
||||||
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
|
|
||||||
node: type_node, type: "int_type")
|
|
||||||
|
|
||||||
var walker = Walker(node: type_node)
|
|
||||||
|
|
||||||
var int_node: Node? = .none
|
|
||||||
|
|
||||||
#MustOr(
|
|
||||||
result: int_node, thing: walker.getNext(),
|
|
||||||
or: Result<P4Type?>.Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: type_node.toSourceLocation(),
|
|
||||||
withError: "Missing elements in int type declaration")))
|
|
||||||
|
|
||||||
// Move passed the keyword.
|
|
||||||
walker.next()
|
|
||||||
|
|
||||||
if let bit_width_node = walker.getNext() {
|
|
||||||
guard let bit_width = Int(bit_width_node.child(at: 1)!.text!),
|
|
||||||
bit_width != 0
|
|
||||||
else {
|
|
||||||
return .Error(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: bit_width_node.toSourceLocation(),
|
|
||||||
withError: "Could not parse \(bit_width_node.text!) into integer"))
|
|
||||||
}
|
|
||||||
return .Ok(P4Int(BitWidth.Width(bit_width)))
|
|
||||||
}
|
|
||||||
return .Ok(P4Int())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension P4String: CompilableType {
|
|
||||||
public static func CompileType(
|
|
||||||
type: SwiftTreeSitter.Node, withContext: CompilerContext
|
|
||||||
) -> Common.Result<(any Common.P4Type)?> {
|
|
||||||
return type.text == "string" ? .Ok(P4String()) : .Ok(.none)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension P4Struct: CompilableType {
|
|
||||||
public static func CompileType(
|
|
||||||
type: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Common.Result<(any Common.P4Type)?> {
|
|
||||||
|
|
||||||
let maybe_parsed_type_id = Identifier.Compile(node: type, withContext: context)
|
|
||||||
guard case .Ok(let parsed_type_id) = maybe_parsed_type_id else {
|
|
||||||
return .Error(maybe_parsed_type_id.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
if case .Ok(let found_type) = context.types.lookup(identifier: parsed_type_id),
|
|
||||||
let found_struct_type = found_type as? P4Struct
|
|
||||||
{
|
|
||||||
return .Ok(found_struct_type)
|
|
||||||
}
|
|
||||||
return .Ok(.none)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Types {
|
|
||||||
static func CompileType(
|
|
||||||
type: SwiftTreeSitter.Node, withContext context: CompilerContext
|
|
||||||
) -> Result<P4QualifiedType> {
|
|
||||||
let type_parsers: [CompilableType.Type] = [
|
|
||||||
P4Boolean.self, P4Int.self, P4String.self, P4Struct.self,
|
|
||||||
]
|
|
||||||
for type_parser in type_parsers {
|
|
||||||
switch type_parser.CompileType(type: type, withContext: context) {
|
|
||||||
case .Ok(.some(let type)): return .Ok(P4QualifiedType(type))
|
|
||||||
case .Ok(.none): continue
|
|
||||||
case .Error(let e): return .Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Result.Error(Error(withMessage: "Type name not recognized"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +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 protocol Visitable {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol LanguageVisitor<Context> {
|
|
||||||
associatedtype Context
|
|
||||||
|
|
||||||
// Program
|
|
||||||
func visit(_ program: Program, _ c: VisitorContext<Context>) -> Result<VisitorContext<Context>>
|
|
||||||
|
|
||||||
// Parser
|
|
||||||
func visit(_ parser: Parser, _ c: VisitorContext<Context>) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ parser_state: InstantiatedParserState, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
|
|
||||||
// Statements
|
|
||||||
func visit(
|
|
||||||
_ variable_declaration: VariableDeclarationStatement, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ conditional: ConditionalStatement, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ block: BlockStatement, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ rtn: ReturnStatement, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ apply: ApplyStatement, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
|
|
||||||
// Expressions
|
|
||||||
func visit(
|
|
||||||
_ keyset: KeysetExpression, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ select_case: SelectCaseExpression, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ select: SelectExpression, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ array_access: ArrayAccessExpression, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ field_access: FieldAccessExpression, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ function_call: FunctionCall, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ binary_operator: BinaryOperatorExpression, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
|
|
||||||
// Declarations
|
|
||||||
func visit(_ decl: Declaration, _ c: VisitorContext<Context>) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ extern_decl: ExternDeclaration, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ func_decl: FunctionDeclaration, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
|
|
||||||
// Control
|
|
||||||
func visit(_ action: Action, _ c: VisitorContext<Context>) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ table_key_entry: TableKeyEntry, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(
|
|
||||||
_ table_property_list: TablePropertyList, _ c: VisitorContext<Context>
|
|
||||||
) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(_ table: Table, _ c: VisitorContext<Context>) -> Result<VisitorContext<Context>>
|
|
||||||
func visit(_ control: Control, _ c: VisitorContext<Context>) -> Result<VisitorContext<Context>>
|
|
||||||
}
|
|
||||||
@@ -1,291 +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 Action: CustomStringConvertible, P4Type, P4DataValue {
|
|
||||||
public func type() -> any P4Type {
|
|
||||||
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 P4Type) -> 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() -> P4DataValue? {
|
|
||||||
return .none
|
|
||||||
}
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
return "Action: "
|
|
||||||
+ "\(self.name) with parameters \(self.params) and body \(String(describing: self.body))"
|
|
||||||
}
|
|
||||||
|
|
||||||
public var body: BlockStatement?
|
|
||||||
public var params: ParameterList
|
|
||||||
public var name: Identifier
|
|
||||||
|
|
||||||
public init(
|
|
||||||
named name: Identifier = Identifier(name: ""),
|
|
||||||
withParameters parameters: ParameterList = ParameterList([]),
|
|
||||||
withBody body: BlockStatement? = .none
|
|
||||||
) {
|
|
||||||
self.name = name
|
|
||||||
self.params = parameters
|
|
||||||
self.body = body
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Actions: CustomStringConvertible {
|
|
||||||
public let actions: [Action]
|
|
||||||
public init(withActions actions: [Action]) {
|
|
||||||
self.actions = actions
|
|
||||||
}
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
return "Actions: "
|
|
||||||
+ actions.map { action in
|
|
||||||
return "\(action)"
|
|
||||||
}.joined(separator: ";")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum TableKeyMatchType {
|
|
||||||
case Exact
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct TableKeyEntry: CustomStringConvertible {
|
|
||||||
public let key: KeysetExpression
|
|
||||||
public let match_type: TableKeyMatchType
|
|
||||||
|
|
||||||
public init(_ key: KeysetExpression, _ match: TableKeyMatchType) {
|
|
||||||
self.key = key
|
|
||||||
self.match_type = match
|
|
||||||
}
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
return "Table Key Entry: " + "\(self.key): \(self.match_type)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct TableKeys: CustomStringConvertible {
|
|
||||||
public let keys: [TableKeyEntry]
|
|
||||||
|
|
||||||
public init(withEntries entries: [TableKeyEntry]) {
|
|
||||||
self.keys = entries
|
|
||||||
}
|
|
||||||
public init() {
|
|
||||||
self.keys = []
|
|
||||||
}
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
return "Table Keys: "
|
|
||||||
+ self.keys.map { key in
|
|
||||||
return "\(key)"
|
|
||||||
}.joined(separator: ";")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
public let actions: TableActionsProperty
|
|
||||||
public let keys: TableKeys
|
|
||||||
public init(withActions actions: TableActionsProperty, withKeys keys: TableKeys) {
|
|
||||||
self.actions = actions
|
|
||||||
self.keys = keys
|
|
||||||
}
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
return "Table Property List: \(self.actions) \(self.keys)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Table: CustomStringConvertible {
|
|
||||||
public let properties: TablePropertyList
|
|
||||||
let name: Identifier
|
|
||||||
public let entries: [(P4Value, TypedIdentifier)]
|
|
||||||
|
|
||||||
public init(
|
|
||||||
withName name: Identifier, withPropertyList property_list: TablePropertyList,
|
|
||||||
withEntries entries: [(P4Value, TypedIdentifier)] = []
|
|
||||||
) {
|
|
||||||
self.name = name
|
|
||||||
self.properties = property_list
|
|
||||||
self.entries = entries
|
|
||||||
}
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
return "Table named: \(self.name) \(self.properties)"
|
|
||||||
}
|
|
||||||
|
|
||||||
/// When the control is evaluated, the value of the x in the table is
|
|
||||||
/// compared to the entries and the match is assocated with an action
|
|
||||||
/// that is invoked when the match occurs!
|
|
||||||
|
|
||||||
public func update(addEntry entry: (P4Value, TypedIdentifier)) -> Table {
|
|
||||||
return Table(
|
|
||||||
withName: self.name, withPropertyList: self.properties, withEntries: self.entries + [entry])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Control: P4Type, P4DataValue, Equatable, CustomStringConvertible {
|
|
||||||
public static func == (lhs: Control, rhs: Control) -> Bool {
|
|
||||||
// Two "bare" controls are always equal.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
public func eq(rhs: any P4Type) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case is Control: true
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func type() -> any P4Type {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any operation between two "bare" parser states is always true.
|
|
||||||
public func eq(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case is Control: true
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func lt(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case is Control: true
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func lte(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case is Control: true
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func gt(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case is Control: true
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func gte(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case is Control: true
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
return "Control named \(self._name) \(self.parameters) \(self.actions) \(self.table)"
|
|
||||||
}
|
|
||||||
|
|
||||||
public let actions: Actions
|
|
||||||
public let table: Table
|
|
||||||
let _parameters: ParameterList
|
|
||||||
let _name: Identifier
|
|
||||||
let apply: ApplyStatement
|
|
||||||
|
|
||||||
public var parameters: ParameterList {
|
|
||||||
_parameters
|
|
||||||
}
|
|
||||||
|
|
||||||
public var name: Identifier {
|
|
||||||
_name
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(
|
|
||||||
named: Identifier, withParameters parameters: ParameterList, withTable table: Table,
|
|
||||||
withActions actions: Actions, withApply apply: ApplyStatement
|
|
||||||
) {
|
|
||||||
self._name = named
|
|
||||||
self._parameters = parameters
|
|
||||||
self.actions = actions
|
|
||||||
self.table = table
|
|
||||||
self.apply = apply
|
|
||||||
}
|
|
||||||
|
|
||||||
public func updateTable(addEntry entry: (P4Value, TypedIdentifier)) -> Control {
|
|
||||||
let table = self.table.update(addEntry: entry)
|
|
||||||
|
|
||||||
return Control(
|
|
||||||
named: self.name, withParameters: self.parameters, withTable: table,
|
|
||||||
withActions: self.actions, withApply: self.apply)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func def() -> P4DataValue? {
|
|
||||||
return .none
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,132 +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 Declaration: P4Type {
|
|
||||||
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.P4Type) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case let rrhs as Declaration:
|
|
||||||
self.identifier.type.baseType().eq(rhs: rrhs.identifier.type.baseType())
|
|
||||||
&& self.extern == rrhs.extern
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func def() -> P4DataValue? {
|
|
||||||
/// TODO: Is a default of the extern'd type the right way to go?
|
|
||||||
return self.identifier.type.baseType().def()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func type() -> any Common.P4Type {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
public var description: String {
|
|
||||||
return "Extern \(self.identifier)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct ExternDeclaration {}
|
|
||||||
|
|
||||||
public struct FunctionDeclaration: P4Type, P4DataValue {
|
|
||||||
public func type() -> any Common.P4Type {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
public func eq(rhs: any Common.P4Type) -> Bool {
|
|
||||||
switch rhs {
|
|
||||||
case let frhs as FunctionDeclaration:
|
|
||||||
return frhs.tipe.eq(self.tipe) && frhs.params == self.params
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func eq(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
switch rhs {
|
|
||||||
case let frhs as FunctionDeclaration: return self.eq(rhs: frhs as P4Type)
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func lt(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
switch rhs {
|
|
||||||
case let frhs as FunctionDeclaration: return self.name < frhs.name
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func lte(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
switch rhs {
|
|
||||||
case let frhs as FunctionDeclaration: return self.name <= frhs.name
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public func gt(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
switch rhs {
|
|
||||||
case let frhs as FunctionDeclaration: return self.name > frhs.name
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func gte(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
switch rhs {
|
|
||||||
case let frhs as FunctionDeclaration: return self.name >= frhs.name
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func def() -> P4DataValue? {
|
|
||||||
return .none
|
|
||||||
}
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
return "Function named \(self.name) that returns \(self.tipe) with parameters \(self.params)"
|
|
||||||
}
|
|
||||||
|
|
||||||
public var body: BlockStatement?
|
|
||||||
public var params: ParameterList
|
|
||||||
public var name: Identifier
|
|
||||||
public var tipe: P4QualifiedType
|
|
||||||
|
|
||||||
public init(
|
|
||||||
named name: Identifier, ofType type: P4QualifiedType, withParameters parameters: ParameterList,
|
|
||||||
withBody body: BlockStatement?
|
|
||||||
) {
|
|
||||||
self.name = name
|
|
||||||
self.tipe = type
|
|
||||||
self.params = parameters
|
|
||||||
self.body = body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,149 +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 KeysetExpression {
|
|
||||||
public let key: EvaluatableExpression
|
|
||||||
|
|
||||||
public init(_ key: EvaluatableExpression) {
|
|
||||||
self.key = key
|
|
||||||
}
|
|
||||||
|
|
||||||
public func compatible(type: P4QualifiedType) -> Result<()> {
|
|
||||||
if let key_type = self.key.type().baseType() as? P4Set {
|
|
||||||
if !key_type.set_type().eq(type) {
|
|
||||||
return .Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Key expression of type set of type \(key_type.set_type()) is not compatible with selector type \(type)"
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else if !self.key.type().eq(type) {
|
|
||||||
return .Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Key expression of type \(self.key.type()) is not compatible with selector type \(type)"
|
|
||||||
))
|
|
||||||
}
|
|
||||||
return .Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct SelectCaseExpression {
|
|
||||||
public let key: KeysetExpression
|
|
||||||
public let next_state_identifier: Identifier
|
|
||||||
public let next_state: ParserState?
|
|
||||||
|
|
||||||
public init(withKey key: KeysetExpression, withNextState next_state_id: Identifier) {
|
|
||||||
self.key = key
|
|
||||||
self.next_state_identifier = next_state_id
|
|
||||||
self.next_state = .none
|
|
||||||
}
|
|
||||||
public init(
|
|
||||||
withKey key: KeysetExpression, withNextState next_state_id: Identifier,
|
|
||||||
withNextState next_state: ParserState?
|
|
||||||
) {
|
|
||||||
self.key = key
|
|
||||||
self.next_state_identifier = next_state_id
|
|
||||||
self.next_state = next_state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct SelectExpression {
|
|
||||||
public let selector: EvaluatableExpression
|
|
||||||
public let case_expressions: [SelectCaseExpression]
|
|
||||||
|
|
||||||
public init(
|
|
||||||
withSelector selector: EvaluatableExpression,
|
|
||||||
withSelectCaseExpressions sces: [SelectCaseExpression]
|
|
||||||
) {
|
|
||||||
self.selector = selector
|
|
||||||
self.case_expressions = sces
|
|
||||||
}
|
|
||||||
|
|
||||||
public func append_checked_sce(sce: SelectCaseExpression) -> SelectExpression {
|
|
||||||
var new_cses = self.case_expressions
|
|
||||||
new_cses.append(sce)
|
|
||||||
return SelectExpression(
|
|
||||||
withSelector: self.selector, withSelectCaseExpressions: new_cses)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public typealias NamedBinaryOperatorEvaluator = (
|
|
||||||
String, P4QualifiedType, (P4Value, P4Value) -> P4DataValue
|
|
||||||
)
|
|
||||||
public typealias BinaryOperatorEvaluator = (P4Value, P4Value) -> P4DataValue
|
|
||||||
|
|
||||||
public struct BinaryOperatorExpression {
|
|
||||||
public let evaluator: NamedBinaryOperatorEvaluator
|
|
||||||
public let left: EvaluatableExpression
|
|
||||||
public let right: EvaluatableExpression
|
|
||||||
|
|
||||||
public init(
|
|
||||||
withEvaluator evaluator: NamedBinaryOperatorEvaluator, withLhs lhs: EvaluatableExpression,
|
|
||||||
withRhs rhs: EvaluatableExpression
|
|
||||||
) {
|
|
||||||
self.evaluator = evaluator
|
|
||||||
self.left = lhs
|
|
||||||
self.right = rhs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct ArrayAccessExpression {
|
|
||||||
public let indexor: EvaluatableExpression
|
|
||||||
public let name: EvaluatableExpression
|
|
||||||
public let type: P4Array
|
|
||||||
|
|
||||||
public init(
|
|
||||||
withName name: EvaluatableExpression, withType type: P4Array,
|
|
||||||
withIndexor indexor: EvaluatableExpression
|
|
||||||
) {
|
|
||||||
self.name = name
|
|
||||||
self.type = type
|
|
||||||
self.indexor = indexor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct FieldAccessExpression {
|
|
||||||
public let field: P4StructFieldIdentifier
|
|
||||||
public let strct: EvaluatableExpression
|
|
||||||
|
|
||||||
public init(withStruct strct: EvaluatableExpression, withField field: P4StructFieldIdentifier) {
|
|
||||||
self.strct = strct
|
|
||||||
self.field = field
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct FunctionCall {
|
|
||||||
public let callee: (FunctionDeclaration?, P4FFI?)
|
|
||||||
public let arguments: ArgumentList
|
|
||||||
public let return_type: P4Type
|
|
||||||
|
|
||||||
public init(_ callee: FunctionDeclaration, withArguments arguments: ArgumentList) {
|
|
||||||
self.callee = (callee, .none)
|
|
||||||
self.arguments = arguments
|
|
||||||
self.return_type = callee.tipe.baseType()
|
|
||||||
}
|
|
||||||
|
|
||||||
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().baseType() as! FunctionDeclaration).tipe.baseType()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,364 +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 LocalElements {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct LocalElement {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct ParserAssignmentStatement {
|
|
||||||
public let lvalue: EvaluatableLValueExpression
|
|
||||||
public let value: EvaluatableExpression
|
|
||||||
|
|
||||||
public init(
|
|
||||||
withLValue lvalue: EvaluatableLValueExpression, withValue value: EvaluatableExpression
|
|
||||||
) {
|
|
||||||
self.lvalue = lvalue
|
|
||||||
self.value = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A P4 Parser State
|
|
||||||
///
|
|
||||||
/// Note: A P4 Parser State is both a type and a value.
|
|
||||||
/// This "bare" parser state represents the state more as a type than a value.
|
|
||||||
public class ParserState: P4Type, P4DataValue, Equatable, CustomStringConvertible {
|
|
||||||
public static func == (lhs: ParserState, rhs: ParserState) -> Bool {
|
|
||||||
// Two "bare" parser states are always equal.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
public func eq(rhs: any Common.P4Type) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case is ParserState: true
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func type() -> any Common.P4Type {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any operation between two "bare" parser states is always true.
|
|
||||||
public func eq(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case is ParserState: true
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func lt(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case is ParserState: true
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func lte(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case is ParserState: true
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func gt(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case is ParserState: true
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func gte(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case is ParserState: true
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
return "Bare Parser State"
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a ParserState
|
|
||||||
public init() {}
|
|
||||||
|
|
||||||
public func def() -> P4DataValue? {
|
|
||||||
return .none
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Instantiated Parser State
|
|
||||||
///
|
|
||||||
/// A parser state is both a type and a value. The Instantiated
|
|
||||||
/// Parser State is the base class for parser states that act more
|
|
||||||
/// as a value than a type.
|
|
||||||
public class InstantiatedParserState: ParserState {
|
|
||||||
public static func == (lhs: InstantiatedParserState, rhs: InstantiatedParserState) -> Bool {
|
|
||||||
return lhs.state == rhs.state
|
|
||||||
}
|
|
||||||
|
|
||||||
public override func eq(rhs: any Common.P4Type) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case is ParserState: true
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override func type() -> any Common.P4Type {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
public override func eq(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case let other as InstantiatedParserState: self.state == other.state
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override func lt(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case let other as InstantiatedParserState: self.state < other.state
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override func lte(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case let other as InstantiatedParserState: self.state <= other.state
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override func gt(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case let other as InstantiatedParserState: self.state > other.state
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override func gte(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case let other as InstantiatedParserState: self.state >= other.state
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public private(set) var state: Identifier
|
|
||||||
public private(set) var statements: [EvaluatableStatement]
|
|
||||||
|
|
||||||
public override var description: String {
|
|
||||||
return "Name: \(state)"
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a ParserState
|
|
||||||
public init(
|
|
||||||
name: Identifier, withStatements stmts: [EvaluatableStatement],
|
|
||||||
) {
|
|
||||||
state = name
|
|
||||||
statements = stmts
|
|
||||||
}
|
|
||||||
|
|
||||||
/// (private) constructor (no transition)
|
|
||||||
///
|
|
||||||
/// accept and reject are the only final states and they are constructed internally.
|
|
||||||
private init(name: Identifier) {
|
|
||||||
state = name
|
|
||||||
statements = Array()
|
|
||||||
}
|
|
||||||
|
|
||||||
public override func def() -> any P4DataValue {
|
|
||||||
return InstantiatedParserState(name: Identifier(name: ""))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ParserStateDirectTransition: InstantiatedParserState {
|
|
||||||
|
|
||||||
private let next_state: Identifier
|
|
||||||
|
|
||||||
public init(
|
|
||||||
name: Identifier, withStatements stmts: [EvaluatableStatement],
|
|
||||||
withNextState next_state: Identifier
|
|
||||||
) {
|
|
||||||
self.next_state = next_state
|
|
||||||
super.init(name: name, withStatements: stmts)
|
|
||||||
}
|
|
||||||
|
|
||||||
public override var description: String {
|
|
||||||
return "State (Name: \(super.state) (direct transition))"
|
|
||||||
}
|
|
||||||
|
|
||||||
public func get_next_state() -> Identifier {
|
|
||||||
return self.next_state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ParserStateNoTransition: InstantiatedParserState {
|
|
||||||
public override init(name: Identifier, withStatements stmts: [any EvaluatableStatement]) {
|
|
||||||
super.init(name: name, withStatements: stmts)
|
|
||||||
}
|
|
||||||
public override var description: String {
|
|
||||||
return "State (Name: \(super.state) (no transition))"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ParserStateSelectTransition: InstantiatedParserState {
|
|
||||||
|
|
||||||
public let selectExpression: SelectExpression
|
|
||||||
|
|
||||||
public override var description: String {
|
|
||||||
return "State (Name: \(super.state) (select transition))"
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(
|
|
||||||
name: Identifier, withStatements stmts: [any EvaluatableStatement],
|
|
||||||
withTransitioniExpression te: SelectExpression
|
|
||||||
) {
|
|
||||||
self.selectExpression = te
|
|
||||||
super.init(name: name, withStatements: stmts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nonisolated(unsafe) public let accept = ParserStateNoTransition(
|
|
||||||
name: Identifier(name: "accept"), withStatements: [])
|
|
||||||
nonisolated(unsafe) public let reject = ParserStateNoTransition(
|
|
||||||
name: Identifier(name: "reject"), withStatements: [])
|
|
||||||
|
|
||||||
public struct ParserStates {
|
|
||||||
public var states: [InstantiatedParserState] = Array()
|
|
||||||
|
|
||||||
public func count() -> Int {
|
|
||||||
return states.count
|
|
||||||
}
|
|
||||||
|
|
||||||
public func find(withIdentifier id: Identifier) -> ParserState? {
|
|
||||||
for state in states {
|
|
||||||
if state.state == id {
|
|
||||||
return .some(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return .none
|
|
||||||
}
|
|
||||||
|
|
||||||
public init() {
|
|
||||||
self.states = Array()
|
|
||||||
}
|
|
||||||
|
|
||||||
private init(withStates states: [InstantiatedParserState]) {
|
|
||||||
self.states = states
|
|
||||||
}
|
|
||||||
|
|
||||||
public func append(state: InstantiatedParserState) -> ParserStates {
|
|
||||||
var new_states = self.states
|
|
||||||
new_states.append(state)
|
|
||||||
return ParserStates(withStates: new_states)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A P4 Parser
|
|
||||||
///
|
|
||||||
/// Note: A Parser is both a type _and_ a value.
|
|
||||||
public struct Parser: P4Type, P4DataValue {
|
|
||||||
public func type() -> any Common.P4Type {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
public func eq(rhs: any Common.P4Type) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case let parser_rhs as Parser: self.name == parser_rhs.name
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func lt(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case let parser_rhs as Parser: self.name < parser_rhs.name
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func lte(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case let parser_rhs as Parser: self.name <= parser_rhs.name
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func gt(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case let parser_rhs as Parser: self.name > parser_rhs.name
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func gte(rhs: any Common.P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case let parser_rhs as Parser: self.name >= parser_rhs.name
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public var states: ParserStates
|
|
||||||
|
|
||||||
public var name: Identifier
|
|
||||||
public var parameters: ParameterList
|
|
||||||
|
|
||||||
public init(withName name: Identifier) {
|
|
||||||
self.states = ParserStates()
|
|
||||||
self.parameters = ParameterList()
|
|
||||||
self.name = name
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(withName name: Identifier, withParameters parameters: ParameterList) {
|
|
||||||
self.states = ParserStates()
|
|
||||||
self.parameters = parameters
|
|
||||||
self.name = name
|
|
||||||
}
|
|
||||||
|
|
||||||
public func findStartState() -> ParserState? {
|
|
||||||
for state in states.states {
|
|
||||||
if state.state == Identifier(name: "start") {
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return .none
|
|
||||||
}
|
|
||||||
|
|
||||||
public func eq(rhs: any P4DataValue) -> Bool {
|
|
||||||
return switch rhs {
|
|
||||||
case let other as Parser: self.name == other.name
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
return "Parser \(self.name) with parameters: \(parameters) and states: \(self.states)"
|
|
||||||
}
|
|
||||||
|
|
||||||
public func def() -> P4DataValue? {
|
|
||||||
return .none
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Launder a parser state into an instantiated parser state.
|
|
||||||
public func AsInstantiatedParserState(_ state: ParserState) -> InstantiatedParserState {
|
|
||||||
return state as! InstantiatedParserState
|
|
||||||
}
|
|
||||||
@@ -1,127 +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 ExpressionStatement {
|
|
||||||
public let expression: EvaluatableExpression
|
|
||||||
|
|
||||||
public init(_ expr: EvaluatableExpression) {
|
|
||||||
self.expression = expr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Program {
|
|
||||||
public var types: [P4Type] = Array()
|
|
||||||
public var externs: [P4Type] = Array()
|
|
||||||
public var instances: [P4QualifiedType] = Array()
|
|
||||||
|
|
||||||
/// Type of closure for filtering results from ``Program/InstancesWithTypes(_:)``
|
|
||||||
public typealias TypeFilter = (P4QualifiedType) -> Bool
|
|
||||||
/// Type of closure for filtering results from ``Program/TypesWithTypes(_:)``
|
|
||||||
public typealias DataTypeFilter = (P4Type) -> Bool
|
|
||||||
|
|
||||||
/// Retrieve global instances in the compiled P4 program.
|
|
||||||
public func InstancesWithTypes() -> [P4QualifiedType] {
|
|
||||||
return self.instances
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve global instances in the compiled P4 program.
|
|
||||||
///
|
|
||||||
/// Use the given filter to select which of the global instances
|
|
||||||
/// from the compiled P4 program to retrieve.
|
|
||||||
///
|
|
||||||
/// If the compiled P4 program (from the source in the
|
|
||||||
/// string `p4_program_with_control_decl`) has two Control
|
|
||||||
/// instances and you only want to select the one named simple,
|
|
||||||
/// you could use a filter like
|
|
||||||
///
|
|
||||||
/// @Snippet(path: "use-program-instanceswithtypes", slice: "include")
|
|
||||||
///
|
|
||||||
public func InstancesWithTypes(_ filter: TypeFilter) -> [P4QualifiedType] {
|
|
||||||
return self.instances.filter { instance in
|
|
||||||
filter(instance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve global types in the compiled P4 program.
|
|
||||||
public func TypesWithTypes() -> [P4Type] {
|
|
||||||
return self.types
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve global types declared in the compiled P4 program.
|
|
||||||
///
|
|
||||||
/// Use the given filter to select which of the global 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 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 TypesWithTypes(_ filter: DataTypeFilter) -> [P4Type] {
|
|
||||||
return self.types.filter { instance in
|
|
||||||
filter(instance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve extern types in the compiled P4 program.
|
|
||||||
public func Externs() -> [P4Type] {
|
|
||||||
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) -> [P4Type] {
|
|
||||||
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.
|
|
||||||
public func starting_parser() -> Result<Parser> {
|
|
||||||
return self.find_parser(withName: Identifier(name: "main_parser"))
|
|
||||||
}
|
|
||||||
|
|
||||||
public func find_parser(withName name: Identifier) -> Result<Parser> {
|
|
||||||
for instance in self.instances {
|
|
||||||
guard let parser = instance.baseType() as? Parser else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if parser.name == name {
|
|
||||||
return .Ok(parser)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return .Error(Error(withMessage: "Could not find parser named \(name)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
public init() {}
|
|
||||||
}
|
|
||||||
@@ -1,74 +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 VariableDeclarationStatement {
|
|
||||||
public var initializer: EvaluatableExpression
|
|
||||||
public var identifier: Identifier
|
|
||||||
public init(identifier: Identifier, withInitializer initializer: EvaluatableExpression) {
|
|
||||||
self.identifier = identifier
|
|
||||||
self.initializer = initializer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct ConditionalStatement {
|
|
||||||
public var condition: EvaluatableExpression
|
|
||||||
public var thenn: EvaluatableStatement
|
|
||||||
public var elss: EvaluatableStatement?
|
|
||||||
|
|
||||||
public init(condition: EvaluatableExpression, withThen thenn: EvaluatableStatement) {
|
|
||||||
self.condition = condition
|
|
||||||
self.thenn = thenn
|
|
||||||
self.elss = .none
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(
|
|
||||||
condition: EvaluatableExpression, withThen thenn: EvaluatableStatement,
|
|
||||||
andElse elss: EvaluatableStatement
|
|
||||||
) {
|
|
||||||
self.condition = condition
|
|
||||||
self.thenn = thenn
|
|
||||||
self.elss = elss
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct BlockStatement {
|
|
||||||
public var statements: [EvaluatableStatement]
|
|
||||||
|
|
||||||
public init(_ statements: [EvaluatableStatement]) {
|
|
||||||
self.statements = statements
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct ReturnStatement {
|
|
||||||
public let value: EvaluatableExpression
|
|
||||||
|
|
||||||
public init(_ value: EvaluatableExpression) {
|
|
||||||
self.value = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct ApplyStatement {
|
|
||||||
public let body: BlockStatement?
|
|
||||||
|
|
||||||
public init() { self.body = .none }
|
|
||||||
public init(_ body: BlockStatement) {
|
|
||||||
self.body = body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,130 +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
|
|
||||||
|
|
||||||
// Witness the parts of a P4 Program that are visitable.
|
|
||||||
extension Program: Visitable {}
|
|
||||||
extension Parser: Visitable {}
|
|
||||||
extension ParserState: Visitable {}
|
|
||||||
extension VariableDeclarationStatement: Visitable {}
|
|
||||||
extension ConditionalStatement: Visitable {}
|
|
||||||
extension BlockStatement: Visitable {}
|
|
||||||
extension ReturnStatement: Visitable {}
|
|
||||||
extension ApplyStatement: Visitable {}
|
|
||||||
extension KeysetExpression: Visitable {}
|
|
||||||
extension SelectCaseExpression: Visitable {}
|
|
||||||
extension SelectExpression: Visitable {}
|
|
||||||
extension ArrayAccessExpression: Visitable {}
|
|
||||||
extension FieldAccessExpression: Visitable {}
|
|
||||||
extension FunctionCall: Visitable {}
|
|
||||||
extension BinaryOperatorExpression: Visitable {}
|
|
||||||
extension Declaration: Visitable {}
|
|
||||||
extension ExternDeclaration: Visitable {}
|
|
||||||
extension FunctionDeclaration: Visitable {}
|
|
||||||
extension Action: Visitable {}
|
|
||||||
extension TableKeyEntry: Visitable {}
|
|
||||||
extension TablePropertyList: Visitable {}
|
|
||||||
extension Table: Visitable {}
|
|
||||||
extension Control: Visitable {}
|
|
||||||
|
|
||||||
/// Context for the visiting process.
|
|
||||||
public struct VisitorContext<UserContext> {
|
|
||||||
let visitor: VisitorDriver<UserContext>
|
|
||||||
let uc: UserContext
|
|
||||||
public init(_ v: VisitorDriver<UserContext>, _ uc: UserContext) {
|
|
||||||
self.visitor = v
|
|
||||||
self.uc = uc
|
|
||||||
}
|
|
||||||
|
|
||||||
public func getUserContext() -> UserContext {
|
|
||||||
return self.uc
|
|
||||||
}
|
|
||||||
|
|
||||||
public func getVisitorDriver() -> VisitorDriver<UserContext> {
|
|
||||||
return self.visitor
|
|
||||||
}
|
|
||||||
|
|
||||||
public func next(uc: UserContext) -> VisitorContext<UserContext> {
|
|
||||||
return VisitorContext<UserContext>(self.visitor, uc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A driver for visiting the components of a parsed P4 program.
|
|
||||||
public struct VisitorDriver<UserContext> {
|
|
||||||
let visitor: any LanguageVisitor<UserContext>
|
|
||||||
|
|
||||||
public init(_ visitor: any LanguageVisitor<UserContext>) {
|
|
||||||
self.visitor = visitor
|
|
||||||
}
|
|
||||||
|
|
||||||
public func generateContext(uc: UserContext) -> VisitorContext<UserContext> {
|
|
||||||
return VisitorContext(self, uc)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Visit a `P4Type`.
|
|
||||||
public func visit(
|
|
||||||
_ t: P4Type, context: VisitorContext<UserContext>
|
|
||||||
) -> Result<VisitorContext<UserContext>> {
|
|
||||||
return switch t {
|
|
||||||
case let vv as Control: visitor.visit(vv, context)
|
|
||||||
case let vv as Parser: visitor.visit(vv, context)
|
|
||||||
default: .Error(Error(withMessage: "Could not visit type \(t)"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Visit a part of a P4 program.
|
|
||||||
public func visit(
|
|
||||||
_ v: Visitable, context: VisitorContext<UserContext>
|
|
||||||
) -> Result<VisitorContext<UserContext>> {
|
|
||||||
return switch v {
|
|
||||||
case let vv as Program: visitor.visit(vv, context)
|
|
||||||
case let vv as Parser: visitor.visit(vv, context)
|
|
||||||
case let vv as InstantiatedParserState: visitor.visit(vv, context)
|
|
||||||
case let vv as VariableDeclarationStatement: visitor.visit(vv, context)
|
|
||||||
case let vv as ConditionalStatement: visitor.visit(vv, context)
|
|
||||||
case let vv as BlockStatement: visitor.visit(vv, context)
|
|
||||||
case let vv as ReturnStatement: visitor.visit(vv, context)
|
|
||||||
case let vv as ApplyStatement: visitor.visit(vv, context)
|
|
||||||
case let vv as KeysetExpression: visitor.visit(vv, context)
|
|
||||||
case let vv as SelectCaseExpression: visitor.visit(vv, context)
|
|
||||||
case let vv as SelectExpression: visitor.visit(vv, context)
|
|
||||||
case let vv as ArrayAccessExpression: visitor.visit(vv, context)
|
|
||||||
case let vv as FieldAccessExpression: visitor.visit(vv, context)
|
|
||||||
case let vv as FunctionCall: visitor.visit(vv, context)
|
|
||||||
case let vv as BinaryOperatorExpression: visitor.visit(vv, context)
|
|
||||||
case let vv as Declaration: visitor.visit(vv, context)
|
|
||||||
case let vv as ExternDeclaration: visitor.visit(vv, context)
|
|
||||||
case let vv as FunctionDeclaration: visitor.visit(vv, context)
|
|
||||||
case let vv as Action: visitor.visit(vv, context)
|
|
||||||
case let vv as TableKeyEntry: visitor.visit(vv, context)
|
|
||||||
case let vv as TablePropertyList: visitor.visit(vv, context)
|
|
||||||
case let vv as Table: visitor.visit(vv, context)
|
|
||||||
case let vv as Control: visitor.visit(vv, context)
|
|
||||||
default: .Error(Error(withMessage: "Could not visit \(v)"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start the process of visiting a P4 program.
|
|
||||||
public func start(_ v: Visitable, context: UserContext) -> Result<UserContext> {
|
|
||||||
let visit_result = self.visit(v, context: VisitorContext(self, context))
|
|
||||||
return switch visit_result {
|
|
||||||
case .Ok(let vc): .Ok(vc.getUserContext())
|
|
||||||
case .Error(let e): .Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,680 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
extension P4Value: CST.Categories.Expression {}
|
||||||
|
|
||||||
|
public struct CST {
|
||||||
|
|
||||||
|
public struct Categories {
|
||||||
|
public protocol LanguageElement {}
|
||||||
|
public protocol Expression: Categories.LanguageElement {}
|
||||||
|
public protocol Statement: Categories.LanguageElement {}
|
||||||
|
public protocol State: Categories.LanguageElement {}
|
||||||
|
public protocol Declaration: Categories.Statement {}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Expression {}
|
||||||
|
|
||||||
|
public struct Statements: Categories.Statement {
|
||||||
|
public let statements: [Categories.Statement]
|
||||||
|
|
||||||
|
public init(_ s: [Categories.Statement]) {
|
||||||
|
self.statements = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct VariableDeclarationStatement: Categories.Statement {
|
||||||
|
public var initializer: Categories.Expression?
|
||||||
|
public var identifier: CST.Identifier
|
||||||
|
public var tipe: CST.Tipe
|
||||||
|
|
||||||
|
public init(
|
||||||
|
identifier: Identifier, withType tipe: CST.Tipe,
|
||||||
|
withInitializer initializer: Categories.Expression?
|
||||||
|
) {
|
||||||
|
self.identifier = identifier
|
||||||
|
self.initializer = initializer
|
||||||
|
self.tipe = tipe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ConditionalStatement: Categories.Statement {
|
||||||
|
public var condition: Categories.Expression
|
||||||
|
public var thenn: Categories.Statement
|
||||||
|
public var elss: Categories.Statement?
|
||||||
|
|
||||||
|
public init(condition: Categories.Expression, withThen thenn: Categories.Statement) {
|
||||||
|
self.condition = condition
|
||||||
|
self.thenn = thenn
|
||||||
|
self.elss = .none
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(
|
||||||
|
condition: Categories.Expression, withThen thenn: Categories.Statement,
|
||||||
|
andElse elss: Categories.Statement
|
||||||
|
) {
|
||||||
|
self.condition = condition
|
||||||
|
self.thenn = thenn
|
||||||
|
self.elss = elss
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct BlockStatement: Categories.Statement {
|
||||||
|
public var statements: Statements
|
||||||
|
|
||||||
|
public init(_ statements: Statements) {
|
||||||
|
self.statements = statements
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ReturnStatement: Categories.Statement {
|
||||||
|
public let value: Categories.Expression
|
||||||
|
|
||||||
|
public init(_ value: Categories.Expression) {
|
||||||
|
self.value = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ApplyStatement: Categories.Statement {
|
||||||
|
public let body: CST.BlockStatement?
|
||||||
|
|
||||||
|
public init() { self.body = .none }
|
||||||
|
public init(_ body: CST.BlockStatement) {
|
||||||
|
self.body = body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Action: CustomStringConvertible {
|
||||||
|
public var description: String {
|
||||||
|
return "Action: "
|
||||||
|
+ "\(self.name) with parameters \(self.params) and body \(String(describing: self.body))"
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: CST.BlockStatement?
|
||||||
|
public var params: CST.ParameterList
|
||||||
|
public var name: CST.Identifier
|
||||||
|
|
||||||
|
public init(
|
||||||
|
named name: CST.Identifier = CST.Identifier(Common.Identifier(name: "")),
|
||||||
|
withParameters parameters: ParameterList = ParameterList([]),
|
||||||
|
withBody body: CST.BlockStatement? = .none
|
||||||
|
) {
|
||||||
|
self.name = name
|
||||||
|
self.params = parameters
|
||||||
|
self.body = body
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Actions: CustomStringConvertible {
|
||||||
|
public let actions: [CST.Action]
|
||||||
|
public init(withActions actions: [CST.Action]) {
|
||||||
|
self.actions = actions
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
return "Actions: "
|
||||||
|
+ actions.map { action in
|
||||||
|
return "\(action)"
|
||||||
|
}.joined(separator: ";")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TableKeyMatchType {
|
||||||
|
case Exact
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct TableKeyEntry: CustomStringConvertible {
|
||||||
|
public let key: CST.KeysetExpression
|
||||||
|
public let match_type: CST.TableKeyMatchType
|
||||||
|
|
||||||
|
public init(_ key: CST.KeysetExpression, _ match: CST.TableKeyMatchType) {
|
||||||
|
self.key = key
|
||||||
|
self.match_type = match
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
return "Table Key Entry: " + "\(self.key): \(self.match_type)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct TableKeys: CustomStringConvertible {
|
||||||
|
public let keys: [CST.TableKeyEntry]
|
||||||
|
|
||||||
|
public init(withEntries entries: [CST.TableKeyEntry]) {
|
||||||
|
self.keys = entries
|
||||||
|
}
|
||||||
|
public init() {
|
||||||
|
self.keys = []
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
return "Table Keys: "
|
||||||
|
+ self.keys.map { key in
|
||||||
|
return "\(key)"
|
||||||
|
}.joined(separator: ";")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct TableActionsProperty: CustomStringConvertible {
|
||||||
|
public let actions: [CST.Identifier]
|
||||||
|
public init(_ actions: [CST.Identifier] = []) {
|
||||||
|
self.actions = actions
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
return "Table Actions: "
|
||||||
|
+ self.actions.map { action in
|
||||||
|
return "\(action)"
|
||||||
|
}.joined(separator: ";")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct TablePropertyList: CustomStringConvertible {
|
||||||
|
public let actions: CST.TableActionsProperty
|
||||||
|
public let keys: CST.TableKeys
|
||||||
|
public init(withActions actions: CST.TableActionsProperty, withKeys keys: CST.TableKeys) {
|
||||||
|
self.actions = actions
|
||||||
|
self.keys = keys
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
return "Table Property List: \(self.actions) \(self.keys)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Table: CustomStringConvertible {
|
||||||
|
public let properties: CST.TablePropertyList
|
||||||
|
let name: CST.Identifier
|
||||||
|
public let entries: [(P4Value, CST.Identifier)]
|
||||||
|
|
||||||
|
public init(
|
||||||
|
withName name: CST.Identifier, withPropertyList property_list: CST.TablePropertyList,
|
||||||
|
withEntries entries: [(P4Value, CST.Identifier)] = []
|
||||||
|
) {
|
||||||
|
self.name = name
|
||||||
|
self.properties = property_list
|
||||||
|
self.entries = entries
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
return "Table named: \(self.name) \(self.properties)"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When the control is evaluated, the value of the x in the table is
|
||||||
|
/// compared to the entries and the match is assocated with an action
|
||||||
|
/// that is invoked when the match occurs!
|
||||||
|
|
||||||
|
public func update(addEntry entry: (P4Value, CST.Identifier)) -> Table {
|
||||||
|
return Table(
|
||||||
|
withName: self.name, withPropertyList: self.properties, withEntries: self.entries + [entry])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Declaration {}
|
||||||
|
|
||||||
|
public struct Control: CustomStringConvertible, Categories.Declaration {
|
||||||
|
public var description: String {
|
||||||
|
return "Control named \(self._name) \(self.parameters) \(self.actions) \(self.table)"
|
||||||
|
}
|
||||||
|
|
||||||
|
public let actions: CST.Actions
|
||||||
|
public let table: CST.Table
|
||||||
|
let _parameters: CST.ParameterList
|
||||||
|
let _name: CST.Identifier
|
||||||
|
let apply: CST.ApplyStatement
|
||||||
|
|
||||||
|
public var parameters: CST.ParameterList {
|
||||||
|
_parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
public var name: CST.Identifier {
|
||||||
|
_name
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(
|
||||||
|
named: CST.Identifier, withParameters parameters: CST.ParameterList,
|
||||||
|
withTable table: CST.Table,
|
||||||
|
withActions actions: CST.Actions, withApply apply: CST.ApplyStatement
|
||||||
|
) {
|
||||||
|
self._name = named
|
||||||
|
self._parameters = parameters
|
||||||
|
self.actions = actions
|
||||||
|
self.table = table
|
||||||
|
self.apply = apply
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateTable(addEntry entry: (P4Value, CST.Identifier)) -> Control {
|
||||||
|
let table = self.table.update(addEntry: entry)
|
||||||
|
|
||||||
|
return Control(
|
||||||
|
named: self.name, withParameters: self.parameters, withTable: table,
|
||||||
|
withActions: self.actions, withApply: self.apply)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func def() -> P4DataValue? {
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ExternDeclaration: Categories.Declaration {
|
||||||
|
public let declaration: CST.Categories.Declaration
|
||||||
|
|
||||||
|
public init(_ declaration: CST.Categories.Declaration) {
|
||||||
|
self.declaration = declaration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct FunctionDeclaration: Categories.Declaration {
|
||||||
|
public var description: String {
|
||||||
|
return "Function named \(self.name) that returns \(self.tipe) with parameters \(self.params)"
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: CST.BlockStatement?
|
||||||
|
public var params: CST.ParameterList
|
||||||
|
public var name: CST.Identifier
|
||||||
|
public var tipe: CST.Tipe
|
||||||
|
|
||||||
|
public init(
|
||||||
|
named name: CST.Identifier, ofType type: CST.Tipe,
|
||||||
|
withParameters parameters: CST.ParameterList,
|
||||||
|
withBody body: CST.BlockStatement?
|
||||||
|
) {
|
||||||
|
self.name = name
|
||||||
|
self.tipe = type
|
||||||
|
self.params = parameters
|
||||||
|
self.body = body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct StructDeclaration: Categories.Declaration {
|
||||||
|
public let fields: [CST.VariableDeclarationStatement]
|
||||||
|
public let identifier: CST.Identifier
|
||||||
|
public init(_ id: CST.Identifier, _ fields: [CST.VariableDeclarationStatement]) {
|
||||||
|
self.identifier = id
|
||||||
|
self.fields = fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Instantiation: Categories.Statement {
|
||||||
|
public let name: CST.Identifier
|
||||||
|
public var tipe: CST.Identifier
|
||||||
|
public let arguments: CST.ArgumentList
|
||||||
|
|
||||||
|
public init(
|
||||||
|
named name: CST.Identifier, withType tipe: CST.Identifier,
|
||||||
|
withArguments arguments: CST.ArgumentList
|
||||||
|
) {
|
||||||
|
self.name = name
|
||||||
|
self.arguments = arguments
|
||||||
|
self.tipe = tipe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ExpressionStatement: Categories.Statement {
|
||||||
|
public let expression: Categories.Expression
|
||||||
|
|
||||||
|
public init(_ expr: Categories.Expression) {
|
||||||
|
self.expression = expr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Identifier: Categories.Expression {
|
||||||
|
public let id: Common.Identifier
|
||||||
|
public init(_ id: Common.Identifier) {
|
||||||
|
self.id = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Literal: Categories.Expression {
|
||||||
|
public let literal: P4Value
|
||||||
|
public init(_ literal: P4Value) {
|
||||||
|
self.literal = literal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum KeysetExpression: Categories.Expression {
|
||||||
|
case Default
|
||||||
|
case Value(Categories.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SelectCaseExpression: Categories.Expression {
|
||||||
|
public let key: CST.KeysetExpression
|
||||||
|
public let next_state_identifier: CST.Identifier
|
||||||
|
|
||||||
|
public init(withKey key: CST.KeysetExpression, withNextState next_state_id: CST.Identifier) {
|
||||||
|
self.key = key
|
||||||
|
self.next_state_identifier = next_state_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SelectExpression: Categories.Expression {
|
||||||
|
public let selector: Categories.Expression
|
||||||
|
public let case_expressions: [CST.SelectCaseExpression]
|
||||||
|
|
||||||
|
public init(
|
||||||
|
withSelector selector: Categories.Expression,
|
||||||
|
withSelectCaseExpressions sces: [CST.SelectCaseExpression]
|
||||||
|
) {
|
||||||
|
self.selector = selector
|
||||||
|
self.case_expressions = sces
|
||||||
|
}
|
||||||
|
|
||||||
|
public func append_checked_sce(sce: CST.SelectCaseExpression) -> CST.SelectExpression {
|
||||||
|
var new_cses = self.case_expressions
|
||||||
|
new_cses.append(sce)
|
||||||
|
return SelectExpression(
|
||||||
|
withSelector: self.selector, withSelectCaseExpressions: new_cses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BinaryOperatorExpressionType {
|
||||||
|
case Add
|
||||||
|
case Subtract
|
||||||
|
case Multiply
|
||||||
|
case Divide
|
||||||
|
case Lt
|
||||||
|
case Lte
|
||||||
|
case Gt
|
||||||
|
case Gte
|
||||||
|
case Eq
|
||||||
|
case And
|
||||||
|
case Or
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct BinaryOperatorExpression: Categories.Expression {
|
||||||
|
public let left: Categories.Expression
|
||||||
|
public let right: Categories.Expression
|
||||||
|
public let type: BinaryOperatorExpressionType
|
||||||
|
|
||||||
|
public init(
|
||||||
|
withType tipe: BinaryOperatorExpressionType,
|
||||||
|
withLhs lhs: Categories.Expression,
|
||||||
|
withRhs rhs: Categories.Expression
|
||||||
|
) {
|
||||||
|
self.type = tipe
|
||||||
|
self.left = lhs
|
||||||
|
self.right = rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ArrayAccessExpression: Categories.Expression {
|
||||||
|
public let indexor: Categories.Expression
|
||||||
|
public let name: Categories.Expression
|
||||||
|
|
||||||
|
public init(
|
||||||
|
withName name: Categories.Expression,
|
||||||
|
withIndexor indexor: Categories.Expression
|
||||||
|
) {
|
||||||
|
self.name = name
|
||||||
|
self.indexor = indexor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct FieldAccessExpression: Categories.Expression {
|
||||||
|
public let field: Identifier
|
||||||
|
public let strct: Categories.Expression
|
||||||
|
|
||||||
|
public init(withStruct strct: Categories.Expression, withField field: Identifier) {
|
||||||
|
self.strct = strct
|
||||||
|
self.field = field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct FunctionCall: Categories.Expression {
|
||||||
|
public let callee: Identifier
|
||||||
|
public let arguments: ArgumentList
|
||||||
|
|
||||||
|
public init(_ callee: Identifier, withArguments arguments: ArgumentList) {
|
||||||
|
self.callee = callee
|
||||||
|
self.arguments = arguments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct LocalElements {}
|
||||||
|
|
||||||
|
public struct LocalElement {}
|
||||||
|
|
||||||
|
public struct ParserAssignmentStatement: Categories.Statement {
|
||||||
|
public let lvalue: Categories.Expression
|
||||||
|
public let value: Categories.Expression
|
||||||
|
|
||||||
|
public init(
|
||||||
|
withLValue lvalue: Categories.Expression, withValue value: Categories.Expression
|
||||||
|
) {
|
||||||
|
self.lvalue = lvalue
|
||||||
|
self.value = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A P4 Parser State
|
||||||
|
public class ParserState {
|
||||||
|
let name: Identifier
|
||||||
|
public let statements: CST.Statements?
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
return "Parser State named \(self.name)"
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getName() -> Identifier {
|
||||||
|
return self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getStatements() -> CST.Statements? {
|
||||||
|
return self.statements
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a ParserState
|
||||||
|
public init(_ name: Identifier, _ statements: CST.Statements? = .none) {
|
||||||
|
self.name = name
|
||||||
|
self.statements = statements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TransitionStatement
|
||||||
|
///
|
||||||
|
/// Only defined to define Compilable extension.
|
||||||
|
public struct TransitionStatement {}
|
||||||
|
|
||||||
|
public class ParserStateDirectTransition: ParserState, Categories.State {
|
||||||
|
public let next_state_identifier: Identifier?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
name: Identifier, withNextStateIdentifier next_state_id: Identifier,
|
||||||
|
withStatements stmts: CST.Statements? = .none
|
||||||
|
) {
|
||||||
|
self.next_state_identifier = next_state_id
|
||||||
|
super.init(name, stmts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ParserStateNoTransition: ParserState, Categories.State {
|
||||||
|
/// Construct a ParserState
|
||||||
|
public init(
|
||||||
|
name: Identifier, withStatements stmts: CST.Statements? = .none
|
||||||
|
) {
|
||||||
|
super.init(name, stmts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ParserStateSelectTransition: ParserState, Categories.State {
|
||||||
|
|
||||||
|
public let te: SelectExpression
|
||||||
|
|
||||||
|
public init(
|
||||||
|
name: Identifier, withTransitionExpression te: SelectExpression,
|
||||||
|
withStatements stmts: CST.Statements? = .none
|
||||||
|
) {
|
||||||
|
self.te = te
|
||||||
|
super.init(name, stmts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ParserStates {
|
||||||
|
public var states: [Categories.State] = Array()
|
||||||
|
|
||||||
|
public func count() -> Int {
|
||||||
|
return states.count
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(_ states: [Categories.State] = Array()) {
|
||||||
|
self.states = states
|
||||||
|
}
|
||||||
|
|
||||||
|
public func append(state: Categories.State) -> ParserStates {
|
||||||
|
var new_states = self.states
|
||||||
|
new_states.append(state)
|
||||||
|
return ParserStates(new_states)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A P4 Parser
|
||||||
|
///
|
||||||
|
/// Note: A Parser is a type
|
||||||
|
public struct Parser: Categories.Declaration {
|
||||||
|
public var states: ParserStates
|
||||||
|
|
||||||
|
public var name: Identifier
|
||||||
|
public var parameters: ParameterList
|
||||||
|
|
||||||
|
public init(withName name: Identifier) {
|
||||||
|
self.states = ParserStates()
|
||||||
|
self.parameters = ParameterList()
|
||||||
|
self.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(withName name: Identifier, withParameters parameters: ParameterList) {
|
||||||
|
self.states = ParserStates()
|
||||||
|
self.parameters = parameters
|
||||||
|
self.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
return "Parser \(self.name) with parameters: \(parameters) and states: \(self.states)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Types {}
|
||||||
|
|
||||||
|
public struct Tipe {
|
||||||
|
public let tipe: P4QualifiedType
|
||||||
|
public init(_ tipe: P4QualifiedType) {
|
||||||
|
self.tipe = tipe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Parameter: CustomStringConvertible {
|
||||||
|
public var name: CST.Identifier
|
||||||
|
public var type: CST.Tipe
|
||||||
|
|
||||||
|
public init(
|
||||||
|
identifier: Identifier, withType type: CST.Tipe
|
||||||
|
) {
|
||||||
|
self.name = identifier
|
||||||
|
self.type = type
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
return "Parameter: \(self.name) with type \(self.type)"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ParameterList: CustomStringConvertible {
|
||||||
|
public var parameters: [CST.Parameter]
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
self.parameters = Array()
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(_ parameters: [CST.Parameter]) {
|
||||||
|
self.parameters = parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
public func addParameter(_ parameter: CST.Parameter) -> CST.ParameterList {
|
||||||
|
return CST.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: [CST.Argument]
|
||||||
|
|
||||||
|
public init(_ arguments: [CST.Argument] = []) {
|
||||||
|
self.arguments = arguments
|
||||||
|
}
|
||||||
|
|
||||||
|
public func addArgument(_ argument: CST.Argument) -> CST.ArgumentList {
|
||||||
|
return ArgumentList(self.arguments + [argument])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Argument {
|
||||||
|
public let index: Int
|
||||||
|
public let argument: Categories.Expression
|
||||||
|
|
||||||
|
public init(_ argument: Categories.Expression, atIndex index: Int) {
|
||||||
|
self.argument = argument
|
||||||
|
self.index = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Program {
|
||||||
|
public var statements: Statements
|
||||||
|
public init(_ stmts: Statements = Statements([])) {
|
||||||
|
self.statements = stmts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Statement {}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct CSTCompilerContext {
|
||||||
|
public let lexical_context_name: CST.Identifier?
|
||||||
|
public let lexical_context_statements: CST.Statements?
|
||||||
|
public let extern_context: Bool
|
||||||
|
|
||||||
|
public init(
|
||||||
|
_ name: CST.Identifier? = .none, _ stmts: CST.Statements? = .none, _ extern: Bool = false
|
||||||
|
) {
|
||||||
|
self.lexical_context_name = name
|
||||||
|
self.lexical_context_statements = stmts
|
||||||
|
self.extern_context = extern
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(withContextName cn: CST.Identifier?) -> CSTCompilerContext {
|
||||||
|
return CSTCompilerContext(cn, self.lexical_context_statements, self.extern_context)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(withContextStatements stmts: CST.Statements?) -> CSTCompilerContext {
|
||||||
|
return CSTCompilerContext(self.lexical_context_name, stmts, self.extern_context)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(withExtern extern: Bool) -> CSTCompilerContext {
|
||||||
|
return CSTCompilerContext(self.lexical_context_name, self.lexical_context_statements, extern)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,3 +16,16 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import Common
|
import Common
|
||||||
|
import SwiftTreeSitter
|
||||||
|
import TreeSitterExtensions
|
||||||
|
import TreeSitterP4
|
||||||
|
|
||||||
|
extension Node {
|
||||||
|
public func toSourceLocation() -> SourceLocation {
|
||||||
|
return SourceLocation(self.range.location, self.range.length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@attached(member, names: named(ParseStatement))
|
||||||
|
public macro deriveParsableStatement() =
|
||||||
|
#externalMacro(module: "Macros", type: "DeriveParsableStatement")
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// 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 TreeSitterExtensions
|
||||||
|
import TreeSitterP4
|
||||||
|
|
||||||
|
let p4lang = Language(tree_sitter_p4())
|
||||||
|
|
||||||
|
public func ConfigureP4Parser() -> Result<SwiftTreeSitter.Parser> {
|
||||||
|
let p = SwiftTreeSitter.Parser.init()
|
||||||
|
|
||||||
|
do {
|
||||||
|
try p.setLanguage(p4lang)
|
||||||
|
} catch {
|
||||||
|
return Result.Error(Error(withMessage: "Could not configure the P4 parser"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func compile(_ s: String) -> Result<CST.Program> {
|
||||||
|
let maybe_parser = ConfigureP4Parser()
|
||||||
|
guard case .Ok(let p) = maybe_parser else {
|
||||||
|
return .Error(maybe_parser.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = p.parse(s)
|
||||||
|
guard let tree = result,
|
||||||
|
!tree.isError(lang: p4lang),
|
||||||
|
!tree.containsMissing(lang: p4lang)
|
||||||
|
else {
|
||||||
|
return Result.Error(Error(withMessage: "Could not parse the P4 program"))
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let root = tree.rootNode else {
|
||||||
|
return Result.Error(Error(withMessage: "No P4 program compiled"))
|
||||||
|
}
|
||||||
|
|
||||||
|
let compilation_context = CSTCompilerContext()
|
||||||
|
return CST.Program.Parse(node: root, withContext: compilation_context)
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,577 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
extension CST.Identifier: ParsableExpression {
|
||||||
|
public static func ParseExpression(
|
||||||
|
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Expression> {
|
||||||
|
|
||||||
|
#RequireNodeType<Node, CST.Categories.Expression>(
|
||||||
|
node: node, type: "identifier", nice_type_name: "Identifier")
|
||||||
|
|
||||||
|
/// TODO: If there is a value here, then we can make this a compile-time constant!
|
||||||
|
return .Ok(CST.Identifier(Common.Identifier(name: node.text!)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension P4BooleanValue: ParsableExpression {
|
||||||
|
public static func ParseExpression(
|
||||||
|
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Expression> {
|
||||||
|
let node = node.child(at: 0)!
|
||||||
|
#RequireNodeType<Node, CST.Categories.Expression>(
|
||||||
|
node: node, type: "booleanLiteralExpression", nice_type_name: "Boolean Literal Expression")
|
||||||
|
|
||||||
|
if node.text == "false" {
|
||||||
|
return .Ok(CST.Literal(P4Value(P4BooleanValue(withValue: false))))
|
||||||
|
} else if node.text == "true" {
|
||||||
|
return .Ok(CST.Literal(P4Value(P4BooleanValue(withValue: true))))
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Failed to parse boolean literal: \(node.text!)"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension P4IntValue: ParsableExpression {
|
||||||
|
public static func ParseExpression(
|
||||||
|
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Expression> {
|
||||||
|
let node = node.child(at: 0)!
|
||||||
|
|
||||||
|
#RequireNodesType<Node, CST.Categories.Expression>(
|
||||||
|
nodes: node, type: ["integer", "integer_elaborated"],
|
||||||
|
nice_type_names: ["Integer", "Elaborated Integer"])
|
||||||
|
|
||||||
|
var bit_width: BitWidth = BitWidth.Infinite
|
||||||
|
let value_source: String
|
||||||
|
if node.nodeType == "integer_elaborated" {
|
||||||
|
let re = /([0-9]+)([ws])([\-0-9]+)/
|
||||||
|
let integer_components = node.text!.matches(of: re)
|
||||||
|
|
||||||
|
if integer_components.isEmpty || integer_components.count > 1 {
|
||||||
|
return .Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Failed to parse elaborated integer: \(node.text!)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
let width_source = "\(integer_components[0].1)"
|
||||||
|
guard let width = Int(width_source) else {
|
||||||
|
return .Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Failed to parse width from elaborated integer: \(width_source)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO: Handle signed vs. unsigned.
|
||||||
|
|
||||||
|
bit_width = BitWidth.Width(width)
|
||||||
|
value_source = "\(integer_components[0].3)"
|
||||||
|
} else {
|
||||||
|
value_source = node.text!
|
||||||
|
}
|
||||||
|
|
||||||
|
if let parsed_int = Int(value_source) {
|
||||||
|
return .Ok(CST.Literal(P4Value(P4IntValue(withValue: parsed_int, andWidth: bit_width))))
|
||||||
|
} else {
|
||||||
|
return .Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Failed to parse integer: \(node.text!)"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension P4StringValue: ParsableExpression {
|
||||||
|
public static func ParseExpression(
|
||||||
|
node: SwiftTreeSitter.Node, withContext scopes: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Expression> {
|
||||||
|
let node = node.child(at: 0)!
|
||||||
|
#RequireNodeType<Node, CST.Categories.Expression>(
|
||||||
|
node: node, type: "string_literal", nice_type_name: "String Literal")
|
||||||
|
return .Ok(CST.Literal(P4Value(P4StringValue(withValue: node.text!))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CST.Expression: Parsable {
|
||||||
|
public static func Parse(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Expression> {
|
||||||
|
#RequireNodeType<Node, CST.Categories.Expression>(
|
||||||
|
node: node, type: "expression", nice_type_name: "expression")
|
||||||
|
|
||||||
|
let expression_node = node.child(at: 0)!
|
||||||
|
#RequireNodesType<Node, CST.Categories.Expression>(
|
||||||
|
nodes: expression_node, type: ["grouped_expression", "simple_expression"],
|
||||||
|
nice_type_names: ["grouped expression", "simple expression"])
|
||||||
|
|
||||||
|
// If this is a grouped expression, recurse!
|
||||||
|
if expression_node.nodeType == "grouped_expression" {
|
||||||
|
return CST.Expression.Parse(node: expression_node.child(at: 1)!, withContext: context)
|
||||||
|
}
|
||||||
|
|
||||||
|
let expression_parsers: [ParsableExpression.Type] = [
|
||||||
|
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, CST.Identifier.self,
|
||||||
|
CST.BinaryOperatorExpression.self, CST.ArrayAccessExpression.self,
|
||||||
|
CST.FieldAccessExpression.self,
|
||||||
|
CST.FunctionCall.self,
|
||||||
|
]
|
||||||
|
|
||||||
|
for candidate_expression_parser in expression_parsers {
|
||||||
|
switch candidate_expression_parser.ParseExpression(
|
||||||
|
node: expression_node, withContext: context)
|
||||||
|
{
|
||||||
|
case .Ok(let parsed): return .Ok(parsed)
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Error(Error(withMessage: "\(node.range): Could not parse into expression"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CST.KeysetExpression: ParsableExpression {
|
||||||
|
public static func ParseExpression(
|
||||||
|
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Expression> {
|
||||||
|
let keyset_expression_node = node.child(at: 0)!
|
||||||
|
|
||||||
|
#RequireNodesType<Node, CST.Categories.Expression>(
|
||||||
|
nodes: keyset_expression_node, type: ["expression", "default_keyset"],
|
||||||
|
nice_type_names: ["expression", "default keyset"])
|
||||||
|
|
||||||
|
// If there is a default keyset, that's easy!
|
||||||
|
if keyset_expression_node.nodeType == "default_keyset" {
|
||||||
|
return .Ok(CST.KeysetExpression.Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile the expression:
|
||||||
|
let maybe_compiled_set_expression = CST.Expression.Parse(
|
||||||
|
node: keyset_expression_node, withContext: context)
|
||||||
|
guard case .Ok(let compiled_expression) = maybe_compiled_set_expression else {
|
||||||
|
return .Error(maybe_compiled_set_expression.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(CST.KeysetExpression.Value(compiled_expression))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CST.SelectExpression: ParsableExpression {
|
||||||
|
public static func ParseExpression(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Expression> {
|
||||||
|
#RequireNodeType<Node, (CST.SelectExpression, CSTCompilerContext)>(
|
||||||
|
node: node, type: "selectExpression", nice_type_name: "parser select expression")
|
||||||
|
|
||||||
|
guard let selector_node = node.child(at: 2),
|
||||||
|
selector_node.nodeType == "expression"
|
||||||
|
else {
|
||||||
|
return .Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Could not find selector expression"))
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let select_body_node = node.child(at: 5),
|
||||||
|
select_body_node.nodeType == "selectBody"
|
||||||
|
else {
|
||||||
|
return .Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Could not find select expression body"))
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_selector = CST.Expression.Parse(node: selector_node, withContext: context)
|
||||||
|
guard case .Ok(let selector) = maybe_selector else {
|
||||||
|
return .Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: selector_node.toSourceLocation(),
|
||||||
|
withError:
|
||||||
|
"Could not parse transition select expression selector expression: \(maybe_selector.error()!)"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
var sces: [CST.SelectCaseExpression] = Array()
|
||||||
|
var sces_errors: (any Errorable)? = .none
|
||||||
|
|
||||||
|
select_body_node.enumerateNamedChildren { current_node in
|
||||||
|
let maybe_parsed_cse = CST.SelectCaseExpression.ParseExpression(
|
||||||
|
node: current_node, withContext: context)
|
||||||
|
switch maybe_parsed_cse {
|
||||||
|
case .Ok(let parsed_cse): sces.append(parsed_cse as! CST.SelectCaseExpression)
|
||||||
|
case .Error(let e):
|
||||||
|
sces_errors =
|
||||||
|
if let sces_errors = sces_errors {
|
||||||
|
sces_errors.append(error: Error(withMessage: "\(maybe_parsed_cse.error()!)"))
|
||||||
|
} else {
|
||||||
|
e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let sces_errors = sces_errors {
|
||||||
|
return .Error(ErrorWithLabel("Error(s) parsing select cases", sces_errors))
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(
|
||||||
|
CST.SelectExpression(withSelector: selector, withSelectCaseExpressions: sces),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CST.SelectCaseExpression: ParsableExpression {
|
||||||
|
public static func ParseExpression(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Expression> {
|
||||||
|
|
||||||
|
#RequireNodeType<Node, CST.Categories.Expression>(
|
||||||
|
node: node, type: "selectCase", nice_type_name: "Select Case")
|
||||||
|
|
||||||
|
guard let keysetexpression_node = node.child(at: 0),
|
||||||
|
keysetexpression_node.nodeType == "keysetExpression"
|
||||||
|
else {
|
||||||
|
return Result.Error(Error(withMessage: "Missing keyset expression in select case"))
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let targetstate_node = node.child(at: 2),
|
||||||
|
targetstate_node.nodeType == "identifier"
|
||||||
|
else {
|
||||||
|
return Result.Error(Error(withMessage: "Missing target state in select case"))
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_parsed_keysetexpression = CST.KeysetExpression.ParseExpression(
|
||||||
|
node: keysetexpression_node, withContext: context)
|
||||||
|
guard case Result.Ok(let maybe_keysetexpression) = maybe_parsed_keysetexpression else {
|
||||||
|
return Result.Error(maybe_parsed_keysetexpression.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
let keysetexpression = maybe_keysetexpression as! CST.KeysetExpression
|
||||||
|
|
||||||
|
let maybe_parsed_targetstate = CST.Identifier.ParseExpression(
|
||||||
|
node: targetstate_node, withContext: context)
|
||||||
|
guard case .Ok(let targetstate) = maybe_parsed_targetstate else {
|
||||||
|
return Result.Error(maybe_parsed_targetstate.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(
|
||||||
|
CST.SelectCaseExpression(
|
||||||
|
withKey: keysetexpression, withNextState: targetstate as! CST.Identifier)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CST.BinaryOperatorExpression: ParsableExpression {
|
||||||
|
public static func ParseExpression(
|
||||||
|
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Expression> {
|
||||||
|
let expression = node.child(at: 0)!
|
||||||
|
|
||||||
|
#RequireNodeType<Node, CST.Categories.Expression>(
|
||||||
|
node: expression, type: "binaryOperatorExpression",
|
||||||
|
nice_type_name: "Binary Operator Expression")
|
||||||
|
let binary_operator_expression_node = expression.child(at: 0)!
|
||||||
|
var walker = Walker(node: binary_operator_expression_node)
|
||||||
|
|
||||||
|
var current_node: Node? = .none
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.Expression>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Malformed binary operator expression"
|
||||||
|
)))
|
||||||
|
|
||||||
|
/// TODO: This macro cannot handle new lines in the arrays
|
||||||
|
// swift-format-ignore
|
||||||
|
#RequireNodesType<Node, CST.Categories.Expression>(
|
||||||
|
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<CST.Categories.Expression>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Missing LHS for binary operator expression")))
|
||||||
|
|
||||||
|
let left_hand_side_raw = current_node!
|
||||||
|
|
||||||
|
walker.next()
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.Expression>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Missing binary operator for binary operator expression")))
|
||||||
|
|
||||||
|
walker.next()
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.Expression>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Missing RHS for binary operator expression")))
|
||||||
|
|
||||||
|
let right_hand_side_raw = current_node!
|
||||||
|
|
||||||
|
let maybe_left_hand_side = CST.Expression.Parse(
|
||||||
|
node: left_hand_side_raw, withContext: context)
|
||||||
|
guard case Result.Ok(let left_hand_side) = maybe_left_hand_side else {
|
||||||
|
return Result.Error(maybe_left_hand_side.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_right_hand_side = CST.Expression.Parse(
|
||||||
|
node: right_hand_side_raw, withContext: context)
|
||||||
|
guard case Result.Ok(let right_hand_side) = maybe_right_hand_side else {
|
||||||
|
return Result.Error(maybe_right_hand_side.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
let evaluators: [String: (String, P4QualifiedType, CST.BinaryOperatorExpressionType)] = [
|
||||||
|
"binaryEqualOperatorExpression": (
|
||||||
|
"Binary Equal", P4QualifiedType(P4Boolean()),
|
||||||
|
CST.BinaryOperatorExpressionType.Eq
|
||||||
|
),
|
||||||
|
"binaryLessThanOperatorExpression": (
|
||||||
|
"Binary Less Than", P4QualifiedType(P4Boolean()),
|
||||||
|
CST.BinaryOperatorExpressionType.Lt
|
||||||
|
),
|
||||||
|
"binaryLessThanEqualOperatorExpression": (
|
||||||
|
"Binary Less Than Or Equal", P4QualifiedType(P4Boolean()),
|
||||||
|
CST.BinaryOperatorExpressionType.Lte
|
||||||
|
),
|
||||||
|
"binaryGreaterThanOperatorExpression": (
|
||||||
|
"Binary Greater Than", P4QualifiedType(P4Boolean()),
|
||||||
|
CST.BinaryOperatorExpressionType.Gt
|
||||||
|
),
|
||||||
|
"binaryGreaterThanEqualOperatorExpression": (
|
||||||
|
"Binary Greater Than Or Equal", P4QualifiedType(P4Boolean()),
|
||||||
|
CST.BinaryOperatorExpressionType.Gte
|
||||||
|
),
|
||||||
|
"binaryAndOperatorExpression": (
|
||||||
|
"Binary Or", P4QualifiedType(P4Boolean()),
|
||||||
|
CST.BinaryOperatorExpressionType.And
|
||||||
|
),
|
||||||
|
"binaryOrOperatorExpression": (
|
||||||
|
"Binary And", P4QualifiedType(P4Boolean()),
|
||||||
|
CST.BinaryOperatorExpressionType.Or
|
||||||
|
),
|
||||||
|
"binaryAddOperatorExpression": (
|
||||||
|
"Binary Add", P4QualifiedType(P4Int()),
|
||||||
|
CST.BinaryOperatorExpressionType.Add
|
||||||
|
),
|
||||||
|
"binarySubtractOperatorExpression": (
|
||||||
|
"Binary Subtract", P4QualifiedType(P4Int()),
|
||||||
|
CST.BinaryOperatorExpressionType.Subtract
|
||||||
|
),
|
||||||
|
"binaryMultiplyOperatorExpression": (
|
||||||
|
"Binary Multiply", P4QualifiedType(P4Int()),
|
||||||
|
CST.BinaryOperatorExpressionType.Multiply
|
||||||
|
),
|
||||||
|
"binaryDivideOperatorExpression": (
|
||||||
|
"Binary Divide", P4QualifiedType(P4Int()),
|
||||||
|
CST.BinaryOperatorExpressionType.Divide
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
guard let selected_evaluator = evaluators[binary_operator_expression_node.nodeType!] else {
|
||||||
|
return Result.Error(
|
||||||
|
Error(withMessage: "No evaluator for \(binary_operator_expression_node.nodeType!)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(
|
||||||
|
CST.BinaryOperatorExpression(
|
||||||
|
withType: selected_evaluator.2,
|
||||||
|
withLhs: left_hand_side, withRhs: right_hand_side))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CST.ArrayAccessExpression: ParsableExpression {
|
||||||
|
public static func ParseExpression(
|
||||||
|
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Expression> {
|
||||||
|
let expression = node.child(at: 0)!
|
||||||
|
|
||||||
|
#RequireNodeType<Node, CST.Categories.Expression>(
|
||||||
|
node: expression, type: "arrayAccessExpression", nice_type_name: "Array Access Expression")
|
||||||
|
let array_access_expression_node = expression
|
||||||
|
|
||||||
|
var walker = Walker(node: array_access_expression_node)
|
||||||
|
var current_node: Node? = .none
|
||||||
|
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.Expression>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Malformed array access expression")))
|
||||||
|
|
||||||
|
#RequireNodeType<Node, CST.Categories.Expression>(
|
||||||
|
node: current_node!, type: "expression",
|
||||||
|
nice_type_name: "array identifier expression")
|
||||||
|
let array_access_identifier_node = current_node!
|
||||||
|
|
||||||
|
walker.next()
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.Expression>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Missing [ for array access expression")))
|
||||||
|
|
||||||
|
walker.next()
|
||||||
|
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.Expression>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Missing indexor expression for array access expression")))
|
||||||
|
|
||||||
|
#RequireNodeType<Node, CST.Categories.Expression>(
|
||||||
|
node: current_node!, type: "expression",
|
||||||
|
nice_type_name: "array indexor expression")
|
||||||
|
|
||||||
|
let array_access_indexor_node = current_node!
|
||||||
|
|
||||||
|
let maybe_array_identifier = CST.Expression.Parse(
|
||||||
|
node: array_access_identifier_node, withContext: context)
|
||||||
|
guard case Result.Ok(let array_identifier) = maybe_array_identifier else {
|
||||||
|
return Result.Error(maybe_array_identifier.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_array_indexor = CST.Expression.Parse(
|
||||||
|
node: array_access_indexor_node, withContext: context)
|
||||||
|
guard case Result.Ok(let array_indexor) = maybe_array_indexor else {
|
||||||
|
return Result.Error(maybe_array_indexor.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(
|
||||||
|
CST.ArrayAccessExpression(
|
||||||
|
withName: array_identifier, withIndexor: array_indexor))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CST.FieldAccessExpression: ParsableExpression {
|
||||||
|
public static func ParseExpression(
|
||||||
|
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Expression> {
|
||||||
|
let expression = node.child(at: 0)!
|
||||||
|
|
||||||
|
#RequireNodeType<Node, CST.Categories.Expression>(
|
||||||
|
node: expression, type: "fieldAccessExpression", nice_type_name: "Array Access Expression")
|
||||||
|
|
||||||
|
let field_access_expression_node = expression
|
||||||
|
|
||||||
|
var walker = Walker(node: field_access_expression_node)
|
||||||
|
var current_node: Node? = .none
|
||||||
|
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.Expression>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Malformed field access expression")))
|
||||||
|
|
||||||
|
#RequireNodeType<Node, CST.Categories.Expression>(
|
||||||
|
node: current_node!, type: "expression",
|
||||||
|
nice_type_name: "struct identifier expression")
|
||||||
|
let struct_identifier_node = current_node!
|
||||||
|
|
||||||
|
walker.next()
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.Expression>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Missing . for field access expression")))
|
||||||
|
|
||||||
|
walker.next()
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.Expression>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Missing field name for field access expression")))
|
||||||
|
|
||||||
|
#RequireNodeType<Node, CST.Categories.Expression>(
|
||||||
|
node: current_node!, type: "identifier",
|
||||||
|
nice_type_name: "field name")
|
||||||
|
|
||||||
|
let field_name_node = current_node!
|
||||||
|
|
||||||
|
// Make sure that the identifier really identifies a struct.
|
||||||
|
let maybe_struct_identifier = CST.Expression.Parse(
|
||||||
|
node: struct_identifier_node, withContext: context)
|
||||||
|
guard case Result.Ok(let struct_identifier) = maybe_struct_identifier else {
|
||||||
|
return Result.Error(maybe_struct_identifier.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_field_name = CST.Identifier.ParseExpression(
|
||||||
|
node: field_name_node, withContext: context)
|
||||||
|
guard case Result.Ok(let field_name) = maybe_field_name else {
|
||||||
|
return Result.Error(maybe_field_name.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(
|
||||||
|
CST.FieldAccessExpression(
|
||||||
|
withStruct: struct_identifier,
|
||||||
|
withField: field_name as! CST.Identifier))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CST.FunctionCall: ParsableExpression {
|
||||||
|
public static func ParseExpression(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Expression> {
|
||||||
|
|
||||||
|
let expression = node.child(at: 0)!
|
||||||
|
#RequireNodeType<Node, CST.Categories.Expression>(
|
||||||
|
node: expression, type: "function_call", nice_type_name: "Function Call")
|
||||||
|
|
||||||
|
var walker = Walker(node: expression)
|
||||||
|
var current_node: Node? = .none
|
||||||
|
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.Expression>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Missing function call component")))
|
||||||
|
|
||||||
|
let maybe_callee_name = CST.Identifier.ParseExpression(
|
||||||
|
node: current_node!, withContext: context)
|
||||||
|
guard case .Ok(let callee_name) = maybe_callee_name else {
|
||||||
|
return Result.Error(maybe_callee_name.error()!)
|
||||||
|
}
|
||||||
|
walker.next()
|
||||||
|
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.Expression>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Missing function call component")))
|
||||||
|
|
||||||
|
let maybe_argument_list = CST.ArgumentList.Parse(node: current_node!, withContext: context)
|
||||||
|
|
||||||
|
guard case .Ok(let arguments) = maybe_argument_list else {
|
||||||
|
return .Error(maybe_argument_list.error()!)
|
||||||
|
}
|
||||||
|
return .Ok(CST.FunctionCall(callee_name as! CST.Identifier, withArguments: arguments))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
// 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 TreeSitterExtensions
|
||||||
|
import TreeSitterP4
|
||||||
|
|
||||||
|
extension CST.LocalElements: Parsable {
|
||||||
|
public typealias C = CST.Categories.Statement
|
||||||
|
public static func Parse(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Statement> {
|
||||||
|
let localElementsParsers: [String: ParsableStatement.Type] = [
|
||||||
|
"variableDeclaration": CST.VariableDeclarationStatement.self
|
||||||
|
]
|
||||||
|
|
||||||
|
guard let parser = localElementsParsers[node.nodeType ?? ""] else {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Unparseable statement type (\(node.nodeType ?? "Unknown Statement Type"))"))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch parser.ParseStatement(node: node, withContext: context) {
|
||||||
|
case Result.Ok(let parsed):
|
||||||
|
return Result.Ok(parsed)
|
||||||
|
case Result.Error(let e):
|
||||||
|
return Result.Error(Error(withMessage: "Failed to parse local element: \(e)"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CST.ParserState: Parsable {
|
||||||
|
public typealias C = CST.Categories.State
|
||||||
|
public static func Parse(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.State> {
|
||||||
|
var walker = Walker(node: node)
|
||||||
|
|
||||||
|
var current_node: Node? = .none
|
||||||
|
|
||||||
|
guard let node_type = node.nodeType,
|
||||||
|
node_type == "parserState"
|
||||||
|
else {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Did not find a parser state declaration"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.State>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Missing elements in parser state declaration")))
|
||||||
|
|
||||||
|
if current_node!.nodeType == "annotations" {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: current_node!.toSourceLocation(),
|
||||||
|
withError: "Annotations in parser state are not yet handled."))
|
||||||
|
|
||||||
|
// Would increment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the keyword state
|
||||||
|
walker.next()
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.State>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Missing elements in parser state declaration")))
|
||||||
|
|
||||||
|
let maybe_state_identifier = CST.Identifier.ParseExpression(
|
||||||
|
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 '{'
|
||||||
|
walker.next()
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.State>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Missing body of state declaration")
|
||||||
|
))
|
||||||
|
|
||||||
|
var errors: (any Errorable)? = .none
|
||||||
|
var parsed_s: CST.Statements? = .none
|
||||||
|
|
||||||
|
if current_node!.nodeType == "parserStatements" {
|
||||||
|
switch CST.Statements.Parse(
|
||||||
|
node: current_node!, withContext: context)
|
||||||
|
{
|
||||||
|
case .Ok(let state_statements):
|
||||||
|
parsed_s = state_statements
|
||||||
|
case .Error(let error):
|
||||||
|
errors =
|
||||||
|
if let errors = errors {
|
||||||
|
errors.append(error: error)
|
||||||
|
} else {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
walker.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let errors = errors {
|
||||||
|
return .Error(errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Categories.State>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Missing transition statement of state declaration")))
|
||||||
|
|
||||||
|
let updated_context = context.update(withContextName: (state_identifier as! CST.Identifier))
|
||||||
|
.update(withContextStatements: parsed_s)
|
||||||
|
|
||||||
|
return CST.TransitionStatement.Parse(node: current_node!, withContext: updated_context)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
extension CST.Program: Parsable {
|
||||||
|
public typealias C = CST.Program
|
||||||
|
public static func Parse(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Common.Result<CST.Program> {
|
||||||
|
|
||||||
|
var statements: [CST.Categories.Statement] = Array()
|
||||||
|
|
||||||
|
var errors: (any Errorable)? = .none
|
||||||
|
|
||||||
|
// Try to parse all top-level declarations.
|
||||||
|
node.enumerateNamedChildren { (declaration_node: Node) in
|
||||||
|
let declaration_parsers: [String: ParsableStatement.Type] = [
|
||||||
|
"declaration": CST.Declaration.self,
|
||||||
|
"instantiation": CST.Instantiation.self,
|
||||||
|
]
|
||||||
|
|
||||||
|
if let parser = declaration_parsers[declaration_node.nodeType!] {
|
||||||
|
let r = parser.ParseStatement(node: declaration_node, withContext: context)
|
||||||
|
switch r {
|
||||||
|
case .Ok(let compiled):
|
||||||
|
statements.append(compiled)
|
||||||
|
case .Error(let e):
|
||||||
|
errors =
|
||||||
|
if let errors = errors {
|
||||||
|
errors.append(error: e)
|
||||||
|
} else {
|
||||||
|
e
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let e = ErrorWithLocation(
|
||||||
|
sourceLocation: declaration_node.toSourceLocation(),
|
||||||
|
withError:
|
||||||
|
"\(declaration_node.nodeType!) cannot be at a P4 program top level")
|
||||||
|
errors =
|
||||||
|
if let errors = errors {
|
||||||
|
errors.append(error: e)
|
||||||
|
} else {
|
||||||
|
e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let errors = errors {
|
||||||
|
return .Error(errors)
|
||||||
|
}
|
||||||
|
return Result.Ok(CST.Program(CST.Statements(statements)))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,566 @@
|
|||||||
|
// 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 TreeSitterExtensions
|
||||||
|
import TreeSitterP4
|
||||||
|
|
||||||
|
extension CST.BlockStatement: Parsable {
|
||||||
|
public typealias C = CST.BlockStatement
|
||||||
|
public static func Parse(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.BlockStatement> {
|
||||||
|
/*
|
||||||
|
#RequireNodeType<Node, AST.BlockStatement>(
|
||||||
|
node: node, type: "blockStatement", nice_type_name: "block statement")
|
||||||
|
|
||||||
|
var walker = Walker(node: node)
|
||||||
|
var current_node: Node? = .none
|
||||||
|
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<AST.BlockStatement>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Malformed block statement")))
|
||||||
|
|
||||||
|
if current_node!.nodeType != "{" {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: current_node!.toSourceLocation(),
|
||||||
|
withError: "Missing { on block statement"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var statements: [AST.AnStatement] = Array()
|
||||||
|
var parse_err: (any Errorable)? = .none
|
||||||
|
|
||||||
|
walker.next()
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<AST.BlockStatement>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Malformed block statement")))
|
||||||
|
|
||||||
|
if current_node!.nodeType == "statements" {
|
||||||
|
switch SpecialCompilers.Statements.Parse(
|
||||||
|
node: current_node!, withContext: context)
|
||||||
|
{
|
||||||
|
case .Ok(let parsed_statements):
|
||||||
|
statements = parsed_statements
|
||||||
|
case .Error(let error):
|
||||||
|
parse_err = error
|
||||||
|
}
|
||||||
|
|
||||||
|
walker.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let err = parse_err {
|
||||||
|
return .Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<AST.BlockStatement>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Malformed block statement")))
|
||||||
|
|
||||||
|
if current_node!.nodeType != "}" {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: current_node!.toSourceLocation(),
|
||||||
|
withError: "Missing } on block statement"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(AST.BlockStatement(statements))
|
||||||
|
*/
|
||||||
|
return .Ok(CST.BlockStatement(CST.Statements([])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@deriveParsableStatement
|
||||||
|
extension CST.BlockStatement: ParsableStatement {}
|
||||||
|
|
||||||
|
extension CST.ConditionalStatement: Parsable {
|
||||||
|
public typealias C = CST.ConditionalStatement
|
||||||
|
public static func Parse(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.ConditionalStatement> {
|
||||||
|
|
||||||
|
#RequireNodeType<Node, CST.ConditionalStatement>(
|
||||||
|
node: node, type: "conditionalStatement", nice_type_name: "conditional statement")
|
||||||
|
|
||||||
|
let maybe_condition_expression = node.child(at: 2)
|
||||||
|
guard let condition_expression = maybe_condition_expression,
|
||||||
|
condition_expression.nodeType == "expression"
|
||||||
|
else {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Did not find condition for conditional statement"))
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_thens = node.child(at: 4)
|
||||||
|
guard let thens = maybe_thens,
|
||||||
|
thens.nodeType == "statement"
|
||||||
|
else {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Did not find then statement block for conditional statement"))
|
||||||
|
}
|
||||||
|
|
||||||
|
guard
|
||||||
|
case .Ok(let condition) = CST.Expression.Parse(
|
||||||
|
node: condition_expression, withContext: context)
|
||||||
|
else {
|
||||||
|
return Result.Error(
|
||||||
|
Error(withMessage: "Could not parse a conditional expression in a conditional statement"))
|
||||||
|
}
|
||||||
|
|
||||||
|
guard
|
||||||
|
case .Ok(let thenns) = CST.Statement.Parse(
|
||||||
|
node: thens, withContext: context)
|
||||||
|
else {
|
||||||
|
return Result.Error(
|
||||||
|
Error(
|
||||||
|
withMessage:
|
||||||
|
"Could not parse the then block in a conditional statement"))
|
||||||
|
}
|
||||||
|
|
||||||
|
let optional_elss: Result<CST.Categories.Statement>? =
|
||||||
|
if let elss = node.child(at: 6) {
|
||||||
|
.some(
|
||||||
|
CST.Statement.Parse(
|
||||||
|
node: elss, withContext: context))
|
||||||
|
} else {
|
||||||
|
.none
|
||||||
|
}
|
||||||
|
|
||||||
|
if let parsed_elss = optional_elss {
|
||||||
|
guard
|
||||||
|
case .Ok(let elss) = parsed_elss
|
||||||
|
else {
|
||||||
|
return Result.Error(
|
||||||
|
Error(
|
||||||
|
withMessage:
|
||||||
|
"Could not parse the else block in a conditional statement"))
|
||||||
|
}
|
||||||
|
return .Ok(
|
||||||
|
CST.ConditionalStatement(condition: condition, withThen: thenns, andElse: elss))
|
||||||
|
}
|
||||||
|
return .Ok(CST.ConditionalStatement(condition: condition, withThen: thenns))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@deriveParsableStatement
|
||||||
|
extension CST.ConditionalStatement: ParsableStatement {}
|
||||||
|
|
||||||
|
extension CST.VariableDeclarationStatement: Parsable {
|
||||||
|
public typealias C = CST.VariableDeclarationStatement
|
||||||
|
public static func Parse(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.VariableDeclarationStatement> {
|
||||||
|
|
||||||
|
#RequireNodeType<Node, CST.VariableDeclarationStatement>(
|
||||||
|
node: node, type: "variableDeclaration", nice_type_name: "variable declaration statement")
|
||||||
|
|
||||||
|
let maybe_typeref = node.child(at: 0)
|
||||||
|
guard let typeref = maybe_typeref,
|
||||||
|
typeref.nodeType == "typeRef"
|
||||||
|
else {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Did not find type name for variable declaration statement"))
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_variablename = node.child(at: 1)
|
||||||
|
guard let variablename = maybe_variablename,
|
||||||
|
variablename.nodeType == "identifier"
|
||||||
|
else {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Did not find identifier name for variable declaration statement"))
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_rvalue = node.childCount > 3 ? node.child(at: 3) : .none
|
||||||
|
|
||||||
|
guard
|
||||||
|
case .Ok(let parsed_variablename) = CST.Identifier.ParseExpression(
|
||||||
|
node: variablename, withContext: context)
|
||||||
|
else {
|
||||||
|
return Result.Error(
|
||||||
|
Error(withMessage: "Could not parse variable name"))
|
||||||
|
}
|
||||||
|
|
||||||
|
guard
|
||||||
|
case .Ok(let declaration_p4_type) = CST.Types.ParseType(type: typeref, withContext: context)
|
||||||
|
else {
|
||||||
|
return Result.Error(
|
||||||
|
Error(withMessage: "Could not parse a P4 type from \(typeref.text!)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var initializer: CST.Categories.Expression? = .none
|
||||||
|
|
||||||
|
// If there is an initializer, it must be an expression.
|
||||||
|
if let initializer_expression = maybe_rvalue {
|
||||||
|
guard initializer_expression.nodeType == "expression" else {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "initial value for declaration statement is not an expression"))
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_parsed_rvalue = CST.Expression.Parse(
|
||||||
|
node: initializer_expression, withContext: context)
|
||||||
|
guard
|
||||||
|
case .Ok(let parsed_rvalue) = maybe_parsed_rvalue
|
||||||
|
else {
|
||||||
|
return .Error(maybe_parsed_rvalue.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
initializer = parsed_rvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Ok(
|
||||||
|
CST.VariableDeclarationStatement(
|
||||||
|
identifier: parsed_variablename as! CST.Identifier, withType: declaration_p4_type,
|
||||||
|
withInitializer: initializer),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@deriveParsableStatement
|
||||||
|
extension CST.VariableDeclarationStatement: ParsableStatement {}
|
||||||
|
|
||||||
|
extension CST.ExpressionStatement: Parsable {
|
||||||
|
public typealias C = CST.ExpressionStatement
|
||||||
|
public static func Parse(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.ExpressionStatement> {
|
||||||
|
#RequireNodeType<Node, (P4Statement)>(
|
||||||
|
node: node, type: "expressionStatement", nice_type_name: "expression statement")
|
||||||
|
|
||||||
|
let expression_node = node.child(at: 0)!
|
||||||
|
|
||||||
|
return switch CST.Expression.Parse(node: expression_node, withContext: context) {
|
||||||
|
case .Ok(let expression): .Ok(CST.ExpressionStatement(expression))
|
||||||
|
case .Error(let e): .Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@deriveParsableStatement
|
||||||
|
extension CST.ExpressionStatement: ParsableStatement {}
|
||||||
|
|
||||||
|
extension CST.ParserAssignmentStatement: Parsable {
|
||||||
|
public typealias C = CST.ParserAssignmentStatement
|
||||||
|
public static func Parse(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.ParserAssignmentStatement> {
|
||||||
|
|
||||||
|
#RequireNodeType<Node, CST.ParserAssignmentStatement>(
|
||||||
|
node: node, type: "assignmentStatement", nice_type_name: "assignment statement")
|
||||||
|
|
||||||
|
guard let lvalue_node = node.child(at: 0),
|
||||||
|
lvalue_node.nodeType == "expression"
|
||||||
|
else {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Missing lvalue in assignment statement"))
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let rvalue_node = node.child(at: 2),
|
||||||
|
rvalue_node.nodeType == "expression"
|
||||||
|
else {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Missing rvalue in assignment statement"))
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_parsed_rvalue = CST.Expression.Parse(
|
||||||
|
node: rvalue_node, withContext: context)
|
||||||
|
guard case Result.Ok(let rvalue) = maybe_parsed_rvalue else {
|
||||||
|
return Result.Error(maybe_parsed_rvalue.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_parsed_lvalue = CST.Expression.Parse(node: lvalue_node, withContext: context)
|
||||||
|
guard case .Ok(let lvalue) = maybe_parsed_lvalue else {
|
||||||
|
return Result.Error(maybe_parsed_lvalue.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Ok(
|
||||||
|
CST.ParserAssignmentStatement(
|
||||||
|
withLValue: lvalue,
|
||||||
|
withValue: rvalue
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@deriveParsableStatement
|
||||||
|
extension CST.ParserAssignmentStatement: ParsableStatement {}
|
||||||
|
|
||||||
|
extension CST.ReturnStatement: Parsable {
|
||||||
|
public typealias C = CST.ReturnStatement
|
||||||
|
public static func Parse(
|
||||||
|
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.ReturnStatement> {
|
||||||
|
#RequireNodeType<Node, CST.ReturnStatement>(
|
||||||
|
node: node, type: "return_statement", nice_type_name: "return statement")
|
||||||
|
|
||||||
|
let expression_node = node.child(at: 1)!
|
||||||
|
|
||||||
|
return switch CST.Expression.Parse(node: expression_node, withContext: context) {
|
||||||
|
case .Ok(let result): .Ok(CST.ReturnStatement(result))
|
||||||
|
case .Error(let e): .Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@deriveParsableStatement
|
||||||
|
extension CST.ReturnStatement: ParsableStatement {}
|
||||||
|
|
||||||
|
extension CST.ApplyStatement: Parsable {
|
||||||
|
public typealias C = CST.ApplyStatement
|
||||||
|
public static func Parse(
|
||||||
|
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.ApplyStatement> {
|
||||||
|
#RequireNodeType<Node, CST.ApplyStatement>(
|
||||||
|
node: node, type: "apply_statement", nice_type_name: "apply statement")
|
||||||
|
|
||||||
|
let expression_node = node.child(at: 1)!
|
||||||
|
|
||||||
|
return switch CST.BlockStatement.Parse(node: expression_node, withContext: context) {
|
||||||
|
case .Ok(let statement):
|
||||||
|
.Ok(CST.ApplyStatement(statement))
|
||||||
|
case .Error(let e): .Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@deriveParsableStatement
|
||||||
|
extension CST.ApplyStatement: ParsableStatement {}
|
||||||
|
|
||||||
|
extension CST.Instantiation: Parsable {
|
||||||
|
public typealias C = CST.Instantiation
|
||||||
|
public static func Parse(
|
||||||
|
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Instantiation> {
|
||||||
|
|
||||||
|
let expression = node
|
||||||
|
#RequireNodeType<Node, CST.Instantiation>(
|
||||||
|
node: expression, type: "instantiation", nice_type_name: "instantiation statement")
|
||||||
|
|
||||||
|
var walker = Walker(node: expression)
|
||||||
|
var current_node: Node? = .none
|
||||||
|
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Instantiation>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Missing function call component")))
|
||||||
|
|
||||||
|
let maybe_instantiated_type_name = CST.Identifier.ParseExpression(
|
||||||
|
node: current_node!, withContext: context)
|
||||||
|
guard case .Ok(let instantiated_type_name) = maybe_instantiated_type_name else {
|
||||||
|
return Result.Error(maybe_instantiated_type_name.error()!)
|
||||||
|
}
|
||||||
|
walker.next()
|
||||||
|
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Instantiation>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Missing instantiation component")))
|
||||||
|
|
||||||
|
let maybe_argument_list = CST.ArgumentList.Parse(node: current_node!, withContext: context)
|
||||||
|
|
||||||
|
guard case .Ok(let arguments) = maybe_argument_list else {
|
||||||
|
return .Error(maybe_argument_list.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
walker.next()
|
||||||
|
|
||||||
|
#MustOr(
|
||||||
|
result: current_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Instantiation>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Missing instantiation name")))
|
||||||
|
|
||||||
|
let name = CST.Identifier.ParseExpression(node: current_node!, withContext: context)
|
||||||
|
guard case .Ok(let name) = name else {
|
||||||
|
return .Error(name.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(
|
||||||
|
CST.Instantiation(
|
||||||
|
named: name as! CST.Identifier, withType: instantiated_type_name as! CST.Identifier,
|
||||||
|
withArguments: arguments))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@deriveParsableStatement
|
||||||
|
extension CST.Instantiation: ParsableStatement {}
|
||||||
|
|
||||||
|
extension CST.Statements: Parsable {
|
||||||
|
public typealias C = CST.Statements
|
||||||
|
public static func Parse(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Statements> {
|
||||||
|
if node.nodeType != "statements" && node.nodeType != "parserStatements" {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Did not find expected statements"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var errors: (any Errorable)? = .none
|
||||||
|
var parsed_s: [CST.Categories.Statement] = Array()
|
||||||
|
|
||||||
|
node.enumerateNamedChildren { node in
|
||||||
|
switch CST.Statement.Parse(
|
||||||
|
node: node, withContext: context)
|
||||||
|
{
|
||||||
|
case .Ok(let parsed_statement):
|
||||||
|
parsed_s.append(parsed_statement)
|
||||||
|
case .Error(let e):
|
||||||
|
errors =
|
||||||
|
if let errors = errors {
|
||||||
|
errors.append(error: e)
|
||||||
|
} else {
|
||||||
|
e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let errors = errors {
|
||||||
|
return .Error(errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Ok(CST.Statements(parsed_s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CST.TransitionStatement: Parsable {
|
||||||
|
public static func Parse(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.State> {
|
||||||
|
|
||||||
|
guard let state_identifier = context.lexical_context_name else {
|
||||||
|
return .Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Cannot parse a transition statement without the name of the containing state."
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
let stmts = context.lexical_context_statements
|
||||||
|
|
||||||
|
#RequireNodeType<Node, P4Statement>(
|
||||||
|
node: node, type: "parserTransitionStatement", nice_type_name: "parser transition statement"
|
||||||
|
)
|
||||||
|
|
||||||
|
guard let tse_node = node.child(at: 1),
|
||||||
|
tse_node.nodeType! == "transitionSelectionExpression"
|
||||||
|
else {
|
||||||
|
return .Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Could not find transition select expression"))
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let next_node = tse_node.child(at: 0) else {
|
||||||
|
return .Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Could not find the next token in a transition selection expression"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the next node is an identifier, we have the simple form ...
|
||||||
|
if next_node.nodeType == "identifier" {
|
||||||
|
let maybe_parsed_next_state_id = CST.Identifier.ParseExpression(
|
||||||
|
node: next_node, withContext: context)
|
||||||
|
switch maybe_parsed_next_state_id {
|
||||||
|
case .Ok(let next_state_id):
|
||||||
|
return .Ok(
|
||||||
|
CST.ParserStateDirectTransition(
|
||||||
|
name: (state_identifier),
|
||||||
|
withNextStateIdentifier: next_state_id as! CST.Identifier, withStatements: stmts))
|
||||||
|
case .Error(let e):
|
||||||
|
return .Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We know that the next node is a select expression.
|
||||||
|
return
|
||||||
|
switch CST.SelectExpression.ParseExpression(node: next_node, withContext: context)
|
||||||
|
{
|
||||||
|
case .Ok(let tse):
|
||||||
|
.Ok(
|
||||||
|
CST.ParserStateSelectTransition(
|
||||||
|
name: state_identifier, withTransitionExpression: tse as! CST.SelectExpression,
|
||||||
|
withStatements: stmts,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case .Error(let e): .Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CST.Statement: Parsable {
|
||||||
|
public typealias C = CST.Categories.Statement
|
||||||
|
public static func Parse(
|
||||||
|
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Statement> {
|
||||||
|
|
||||||
|
if node.nodeType != "parserStatement" && node.nodeType != "statement" {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(), withError: "Missing expected parser statement")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let statement = node.child(at: 0)!
|
||||||
|
|
||||||
|
let statementParsers: [String: ParsableStatement.Type] = [
|
||||||
|
"assignmentStatement": CST.ParserAssignmentStatement.self,
|
||||||
|
"expressionStatement": CST.ExpressionStatement.self,
|
||||||
|
"variableDeclaration": CST.VariableDeclarationStatement.self,
|
||||||
|
"conditionalStatement": CST.ConditionalStatement.self,
|
||||||
|
"blockStatement": CST.BlockStatement.self,
|
||||||
|
"return_statement": CST.ReturnStatement.self,
|
||||||
|
]
|
||||||
|
guard let parser = statementParsers[statement.nodeType ?? ""] else {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: statement.toSourceLocation(),
|
||||||
|
withError:
|
||||||
|
"Unparseable statement type (\(statement.nodeType ?? "Unknown Statement Type"))"))
|
||||||
|
}
|
||||||
|
switch parser.ParseStatement(node: statement, withContext: context) {
|
||||||
|
case Result.Ok(let parsed):
|
||||||
|
return .Ok(parsed)
|
||||||
|
case Result.Error(let e):
|
||||||
|
return .Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: node.toSourceLocation(),
|
||||||
|
withError: "Failed to parse a statement element: \(e)"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
// 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 TreeSitterExtensions
|
||||||
|
import TreeSitterP4
|
||||||
|
|
||||||
|
extension P4Boolean: MaybeParsableType {
|
||||||
|
public static func MaybeParseType(
|
||||||
|
type: SwiftTreeSitter.Node, withContext: CSTCompilerContext
|
||||||
|
) -> Result<(CST.Tipe)> {
|
||||||
|
return type.nodeType == "bool"
|
||||||
|
? .Ok(CST.Tipe(P4QualifiedType(P4Boolean())))
|
||||||
|
: .Error(Error(withMessage: "Invalid parser selected for \(type.nodeType!)"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension P4Int: MaybeParsableType {
|
||||||
|
public static func MaybeParseType(
|
||||||
|
type: SwiftTreeSitter.Node, withContext: CSTCompilerContext
|
||||||
|
) -> Result<(CST.Tipe)> {
|
||||||
|
#RequireNodeType<Node, CST.Tipe>(node: type, type: "int_type", nice_type_name: "Integer")
|
||||||
|
|
||||||
|
var walker = Walker(node: type)
|
||||||
|
|
||||||
|
var int_node: Node? = .none
|
||||||
|
|
||||||
|
#MustOr(
|
||||||
|
result: int_node, thing: walker.getNext(),
|
||||||
|
or: Result<CST.Tipe>.Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: type.toSourceLocation(),
|
||||||
|
withError: "Missing elements in int type declaration")))
|
||||||
|
|
||||||
|
// Move passed the keyword -- now see whether there is a width
|
||||||
|
walker.next()
|
||||||
|
|
||||||
|
if let bit_width_node = walker.getNext() {
|
||||||
|
guard let bit_width = Int(bit_width_node.child(at: 1)!.text!),
|
||||||
|
bit_width != 0
|
||||||
|
else {
|
||||||
|
return .Error(
|
||||||
|
ErrorWithLocation(
|
||||||
|
sourceLocation: bit_width_node.toSourceLocation(),
|
||||||
|
withError: "Could not parse \(bit_width_node.text!) into integer"))
|
||||||
|
}
|
||||||
|
return .Ok(CST.Tipe(P4QualifiedType(P4Int(BitWidth.Width(bit_width)))))
|
||||||
|
}
|
||||||
|
return .Ok(CST.Tipe(P4QualifiedType(P4Int(BitWidth.Infinite))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension P4String: MaybeParsableType {
|
||||||
|
public static func MaybeParseType(
|
||||||
|
type: SwiftTreeSitter.Node, withContext: CSTCompilerContext
|
||||||
|
) -> Result<(CST.Tipe)> {
|
||||||
|
return type.nodeType == "string"
|
||||||
|
? .Ok(CST.Tipe(P4QualifiedType(P4String())))
|
||||||
|
: .Error(Error(withMessage: "Invalid parser selected for \(type.nodeType!)"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CST.Types: ParsableType {
|
||||||
|
public static func ParseType(
|
||||||
|
type: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Tipe> {
|
||||||
|
#RequireNodeType<Node, CST.Tipe>(node: type, type: "typeRef", nice_type_name: "Type Reference")
|
||||||
|
let type = type.child(at: 0)!
|
||||||
|
if type.nodeType == "baseType" {
|
||||||
|
let type = type.child(at: 0)!
|
||||||
|
let base_type_parsers: [String: MaybeParsableType.Type] = [
|
||||||
|
"bool": P4Boolean.self, "int_type": P4Int.self, "string": P4String.self,
|
||||||
|
]
|
||||||
|
guard let parser = base_type_parsers[type.nodeType!] else {
|
||||||
|
return Result.Error(Error(withMessage: "No parser for type \(type.nodeType!)"))
|
||||||
|
}
|
||||||
|
switch parser.MaybeParseType(type: type, withContext: context) {
|
||||||
|
case .Ok(let type): return .Ok(type)
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.Error(Error(withMessage: "Type name not recognized"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
// 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 TreeSitterExtensions
|
||||||
|
import TreeSitterP4
|
||||||
|
|
||||||
|
/*
|
||||||
|
public protocol CompilableValue {
|
||||||
|
static func CompileValue(withValue value: String) -> Result<P4DataValue>
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public protocol MaybeParsableType {
|
||||||
|
static func MaybeParseType(
|
||||||
|
type: SwiftTreeSitter.Node, withContext: CSTCompilerContext
|
||||||
|
) -> Result<CST.Tipe>
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol ParsableType {
|
||||||
|
static func ParseType(
|
||||||
|
type: SwiftTreeSitter.Node, withContext: CSTCompilerContext
|
||||||
|
) -> Result<CST.Tipe>
|
||||||
|
}
|
||||||
|
public protocol ParsableExpression {
|
||||||
|
static func ParseExpression(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Expression>
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol Parsable<C> {
|
||||||
|
associatedtype C
|
||||||
|
static func Parse(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<C>
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol ParsableStatement {
|
||||||
|
static func ParseStatement(
|
||||||
|
node: Node, withContext context: CSTCompilerContext
|
||||||
|
) -> Result<CST.Categories.Statement>
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol CSTVisitor<T> {
|
||||||
|
associatedtype T
|
||||||
|
|
||||||
|
// Declarations
|
||||||
|
func visit(node: CST.Control, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
func visit(node: CST.ExternDeclaration, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
func visit(node: CST.FunctionDeclaration, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
func visit(node: CST.StructDeclaration, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
func visit(node: CST.Parser, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
|
||||||
|
// Statements
|
||||||
|
func visit(node: CST.Statements, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
func visit(
|
||||||
|
node: CST.VariableDeclarationStatement, driver: CSTVisitorDriver, context: T
|
||||||
|
) -> Result<T>
|
||||||
|
func visit(node: CST.ExpressionStatement, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
|
||||||
|
// Expressions
|
||||||
|
func visit(node: CST.KeysetExpression, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
func visit(node: CST.SelectCaseExpression, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
func visit(node: CST.SelectExpression, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
func visit(node: CST.BinaryOperatorExpression, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
func visit(node: CST.Literal, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
func visit(node: CST.Identifier, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
|
||||||
|
// Parser
|
||||||
|
func visit(
|
||||||
|
node: CST.ParserStateDirectTransition, driver: CSTVisitorDriver, context: T
|
||||||
|
) -> Result<T>
|
||||||
|
func visit(node: CST.ParserStateNoTransition, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||||
|
func visit(
|
||||||
|
node: CST.ParserStateSelectTransition, driver: CSTVisitorDriver, context: T
|
||||||
|
) -> Result<T>
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
// 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 CSTVisitorDriver {
|
||||||
|
public init() {}
|
||||||
|
|
||||||
|
public func visit<T>(
|
||||||
|
_ elem: any CST.Categories.LanguageElement, visitor: any CSTVisitor<T>, context: T
|
||||||
|
) -> Result<T> {
|
||||||
|
return switch elem {
|
||||||
|
case let elem as CST.Categories.Expression:
|
||||||
|
visit(expression: elem, visitor: visitor, context: context)
|
||||||
|
case let elem as CST.Categories.Statement:
|
||||||
|
visit(statement: elem, visitor: visitor, context: context)
|
||||||
|
case let elem as CST.Categories.State: visit(state: elem, visitor: visitor, context: context)
|
||||||
|
case let elem as CST.Categories.Declaration:
|
||||||
|
visit(declaration: elem, visitor: visitor, context: context)
|
||||||
|
default: .Error(Error(withMessage: "AST Language Element (\(elem)) Is Not Visitable"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func visit<T>(
|
||||||
|
declaration: any CST.Categories.Declaration, visitor: any CSTVisitor<T>, context: T
|
||||||
|
) -> Result<T> {
|
||||||
|
return switch declaration {
|
||||||
|
case let elem as CST.Control: visitor.visit(node: elem, driver: self, context: context)
|
||||||
|
case let elem as CST.ExternDeclaration:
|
||||||
|
visitor.visit(node: elem, driver: self, context: context)
|
||||||
|
case let elem as CST.FunctionDeclaration:
|
||||||
|
visitor.visit(node: elem, driver: self, context: context)
|
||||||
|
case let elem as CST.StructDeclaration:
|
||||||
|
visitor.visit(node: elem, driver: self, context: context)
|
||||||
|
case let elem as CST.Parser: visitor.visit(node: elem, driver: self, context: context)
|
||||||
|
default: .Error(Error(withMessage: "AST Declaration Element (\(declaration)) Is Not Visitable"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func visit<T>(
|
||||||
|
expression: any CST.Categories.Expression, visitor: any CSTVisitor<T>, context: T
|
||||||
|
) -> Result<T> {
|
||||||
|
return switch expression {
|
||||||
|
case let s as CST.Identifier:
|
||||||
|
visitor.visit(node: s, driver: self, context: context)
|
||||||
|
case let s as CST.KeysetExpression:
|
||||||
|
visitor.visit(node: s, driver: self, context: context)
|
||||||
|
case let s as CST.SelectCaseExpression:
|
||||||
|
visitor.visit(node: s, driver: self, context: context)
|
||||||
|
case let e as CST.SelectExpression:
|
||||||
|
visitor.visit(node: e, driver: self, context: context)
|
||||||
|
case let e as CST.BinaryOperatorExpression:
|
||||||
|
visitor.visit(node: e, driver: self, context: context)
|
||||||
|
case let e as CST.Literal: visitor.visit(node: e, driver: self, context: context)
|
||||||
|
default: .Error(Error(withMessage: "AST Expression Element (\(expression)) Is Not Visitable"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func visit<T>(
|
||||||
|
state: any CST.Categories.State, visitor: any CSTVisitor<T>, context: T
|
||||||
|
) -> Result<T> {
|
||||||
|
return switch state {
|
||||||
|
case let s as CST.ParserStateDirectTransition:
|
||||||
|
visitor.visit(node: s, driver: self, context: context)
|
||||||
|
case let s as CST.ParserStateNoTransition:
|
||||||
|
visitor.visit(node: s, driver: self, context: context)
|
||||||
|
case let s as CST.ParserStateSelectTransition:
|
||||||
|
visitor.visit(node: s, driver: self, context: context)
|
||||||
|
default: .Error(Error(withMessage: "AST State Element (\(state)) Is Not Visitable"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func visit<T>(
|
||||||
|
statement: any CST.Categories.Statement, visitor: any CSTVisitor<T>, context: T
|
||||||
|
) -> Result<T> {
|
||||||
|
return switch statement {
|
||||||
|
case let s as CST.Statements: visitor.visit(node: s, driver: self, context: context)
|
||||||
|
case let s as CST.ExpressionStatement: visitor.visit(node: s, driver: self, context: context)
|
||||||
|
case let s as CST.VariableDeclarationStatement:
|
||||||
|
visitor.visit(node: s, driver: self, context: context)
|
||||||
|
case let s as CST.Parser: visitor.visit(node: s, driver: self, context: context)
|
||||||
|
default: .Error(Error(withMessage: "AST Statement Element (\(statement)) Is Not Visitable"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func start<T>(
|
||||||
|
program: CST.Program, visitor: any CSTVisitor<T>, context: T
|
||||||
|
) -> Result<T> {
|
||||||
|
|
||||||
|
var context = context
|
||||||
|
switch visit(statement: program.statements, visitor: visitor, context: context) {
|
||||||
|
case .Ok(let c): context = c
|
||||||
|
case .Error(let e): return .Error(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,284 +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
|
|
||||||
import P4Lang
|
|
||||||
|
|
||||||
public struct Generated {
|
|
||||||
let gen: String
|
|
||||||
public init(_ base: String = "") {
|
|
||||||
self.gen = base
|
|
||||||
}
|
|
||||||
|
|
||||||
public func getGeneratedCode() -> String {
|
|
||||||
return self.gen
|
|
||||||
}
|
|
||||||
|
|
||||||
public func append(_ a: String) -> Generated {
|
|
||||||
return Generated(self.gen + a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate code for a P4 program.
|
|
||||||
///
|
|
||||||
/// See the [P4 behavioral model](https://github.com/p4lang/behavioral-model) for
|
|
||||||
/// the format.
|
|
||||||
public struct CodeGenerator: LanguageVisitor {
|
|
||||||
|
|
||||||
public init() {}
|
|
||||||
|
|
||||||
/// Generate code.
|
|
||||||
public func codeGen(_ node: any Visitable) -> Result<Generated> {
|
|
||||||
let visitor = VisitorDriver<Generated>(self)
|
|
||||||
let generated = Generated()
|
|
||||||
return visitor.start(node, context: generated)
|
|
||||||
}
|
|
||||||
|
|
||||||
public typealias Context = Generated
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: Program, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
|
|
||||||
var result: Result<VisitorContext<Generated>> = Fold(
|
|
||||||
input: v.types, initial: .Ok(c.next(uc: c.getUserContext().append("[")))
|
|
||||||
) { (current, acc) in
|
|
||||||
return switch acc {
|
|
||||||
case .Ok(let acc): acc.getVisitorDriver().visit(current, context: acc)
|
|
||||||
case .Error(let e): .Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result = Fold(
|
|
||||||
input: v.externs, initial: result
|
|
||||||
) { (current, acc) in
|
|
||||||
return switch acc {
|
|
||||||
case .Ok(let acc): acc.getVisitorDriver().visit(current, context: acc)
|
|
||||||
case .Error(let e): .Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result = Fold(
|
|
||||||
input: v.instances, initial: result
|
|
||||||
) { (current, acc) in
|
|
||||||
return switch acc {
|
|
||||||
case .Ok(let acc): acc.getVisitorDriver().visit(current.baseType(), context: acc)
|
|
||||||
case .Error(let e): .Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result = result.map {
|
|
||||||
.Ok($0.next(uc: $0.getUserContext().append("]")))
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: Parser, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
var initial = "{"
|
|
||||||
initial += "name: \"\(v.name)\","
|
|
||||||
initial += "init_state: \"start\","
|
|
||||||
initial += "parse_states: ["
|
|
||||||
|
|
||||||
let result: Result<VisitorContext<Generated>> = Fold(
|
|
||||||
input: v.states.states, initial: .Ok(c.next(uc: c.getUserContext().append(initial)))
|
|
||||||
) { (current, acc) in
|
|
||||||
return switch acc {
|
|
||||||
case .Ok(let acc):
|
|
||||||
switch self.visit(current, acc) {
|
|
||||||
case .Ok(let gend): .Ok(gend.next(uc: gend.getUserContext().append(",")))
|
|
||||||
case .Error(let e): .Error(e)
|
|
||||||
}
|
|
||||||
case .Error(let e): .Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.map {
|
|
||||||
.Ok($0.next(uc: $0.getUserContext().append("]}")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: InstantiatedParserState, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
let direct_transition_codegen = {
|
|
||||||
(
|
|
||||||
_: ParserStateDirectTransition, c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> in
|
|
||||||
return .Ok(c.next(uc: c.getUserContext().append("[]")))
|
|
||||||
}
|
|
||||||
|
|
||||||
let no_transition_codegen = {
|
|
||||||
(
|
|
||||||
_: ParserStateNoTransition, c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> in
|
|
||||||
return .Ok(c.next(uc: c.getUserContext().append("[]")))
|
|
||||||
}
|
|
||||||
|
|
||||||
let select_transition_codegen = {
|
|
||||||
(
|
|
||||||
state: ParserStateSelectTransition, c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> in
|
|
||||||
return switch self.visit(state.selectExpression, c.next(uc: c.getUserContext().append("["))) {
|
|
||||||
case .Ok(let res): .Ok(res.next(uc: res.getUserContext().append("]")))
|
|
||||||
case .Error(let e): .Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var initial = "{"
|
|
||||||
initial += "name: \"\(v.state)\","
|
|
||||||
initial += "transitions: "
|
|
||||||
|
|
||||||
let result: Result<VisitorContext<Generated>> =
|
|
||||||
switch v {
|
|
||||||
case let s as ParserStateSelectTransition:
|
|
||||||
select_transition_codegen(s, c.next(uc: c.getUserContext().append(initial)))
|
|
||||||
case let s as ParserStateDirectTransition:
|
|
||||||
direct_transition_codegen(s, c.next(uc: c.getUserContext().append(initial)))
|
|
||||||
case let s as ParserStateNoTransition:
|
|
||||||
no_transition_codegen(s, c.next(uc: c.getUserContext().append(initial)))
|
|
||||||
default:
|
|
||||||
.Error(Error(withMessage: "Could not code gen \(self)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.map {
|
|
||||||
.Ok($0.next(uc: $0.getUserContext().append("}")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: VariableDeclarationStatement, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: ConditionalStatement, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: BlockStatement, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: ReturnStatement, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: ApplyStatement, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: KeysetExpression, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: SelectCaseExpression, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: SelectExpression, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c.next(uc: c.getUserContext().append("Select")))
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: ArrayAccessExpression, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: FieldAccessExpression, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: FunctionCall, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: BinaryOperatorExpression, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: Declaration, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: ExternDeclaration, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: FunctionDeclaration, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: Action, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: TableKeyEntry, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: TablePropertyList, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(_ v: Table, _ c: VisitorContext<Generated>) -> Result<VisitorContext<Generated>>
|
|
||||||
{
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func visit(
|
|
||||||
_ v: Control, _ c: VisitorContext<Generated>
|
|
||||||
) -> Result<VisitorContext<Generated>> {
|
|
||||||
return .Ok(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +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
|
|
||||||
import P4Lang
|
|
||||||
|
|
||||||
public func Call<T>(
|
|
||||||
body: (ProgramExecution) -> (Result<T>, ProgramExecution), withArguments args: ArgumentList,
|
|
||||||
withParameters params: ParameterList, inExecution execution: ProgramExecution
|
|
||||||
) -> (Result<T>, ProgramExecution) {
|
|
||||||
|
|
||||||
if case .Error(let e) = args.compatible(params) {
|
|
||||||
return (.Error(e), execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
var called_execution = execution.enter_scope()
|
|
||||||
|
|
||||||
for (parameter, argument) in zip(params.parameters, args.arguments) {
|
|
||||||
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)
|
|
||||||
guard case (.Ok(let argument_value), let updated_execution) = maybe_argument_value else {
|
|
||||||
return (
|
|
||||||
.Error(Error(withMessage: "Cannot evaluate argument \(arg_idx): \(argument)")),
|
|
||||||
called_execution.exit_scope()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
called_execution = updated_execution.declare(
|
|
||||||
identifier: parameter.name, withValue: argument_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
let (maybe_call_result, updated_execution) = body(called_execution)
|
|
||||||
guard case .Ok(let call_result) = maybe_call_result else {
|
|
||||||
return (.Error(maybe_call_result.error()!), updated_execution.exit_scope())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before returning, update the (in)out parameters!
|
|
||||||
var inout_scopes = updated_execution.exit_scope().scopes
|
|
||||||
|
|
||||||
for (parameter, argument) in zip(params.parameters, args.arguments) {
|
|
||||||
if let param_direction = parameter.type.direction(),
|
|
||||||
param_direction == Direction.InOut || param_direction == Direction.Out
|
|
||||||
{
|
|
||||||
// Let's make sure that it is an evaluatable l value!
|
|
||||||
guard let arg_lvalue = argument.argument as? EvaluatableLValueExpression else {
|
|
||||||
return (
|
|
||||||
.Error(Error(withMessage: "(in)out parameter argument is not lvalue")),
|
|
||||||
updated_execution.exit_scope()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard
|
|
||||||
case .Ok(let arg_new_value) = updated_execution.scopes.lookup(identifier: parameter.name)
|
|
||||||
else {
|
|
||||||
return (
|
|
||||||
.Error(Error(withMessage: "Could not get (in)out parameter value from scope")),
|
|
||||||
updated_execution.exit_scope()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch arg_lvalue.set(
|
|
||||||
to: arg_new_value, inScopes: inout_scopes, duringExecution: updated_execution)
|
|
||||||
{
|
|
||||||
case .Ok((let updated_scopes, _)): inout_scopes = updated_scopes
|
|
||||||
case .Error(let e): return (.Error(e), updated_execution.exit_scope())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (.Ok(call_result), updated_execution.replaceScopes(inout_scopes))
|
|
||||||
}
|
|
||||||
@@ -1,104 +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
|
|
||||||
import P4Lang
|
|
||||||
|
|
||||||
extension Control: LibraryCallable {
|
|
||||||
public typealias T = P4TableHitMissValue
|
|
||||||
public func call(
|
|
||||||
execution: Common.ProgramExecution, arguments: ArgumentList
|
|
||||||
) -> (P4TableHitMissValue, Common.ProgramExecution) {
|
|
||||||
|
|
||||||
var control_execution = execution.enter_scope()
|
|
||||||
|
|
||||||
// Add initial values to the global scope
|
|
||||||
for (name, value) in execution.getGlobalValues() {
|
|
||||||
control_execution = control_execution.declare(identifier: name, withValue: value)
|
|
||||||
}
|
|
||||||
|
|
||||||
let call_body: (ProgramExecution) -> (Result<P4TableHitMissValue>, ProgramExecution) = {
|
|
||||||
execution in
|
|
||||||
var control_execution = execution
|
|
||||||
|
|
||||||
for action in self.actions.actions {
|
|
||||||
control_execution = control_execution.declare(
|
|
||||||
identifier: action.name, withValue: P4Value(action))
|
|
||||||
}
|
|
||||||
|
|
||||||
for key in self.table.properties.keys.keys {
|
|
||||||
// Every evaluation of the key starts from an unchanged execution context.
|
|
||||||
let (key_eval, updated_execution) = key.key.evaluate(execution: control_execution)
|
|
||||||
|
|
||||||
guard case .Ok(let key_val) = key_eval else {
|
|
||||||
return (.Error(key_eval.error()!), updated_execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ASSUME: The first matching entry is the one to do.
|
|
||||||
/// TODO: Check whether this matches architecture.
|
|
||||||
for (val, action) in self.table.entries {
|
|
||||||
|
|
||||||
// Skip those with mismatching types.
|
|
||||||
|
|
||||||
if !val.type().eq(key_val.type()) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ASSUME: All matches are exact.
|
|
||||||
if val.eq(key_val) {
|
|
||||||
// Lookup action!
|
|
||||||
|
|
||||||
let maybe_action = updated_execution.scopes.lookup(identifier: action)
|
|
||||||
guard case .Ok(let action) = maybe_action else {
|
|
||||||
return (.Error(maybe_action.error()!), updated_execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
let aaction = (action.dataValue() as! Action)
|
|
||||||
|
|
||||||
return switch aaction.evaluate(execution: updated_execution) {
|
|
||||||
case (ControlFlow.Error, let updated_execution):
|
|
||||||
(.Error(updated_execution.getError()!), updated_execution)
|
|
||||||
case (_, let updated_execution): (.Ok(P4TableHitMissValue.Hit), updated_execution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (.Ok(P4TableHitMissValue.Miss), control_execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch Call(
|
|
||||||
body: call_body, withArguments: arguments, withParameters: self.parameters,
|
|
||||||
inExecution: control_execution)
|
|
||||||
{
|
|
||||||
case (.Ok(let r), let updated_execution): return (r, updated_execution)
|
|
||||||
case (.Error(let e), let updated_execution):
|
|
||||||
return (P4TableHitMissValue.Miss, updated_execution.setError(error: e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Action: EvaluatableStatement {
|
|
||||||
public func evaluate(
|
|
||||||
execution: Common.ProgramExecution
|
|
||||||
) -> (Common.ControlFlow, Common.ProgramExecution) {
|
|
||||||
if let body = self.body {
|
|
||||||
return body.evaluate(execution: execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ControlFlow.Next, execution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,536 +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
|
|
||||||
import P4Lang
|
|
||||||
|
|
||||||
extension SelectCaseExpression: EvaluatableExpression {
|
|
||||||
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
|
|
||||||
return (execution.scopes.lookup(identifier: next_state_identifier), execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func type() -> P4QualifiedType {
|
|
||||||
return P4QualifiedType(ParserState())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SelectExpression: EvaluatableExpression {
|
|
||||||
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
|
|
||||||
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),
|
|
||||||
kse.eq(selector_value)
|
|
||||||
{
|
|
||||||
//let result = sce.evaluate(execution: updated_execution)
|
|
||||||
let result = updated_execution.evaluator.EvaluateExpression(
|
|
||||||
sce, inExecution: updated_execution)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (.Error(Error(withMessage: "No key matched the selector")), updated_execution)
|
|
||||||
case (.Error(let e), let updated_execution): return (.Error(e), updated_execution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func type() -> P4QualifiedType {
|
|
||||||
return P4QualifiedType(ParserState())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variables are evaluatable because they can be looked up by identifiers.
|
|
||||||
extension TypedIdentifier: EvaluatableExpression {
|
|
||||||
public func type() -> P4QualifiedType {
|
|
||||||
return self.type
|
|
||||||
}
|
|
||||||
|
|
||||||
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
|
|
||||||
return (execution.scopes.lookup(identifier: self), execution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variables are evaluatable because they can be looked up by identifiers.
|
|
||||||
extension TypedIdentifier: EvaluatableLValueExpression {
|
|
||||||
public func set(
|
|
||||||
to: P4Value, inScopes scopes: Common.VarValueScopes,
|
|
||||||
duringExecution execution: ProgramExecution
|
|
||||||
) -> Common.Result<(Common.VarValueScopes, P4Value)> {
|
|
||||||
if case .Error(let e) = scopes.lookup(identifier: self) {
|
|
||||||
return .Error(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Ok((scopes.set(identifier: self, withValue: to), to))
|
|
||||||
}
|
|
||||||
|
|
||||||
public func check(
|
|
||||||
to: any Common.EvaluatableExpression, inScopes scopes: Common.VarTypeScopes
|
|
||||||
) -> Result<()> {
|
|
||||||
guard case .Ok(let type) = scopes.lookup(identifier: self) else {
|
|
||||||
return .Error(Error(withMessage: "Cannot assign to identifier not in scope"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return switch type.assignableFromType(to.type()) {
|
|
||||||
case TypeCheckResults.IncompatibleTypes:
|
|
||||||
.Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Cannot assign value with type \(to.type()) to identifier \(self) with type \(type)"))
|
|
||||||
case TypeCheckResults.ReadOnly:
|
|
||||||
.Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Cannot assign value with type \(to.type()) to identifier \(self) that is read only"))
|
|
||||||
case TypeCheckResults.WrongDirection:
|
|
||||||
.Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Cannot assign value with type \(to.type()) to identifier \(self) that is in parameter")
|
|
||||||
)
|
|
||||||
case TypeCheckResults.Ok: .Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func binary_equal_operator_evaluator(left: P4Value, right: P4Value) -> P4DataValue {
|
|
||||||
return Map(input: left.dataValue().eq(rhs: right.dataValue())) { input in
|
|
||||||
P4BooleanValue(withValue: input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func binary_lt_operator_evaluator(left: P4Value, right: P4Value) -> P4DataValue {
|
|
||||||
return Map(input: left.dataValue().lt(rhs: right.dataValue())) { input in
|
|
||||||
P4BooleanValue(withValue: input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func binary_lte_operator_evaluator(left: P4Value, right: P4Value) -> P4DataValue {
|
|
||||||
return Map(input: left.dataValue().lte(rhs: right.dataValue())) { input in
|
|
||||||
P4BooleanValue(withValue: input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func binary_gt_operator_evaluator(left: P4Value, right: P4Value) -> P4DataValue {
|
|
||||||
return Map(input: left.dataValue().gt(rhs: right.dataValue())) { input in
|
|
||||||
P4BooleanValue(withValue: input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func binary_gte_operator_evaluator(left: P4Value, right: P4Value) -> P4DataValue {
|
|
||||||
return Map(input: left.dataValue().gte(rhs: right.dataValue())) { input in
|
|
||||||
P4BooleanValue(withValue: input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func binary_and_operator_evaluator(left: P4Value, right: P4Value) -> P4DataValue {
|
|
||||||
let bleft = left.dataValue() as! P4BooleanValue
|
|
||||||
let bright = right.dataValue() as! P4BooleanValue
|
|
||||||
return Map(input: bleft.access() && bright.access()) { input in
|
|
||||||
P4BooleanValue(withValue: input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func binary_or_operator_evaluator(left: P4Value, right: P4Value) -> P4DataValue {
|
|
||||||
let bleft = left.dataValue() as! P4BooleanValue
|
|
||||||
let bright = right.dataValue() as! P4BooleanValue
|
|
||||||
return Map(input: bleft.access() || bright.access()) { input in
|
|
||||||
P4BooleanValue(withValue: input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func binary_add_operator_evaluator(left: P4Value, right: P4Value) -> P4DataValue {
|
|
||||||
let ileft = left.dataValue() as! P4IntValue
|
|
||||||
let iright = right.dataValue() as! P4IntValue
|
|
||||||
return Map(input: ileft.access() + iright.access()) { input in
|
|
||||||
P4IntValue(withValue: input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func binary_subtract_operator_evaluator(left: P4Value, right: P4Value) -> P4DataValue {
|
|
||||||
let ileft = left.dataValue() as! P4IntValue
|
|
||||||
let iright = right.dataValue() as! P4IntValue
|
|
||||||
return Map(input: ileft.access() - iright.access()) { input in
|
|
||||||
P4IntValue(withValue: input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func binary_multiply_operator_evaluator(left: P4Value, right: P4Value) -> P4DataValue {
|
|
||||||
let ileft = left.dataValue() as! P4IntValue
|
|
||||||
let iright = right.dataValue() as! P4IntValue
|
|
||||||
return Map(input: ileft.access() * iright.access()) { input in
|
|
||||||
P4IntValue(withValue: input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func binary_divide_operator_evaluator(left: P4Value, right: P4Value) -> P4DataValue {
|
|
||||||
let ileft = left.dataValue() as! P4IntValue
|
|
||||||
let iright = right.dataValue() as! P4IntValue
|
|
||||||
return Map(input: ileft.access() / iright.access()) { input in
|
|
||||||
P4IntValue(withValue: input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// swift-format-ignore
|
|
||||||
public typealias BinaryOperatorChecker = (EvaluatableExpression, EvaluatableExpression) -> Result<()>
|
|
||||||
|
|
||||||
public func binary_and_or_operator_checker(
|
|
||||||
left: EvaluatableExpression, right: EvaluatableExpression
|
|
||||||
) -> Result<()> {
|
|
||||||
// Check that both are Boolean-typed things!
|
|
||||||
if !(left.type().baseType().eq(rhs: P4Boolean()) && right.type().baseType().eq(rhs: P4Boolean()))
|
|
||||||
{
|
|
||||||
return .Error(Error(withMessage: "And/Or on operands with non-bool type is not allowed"))
|
|
||||||
}
|
|
||||||
return .Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
public func binary_int_math_operator_checker(
|
|
||||||
left: EvaluatableExpression, right: EvaluatableExpression
|
|
||||||
) -> Result<()> {
|
|
||||||
// Check that both are int-typed things!
|
|
||||||
if !(left.type().baseType().eq(rhs: P4Int()) && right.type().baseType().eq(rhs: P4Int())) {
|
|
||||||
return .Error(
|
|
||||||
Error(withMessage: "Mathematical operation on operands with non-int type is not allowed"))
|
|
||||||
}
|
|
||||||
return .Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
guard case (.Ok(let evaluated_right), let updated_execution) = maybe_evaluated_right else {
|
|
||||||
return maybe_evaluated_right
|
|
||||||
}
|
|
||||||
|
|
||||||
return (.Ok(P4Value(self.evaluator.2(evaluated_left, evaluated_right))), updated_execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func type() -> P4QualifiedType {
|
|
||||||
return self.evaluator.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
guard case (.Ok(let indexor), let updated_execution) = maybe_indexor else {
|
|
||||||
return maybe_indexor
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let indexor_int = indexor.dataValue() as? P4IntValue else {
|
|
||||||
return (.Error(Error(withMessage: "\(indexor) cannot index an array")), updated_execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let array = name.dataValue() as? P4ArrayValue else {
|
|
||||||
return (.Error(Error(withMessage: "\(name) does not name an array")), updated_execution)
|
|
||||||
}
|
|
||||||
let accessed = array.access(indexor_int.access())
|
|
||||||
|
|
||||||
return (.Ok(accessed), updated_execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func type() -> P4QualifiedType {
|
|
||||||
return self.type.value_type()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ArrayAccessExpression: EvaluatableLValueExpression {
|
|
||||||
public func set(
|
|
||||||
to: P4Value, inScopes scopes: Common.VarValueScopes,
|
|
||||||
duringExecution execution: ProgramExecution
|
|
||||||
) -> Common.Result<(Common.VarValueScopes, P4Value)> {
|
|
||||||
|
|
||||||
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)
|
|
||||||
guard case (.Ok(let value), let updated_execution) = maybe_value else {
|
|
||||||
return .Error(
|
|
||||||
Error(withMessage: "\(self.name) cannot be evaluated: \(maybe_value.0.error()!)"))
|
|
||||||
}
|
|
||||||
guard let array_value = value.dataValue() as? P4ArrayValue else {
|
|
||||||
return Result.Error(Error(withMessage: "\(self.name) does not identify an array"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
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()!)")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
guard let indexor_int = indexor_value.dataValue() as? P4IntValue else {
|
|
||||||
return Result.Error(Error(withMessage: "\(self.indexor) cannot be used to index an array"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we have an array and an index!
|
|
||||||
|
|
||||||
let maybe_updated_array_data_value = array_value.set(index: indexor_int.access(), to: to)
|
|
||||||
guard case .Ok(let new_array_value) = maybe_updated_array_data_value else {
|
|
||||||
return .Error(maybe_updated_array_data_value.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_updated_array_value = value.update(withNewValue: new_array_value)
|
|
||||||
guard case .Ok(let updated_array_value) = maybe_updated_array_value else {
|
|
||||||
return .Error(maybe_updated_array_value.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
let array_lvalue = self.name as! EvaluatableLValueExpression
|
|
||||||
return array_lvalue.set(
|
|
||||||
to: updated_array_value, inScopes: scopes, duringExecution: updated_execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func check(
|
|
||||||
to: any Common.EvaluatableExpression, inScopes scopes: Common.VarTypeScopes
|
|
||||||
) -> Common.Result<()> {
|
|
||||||
|
|
||||||
return switch self.type.value_type().assignableFromType(to.type()) {
|
|
||||||
case TypeCheckResults.IncompatibleTypes:
|
|
||||||
.Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Cannot assign value of type \(to.type()) to array with values of type \(self.name.type())"
|
|
||||||
))
|
|
||||||
case TypeCheckResults.ReadOnly:
|
|
||||||
.Error(
|
|
||||||
Error(
|
|
||||||
withMessage: "Cannot assign value of type \(to.type()) to array \(self) that is read only"
|
|
||||||
))
|
|
||||||
case TypeCheckResults.WrongDirection:
|
|
||||||
.Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Cannot assign value of type \(to.type()) to array \(self) that is in parameter"))
|
|
||||||
case TypeCheckResults.Ok:
|
|
||||||
// Now, check the type of the array itself.
|
|
||||||
switch self.name.type().assignable() {
|
|
||||||
case TypeCheckResults.ReadOnly:
|
|
||||||
.Error(Error(withMessage: "Cannot assign to array \(self) that is read only"))
|
|
||||||
case TypeCheckResults.WrongDirection:
|
|
||||||
.Error(Error(withMessage: "Cannot assign to array \(self) that is in parameter"))
|
|
||||||
case TypeCheckResults.Ok: .Ok(())
|
|
||||||
default: .Error(Error(withMessage: "Cannot assign to array \(self)"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FieldAccessExpression: EvaluatableExpression {
|
|
||||||
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
|
|
||||||
|
|
||||||
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)
|
|
||||||
guard case (.Ok(let strct), let updated_execution) = maybe_struct else {
|
|
||||||
return maybe_struct
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let struct_strct = strct.dataValue() as? P4StructValue else {
|
|
||||||
return (.Error(Error(withMessage: "\(strct) does not identify a struct")), updated_execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TODO: Create a default value?
|
|
||||||
guard let value = struct_strct.get(field: self.field) else {
|
|
||||||
return (.Error(Error(withMessage: "Missing value")), updated_execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (.Ok(value), updated_execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func type() -> P4QualifiedType {
|
|
||||||
return self.field.type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FieldAccessExpression: EvaluatableLValueExpression {
|
|
||||||
public func set(
|
|
||||||
to: P4Value, inScopes scopes: Common.VarValueScopes,
|
|
||||||
duringExecution execution: ProgramExecution
|
|
||||||
) -> Common.Result<(Common.VarValueScopes, P4Value)> {
|
|
||||||
// For purposes of documentation, assume the field access expression we are evaluating is
|
|
||||||
// (strct_id).field_id = new_field_value
|
|
||||||
// where strct_id expands to
|
|
||||||
// (identifier.field_id1.field_id2...).field_id = new_field_value
|
|
||||||
|
|
||||||
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)
|
|
||||||
guard case (.Ok(let value), let updated_execution) = maybe_value else {
|
|
||||||
return .Error(
|
|
||||||
Error(withMessage: "\(self.strct) cannot be evaluated: \(maybe_value.0.error()!)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let struct_value = value.dataValue() as? P4StructValue else {
|
|
||||||
return .Error(Error(withMessage: "\(self.strct) does not identify a struct"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we know that struct_id identifies a structure value.
|
|
||||||
|
|
||||||
// Update field_id of that structure and get the new structure value.
|
|
||||||
let maybe_new_struct_data_value = struct_value.set(field: self.field, to: to)
|
|
||||||
guard case .Ok(let new_struct_data_value) = maybe_new_struct_data_value else {
|
|
||||||
return .Error(maybe_new_struct_data_value.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_new_struct_value = value.update(withNewValue: new_struct_data_value)
|
|
||||||
guard case .Ok(let new_struct_value) = maybe_new_struct_value else {
|
|
||||||
return .Error(maybe_new_struct_value.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
// That new structure value should be assignable to the lvalue that is strct_id.
|
|
||||||
// We use recursion here -- ultimately finding our way to a TypedIdentifier that
|
|
||||||
// will update the scope. Pretty cool!
|
|
||||||
let struct_lvalue = self.strct as! EvaluatableLValueExpression
|
|
||||||
return struct_lvalue.set(
|
|
||||||
to: new_struct_value, inScopes: scopes, duringExecution: updated_execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func check(
|
|
||||||
to: any Common.EvaluatableExpression, inScopes scopes: Common.VarTypeScopes
|
|
||||||
) -> Common.Result<()> {
|
|
||||||
return switch self.field.type().assignableFromType(to.type()) {
|
|
||||||
case TypeCheckResults.IncompatibleTypes:
|
|
||||||
.Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Cannot assign value of type \(to.type()) to field \(self.field) of type \(self.type())"
|
|
||||||
))
|
|
||||||
case TypeCheckResults.ReadOnly:
|
|
||||||
.Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Cannot assign value of type \(to.type()) to field \(self.field) that is read only"
|
|
||||||
))
|
|
||||||
case TypeCheckResults.Ok:
|
|
||||||
// Now, check the type of the struct itself.
|
|
||||||
switch self.strct.type().assignable() {
|
|
||||||
case TypeCheckResults.ReadOnly:
|
|
||||||
.Error(
|
|
||||||
Error(
|
|
||||||
withMessage: "Cannot assign to field \(self.field) of \(self.strct) that is read only"))
|
|
||||||
case TypeCheckResults.WrongDirection:
|
|
||||||
.Error(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Cannot assign to field \(self.field) of \(self.strct) that is in parameter"))
|
|
||||||
case TypeCheckResults.Ok: .Ok(())
|
|
||||||
default: .Error(Error(withMessage: "Cannot assign to field \(self.field) of \(self.strct)"))
|
|
||||||
}
|
|
||||||
default: .Error(Error(withMessage: "Cannot assign to field \(self.field) of \(self.strct)"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension KeysetExpression: EvaluatableExpression {
|
|
||||||
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
|
|
||||||
//return self.key.evaluate(execution: execution)
|
|
||||||
return execution.evaluator.EvaluateExpression(self.key, inExecution: execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func type() -> P4QualifiedType {
|
|
||||||
return self.key.type()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FunctionCall: EvaluatableExpression {
|
|
||||||
public func evaluate(
|
|
||||||
execution: Common.ProgramExecution
|
|
||||||
) -> (Common.Result<P4Value>, ProgramExecution) {
|
|
||||||
|
|
||||||
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.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")),
|
|
||||||
execution
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Call(
|
|
||||||
body: call_body, withArguments: self.arguments, withParameters: body.1,
|
|
||||||
inExecution: execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func type() -> P4QualifiedType {
|
|
||||||
return P4QualifiedType(self.return_type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension P4Value: EvaluatableExpression {
|
|
||||||
public func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution) {
|
|
||||||
return (.Ok(self), execution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,198 +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
|
|
||||||
import P4Lang
|
|
||||||
|
|
||||||
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)
|
|
||||||
guard case (.Ok(let value), let updated_execution) = result else {
|
|
||||||
return (ControlFlow.Error, execution.setError(error: result.0.error()!))
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_updated_scopes = self.lvalue.set(
|
|
||||||
to: value, inScopes: execution.scopes, duringExecution: updated_execution)
|
|
||||||
guard case Result.Ok(let updated_scopes) = maybe_updated_scopes else {
|
|
||||||
return (ControlFlow.Error, execution.setError(error: maybe_updated_scopes.error()!))
|
|
||||||
}
|
|
||||||
execution.scopes = updated_scopes.0
|
|
||||||
|
|
||||||
return (ControlFlow.Next, updated_execution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ParserStateDirectTransition: EvaluatableParserState {
|
|
||||||
public func execute(
|
|
||||||
program: Common.ProgramExecution
|
|
||||||
) -> (any EvaluatableParserState, Common.ProgramExecution) {
|
|
||||||
var program = program.enter_scope()
|
|
||||||
|
|
||||||
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())
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
reject,
|
|
||||||
next_execution.exit_scope().setError(
|
|
||||||
error: Error(withMessage: "Invalid control flow (\(control_flow) in parser)"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = program.scopes.lookup(identifier: get_next_state())
|
|
||||||
|
|
||||||
if case .Ok(let value) = res {
|
|
||||||
if value.type().baseType().eq(rhs: self) {
|
|
||||||
return (value.dataValue() as! EvaluatableParserState, program.exit_scope())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
program = program.setError(error: res.error()!).exit_scope()
|
|
||||||
|
|
||||||
return (self, program.exit_scope())
|
|
||||||
}
|
|
||||||
|
|
||||||
public func done() -> Bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
public func state() -> P4Lang.ParserState {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ParserStateNoTransition: EvaluatableParserState {
|
|
||||||
public func execute(
|
|
||||||
program: Common.ProgramExecution
|
|
||||||
) -> (any EvaluatableParserState, Common.ProgramExecution) {
|
|
||||||
return (self, program)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func done() -> Bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
public func state() -> P4Lang.ParserState {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ParserStateSelectTransition: EvaluatableParserState {
|
|
||||||
public func execute(
|
|
||||||
program: Common.ProgramExecution
|
|
||||||
) -> (any EvaluatableParserState, Common.ProgramExecution) {
|
|
||||||
var program = program.enter_scope()
|
|
||||||
|
|
||||||
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())
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
reject,
|
|
||||||
next_execution.exit_scope().setError(
|
|
||||||
error: Error(withMessage: "Invalid control flow (\(control_flow) in parser)"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
//switch self.selectExpression.evaluate(execution: program) {
|
|
||||||
switch program.evaluator.EvaluateExpression(self.selectExpression, inExecution: program) {
|
|
||||||
case (.Ok(let value), let program):
|
|
||||||
if value.type().baseType().eq(rhs: self) {
|
|
||||||
return (value.dataValue() as! EvaluatableParserState, program.exit_scope())
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
self,
|
|
||||||
program.setError(
|
|
||||||
error: Error(withMessage: "Select transition transitioned to a none state"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case (.Error(let e), let program): return (self, program.setError(error: e).exit_scope())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func done() -> Bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
public func state() -> P4Lang.ParserState {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Parser: LibraryCallable {
|
|
||||||
public typealias T = InstantiatedParserState
|
|
||||||
public func call(
|
|
||||||
execution: Common.ProgramExecution, arguments: ArgumentList
|
|
||||||
) -> (P4Lang.InstantiatedParserState, Common.ProgramExecution) {
|
|
||||||
var execution = execution.enter_scope()
|
|
||||||
|
|
||||||
execution = execution.declare(
|
|
||||||
identifier: AsInstantiatedParserState(accept.state()).state,
|
|
||||||
withValue: P4Value(accept, P4QualifiedType.ReadOnly(accept.type())))
|
|
||||||
execution = execution.declare(
|
|
||||||
identifier: AsInstantiatedParserState(reject.state()).state,
|
|
||||||
withValue: P4Value(reject, P4QualifiedType.ReadOnly(reject.type())))
|
|
||||||
|
|
||||||
// Add initial values to the global scope
|
|
||||||
for (name, value) in execution.getGlobalValues() {
|
|
||||||
execution = execution.declare(identifier: name, withValue: value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// First, add every state to the scope!
|
|
||||||
for state in self.states.states {
|
|
||||||
execution = execution.declare(
|
|
||||||
identifier: state.state, withValue: P4Value(state))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let _current_state = self.findStartState(),
|
|
||||||
var current_state = _current_state as? EvaluatableParserState
|
|
||||||
else {
|
|
||||||
return (
|
|
||||||
reject, execution.setError(error: Error(withMessage: "Could not find the start state"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let call_body: (ProgramExecution) -> (Result<P4Value>, ProgramExecution) = {
|
|
||||||
(execution: ProgramExecution) in
|
|
||||||
var current_execution = execution
|
|
||||||
// Evaluate until the state is either accept or reject.
|
|
||||||
while !current_state.done() && !current_execution.hasError() {
|
|
||||||
(current_state, current_execution) = current_state.execute(program: current_execution)
|
|
||||||
}
|
|
||||||
return (.Ok(P4Value(AsInstantiatedParserState(current_state.state()))), current_execution)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
switch Call(
|
|
||||||
body: call_body, withArguments: arguments, withParameters: parameters,
|
|
||||||
inExecution: execution)
|
|
||||||
{
|
|
||||||
case (.Ok(let value), let updated_execution):
|
|
||||||
(value.dataValue() as! InstantiatedParserState, updated_execution)
|
|
||||||
case (.Error(let e), let updated_execution):
|
|
||||||
(reject, updated_execution.setError(error: Error(withMessage: "Cannot call parser: \(e)")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +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
|
|
||||||
import P4Lang
|
|
||||||
|
|
||||||
public protocol Execution {
|
|
||||||
func execute(execution: ProgramExecution) -> ProgramExecution
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol EvaluatableParserState: P4DataValue {
|
|
||||||
func execute(program: ProgramExecution) -> (EvaluatableParserState, ProgramExecution)
|
|
||||||
func done() -> Bool
|
|
||||||
func state() -> ParserState
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines an interface for P4 components that can be invoked directly by the p4rse library user
|
|
||||||
public protocol LibraryCallable<T> {
|
|
||||||
associatedtype T
|
|
||||||
func call(execution: ProgramExecution, arguments: ArgumentList) -> (T, ProgramExecution)
|
|
||||||
}
|
|
||||||
@@ -1,91 +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
|
|
||||||
import P4Lang
|
|
||||||
|
|
||||||
/// The runtime for a parser
|
|
||||||
public struct Runtime<U, T: LibraryCallable<U>>: CustomStringConvertible {
|
|
||||||
public var callable: T
|
|
||||||
|
|
||||||
let initialValues: VarValueScopes?
|
|
||||||
|
|
||||||
init(callable: T) {
|
|
||||||
self.callable = callable
|
|
||||||
self.initialValues = .none
|
|
||||||
}
|
|
||||||
|
|
||||||
init(callable: T, withGlobalValues initial: VarValueScopes?) {
|
|
||||||
self.callable = callable
|
|
||||||
self.initialValues = initial
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a parser runtime from a P4 program
|
|
||||||
public static func create(
|
|
||||||
program: P4Lang.Program
|
|
||||||
) -> Result<Runtime<InstantiatedParserState, Parser>> {
|
|
||||||
return Runtime.create(program: program, withGlobalValues: .none)
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func create(
|
|
||||||
program: P4Lang.Program, withGlobalValues initial: VarValueScopes?
|
|
||||||
) -> Result<Runtime<InstantiatedParserState, Parser>> {
|
|
||||||
return switch program.starting_parser() {
|
|
||||||
case .Ok(let parser):
|
|
||||||
.Ok(
|
|
||||||
P4Runtime.Runtime<InstantiatedParserState, Parser>(
|
|
||||||
callable: parser, withGlobalValues: initial))
|
|
||||||
case .Error(let error): .Error(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func create(
|
|
||||||
control: P4Lang.Control, withGlobalValues initial: VarValueScopes?
|
|
||||||
) -> Result<Runtime<P4TableHitMissValue, Control>> {
|
|
||||||
return .Ok(
|
|
||||||
P4Runtime.Runtime<P4TableHitMissValue, Control>(callable: control, withGlobalValues: initial))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run a P4 parser with no arguments
|
|
||||||
public func run() -> Result<(U, ProgramExecution)> {
|
|
||||||
return self.run(withArguments: ArgumentList([]))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run the P4 parser on a given packet
|
|
||||||
public func run(
|
|
||||||
withArguments arguments: ArgumentList, inExecution pe: ProgramExecution = ProgramExecution()
|
|
||||||
) -> Result<(U, ProgramExecution)> {
|
|
||||||
|
|
||||||
let npe =
|
|
||||||
if let globals = initialValues {
|
|
||||||
pe.setGlobalValues(globals)
|
|
||||||
} else {
|
|
||||||
pe
|
|
||||||
}
|
|
||||||
|
|
||||||
let (end_state, execution) = callable.call(execution: npe, arguments: arguments)
|
|
||||||
if let error = execution.getError() {
|
|
||||||
return .Error(error)
|
|
||||||
}
|
|
||||||
return .Ok((end_state, execution))
|
|
||||||
}
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
return "Runtime:\nExecution: \(callable)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,135 +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
|
|
||||||
import P4Lang
|
|
||||||
|
|
||||||
extension BlockStatement: EvaluatableStatement {
|
|
||||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
|
||||||
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"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension VariableDeclarationStatement: EvaluatableStatement {
|
|
||||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
|
||||||
guard
|
|
||||||
//case (.Ok(let initial_value), let execution) = self.initializer.evaluate(execution: execution)
|
|
||||||
case (.Ok(let initial_value), let execution) = execution.evaluator.EvaluateExpression(
|
|
||||||
self.initializer, inExecution: execution)
|
|
||||||
else {
|
|
||||||
return (
|
|
||||||
ControlFlow.Error,
|
|
||||||
execution.setError(error: Error(withMessage: "Could not evaluate \(self.initializer)"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
let new_scopes = execution.scopes.declare(identifier: self.identifier, withValue: initial_value)
|
|
||||||
execution.scopes = new_scopes
|
|
||||||
return (ControlFlow.Next, execution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ConditionalStatement: EvaluatableStatement {
|
|
||||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
|
||||||
guard
|
|
||||||
//case (.Ok(let evaluated_condition), let execution) = self.condition.evaluate(execution: execution)
|
|
||||||
case (.Ok(let evaluated_condition), let execution) = execution.evaluator.EvaluateExpression(
|
|
||||||
self.condition, inExecution: execution)
|
|
||||||
else {
|
|
||||||
return (
|
|
||||||
ControlFlow.Error,
|
|
||||||
execution.setError(error: Error(withMessage: "Could not evaluate \(self.condition)"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !evaluated_condition.type().baseType().eq(rhs: P4Boolean()) {
|
|
||||||
return (
|
|
||||||
ControlFlow.Error,
|
|
||||||
execution.setError(error: Error(withMessage: "Condition expression is not a Boolean"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if evaluated_condition.dataValue().eq(rhs: P4BooleanValue.init(withValue: true)) {
|
|
||||||
let execution = execution.enter_scope()
|
|
||||||
switch self.thenn.evaluate(execution: execution) {
|
|
||||||
case (ControlFlow.Next, let result): return (ControlFlow.Next, result.exit_scope())
|
|
||||||
case (ControlFlow.Error, let result): return (ControlFlow.Error, result.exit_scope())
|
|
||||||
case (let cf, let result):
|
|
||||||
return (
|
|
||||||
ControlFlow.Next,
|
|
||||||
result.setError(
|
|
||||||
error: Error(withMessage: "Invalid control flow \(cf) in conditional statement"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if let elss = self.elss {
|
|
||||||
let execution = execution.enter_scope()
|
|
||||||
switch elss.evaluate(execution: execution) {
|
|
||||||
case (ControlFlow.Next, let result): return (ControlFlow.Next, result.exit_scope())
|
|
||||||
case (ControlFlow.Error, let result): return (ControlFlow.Error, result.exit_scope())
|
|
||||||
case (let cf, let result):
|
|
||||||
return (
|
|
||||||
ControlFlow.Next,
|
|
||||||
result.setError(
|
|
||||||
error: Error(withMessage: "Invalid control flow \(cf) in conditional statement"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (ControlFlow.Next, execution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ExpressionStatement: EvaluatableStatement {
|
|
||||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
|
||||||
|
|
||||||
// Evaluate, there might be side effects!
|
|
||||||
//return switch self.expression.evaluate(execution: execution) {
|
|
||||||
return switch execution.evaluator.EvaluateExpression(self.expression, inExecution: execution) {
|
|
||||||
case (.Ok(_), let updated_context): (ControlFlow.Next, updated_context)
|
|
||||||
case (.Error(let e), let updated_context):
|
|
||||||
(ControlFlow.Next, updated_context.setError(error: e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ReturnStatement: EvaluatableStatement {
|
|
||||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
|
||||||
//return switch self.value.evaluate(execution: execution) {
|
|
||||||
return switch execution.evaluator.EvaluateExpression(self.value, inExecution: execution) {
|
|
||||||
case (.Ok(let v), let execution): (ControlFlow.Return(v), execution)
|
|
||||||
case (.Error(let e), let execution): (ControlFlow.Error, execution.setError(error: e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ApplyStatement: EvaluatableStatement {
|
|
||||||
public func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution) {
|
|
||||||
return (ControlFlow.Next, execution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
state start {
|
||||||
|
Testing ts;
|
||||||
|
ts.yesno = true;
|
||||||
|
ts.count = 5;
|
||||||
|
transition select (ts.yesno == "testing") {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
parser main_parser() {
|
||||||
|
#include <file-loc-error-parser-state.p4>
|
||||||
|
};
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
struct Testing {
|
||||||
|
bool yesno;
|
||||||
|
int count;
|
||||||
|
};
|
||||||
|
#include <file-loc-error-parser.p4>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Error: In file-loc-error-parser-state.p4 included at position 23 in file-loc-error-parser.p4 included at position 51 in file-loc-error.p4, there was an error:
|
||||||
|
...ect (ts.yesno == "testing") {...
|
||||||
|
Could not parse transition select expression selector expression: Types of values used with binary expression are not the same
|
||||||
@@ -1,256 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Runtime
|
|
||||||
import P4Lang
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_array_access() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = ta[1] == 2;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ta"), withValue: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Int()))))
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ta"),
|
|
||||||
withValue: P4Value(P4ArrayValue(withType: P4QualifiedType(P4Int()), withValue: [
|
|
||||||
P4Value(P4IntValue(withValue: 1)), P4Value(P4IntValue(withValue: 2)), P4Value(P4IntValue(withValue: 3))
|
|
||||||
])))
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_array_access_invalid_type() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = ta[1];
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ta"), withValue: P4QualifiedType(P4Int()))
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage: "{49, 22}: Failed to parse a statement element: {65, 2}: ta does not name an array type"
|
|
||||||
),
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_array_access2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = ta[0] == 2;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ta"), withValue: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Int()))))
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ta"),
|
|
||||||
withValue: P4Value(P4ArrayValue(withType: P4QualifiedType(P4Int()), withValue: [
|
|
||||||
P4Value(P4IntValue(withValue: 1)), P4Value(P4IntValue(withValue: 2)), P4Value(P4IntValue(withValue: 3))
|
|
||||||
])))
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_array_access3() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (ta[0] == 1) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ta"), withValue: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Int()))))
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ta"),
|
|
||||||
withValue: P4Value(P4ArrayValue(withType: P4QualifiedType(P4Int()), withValue: [
|
|
||||||
P4Value(P4IntValue(withValue: 1)), P4Value(P4IntValue(withValue: 2)), P4Value(P4IntValue(withValue: 3)),
|
|
||||||
])))
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_array_access4() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (ta[1] == 2) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ta"), withValue: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Int()))))
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ta"),
|
|
||||||
withValue: P4Value(P4ArrayValue(withType: P4QualifiedType(P4Int()), withValue: [
|
|
||||||
P4Value(P4IntValue(withValue: 1)), P4Value(P4IntValue(withValue: 2)), P4Value(P4IntValue(withValue: 3)),
|
|
||||||
])))
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_array_access_nested() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
int where_to = ta[0][0];
|
|
||||||
transition select (where_to == 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ta"), withValue: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Int()))))))
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
|
|
||||||
let nested = P4Value(P4ArrayValue(
|
|
||||||
withType: P4QualifiedType(P4Int()),
|
|
||||||
withValue: [P4Value(P4IntValue(withValue: 5)), P4Value(P4IntValue(withValue: 2)), P4Value(P4IntValue(withValue: 3))]))
|
|
||||||
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ta"),
|
|
||||||
withValue: P4Value(P4ArrayValue(withType: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Int()))), withValue: [nested])))
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_array_set() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
ta[1] = 3;
|
|
||||||
bool where_to = ta[1] == 3;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ta"), withValue: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Int()))))
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ta"),
|
|
||||||
withValue: P4Value(P4ArrayValue(withType: P4QualifiedType(P4Int()), withValue: [
|
|
||||||
P4Value(P4IntValue(withValue: 1)), P4Value(P4IntValue(withValue: 2)), P4Value(P4IntValue(withValue: 3)),
|
|
||||||
])))
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_array_set_nested() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
ta[0][0] = 5;
|
|
||||||
int where_to = ta[0][0];
|
|
||||||
transition select (where_to == 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ta"), withValue: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Int()))))))
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
|
|
||||||
let nested = P4Value(P4ArrayValue(
|
|
||||||
withType: P4QualifiedType(P4Int()),
|
|
||||||
withValue: [P4Value(P4IntValue(withValue: 1)), P4Value(P4IntValue(withValue: 2)), P4Value(P4IntValue(withValue: 3))]))
|
|
||||||
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ta"),
|
|
||||||
withValue: P4Value(P4ArrayValue(withType: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Int()))), withValue: [nested])))
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
// p4rse, Copyright 202false, 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
|
|
||||||
|
|
||||||
// And/Or binary operator tests ...
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_and() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true && true) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_and2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false && false) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_and3() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false && true) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_and4() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true && false) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_or() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true || true) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_or2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true || false) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_or3() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false || true) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_or4() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false || false) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_grouped() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true && (false || true)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_grouped2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true && (false || false)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
@@ -1,296 +0,0 @@
|
|||||||
// p4rse, Copyright 202false, 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
|
|
||||||
|
|
||||||
// Bool binary operator tests ...
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_equal_bool() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true == true) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_equal_not_equal_bool() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true == false) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_bool() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false < true) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_equal_bool() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false <= true) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_equal_bool2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true <= true) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_bool() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true > false) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_equal_bool() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false >= false) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_equal_bool2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true >= false) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_bool_not() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true < false) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_bool_not2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true < true) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_equal_bool_not() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true <= false) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_bool_not() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false > true) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_bool_not2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true > true) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_equal_bool_not() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false >= true) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
@@ -1,635 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
// Integer binary operator tests ...
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_equal_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (5 == 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_equal_not_equal_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (5 == 6) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (5 < 6) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_equal_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (5 <= 6) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_equal_integer2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (6 <= 6) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (7 > 6) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_equal_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (6 >= 6) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_equal_integer2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (7 >= 6) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_integer_not() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (6 < 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_integer_not2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (5 < 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_equal_integer_not() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (6 <= 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_integer_not() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (5 > 6) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_integer_not2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (5 > 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_equal_integer_not() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (5 >= 6) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Add Integers
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_add_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (10 == (5 + 5)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_add_non_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (10 == (true + 5)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{72, 16}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
|
|
||||||
),
|
|
||||||
Program.Compile(simple)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_add_non_integer2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (10 == (5 + false)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{72, 17}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
|
|
||||||
),
|
|
||||||
Program.Compile(simple)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_add_non_integer3() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (10 == (false + false)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{72, 21}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
|
|
||||||
),
|
|
||||||
Program.Compile(simple)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subtract Integers
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_subtract_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (0 == (5 - 5)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_subtract_non_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (10 == (true - 5)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{72, 16}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
|
|
||||||
),
|
|
||||||
Program.Compile(simple)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_subtract_non_integer2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (10 == (5 - false)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{72, 17}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
|
|
||||||
),
|
|
||||||
Program.Compile(simple)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_subtract_non_integer3() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (10 == (false - false)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{72, 21}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
|
|
||||||
),
|
|
||||||
Program.Compile(simple)))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Multiply Integers
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_multiply_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (25 == (5 * 5)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_multiply_non_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (10 == (true * 5)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{72, 16}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
|
|
||||||
),
|
|
||||||
Program.Compile(simple)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_multiply_non_integer2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (10 == (5 * false)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{72, 17}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
|
|
||||||
),
|
|
||||||
Program.Compile(simple)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_multiply_non_integer3() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (10 == (false * false)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{72, 21}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
|
|
||||||
),
|
|
||||||
Program.Compile(simple)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Divide Integers
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_divide_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (1 == (5 / 5)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_divide_non_integer() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (10 == (true / 5)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{72, 16}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
|
|
||||||
),
|
|
||||||
Program.Compile(simple)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_divide_non_integer2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (10 == (5 / false)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{72, 17}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
|
|
||||||
),
|
|
||||||
Program.Compile(simple)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_divide_non_integer3() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (10 == (false / false)) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{72, 21}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
|
|
||||||
),
|
|
||||||
Program.Compile(simple)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,220 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
// String binary operator tests ...
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_equal_string() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select ("five" == "five") {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_equal_not_equal_string() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select ("five" == "six") {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_string() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select ("five" < "six") {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_equal_string() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select ("six" <= "six") {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_string() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select ("twelve" > "six") {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_equal_string() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select ("twelve" >= "six") {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_string_not() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select ("six" < "five") {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_less_than_equal_string_not() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select ("six" <= "five") {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_string_not() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select ("five" > "six") {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_binary_operator_greater_than_equal_string_not() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select ("five" >= "six") {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
@@ -1,201 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_struct_equality_empty() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
Testing one;
|
|
||||||
Testing two;
|
|
||||||
transition select (one == two) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let test_declarations = VarTypeScopes().enter()
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
var test_types = TypeTypeScopes().enter()
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
test_types = test_types.declare(identifier: Identifier(name: "Testing"), withValue: struct_type)
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations, withGlobalTypes: test_types))
|
|
||||||
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_struct_equality_one_empty() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
Testing one;
|
|
||||||
Testing two;
|
|
||||||
|
|
||||||
one.yesno = true;
|
|
||||||
|
|
||||||
transition select (one == two) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let test_declarations = VarTypeScopes().enter()
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
var test_types = TypeTypeScopes().enter()
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
test_types = test_types.declare(identifier: Identifier(name: "Testing"), withValue: struct_type)
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations, withGlobalTypes: test_types))
|
|
||||||
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_struct_equality_neither_empty() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
Testing one;
|
|
||||||
Testing two;
|
|
||||||
|
|
||||||
one.yesno = true;
|
|
||||||
two.yesno = true;
|
|
||||||
|
|
||||||
transition select (one == two) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let test_declarations = VarTypeScopes().enter()
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
var test_types = TypeTypeScopes().enter()
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
test_types = test_types.declare(identifier: Identifier(name: "Testing"), withValue: struct_type)
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations, withGlobalTypes: test_types))
|
|
||||||
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_struct_equality_neither_empty2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
Testing one;
|
|
||||||
Testing two;
|
|
||||||
|
|
||||||
one.yesno = true;
|
|
||||||
two.yesno = true;
|
|
||||||
|
|
||||||
one.count = 5;
|
|
||||||
two.count = 5;
|
|
||||||
|
|
||||||
transition select (one == two) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let test_declarations = VarTypeScopes().enter()
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
var test_types = TypeTypeScopes().enter()
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
test_types = test_types.declare(identifier: Identifier(name: "Testing"), withValue: struct_type)
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations, withGlobalTypes: test_types))
|
|
||||||
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_struct_equality_neither_empty3() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
Testing one;
|
|
||||||
Testing two;
|
|
||||||
|
|
||||||
one.yesno = true;
|
|
||||||
two.yesno = true;
|
|
||||||
|
|
||||||
one.count = 5;
|
|
||||||
two.count = 6;
|
|
||||||
|
|
||||||
transition select (one == two) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let test_declarations = VarTypeScopes().enter()
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
var test_types = TypeTypeScopes().enter()
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
test_types = test_types.declare(identifier: Identifier(name: "Testing"), withValue: struct_type)
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations, withGlobalTypes: test_types))
|
|
||||||
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,46 +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 Foundation
|
|
||||||
import Testing
|
|
||||||
|
|
||||||
/// Success
|
|
||||||
///
|
|
||||||
@CliTest()
|
|
||||||
func simple_cli_test() -> [String] {
|
|
||||||
return ["p4ce", "--plain", "compile", "simple.p4", "-I", "TestData/Sources/"]
|
|
||||||
}
|
|
||||||
|
|
||||||
@CliTest()
|
|
||||||
func simple_cli_testa() -> [String] {
|
|
||||||
return ["p4ce", "--plain", "compile", "simple.p4", "-I", "TestData/Sources/"]
|
|
||||||
}
|
|
||||||
|
|
||||||
@CliTest()
|
|
||||||
func simple_cli_preprocessor_test() -> [String] {
|
|
||||||
return ["p4ce", "--plain", "preprocess", "simple.p4", "-I", "TestData/Sources/"]
|
|
||||||
}
|
|
||||||
|
|
||||||
@CliTest()
|
|
||||||
func simple_cli_preprocessor_test_file_not_found() -> [String] {
|
|
||||||
return ["p4ce", "--plain", "preprocess", "simple.p", "-I", "TestData/Sources/"]
|
|
||||||
}
|
|
||||||
|
|
||||||
@CliTest()
|
|
||||||
func simple_cli_compilation_error() -> [String] {
|
|
||||||
return ["p4ce", "--plain", "compile", "action-parameters-wrong-order.p4", "-I", "TestData/Sources/"]
|
|
||||||
}
|
|
||||||
@@ -1,98 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Compiler
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Runtime
|
|
||||||
|
|
||||||
func shrink(_ from: String) -> String {
|
|
||||||
return from.replacing(Regex(#/\n[\s]+/#), with: "").replacing("\n", with: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_codegen_parser_state_direct_transition() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
true;
|
|
||||||
transition start;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let cg = try #UseOkResult(CodeGenerator().codeGen(program))
|
|
||||||
|
|
||||||
let expected = shrink(
|
|
||||||
"""
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: "main_parser",
|
|
||||||
init_state: "start",
|
|
||||||
parse_states: [
|
|
||||||
{
|
|
||||||
name: "start",
|
|
||||||
transitions: []
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
""")
|
|
||||||
|
|
||||||
#expect(expected == cg.getGeneratedCode())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_codegen_parser_state_select_transition() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false) {
|
|
||||||
true: reject;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let cg = try #UseOkResult(CodeGenerator().codeGen(program))
|
|
||||||
|
|
||||||
/// TODO: Fix this test.
|
|
||||||
let expected = shrink(
|
|
||||||
"""
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: "main_parser",
|
|
||||||
init_state: "start",
|
|
||||||
parse_states: [
|
|
||||||
{
|
|
||||||
name: "start",
|
|
||||||
transitions: [Select]
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
""")
|
|
||||||
|
|
||||||
#expect(expected == cg.getGeneratedCode())
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
// 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 SwiftTreeSitter
|
||||||
|
import Testing
|
||||||
|
import TreeSitter
|
||||||
|
import TreeSitterP4
|
||||||
|
|
||||||
|
@testable import P4Parser
|
||||||
|
@testable import P4CodeGen
|
||||||
|
|
||||||
|
@Test func test_text_serializer() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
parser main_parser(inout int x) {
|
||||||
|
state start {
|
||||||
|
true;
|
||||||
|
transition accept;
|
||||||
|
}
|
||||||
|
state start {
|
||||||
|
transition select (true) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
|
||||||
|
let program = try #UseOkResult(compile(simple_parser_declaration))
|
||||||
|
let v = CSTTextSerializer()
|
||||||
|
let c = CSTTextSerializerContext();
|
||||||
|
let vd = CSTVisitorDriver();
|
||||||
|
let result = try #UseOkResult((vd.start(program: program, visitor: v, context: c)))
|
||||||
|
|
||||||
|
let expected = """
|
||||||
|
Parser Expression
|
||||||
|
State: Direct Transition
|
||||||
|
Statements:
|
||||||
|
Expression Statement:
|
||||||
|
Literal Expression
|
||||||
|
Next State:
|
||||||
|
Identifier: accept
|
||||||
|
State: Select Transition
|
||||||
|
Select Expression:
|
||||||
|
Selector:
|
||||||
|
Literal Expression
|
||||||
|
Case Expressions:
|
||||||
|
Case Expression:
|
||||||
|
Keyset Expression:
|
||||||
|
Next State:
|
||||||
|
Identifier: accept
|
||||||
|
Case Expression:
|
||||||
|
Keyset Expression:
|
||||||
|
Next State:
|
||||||
|
Identifier: reject
|
||||||
|
|
||||||
|
"""
|
||||||
|
#expect(result.serialized == expected)
|
||||||
|
}
|
||||||
@@ -1,82 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_simple_parser_with_conditional_statement() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool x = true;
|
|
||||||
string check = "Invalid";
|
|
||||||
if (x) {
|
|
||||||
x = false;
|
|
||||||
check = "valid";
|
|
||||||
}
|
|
||||||
transition select (x) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_with_conditional_statement_and_else() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool x = false;
|
|
||||||
string check = "Invalid";
|
|
||||||
if (x) {
|
|
||||||
x = false;
|
|
||||||
check = "a";
|
|
||||||
} else {
|
|
||||||
x = true;
|
|
||||||
check = "b";
|
|
||||||
}
|
|
||||||
transition select (x) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,402 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_simple_control_declaration() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
control simple() {
|
|
||||||
action a() {
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
true: exact;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let x = { (tipe: P4QualifiedType) -> Bool in
|
|
||||||
switch tipe.baseType() {
|
|
||||||
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_declaration2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
struct Testing {
|
|
||||||
};
|
|
||||||
control simple() {
|
|
||||||
action a() {
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
true: exact;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
control complex() {
|
|
||||||
action b() {
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
true: exact;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let filter = { (tipe: P4QualifiedType) -> Bool in
|
|
||||||
switch tipe.baseType() {
|
|
||||||
case let c as Control: c.name == "simple" || c.name == "complex"
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
#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: P4QualifiedType) -> Bool in
|
|
||||||
switch tipe.baseType() {
|
|
||||||
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) {
|
|
||||||
action a() {
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
x: exact;
|
|
||||||
y: exact;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
#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) {
|
|
||||||
action a(int z) {
|
|
||||||
z = 5;
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
x: exact;
|
|
||||||
y: exact;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
#expect(#RequireOkResult(Program.Compile(simple_parser_declaration)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_control_declaration_with_action_using_parameter_wrong_type() 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 {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{57, 10}: Failed to parse a statement element: {57, 1}: Cannot assign value with type Boolean to identifier z with type Int (width: Infinite)"
|
|
||||||
),
|
|
||||||
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))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_control_declaration_with_action_with_params_right_order() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
control simple(int x, int y) {
|
|
||||||
action a(inout int ax, bool ay) {
|
|
||||||
ax = 5;
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
x: exact;
|
|
||||||
y: exact;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let x = { (tipe: P4QualifiedType) -> Bool in
|
|
||||||
switch tipe.baseType() {
|
|
||||||
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_action_with_params_wrong_order() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
control simple(int x, int y) {
|
|
||||||
action a(int ax, inout bool ay) {
|
|
||||||
ay = false;
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
x: exact;
|
|
||||||
y: exact;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: SourceLocation(41, 23),
|
|
||||||
withError: "All parameters with direction must precede directionless parameters"),
|
|
||||||
Program.Compile(simple_parser_declaration))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_control_declaration_with_action_with_params_wrong_order_interspersed() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
control simple(int x, int y) {
|
|
||||||
action a(inout bool aa, int ax, inout bool ay) {
|
|
||||||
aa = false;
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
x: exact;
|
|
||||||
y: exact;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
ErrorWithLocation(
|
|
||||||
sourceLocation: SourceLocation(41, 38),
|
|
||||||
withError: "All parameters with direction must precede directionless parameters"),
|
|
||||||
Program.Compile(simple_parser_declaration))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,586 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_control_single_key() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
control simple(inout int result, bool x) {
|
|
||||||
action a() {
|
|
||||||
result = 5;
|
|
||||||
}
|
|
||||||
action b() {
|
|
||||||
result = 7;
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
x: exact;
|
|
||||||
}
|
|
||||||
actions = {
|
|
||||||
a;
|
|
||||||
b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
|
|
||||||
// Pull the control out of the compiled program.
|
|
||||||
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
|
|
||||||
switch tipe.baseType() {
|
|
||||||
case let c as Control: c.name == "simple"
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var control = ((controls[0].baseType() as P4Type) as! Control)
|
|
||||||
|
|
||||||
// Add entries to the table.
|
|
||||||
control = control.updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4BooleanValue(withValue: true)),
|
|
||||||
TypedIdentifier(name: "a", withType: P4QualifiedType(Action()))
|
|
||||||
)
|
|
||||||
).updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4BooleanValue(withValue: false)),
|
|
||||||
TypedIdentifier(name: "b", withType: P4QualifiedType(Action()))
|
|
||||||
))
|
|
||||||
|
|
||||||
// Set a variable in the global scope for the inout first parameter.
|
|
||||||
var global_values = VarValueScopes().enter()
|
|
||||||
global_values = global_values.declare(
|
|
||||||
identifier: Identifier(name: "result_arg"),
|
|
||||||
withValue: P4Value(
|
|
||||||
P4IntValue(withValue: 0),
|
|
||||||
P4QualifiedType(P4Int())))
|
|
||||||
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
|
|
||||||
|
|
||||||
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
|
|
||||||
withArguments: ArgumentList([
|
|
||||||
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
|
|
||||||
Argument(P4Value(P4BooleanValue(withValue: true)), atIndex: 1),
|
|
||||||
])))
|
|
||||||
|
|
||||||
// We expect there to be a hit.
|
|
||||||
#expect(hit_miss == P4TableHitMissValue.Hit)
|
|
||||||
|
|
||||||
// And that the proper action was invoked.
|
|
||||||
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
|
|
||||||
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 5))))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_control_single_key_false() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
control simple(inout int result, bool x) {
|
|
||||||
action a() {
|
|
||||||
result = 5;
|
|
||||||
}
|
|
||||||
action b() {
|
|
||||||
result = 7;
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
x: exact;
|
|
||||||
}
|
|
||||||
actions = {
|
|
||||||
a;
|
|
||||||
b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
|
|
||||||
// Pull the control out of the compiled program.
|
|
||||||
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
|
|
||||||
switch tipe.baseType() {
|
|
||||||
case let c as Control: c.name == "simple"
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var control = ((controls[0].baseType() as P4Type) as! Control)
|
|
||||||
|
|
||||||
// Add entries to the table.
|
|
||||||
control = control.updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4BooleanValue(withValue: true)),
|
|
||||||
TypedIdentifier(name: "a", withType: P4QualifiedType(Action()))
|
|
||||||
)
|
|
||||||
).updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4BooleanValue(withValue: false)),
|
|
||||||
TypedIdentifier(name: "b", withType: P4QualifiedType(Action()))
|
|
||||||
))
|
|
||||||
|
|
||||||
// Set a variable in the global scope for the inout first parameter.
|
|
||||||
var global_values = VarValueScopes().enter()
|
|
||||||
global_values = global_values.declare(
|
|
||||||
identifier: Identifier(name: "result_arg"),
|
|
||||||
withValue: P4Value(
|
|
||||||
P4IntValue(withValue: 0),
|
|
||||||
P4QualifiedType(P4Int())))
|
|
||||||
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
|
|
||||||
|
|
||||||
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
|
|
||||||
withArguments: ArgumentList([
|
|
||||||
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
|
|
||||||
Argument(P4Value(P4BooleanValue(withValue: false)), atIndex: 1),
|
|
||||||
])))
|
|
||||||
|
|
||||||
// We expect there to be a hit.
|
|
||||||
#expect(hit_miss == P4TableHitMissValue.Hit)
|
|
||||||
|
|
||||||
// And that the proper action was invoked.
|
|
||||||
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
|
|
||||||
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 7))))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_control_single_integer_key_hit() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
control simple(inout int result, int x) {
|
|
||||||
action a() {
|
|
||||||
result = 5;
|
|
||||||
}
|
|
||||||
action b() {
|
|
||||||
result = 7;
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
x: exact;
|
|
||||||
}
|
|
||||||
actions = {
|
|
||||||
a;
|
|
||||||
b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
|
|
||||||
// Pull the control out of the compiled program.
|
|
||||||
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
|
|
||||||
switch tipe.baseType() {
|
|
||||||
case let c as Control: c.name == "simple"
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var control = ((controls[0].baseType() as P4Type) as! Control)
|
|
||||||
|
|
||||||
// Add entries to the table.
|
|
||||||
control = control.updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4IntValue(withValue: 5)),
|
|
||||||
TypedIdentifier(name: "a", withType: P4QualifiedType(Action()))
|
|
||||||
)
|
|
||||||
).updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4IntValue(withValue: 2)),
|
|
||||||
TypedIdentifier(name: "b", withType: P4QualifiedType(Action()))
|
|
||||||
))
|
|
||||||
|
|
||||||
// Set a variable in the global scope for the inout first parameter.
|
|
||||||
var global_values = VarValueScopes().enter()
|
|
||||||
global_values = global_values.declare(
|
|
||||||
identifier: Identifier(name: "result_arg"),
|
|
||||||
withValue: P4Value(
|
|
||||||
P4IntValue(withValue: 0),
|
|
||||||
P4QualifiedType(P4Int())))
|
|
||||||
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
|
|
||||||
|
|
||||||
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
|
|
||||||
withArguments: ArgumentList([
|
|
||||||
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
|
|
||||||
Argument(P4Value(P4IntValue(withValue: 5)), atIndex: 1),
|
|
||||||
])))
|
|
||||||
|
|
||||||
// We expect there to be a hit.
|
|
||||||
#expect(hit_miss == P4TableHitMissValue.Hit)
|
|
||||||
|
|
||||||
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
|
|
||||||
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 5))))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_control_single_integer_key_miss() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
control simple(inout int result, int x) {
|
|
||||||
action a() {
|
|
||||||
result = 5;
|
|
||||||
}
|
|
||||||
action b() {
|
|
||||||
result = 7;
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
x: exact;
|
|
||||||
}
|
|
||||||
actions = {
|
|
||||||
a;
|
|
||||||
b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
|
|
||||||
// Pull the control out of the compiled program.
|
|
||||||
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
|
|
||||||
switch tipe.baseType() {
|
|
||||||
case let c as Control: c.name == "simple"
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var control = ((controls[0].baseType() as P4Type) as! Control)
|
|
||||||
|
|
||||||
// Add entries to the table.
|
|
||||||
control = control.updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4IntValue(withValue: 1)),
|
|
||||||
TypedIdentifier(name: "a", withType: P4QualifiedType(Action()))
|
|
||||||
)
|
|
||||||
).updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4IntValue(withValue: 2)),
|
|
||||||
TypedIdentifier(name: "b", withType: P4QualifiedType(Action()))
|
|
||||||
))
|
|
||||||
|
|
||||||
// Set a variable in the global scope for the inout first parameter.
|
|
||||||
var global_values = VarValueScopes().enter()
|
|
||||||
global_values = global_values.declare(
|
|
||||||
identifier: Identifier(name: "result_arg"),
|
|
||||||
withValue: P4Value(
|
|
||||||
P4IntValue(withValue: 0),
|
|
||||||
P4QualifiedType(P4Int())))
|
|
||||||
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
|
|
||||||
|
|
||||||
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
|
|
||||||
withArguments: ArgumentList([
|
|
||||||
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
|
|
||||||
Argument(P4Value(P4IntValue(withValue: 3)), atIndex: 1),
|
|
||||||
])))
|
|
||||||
|
|
||||||
// We expect there to be a hit.
|
|
||||||
#expect(hit_miss == P4TableHitMissValue.Miss)
|
|
||||||
|
|
||||||
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
|
|
||||||
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 0))))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_control_multiple_keys() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
control simple(inout int result, bool x, int f) {
|
|
||||||
action a() {
|
|
||||||
result = 5;
|
|
||||||
}
|
|
||||||
action b() {
|
|
||||||
result = 7;
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
x: exact;
|
|
||||||
f: exact;
|
|
||||||
}
|
|
||||||
actions = {
|
|
||||||
a;
|
|
||||||
b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
|
|
||||||
// Pull the control out of the compiled program.
|
|
||||||
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
|
|
||||||
switch tipe.baseType() {
|
|
||||||
case let c as Control: c.name == "simple"
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var control = ((controls[0].baseType() as P4Type) as! Control)
|
|
||||||
|
|
||||||
// Add entries to the table.
|
|
||||||
control = control.updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4BooleanValue(withValue: true)),
|
|
||||||
TypedIdentifier(name: "a", withType: P4QualifiedType(Action()))
|
|
||||||
)
|
|
||||||
).updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4IntValue(withValue: 5)),
|
|
||||||
TypedIdentifier(name: "b", withType: P4QualifiedType(Action()))
|
|
||||||
))
|
|
||||||
|
|
||||||
// Set a variable in the global scope for the inout first parameter.
|
|
||||||
var global_values = VarValueScopes().enter()
|
|
||||||
global_values = global_values.declare(
|
|
||||||
identifier: Identifier(name: "result_arg"),
|
|
||||||
withValue: P4Value(
|
|
||||||
P4IntValue(withValue: 0),
|
|
||||||
P4QualifiedType(P4Int())))
|
|
||||||
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
|
|
||||||
|
|
||||||
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
|
|
||||||
withArguments: ArgumentList([
|
|
||||||
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
|
|
||||||
Argument(P4Value(P4BooleanValue(withValue: false)), atIndex: 1), // false will make the x key miss.
|
|
||||||
Argument(P4Value(P4IntValue(withValue: 5)), atIndex: 2),
|
|
||||||
])))
|
|
||||||
|
|
||||||
// We expect there to be a hit -- but from the second key!
|
|
||||||
#expect(hit_miss == P4TableHitMissValue.Hit)
|
|
||||||
|
|
||||||
// And that the proper action was invoked.
|
|
||||||
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
|
|
||||||
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 7))))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_control_key_from_struct() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
struct K {
|
|
||||||
int i;
|
|
||||||
int j;
|
|
||||||
};
|
|
||||||
control simple(inout int result, K k) {
|
|
||||||
action a() {
|
|
||||||
result = 5;
|
|
||||||
}
|
|
||||||
action b() {
|
|
||||||
result = 7;
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
k.i: exact;
|
|
||||||
}
|
|
||||||
actions = {
|
|
||||||
a;
|
|
||||||
b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
|
|
||||||
// Pull the control out of the compiled program.
|
|
||||||
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
|
|
||||||
switch tipe.baseType() {
|
|
||||||
case let c as Control: c.name == "simple"
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var control = ((controls[0].baseType() as P4Type) as! Control)
|
|
||||||
|
|
||||||
let k_fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "i", withType: P4QualifiedType(P4Int())),
|
|
||||||
P4StructFieldIdentifier(name: "j", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let k_type = P4Struct(withName: Identifier(name: "K"), andFields: k_fields)
|
|
||||||
|
|
||||||
let k_instance = P4StructValue(withType: k_type, andInitializers: [
|
|
||||||
P4Value(P4IntValue(withValue: 5)),
|
|
||||||
P4Value(P4IntValue(withValue: 1)),
|
|
||||||
])
|
|
||||||
|
|
||||||
// Add entries to the table.
|
|
||||||
control = control.updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4IntValue(withValue: 5)),
|
|
||||||
TypedIdentifier(name: "a", withType: P4QualifiedType(Action()))
|
|
||||||
)
|
|
||||||
).updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4IntValue(withValue: 4)),
|
|
||||||
TypedIdentifier(name: "b", withType: P4QualifiedType(Action()))
|
|
||||||
))
|
|
||||||
|
|
||||||
// Set a variable in the global scope for the inout first parameter.
|
|
||||||
var global_values = VarValueScopes().enter()
|
|
||||||
global_values = global_values.declare(
|
|
||||||
identifier: Identifier(name: "result_arg"),
|
|
||||||
withValue: P4Value(
|
|
||||||
P4IntValue(withValue: 0),
|
|
||||||
P4QualifiedType(P4Int())))
|
|
||||||
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
|
|
||||||
|
|
||||||
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
|
|
||||||
withArguments: ArgumentList([
|
|
||||||
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
|
|
||||||
Argument(P4Value(k_instance), atIndex: 1),
|
|
||||||
])))
|
|
||||||
|
|
||||||
// We expect there to be a hit.
|
|
||||||
#expect(hit_miss == P4TableHitMissValue.Hit)
|
|
||||||
|
|
||||||
// And that the proper action was invoked.
|
|
||||||
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
|
|
||||||
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 5))))
|
|
||||||
|
|
||||||
|
|
||||||
// Now, check whether the b action can be invoked.
|
|
||||||
let k_instance2 = P4StructValue(withType: k_type, andInitializers: [
|
|
||||||
P4Value(P4IntValue(withValue: 4)),
|
|
||||||
P4Value(P4IntValue(withValue: 1)),
|
|
||||||
])
|
|
||||||
|
|
||||||
// Set a variable in the global scope for the inout first parameter.
|
|
||||||
var next_global_values = VarValueScopes().enter()
|
|
||||||
next_global_values = global_values.declare(
|
|
||||||
identifier: Identifier(name: "result_arg"),
|
|
||||||
withValue: P4Value(
|
|
||||||
P4IntValue(withValue: 0),
|
|
||||||
P4QualifiedType(P4Int())))
|
|
||||||
|
|
||||||
let runtime2 = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: next_global_values))
|
|
||||||
|
|
||||||
let (hit_miss2, updated_execution2) = try #UseOkResult(runtime2.run(
|
|
||||||
withArguments: ArgumentList([
|
|
||||||
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
|
|
||||||
Argument(P4Value(k_instance2), atIndex: 1),
|
|
||||||
])))
|
|
||||||
|
|
||||||
// We expect there to be a hit.
|
|
||||||
#expect(hit_miss2 == P4TableHitMissValue.Hit)
|
|
||||||
|
|
||||||
// And that the proper action was invoked.
|
|
||||||
let result_arg2 = try #UseOkResult(updated_execution2.scopes.lookup(identifier: Identifier(name: "result_arg")))
|
|
||||||
#expect(result_arg2.eq(P4Value(P4IntValue(withValue: 7))))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_control_key_from_struct_miss() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
struct K {
|
|
||||||
int i;
|
|
||||||
int j;
|
|
||||||
};
|
|
||||||
control simple(inout int result, K k) {
|
|
||||||
action a() {
|
|
||||||
result = 5;
|
|
||||||
}
|
|
||||||
action b() {
|
|
||||||
result = 7;
|
|
||||||
}
|
|
||||||
table t {
|
|
||||||
key = {
|
|
||||||
k.i: exact;
|
|
||||||
}
|
|
||||||
actions = {
|
|
||||||
a;
|
|
||||||
b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
|
|
||||||
// Pull the control out of the compiled program.
|
|
||||||
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
|
|
||||||
switch tipe.baseType() {
|
|
||||||
case let c as Control: c.name == "simple"
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var control = ((controls[0].baseType() as P4Type) as! Control)
|
|
||||||
|
|
||||||
let k_fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "i", withType: P4QualifiedType(P4Int())),
|
|
||||||
P4StructFieldIdentifier(name: "j", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let k_type = P4Struct(withName: Identifier(name: "K"), andFields: k_fields)
|
|
||||||
|
|
||||||
let k_instance = P4StructValue(withType: k_type, andInitializers: [
|
|
||||||
P4Value(P4IntValue(withValue: 8)),
|
|
||||||
P4Value(P4IntValue(withValue: 1)),
|
|
||||||
])
|
|
||||||
|
|
||||||
// Add entries to the table.
|
|
||||||
control = control.updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4IntValue(withValue: 5)),
|
|
||||||
TypedIdentifier(name: "a", withType: P4QualifiedType(Action()))
|
|
||||||
)
|
|
||||||
).updateTable(
|
|
||||||
addEntry: (
|
|
||||||
P4Value(P4IntValue(withValue: 4)),
|
|
||||||
TypedIdentifier(name: "b", withType: P4QualifiedType(Action()))
|
|
||||||
))
|
|
||||||
|
|
||||||
// Set a variable in the global scope for the inout first parameter.
|
|
||||||
var global_values = VarValueScopes().enter()
|
|
||||||
global_values = global_values.declare(
|
|
||||||
identifier: Identifier(name: "result_arg"),
|
|
||||||
withValue: P4Value(
|
|
||||||
P4IntValue(withValue: 0),
|
|
||||||
P4QualifiedType(P4Int())))
|
|
||||||
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
|
|
||||||
|
|
||||||
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
|
|
||||||
withArguments: ArgumentList([
|
|
||||||
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
|
|
||||||
Argument(P4Value(k_instance), atIndex: 1),
|
|
||||||
])))
|
|
||||||
|
|
||||||
// We expect there to be a hit.
|
|
||||||
#expect(hit_miss == P4TableHitMissValue.Miss)
|
|
||||||
|
|
||||||
// And that the proper action was invoked.
|
|
||||||
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
|
|
||||||
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 0))))
|
|
||||||
}
|
|
||||||
@@ -1,205 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Runtime
|
|
||||||
import P4Lang
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_struct_declaration_and_field_write() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
struct Testing {
|
|
||||||
bool yesno;
|
|
||||||
int count;
|
|
||||||
};
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
Testing ts;
|
|
||||||
ts.yesno = true;
|
|
||||||
bool where_to = ts.yesno;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_struct_declaration_and_field_write_field_read() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
struct Testing {
|
|
||||||
bool yesno;
|
|
||||||
int count;
|
|
||||||
};
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
Testing ts;
|
|
||||||
ts.yesno = true;
|
|
||||||
ts.count = 5;
|
|
||||||
transition select (ts.count == 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_struct_declaration_and_field_read_defaults() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
struct Testing {
|
|
||||||
bool yesno;
|
|
||||||
int count;
|
|
||||||
};
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
Testing ts;
|
|
||||||
transition select (ts.count == 0) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_struct_declaration_and_field_read_defaults_sc() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
struct Testing {
|
|
||||||
bool yesno;
|
|
||||||
int count;
|
|
||||||
};
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
Testing ts;
|
|
||||||
transition select (ts.count) {
|
|
||||||
0: accept;
|
|
||||||
_: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_struct_declaration_and_field_read_defaults_sc2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
struct Testing {
|
|
||||||
bool yesno;
|
|
||||||
int count;
|
|
||||||
};
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
Testing ts;
|
|
||||||
ts.count = 1;
|
|
||||||
transition select (ts.count) {
|
|
||||||
0: accept;
|
|
||||||
_: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_function_declaration() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
bool functionb() {
|
|
||||||
int count;
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
#expect(#RequireOkResult(Program.Compile(simple_parser_declaration)))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test func test_function_declaration_with_parameters() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
bool functionb(bool x) {
|
|
||||||
x = true;
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
#expect(#RequireOkResult(Program.Compile(simple_parser_declaration)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_function_declaration_with_parameters_and_direction() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
bool functionb(in bool x, out string y, inout int z) {
|
|
||||||
x = true;
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{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))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_function_declaration_with_parameters_and_direction_struct() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
struct Testing {
|
|
||||||
bool yesno;
|
|
||||||
int count;
|
|
||||||
};
|
|
||||||
bool functionb(in Testing x, out string y, inout int z) {
|
|
||||||
x.yesno = true;
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{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))
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,58 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_error_with_location_formatting() async throws {
|
|
||||||
let formatter = FormatterAnsi()
|
|
||||||
let e = ErrorWithLocation(sourceLocation: SourceLocation(1, 5), withError: "There was an error")
|
|
||||||
let formatted = e.format(formatter)
|
|
||||||
#expect(formatted == "\u{1B}[31;1m{1, 5}\u{1B}[0m: There was an error")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_errors_with_location_no_formatting() async throws {
|
|
||||||
let e = ErrorWithLocation(sourceLocation: SourceLocation(1, 5), withError: "There was an error")
|
|
||||||
let e1 = ErrorWithLocation(
|
|
||||||
sourceLocation: SourceLocation(10, 5), withError: "There was another error")
|
|
||||||
|
|
||||||
let formatted = e.append(error: e1).format(FormatterPlain())
|
|
||||||
|
|
||||||
#expect(formatted == "{1, 5}: There was an error\n{10, 5}: There was another error")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_errors_with_location_ansi_formatting() async throws {
|
|
||||||
let e = ErrorWithLocation(sourceLocation: SourceLocation(1, 5), withError: "There was an error")
|
|
||||||
let e1 = ErrorWithLocation(
|
|
||||||
sourceLocation: SourceLocation(10, 5), withError: "There was another error")
|
|
||||||
|
|
||||||
let formatted = e.append(error: e1).format(FormatterAnsi())
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
formatted
|
|
||||||
== "\u{1B}[31;1m{1, 5}\u{1B}[0m: There was an error\n\u{1B}[31;1m{10, 5}\u{1B}[0m: There was another error"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,88 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_expression_grouped_equal() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool x = (true == (1 < 4));
|
|
||||||
transition select (x) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_expression_grouped_or() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool x = (false || (1 < 4));
|
|
||||||
transition select (x) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_expression_grouped_and() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool x = (false && (1 < 4));
|
|
||||||
transition select (x) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
@@ -1,186 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_function_call_scoped_name_collision() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
bool functionb(bool c) {
|
|
||||||
return c;
|
|
||||||
};
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
int c = 5;
|
|
||||||
bool b = functionb(true);
|
|
||||||
transition select (b) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(parser.states.count() == 1)
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_function_call_scoped_name_collision2() async throws {
|
|
||||||
// Test whether the assignment to c leaks out of the function call scope.
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
bool functionb(bool c) {
|
|
||||||
c = true;
|
|
||||||
return c;
|
|
||||||
};
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool c = false;
|
|
||||||
bool b = functionb(true);
|
|
||||||
transition select (c) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(parser.states.count() == 1)
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_function_call_scoped_name_collision_inout() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
bool functionb(inout bool c) {
|
|
||||||
c = true;
|
|
||||||
return c;
|
|
||||||
};
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool c = false;
|
|
||||||
bool b = functionb(c);
|
|
||||||
transition select (c) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(parser.states.count() == 1)
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test func test_function_call_integer_return_value() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
int functionb(int c) {
|
|
||||||
return c;
|
|
||||||
};
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
int c = 5;
|
|
||||||
transition select (5 == functionb(c)) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(parser.states.count() == 1)
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_function_call_integer_return_value2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
int functionb(int c) {
|
|
||||||
return c;
|
|
||||||
};
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
int c = 5;
|
|
||||||
transition select (4 == functionb(c)) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(parser.states.count() == 1)
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_function_call_invalid_return_type() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
int functionb(int c) {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
int c = 5;
|
|
||||||
transition select (4 == functionb(c)) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let error = try #UseErrorResult(Program.Compile(simple_parser_declaration))
|
|
||||||
|
|
||||||
#expect(error.msg().contains("{29, 12}: Type of expression in return statement (Boolean) is not compatible with function return type (Int (width: Infinite))"))
|
|
||||||
}
|
|
||||||
@@ -1,203 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_simple_parser_with_transition_select_case_nondefault_expressions() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(parser.states.count() == 1)
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_with_transition_select_case_default_expression() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (5) {
|
|
||||||
5: reject;
|
|
||||||
_: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(parser.states.count() == 1)
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_with_transition_select_case_default_expression2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (1) {
|
|
||||||
5: reject;
|
|
||||||
_: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(parser.states.count() == 1)
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_with_transition_select_case_default_expression3() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (6) {
|
|
||||||
5: reject;
|
|
||||||
6: reject;
|
|
||||||
_: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(parser.states.count() == 1)
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_with_transition_select_case_invalid_type() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (6) {
|
|
||||||
true: reject;
|
|
||||||
6: reject;
|
|
||||||
_: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"Error(s) parsing select cases: {81, 4}: Key expression of type Boolean is not compatible with selector type Int (width: Infinite)"
|
|
||||||
),
|
|
||||||
Program.Compile(simple_parser_declaration)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_select_expression_selection_order() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (5) {
|
|
||||||
5: reject;
|
|
||||||
5: accept;
|
|
||||||
_: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(parser.states.count() == 1)
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_select_expression_from_parser_parameters() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser(bool pmtr, string smtr, int imtr) {
|
|
||||||
state start {
|
|
||||||
transition select (pmtr) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
|
|
||||||
let args = ArgumentList([
|
|
||||||
Argument(P4Value(P4BooleanValue(withValue: false)), atIndex: 1), Argument(P4Value(P4StringValue(withValue: "Testing")), atIndex: 2), Argument(P4Value(P4IntValue(withValue: 5)), atIndex: 3),
|
|
||||||
])
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: args))
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_select_expression_from_parser_parameters2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser(bool pmtr, string smtr, int imtr) {
|
|
||||||
state start {
|
|
||||||
transition select (imtr == 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
|
|
||||||
let args = ArgumentList([
|
|
||||||
Argument(P4Value(P4BooleanValue(withValue: false)), atIndex: 1), Argument(P4Value(P4StringValue(withValue: "Testing")), atIndex: 2), Argument(P4Value(P4IntValue(withValue: 5)), atIndex: 3),
|
|
||||||
])
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: args))
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
@@ -1,124 +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
|
|
||||||
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.P4QualifiedType {
|
|
||||||
return P4QualifiedType(
|
|
||||||
FunctionDeclaration(
|
|
||||||
named: Identifier(name: "externally"), ofType: P4QualifiedType(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.P4QualifiedType {
|
|
||||||
return P4QualifiedType(
|
|
||||||
FunctionDeclaration(
|
|
||||||
named: Identifier(name: "externally"), ofType: P4QualifiedType(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.Runtime<InstantiatedParserState, P4Lang.Parser>.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.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
@@ -1,113 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Runtime
|
|
||||||
import P4Lang
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_statement_interloper() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state starts {
|
|
||||||
bool where_to = false;
|
|
||||||
int va = 5;
|
|
||||||
transition accept;
|
|
||||||
}
|
|
||||||
state start {
|
|
||||||
bool where_to = true;
|
|
||||||
where_to = true;
|
|
||||||
transition select (where_to) {
|
|
||||||
false: reject;
|
|
||||||
true: starts;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
|
|
||||||
var statements_executed: [String] = Array()
|
|
||||||
|
|
||||||
let ev = InterloperEvaluator().setStatementInterloper() { (statement, cf, execution) in
|
|
||||||
statements_executed.append("\(statement)")
|
|
||||||
}
|
|
||||||
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: ArgumentList(), inExecution: ProgramExecution(ev)))
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
|
|
||||||
#expect(statements_executed[0].hasPrefix("VariableDeclarationStatement"))
|
|
||||||
#expect(statements_executed[1].hasPrefix("ParserAssignmentStatement"))
|
|
||||||
// Moved into starts
|
|
||||||
#expect(statements_executed[2].hasPrefix("VariableDeclarationStatement"))
|
|
||||||
#expect(statements_executed[3].hasPrefix("VariableDeclarationStatement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_expression_interloper() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state starts {
|
|
||||||
bool where_to = false;
|
|
||||||
int va = 5;
|
|
||||||
transition accept;
|
|
||||||
}
|
|
||||||
state start {
|
|
||||||
bool where_to = true;
|
|
||||||
where_to = true;
|
|
||||||
transition select (where_to) {
|
|
||||||
false: reject;
|
|
||||||
true: starts;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
|
|
||||||
var expressions_evaluated: [String] = Array()
|
|
||||||
|
|
||||||
let ev = InterloperEvaluator().setExpressionInterloper() { expression, result, execution in
|
|
||||||
expressions_evaluated.append("\(expression)")
|
|
||||||
}
|
|
||||||
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: ArgumentList(), inExecution: ProgramExecution(ev)))
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
|
|
||||||
#expect(expressions_evaluated[0].hasPrefix("Value: true of Boolean"))
|
|
||||||
#expect(expressions_evaluated[1].hasPrefix("Value: true of Boolean"))
|
|
||||||
#expect(expressions_evaluated[2].hasPrefix("where_to"))
|
|
||||||
#expect(expressions_evaluated[3].hasPrefix("Value: false of Boolean"))
|
|
||||||
#expect(expressions_evaluated[4].hasPrefix("KeysetExpression"))
|
|
||||||
#expect(expressions_evaluated[5].hasPrefix("Value: true of Boolean"))
|
|
||||||
#expect(expressions_evaluated[6].hasPrefix("KeysetExpression"))
|
|
||||||
#expect(expressions_evaluated[7].hasPrefix("SelectCaseExpression"))
|
|
||||||
#expect(expressions_evaluated[8].hasPrefix("SelectExpression"))
|
|
||||||
// Moved into starts
|
|
||||||
#expect(expressions_evaluated[9].hasPrefix("Value: false of Boolean"))
|
|
||||||
#expect(expressions_evaluated[10].hasPrefix("Value: 5 of Int"))
|
|
||||||
}
|
|
||||||
@@ -1,184 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
import P4Lang
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_simple_parser_syntax_error() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state
|
|
||||||
transition start;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(withMessage: "Could not compile the P4 program"),
|
|
||||||
Program.Compile(simple_parser_declaration)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_compilation_with_statement() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
true;
|
|
||||||
transition start;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
|
|
||||||
#expect(parser.states.count() == 1)
|
|
||||||
|
|
||||||
let state = AsInstantiatedParserState((try! #require(parser.states.find(withIdentifier: Identifier(name: "start")))))
|
|
||||||
#expect(state.state == Identifier(name: "start"))
|
|
||||||
#expect(state.statements.count == 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_invalid_transition_expression_keyset_expressions() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false) {
|
|
||||||
asdf: reject;
|
|
||||||
asde: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let compilation_error = try #UseErrorResult(Program.Compile(simple_parser_declaration))
|
|
||||||
|
|
||||||
#expect(compilation_error.msg().contains("asde"))
|
|
||||||
#expect(compilation_error.msg().contains("asdf"))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_compiler_macro_nodetype_test() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false) {
|
|
||||||
asdf: reject;
|
|
||||||
asde: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let p = try! #UseOkResult(ConfigureP4Parser())
|
|
||||||
let result = try! #require(p.parse(simple))
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult<(EvaluatableStatement, CompilerContext)>(
|
|
||||||
ErrorWithLocation(sourceLocation: SourceLocation(2, 154), withError: "Did not find assignment statement"),
|
|
||||||
ParserAssignmentStatement.Compile( // Note: Calling ParserAssignmentStatement compilation directly.
|
|
||||||
node: result.rootNode!, withContext: CompilerContext())))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_compiler_parser_with_parameters() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser(bool pmtr) {
|
|
||||||
state start {
|
|
||||||
transition accept;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try! #UseOkResult(Program.Compile(simple))
|
|
||||||
let parser = try! #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let parameters = parser.parameters
|
|
||||||
|
|
||||||
// Check that the parameters match.
|
|
||||||
#expect(parameters.parameters.count == 1)
|
|
||||||
|
|
||||||
#expect(parameters.parameters[0].name == Identifier(name: "pmtr"))
|
|
||||||
#expect(parameters.parameters[0].type.baseType().eq(rhs: P4Boolean()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_compiler_parser_with_multiple_parameters() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser(bool pmtr, string smtr) {
|
|
||||||
state start {
|
|
||||||
transition accept;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try! #UseOkResult(Program.Compile(simple))
|
|
||||||
let parser = try! #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let parameters = parser.parameters
|
|
||||||
|
|
||||||
// Check that the parameters match.
|
|
||||||
#expect(parameters.parameters.count == 2)
|
|
||||||
|
|
||||||
#expect(parameters.parameters[0].name == Identifier(name: "pmtr"))
|
|
||||||
#expect(parameters.parameters[0].type.baseType().eq(rhs: P4Boolean()))
|
|
||||||
|
|
||||||
#expect(parameters.parameters[1].name == Identifier(name: "smtr"))
|
|
||||||
#expect(parameters.parameters[1].type.baseType().eq(rhs: P4String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_compiler_parser_with_multiple_parameters2() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser(bool pmtr, string smtr, int imtr) {
|
|
||||||
state start {
|
|
||||||
transition accept;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try! #UseOkResult(Program.Compile(simple))
|
|
||||||
let parser = try! #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let parameters = parser.parameters
|
|
||||||
|
|
||||||
// Check that the parameters match.
|
|
||||||
#expect(parameters.parameters.count == 3)
|
|
||||||
|
|
||||||
#expect(parameters.parameters[0].name == Identifier(name: "pmtr"))
|
|
||||||
#expect(parameters.parameters[0].type.baseType().eq(rhs: P4Boolean()))
|
|
||||||
|
|
||||||
#expect(parameters.parameters[1].name == Identifier(name: "smtr"))
|
|
||||||
#expect(parameters.parameters[1].type.baseType().eq(rhs: P4String()))
|
|
||||||
|
|
||||||
#expect(parameters.parameters[2].name == Identifier(name: "imtr"))
|
|
||||||
#expect(parameters.parameters[2].type.baseType().eq(rhs: P4Int()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_compiler_parser_use_parameters() async throws {
|
|
||||||
let simple = """
|
|
||||||
parser main_parser(bool pmtr, string smtr, int imtr) {
|
|
||||||
state start {
|
|
||||||
pmtr = true;
|
|
||||||
transition accept;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(#RequireOkResult(Program.Compile(simple)))
|
|
||||||
}
|
|
||||||
@@ -1,173 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_simple_runtime() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
true;
|
|
||||||
transition accept;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
// We should be in the accept state.
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_runtime_to_accept() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
true;
|
|
||||||
transition reject;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
// We should be in the accept state.
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_runtime_no_start_state() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state tart {
|
|
||||||
true;
|
|
||||||
transition reject;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult<(InstantiatedParserState, ProgramExecution)>(
|
|
||||||
Error(withMessage: "Could not find the start state"),
|
|
||||||
runtime.run()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_runtime_parser_with_parameters() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser(bool pmtr, string smtr, int imtr) {
|
|
||||||
state start {
|
|
||||||
transition select (pmtr) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
|
|
||||||
let args = ArgumentList([
|
|
||||||
Argument(P4Value(P4BooleanValue(withValue: true)), atIndex: 1), Argument(P4Value(P4StringValue(withValue: "Testing")), atIndex: 2), Argument(P4Value(P4IntValue(withValue: 5)), atIndex: 3),
|
|
||||||
])
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run(withArguments: args))
|
|
||||||
// We should be in the accept state.
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_runtime_parser_with_mismatched_parameter_types() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser(bool pmtr, string smtr, int imtr) {
|
|
||||||
state start {
|
|
||||||
transition select (pmtr) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
|
|
||||||
let args = ArgumentList([
|
|
||||||
Argument(P4Value(P4BooleanValue(withValue: true)), atIndex: 1), Argument(P4Value(P4BooleanValue(withValue: false)), atIndex: 2), Argument(P4Value(P4IntValue(withValue: 5)), atIndex: 3),
|
|
||||||
])
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult<(InstantiatedParserState, ProgramExecution)>(
|
|
||||||
Error(withMessage: "Cannot call parser: Argument 2's type (Boolean) is incompatible with the parameter type (String)"),
|
|
||||||
runtime.run(withArguments: args)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_runtime_parser_with_mismatched_parameter_types2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser(bool pmtr, string smtr, int imtr) {
|
|
||||||
state start {
|
|
||||||
transition select (pmtr) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
|
|
||||||
let args = ArgumentList([
|
|
||||||
Argument(P4Value(P4IntValue(withValue: 5)), atIndex: 1), Argument(P4Value(P4StringValue(withValue: "Testing")), atIndex: 2), Argument(P4Value(P4IntValue(withValue: 5)), atIndex: 3),
|
|
||||||
])
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult<(InstantiatedParserState, ProgramExecution)>(
|
|
||||||
Error(withMessage: "Cannot call parser: Argument 1's type (Int (width: Infinite)) is incompatible with the parameter type (Boolean)"),
|
|
||||||
runtime.run(withArguments: args)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_runtime_parser_with_mismatched_parameter_counts() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser(bool pmtr, string smtr, int imtr) {
|
|
||||||
state start {
|
|
||||||
transition select (pmtr) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let args = ArgumentList([Argument(P4Value(P4BooleanValue(withValue: true)), atIndex: 0)])
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult<(InstantiatedParserState, ProgramExecution)>(
|
|
||||||
Error(withMessage: "Cannot call parser: 1 arguments found but 3 required"),
|
|
||||||
runtime.run(withArguments: args)))
|
|
||||||
}
|
|
||||||
@@ -1,159 +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
|
|
||||||
import Foundation
|
|
||||||
import P4Lang
|
|
||||||
import Macros
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_simple_local_element_variable_declaration() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool b = false;
|
|
||||||
string s = "testing";
|
|
||||||
true;
|
|
||||||
false;
|
|
||||||
true;
|
|
||||||
transition reject;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_scope() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state starts {
|
|
||||||
bool where_to = false;
|
|
||||||
int va = 5;
|
|
||||||
transition accept;
|
|
||||||
}
|
|
||||||
state start {
|
|
||||||
bool where_to = true;
|
|
||||||
transition select (where_to) {
|
|
||||||
false: reject;
|
|
||||||
true: starts;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_scope2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state starts {
|
|
||||||
bool where_to = false;
|
|
||||||
int va = 5;
|
|
||||||
transition accept;
|
|
||||||
}
|
|
||||||
state start {
|
|
||||||
bool where_to = true;
|
|
||||||
where_to = false;
|
|
||||||
transition select (where_to) {
|
|
||||||
false: reject;
|
|
||||||
true: starts;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_assignment() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = true;
|
|
||||||
string where_from = "here";
|
|
||||||
where_to = false;
|
|
||||||
where_from = "there";
|
|
||||||
transition select (where_to) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_nested_declaration_assignment() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = true;
|
|
||||||
string where_from = "here";
|
|
||||||
string where_where = "here";
|
|
||||||
if (where_to) {
|
|
||||||
bool where_from = true;
|
|
||||||
if (where_from) {
|
|
||||||
where_to = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
where_from = "there";
|
|
||||||
transition select (where_to) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
@@ -1,66 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_scope() async throws {
|
|
||||||
let s = VarTypeScope()
|
|
||||||
let s2 = s.declare(identifier: Identifier(name: "first"), withValue: P4QualifiedType(P4Int()))
|
|
||||||
let found_first = try! #require(s2.lookup(identifier: Identifier(name: "first")))
|
|
||||||
|
|
||||||
#expect(found_first.baseType().eq(rhs: P4Int()))
|
|
||||||
#expect(s2.count == 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_scope_no_set() async throws {
|
|
||||||
var ss = VarTypeScopes().enter()
|
|
||||||
ss = ss.declare(identifier: Identifier(name: "first"), withValue: P4QualifiedType(P4Int()))
|
|
||||||
ss = ss.enter()
|
|
||||||
ss = ss.declare(identifier: Identifier(name: "second"), withValue: P4QualifiedType(P4Boolean()))
|
|
||||||
|
|
||||||
let found_first = try! #UseOkResult(ss.lookup(identifier: Identifier(name: "first")))
|
|
||||||
let found_second = try! #UseOkResult(ss.lookup(identifier: Identifier(name: "second")))
|
|
||||||
|
|
||||||
#expect(found_first.baseType().eq(rhs: P4Int()))
|
|
||||||
#expect(found_second.baseType().eq(rhs: P4Boolean()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_scope_set() async throws {
|
|
||||||
var ss = VarTypeScopes().enter()
|
|
||||||
let id = Identifier(name: "first")
|
|
||||||
let id_type = P4QualifiedType(P4Int())
|
|
||||||
|
|
||||||
ss = ss.declare(identifier: id, withValue: id_type)
|
|
||||||
ss = ss.enter()
|
|
||||||
ss = ss.declare(identifier: Identifier(name: "second"), withValue: P4QualifiedType(P4Boolean()))
|
|
||||||
// Change the value of `first`.
|
|
||||||
ss = ss.set(identifier: id, withValue: P4QualifiedType(P4String()))
|
|
||||||
|
|
||||||
// Verify the change!
|
|
||||||
let found = try! #UseOkResult(ss.lookup(identifier: id))
|
|
||||||
|
|
||||||
#expect(found.baseType().eq(rhs: P4String()))
|
|
||||||
}
|
|
||||||
+11
-17
@@ -17,9 +17,6 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Macros
|
import Macros
|
||||||
import P4Compiler
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
import SwiftTreeSitter
|
||||||
import SystemPackage
|
import SystemPackage
|
||||||
import Testing
|
import Testing
|
||||||
@@ -27,6 +24,7 @@ import TreeSitter
|
|||||||
import TreeSitterP4
|
import TreeSitterP4
|
||||||
|
|
||||||
@testable import Common
|
@testable import Common
|
||||||
|
@testable import P4Parser
|
||||||
|
|
||||||
@Test func test_preprocessor() async throws {
|
@Test func test_preprocessor() async throws {
|
||||||
let sm = SourceManager(["./TestData/Sources/"])
|
let sm = SourceManager(["./TestData/Sources/"])
|
||||||
@@ -34,8 +32,7 @@ import TreeSitterP4
|
|||||||
let file = FilePath.init(stringLiteral: "simple.p4")
|
let file = FilePath.init(stringLiteral: "simple.p4")
|
||||||
|
|
||||||
let source = try! (#UseOkResult(prep.preprocess(file)))
|
let source = try! (#UseOkResult(prep.preprocess(file)))
|
||||||
let program = try! #UseOkResult(Program.Compile(source.getSource()))
|
#expect(#RequireOkResult(compile(source.getSource())))
|
||||||
#expect(#RequireOkResult((program.find_parser(withName: Identifier(name: "main_parser")))))
|
|
||||||
#expect(source.getLocations().getPath() == file)
|
#expect(source.getLocations().getPath() == file)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,8 +42,7 @@ import TreeSitterP4
|
|||||||
let file = FilePath.init(stringLiteral: "simple.p4")
|
let file = FilePath.init(stringLiteral: "simple.p4")
|
||||||
|
|
||||||
let source = try! (#UseOkResult(prep.preprocess(file)))
|
let source = try! (#UseOkResult(prep.preprocess(file)))
|
||||||
let program = try! #UseOkResult(Program.Compile(source.getSource()))
|
#expect(#RequireOkResult(compile(source.getSource())))
|
||||||
#expect(#RequireOkResult((program.find_parser(withName: Identifier(name: "main_parser")))))
|
|
||||||
#expect(source.getLocations().getPath() == file)
|
#expect(source.getLocations().getPath() == file)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,8 +54,7 @@ import TreeSitterP4
|
|||||||
#expect(#RequireOkResult(prep.preprocess(file)))
|
#expect(#RequireOkResult(prep.preprocess(file)))
|
||||||
|
|
||||||
let source = try! (#UseOkResult(prep.preprocess(file)))
|
let source = try! (#UseOkResult(prep.preprocess(file)))
|
||||||
let program = try! #UseOkResult(Program.Compile(source.getSource()))
|
#expect(#RequireOkResult(compile(source.getSource())))
|
||||||
#expect(#RequireOkResult((program.find_parser(withName: Identifier(name: "main_parser")))))
|
|
||||||
#expect(source.getLocations().getPath() == file)
|
#expect(source.getLocations().getPath() == file)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,8 +66,7 @@ import TreeSitterP4
|
|||||||
#expect(#RequireOkResult(prep.preprocess(file)))
|
#expect(#RequireOkResult(prep.preprocess(file)))
|
||||||
|
|
||||||
let source = try! (#UseOkResult(prep.preprocess(file)))
|
let source = try! (#UseOkResult(prep.preprocess(file)))
|
||||||
let program = try! #UseOkResult(Program.Compile(source.getSource()))
|
#expect(#RequireOkResult(compile(source.getSource())))
|
||||||
#expect(#RequireOkResult((program.find_parser(withName: Identifier(name: "main_parser")))))
|
|
||||||
#expect(source.getLocations().getPath() == file)
|
#expect(source.getLocations().getPath() == file)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,12 +138,14 @@ import TreeSitterP4
|
|||||||
let source = try! (#UseOkResult(prep.preprocess(file)))
|
let source = try! (#UseOkResult(prep.preprocess(file)))
|
||||||
|
|
||||||
#expect(source.getLocations().getLocation() == SourceLocation(0..<173))
|
#expect(source.getLocations().getLocation() == SourceLocation(0..<173))
|
||||||
|
/*
|
||||||
#expect(
|
#expect(
|
||||||
source.getLocations().getNestedLocations()[0].getNestedLocations()[0].getLocation()
|
source.getLocations().getNestedLocations()[0].getNestedLocations()[0].getLocation()
|
||||||
== SourceLocation(27..<47))
|
== SourceLocation(27..<47))
|
||||||
#expect(
|
#expect(
|
||||||
source.getLocations().getNestedLocations()[0].getNestedLocations()[1].getLocation()
|
source.getLocations().getNestedLocations()[0].getNestedLocations()[1].getLocation()
|
||||||
== SourceLocation(48..<166))
|
== SourceLocation(48..<166))
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test func test_preprocessor_nested_includes_annotated_source() async throws {
|
@Test func test_preprocessor_nested_includes_annotated_source() async throws {
|
||||||
@@ -192,13 +188,11 @@ import TreeSitterP4
|
|||||||
let expected_nested_file = FilePath(stringLiteral: "file-loc-parser.p4")
|
let expected_nested_file = FilePath(stringLiteral: "file-loc-parser.p4")
|
||||||
let expected_nested_nested_file = FilePath(stringLiteral: "file-loc-parser-state.p4")
|
let expected_nested_nested_file = FilePath(stringLiteral: "file-loc-parser-state.p4")
|
||||||
|
|
||||||
let found_file = try! #require(source.pathForLocation(0))
|
let found_nested_files = try! #require(source.pathForLocation(78))
|
||||||
let found_nested_file = try! #require(source.pathForLocation(55))
|
|
||||||
let found_nested_nested_file = try! #require(source.pathForLocation(78))
|
|
||||||
|
|
||||||
#expect(found_file == expected_file)
|
#expect(found_nested_files[0].path == expected_file)
|
||||||
#expect(found_nested_file == expected_nested_file)
|
#expect(found_nested_files[1].path == expected_nested_file)
|
||||||
#expect(found_nested_nested_file == expected_nested_nested_file)
|
#expect(found_nested_files[2].path == expected_nested_nested_file)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test func test_source_location_contains() async throws {
|
@Test func test_source_location_contains() async throws {
|
||||||
@@ -1,465 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Runtime
|
|
||||||
import P4Lang
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_field_access() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = ts.yesno;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ts"), withValue: P4QualifiedType(struct_type))
|
|
||||||
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ts"),
|
|
||||||
withValue: P4Value(P4StructValue(withType: struct_type, andInitializers: [
|
|
||||||
P4Value(P4BooleanValue(withValue: true)),
|
|
||||||
P4Value(P4IntValue(withValue: 5)),
|
|
||||||
])))
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_field_access_declared() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
Testing ts;
|
|
||||||
ts.yesno = true;
|
|
||||||
bool where_to = ts.yesno;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_types = TypeTypeScopes().enter()
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
test_types = test_types.declare(identifier: Identifier(name: "Testing"), withValue: struct_type)
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: .none, withGlobalTypes: test_types))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_field_access_declared2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
Testing ts;
|
|
||||||
ts.yesno = true;
|
|
||||||
ts.count = 5;
|
|
||||||
bool where_to = ts.yesno;
|
|
||||||
transition select (ts.count == 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_types = TypeTypeScopes().enter()
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
test_types = test_types.declare(identifier: Identifier(name: "Testing"), withValue: struct_type)
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: .none, withGlobalTypes: test_types))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_field_access_opp() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = ts.yesno;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ts"), withValue: P4QualifiedType(struct_type))
|
|
||||||
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ts"),
|
|
||||||
withValue: P4Value(P4StructValue(withType: struct_type, andInitializers: [
|
|
||||||
P4Value(P4BooleanValue(withValue: false)),
|
|
||||||
P4Value(P4IntValue(withValue: 5)),
|
|
||||||
])))
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test func test_field_access2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (ts.count == 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ts"), withValue: P4QualifiedType(struct_type))
|
|
||||||
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ts"),
|
|
||||||
withValue: P4Value(P4StructValue(withType: struct_type, andInitializers: [
|
|
||||||
P4Value(P4BooleanValue(withValue: true)),
|
|
||||||
P4Value(P4IntValue(withValue: 5)),
|
|
||||||
])))
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_field_access2_opp() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (ts.count == 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ts"), withValue: P4QualifiedType(struct_type))
|
|
||||||
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ts"),
|
|
||||||
withValue: P4Value(P4StructValue(withType: struct_type, andInitializers: [
|
|
||||||
P4Value(P4BooleanValue(withValue: true)),
|
|
||||||
P4Value(P4IntValue(withValue: 8)),
|
|
||||||
])))
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_field_access_nested() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
int where_to = ts.ty.count;
|
|
||||||
transition select (where_to == 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
|
|
||||||
let ty_fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let ty_struct_type = P4Struct(withName: Identifier(name: "nested"), andFields: ty_fields)
|
|
||||||
|
|
||||||
let ts_fields = P4StructFields([P4StructFieldIdentifier(name: "ty", withType: P4QualifiedType(ty_struct_type))])
|
|
||||||
let ts_struct_type = P4Struct(withName: Identifier(name: "outer"), andFields: ts_fields)
|
|
||||||
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ts"), withValue: P4QualifiedType(ts_struct_type))
|
|
||||||
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ts"),
|
|
||||||
withValue: P4Value(P4StructValue(
|
|
||||||
withType: ts_struct_type,
|
|
||||||
andInitializers: [
|
|
||||||
P4Value(P4StructValue(
|
|
||||||
withType: ty_struct_type,
|
|
||||||
andInitializers: [
|
|
||||||
P4Value(P4BooleanValue(withValue: true)),
|
|
||||||
P4Value(P4IntValue(withValue: 5)),
|
|
||||||
]))
|
|
||||||
])))
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_field_write() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
ts.yesno = true;
|
|
||||||
bool where_to = ts.yesno;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ts"), withValue: P4QualifiedType(struct_type))
|
|
||||||
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ts"),
|
|
||||||
withValue: P4Value(P4StructValue(withType: struct_type, andInitializers: [
|
|
||||||
P4Value(P4BooleanValue(withValue: false)),
|
|
||||||
P4Value(P4IntValue(withValue: 5)),
|
|
||||||
])))
|
|
||||||
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_field_write_invalid_type() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
ts.yesno = 5;
|
|
||||||
transition accept;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ts"), withValue: P4QualifiedType(struct_type))
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage: "{49, 13}: Failed to parse a statement element: {49, 8}: Cannot assign value of type Int (width: Infinite) to field yesno of type Boolean"
|
|
||||||
),
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_field_write_nested() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
ts.ty.count = 5;
|
|
||||||
int where_to = ts.ty.count;
|
|
||||||
transition select (where_to == 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
|
|
||||||
let ty_fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let ty_struct_type = P4Struct(withName: Identifier(name: "nested"), andFields: ty_fields)
|
|
||||||
|
|
||||||
let ts_fields = P4StructFields([P4StructFieldIdentifier(name: "ty", withType: P4QualifiedType(ty_struct_type))])
|
|
||||||
let ts_struct_type = P4Struct(withName: Identifier(name: "outer"), andFields: ts_fields)
|
|
||||||
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ts"), withValue: P4QualifiedType(ts_struct_type))
|
|
||||||
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ts"),
|
|
||||||
withValue: P4Value(P4StructValue(
|
|
||||||
withType: ts_struct_type,
|
|
||||||
andInitializers: [
|
|
||||||
P4Value(P4StructValue(
|
|
||||||
withType: ty_struct_type,
|
|
||||||
andInitializers: [
|
|
||||||
P4Value(P4BooleanValue(withValue: true)),
|
|
||||||
P4Value(P4IntValue(withValue: 7)),
|
|
||||||
]))
|
|
||||||
])))
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_field_write_nested2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
ts.ty.count = 3;
|
|
||||||
ts.ty.count = 5;
|
|
||||||
int where_to = ts.ty.count;
|
|
||||||
transition select (where_to == 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
|
|
||||||
let ty_fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let ty_struct_type = P4Struct(withName: Identifier(name: "nested"), andFields: ty_fields)
|
|
||||||
|
|
||||||
let ts_fields = P4StructFields([P4StructFieldIdentifier(name: "ty", withType: P4QualifiedType(ty_struct_type))])
|
|
||||||
let ts_struct_type = P4Struct(withName: Identifier(name: "outer"), andFields: ts_fields)
|
|
||||||
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ts"), withValue: P4QualifiedType(ts_struct_type))
|
|
||||||
|
|
||||||
var test_values = VarValueScopes().enter()
|
|
||||||
|
|
||||||
test_values = test_values.declare(
|
|
||||||
identifier: Identifier(name: "ts"),
|
|
||||||
withValue: P4Value(P4StructValue(
|
|
||||||
withType: ts_struct_type,
|
|
||||||
andInitializers: [
|
|
||||||
P4Value(P4StructValue(
|
|
||||||
withType: ty_struct_type,
|
|
||||||
andInitializers: [
|
|
||||||
P4Value(P4BooleanValue(withValue: true)),
|
|
||||||
P4Value(P4IntValue(withValue: 7)),
|
|
||||||
]))
|
|
||||||
])))
|
|
||||||
let program = try #UseOkResult(
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program, withGlobalValues: test_values))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_field_write_nested_invalid_type() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
ts.ty.count = false;
|
|
||||||
int where_to = ts.ty.count;
|
|
||||||
transition select (where_to == 5) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_declarations = VarTypeScopes().enter()
|
|
||||||
|
|
||||||
let ty_fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
let ty_struct_type = P4Struct(withName: Identifier(name: "nested"), andFields: ty_fields)
|
|
||||||
|
|
||||||
let ts_fields = P4StructFields([P4StructFieldIdentifier(name: "ty", withType: P4QualifiedType(ty_struct_type))])
|
|
||||||
let ts_struct_type = P4Struct(withName: Identifier(name: "outer"), andFields: ts_fields)
|
|
||||||
|
|
||||||
test_declarations = test_declarations.declare(identifier: Identifier(name: "ts"), withValue: P4QualifiedType(ts_struct_type))
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage: "{49, 20}: Failed to parse a statement element: {49, 11}: Cannot assign value of type Boolean to field count of type Int (width: Infinite)"
|
|
||||||
),
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_declarations))
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,57 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Runtime
|
|
||||||
import P4Lang
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_style_add_format() async throws {
|
|
||||||
let red = Style(StyleColor.Red)
|
|
||||||
let bold_red = red.update(addFormat: StyleFormat.Bold)
|
|
||||||
|
|
||||||
#expect(bold_red == Style(StyleColor.Red, [StyleFormat.Bold]))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_style_add_format2() async throws {
|
|
||||||
let bold_red = Style(StyleColor.Red, [StyleFormat.Bold])
|
|
||||||
let bold_underline_red = bold_red.update(addFormat: StyleFormat.Underline)
|
|
||||||
|
|
||||||
#expect(bold_underline_red == Style(StyleColor.Red, [StyleFormat.Bold, StyleFormat.Underline]))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test func test_style_remove_format() async throws {
|
|
||||||
let bold_red = Style(StyleColor.Red, [StyleFormat.Bold])
|
|
||||||
let red = bold_red.update(removeFormat: StyleFormat.Bold)
|
|
||||||
|
|
||||||
#expect(red == Style(StyleColor.Red))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_style_remove_format2() async throws {
|
|
||||||
let bold_underline_red = Style(StyleColor.Red, [StyleFormat.Bold, StyleFormat.Underline])
|
|
||||||
let underline_red = bold_underline_red.update(removeFormat: StyleFormat.Bold)
|
|
||||||
|
|
||||||
#expect(underline_red == Style(StyleColor.Red, [StyleFormat.Underline]))
|
|
||||||
}
|
|
||||||
@@ -1,49 +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 Foundation
|
|
||||||
import Common
|
|
||||||
import Macros
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
struct NotStringConvertible {}
|
|
||||||
|
|
||||||
struct StringConvertible: CustomStringConvertible {
|
|
||||||
public var description: String {
|
|
||||||
return "CONVERTED"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_result_type_description_not_convertible() async throws {
|
|
||||||
let result: Result<NotStringConvertible> = Result.Ok(NotStringConvertible());
|
|
||||||
#expect("\(result)" == "Ok(Tests.NotStringConvertible())")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_result_type_description_convertible() async throws {
|
|
||||||
let result: Result<StringConvertible> = Result.Ok(StringConvertible());
|
|
||||||
#expect("\(result)" == "Ok: CONVERTED")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_result_type_p4value_convertible() async throws {
|
|
||||||
let result = Result.Ok(P4Value(P4IntValue(withValue: 5)))
|
|
||||||
#expect("\(result)" == "Ok: Value: 5 of Int (width: Infinite) type of type Int (width: Infinite)")
|
|
||||||
}
|
|
||||||
@@ -1,58 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_invalid_statements() async throws {
|
|
||||||
|
|
||||||
let ret = ReturnStatement(P4Value(P4IntValue(withValue: 5)))
|
|
||||||
let block = BlockStatement([ret])
|
|
||||||
|
|
||||||
#expect(ContainsInvalidStatements(block: block, invalids: [ReturnStatement.self]))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_no_invalid_statements() async throws {
|
|
||||||
|
|
||||||
let exprs = ExpressionStatement(P4Value(P4IntValue(withValue: 5)))
|
|
||||||
let block = BlockStatement([exprs])
|
|
||||||
|
|
||||||
#expect(!ContainsInvalidStatements(block: block, invalids: [ReturnStatement.self]))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_is_invalid_statement() async throws {
|
|
||||||
|
|
||||||
let ret = ReturnStatement(P4Value(P4IntValue(withValue: 5)))
|
|
||||||
|
|
||||||
#expect(ContainsInvalidStatements(statement: ret, invalids: [ReturnStatement.self]))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_no_is_invalid_statement() async throws {
|
|
||||||
|
|
||||||
let exprs = ExpressionStatement(P4Value(P4IntValue(withValue: 5)))
|
|
||||||
|
|
||||||
#expect(!ContainsInvalidStatements(statement: exprs, invalids: [ReturnStatement.self]))
|
|
||||||
}
|
|
||||||
@@ -1,51 +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
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,91 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_simple_parser_with_transition_select_expression() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (true) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(parser.states.count() == 1)
|
|
||||||
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_parser_with_transition_select_expression_to_reject() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false) {
|
|
||||||
false: reject;
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
#expect(parser.states.count() == 1)
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_no_matching_key_transition() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition select (false) {
|
|
||||||
true: accept;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult<(InstantiatedParserState, ProgramExecution)>(
|
|
||||||
Error(withMessage: "No key matched the selector"),
|
|
||||||
runtime.run()))
|
|
||||||
}
|
|
||||||
@@ -1,37 +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 Foundation
|
|
||||||
import Macros
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import Common
|
|
||||||
|
|
||||||
@Test func test_simple_struct() async throws {
|
|
||||||
let fields = P4StructFields([
|
|
||||||
P4StructFieldIdentifier(name: "yesno", withType: P4QualifiedType(P4Boolean())),
|
|
||||||
P4StructFieldIdentifier(name: "count", withType: P4QualifiedType(P4Int())),
|
|
||||||
])
|
|
||||||
|
|
||||||
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
|
||||||
|
|
||||||
#expect(struct_type.fields.count() == 2)
|
|
||||||
#expect(struct_type.fields == fields)
|
|
||||||
}
|
|
||||||
@@ -1,56 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_unsupported_annotations() async throws {
|
|
||||||
let simple_annotated_parser_declaration = """
|
|
||||||
@testing parser main_parser() {
|
|
||||||
state start {
|
|
||||||
transition start;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(withMessage: "{0, 8}: Annotations in parser type are not yet handled."),
|
|
||||||
Program.Compile(simple_annotated_parser_declaration)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_unsupported_annotations_state() async throws {
|
|
||||||
let simple_annotated_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
@testing state start {
|
|
||||||
transition start;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(withMessage: "{26, 8}: Annotations in parser state are not yet handled."),
|
|
||||||
Program.Compile(simple_annotated_parser_declaration)))
|
|
||||||
}
|
|
||||||
@@ -1,375 +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
|
|
||||||
import Foundation
|
|
||||||
import Macros
|
|
||||||
import P4Lang
|
|
||||||
import P4Runtime
|
|
||||||
import SwiftTreeSitter
|
|
||||||
import Testing
|
|
||||||
import TreeSitter
|
|
||||||
import TreeSitterP4
|
|
||||||
|
|
||||||
@testable import P4Compiler
|
|
||||||
|
|
||||||
@Test func test_invalid_types() async throws {
|
|
||||||
for invalid_type_name in ["boo", "str", "in"] {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
\(invalid_type_name) v = 1;
|
|
||||||
transition reject;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let err = Program.Compile(simple_parser_declaration)
|
|
||||||
guard case Result.Error(let e) = err else {
|
|
||||||
assert(false, "Expected an error, but had success")
|
|
||||||
}
|
|
||||||
#expect(
|
|
||||||
e.msg().contains(
|
|
||||||
"Failed to parse a statement element: Could not parse a P4 type from \(invalid_type_name)"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_invalid_type_in_assignment() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
string g = "Testing";
|
|
||||||
string where_to = "Testing";
|
|
||||||
where_to = true;
|
|
||||||
transition reject;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{112, 16}: Failed to parse a statement element: {112, 8}: Cannot assign value with type Boolean to identifier where_to with type String"
|
|
||||||
),
|
|
||||||
Program.Compile(simple_parser_declaration)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_invalid_type_in_assignment2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = true;
|
|
||||||
string where_from = "testing";
|
|
||||||
where_to = where_from;
|
|
||||||
transition reject;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{114, 22}: Failed to parse a statement element: {114, 8}: Cannot assign value with type String to identifier where_to with type Boolean"
|
|
||||||
),
|
|
||||||
Program.Compile(simple_parser_declaration)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_invalid_type_in_declaration() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
string where_from = "testing";
|
|
||||||
bool where_to = where_from;
|
|
||||||
transition reject;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{86, 27}: Failed to parse a statement element: Cannot initialize where_to (with type Boolean) from expression with type String"
|
|
||||||
),
|
|
||||||
Program.Compile(simple_parser_declaration)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_invalid_type_in_declaration2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = true;
|
|
||||||
string where_from = where_to;
|
|
||||||
transition reject;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{77, 29}: Failed to parse a statement element: Cannot initialize where_from (with type String) from expression with type Boolean"
|
|
||||||
),
|
|
||||||
Program.Compile(simple_parser_declaration)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_invalid_type_in_declaration3() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
int<5> specific_width_int = 5;
|
|
||||||
int unspecific_width_int = specific_width_int;
|
|
||||||
transition reject;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let error = try! #UseErrorResult(Program.Compile(simple_parser_declaration))
|
|
||||||
|
|
||||||
#expect(
|
|
||||||
error.msg().contains(
|
|
||||||
"Cannot initialize specific_width_int (with type Int (width: Width(5))) from expression with type Int (width: Infinite)"
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_valid_specific_width_int_type_in_declaration() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
int<5> specific_width_int = 5w5;
|
|
||||||
int<5> unspecific_width_int = specific_width_int;
|
|
||||||
transition reject;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
#expect(#RequireOkResult(Program.Compile(simple_parser_declaration)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_expression_in_declaration_initializer_specific_width_int_type() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
int<5> specific_width_int = 5w5;
|
|
||||||
bool where_to = 5w5 == specific_width_int == true;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
// 5w5 == specific_width_int == true
|
|
||||||
// true == true
|
|
||||||
// true
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_expression_in_declaration_initializer_specific_width_int_type2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
int<5> specific_width_int = 5w5;
|
|
||||||
bool where_to = 5w6 == specific_width_int == false;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
// 5w6 == specific_width_int == false
|
|
||||||
// false == false
|
|
||||||
// true
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test func test_expression_in_declaration_initializer() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = 5 == 5 == true;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
// 5 == 5 == true
|
|
||||||
// true == true
|
|
||||||
// true
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_expression_in_declaration_initializer2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = 5 == 5 == false;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
// 5 == 5 == true
|
|
||||||
// true == false
|
|
||||||
// false
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_expression_in_declaration_initializer_false() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = 6 == 5 == true;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
// 6 == 5 == true
|
|
||||||
// false == true
|
|
||||||
// false
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_expression_in_declaration_initializer_false2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = 6 == 5 == false;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
// 6 == 5 == false
|
|
||||||
// false == false
|
|
||||||
// true
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.accept)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_expression_in_declaration_initializer_invalid_types() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = false == 5 == true;
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
|
||||||
let runtime = try #UseOkResult(
|
|
||||||
P4Runtime.Runtime<InstantiatedParserState, P4Lang.Parser>.create(program: program))
|
|
||||||
let (state_result, _) = try! #UseOkResult(runtime.run())
|
|
||||||
|
|
||||||
// TODO: This test should throw an error.
|
|
||||||
|
|
||||||
// false == 5 == true
|
|
||||||
// false == true
|
|
||||||
// false
|
|
||||||
#expect(AsInstantiatedParserState(state_result) == P4Lang.reject)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_expression_in_declaration_initializer_invalid_types2() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser() {
|
|
||||||
state start {
|
|
||||||
bool where_to = ta[0];
|
|
||||||
transition select (where_to) {
|
|
||||||
true: accept;
|
|
||||||
false: reject;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
var test_types = VarTypeScopes().enter()
|
|
||||||
test_types = test_types.declare(
|
|
||||||
identifier: Identifier(name: "ta"),
|
|
||||||
withValue: P4QualifiedType(P4Array(withValueType: P4QualifiedType(P4Int()))))
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{49, 22}: Failed to parse a statement element: Cannot initialize where_to (with type Boolean) from expression with type Int (width: Infinite)"
|
|
||||||
),
|
|
||||||
Program.Compile(simple_parser_declaration, withGlobalInstances: test_types)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test func test_simple_compiler_parser_parameters_invalid_types() async throws {
|
|
||||||
let simple_parser_declaration = """
|
|
||||||
parser main_parser(bool pmtr, string smtr, int imtr) {
|
|
||||||
state start {
|
|
||||||
pmtr = 1;
|
|
||||||
transition accept;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
#expect(
|
|
||||||
#RequireErrorResult(
|
|
||||||
Error(
|
|
||||||
withMessage:
|
|
||||||
"{85, 9}: Failed to parse a statement element: {85, 4}: Cannot assign value with type Int (width: Infinite) to identifier pmtr with type Boolean"
|
|
||||||
),
|
|
||||||
Program.Compile(simple_parser_declaration)))
|
|
||||||
}
|
|
||||||
@@ -68,7 +68,7 @@ export default grammar({
|
|||||||
|
|
||||||
|
|
||||||
// Instantiation
|
// Instantiation
|
||||||
instantiation: $ => seq($.typeRef, '(', optional($.parameter_list), ')', $.identifier),
|
instantiation: $ => seq($.typeRef, $.arguments, $.identifier),
|
||||||
|
|
||||||
// Declarations
|
// Declarations
|
||||||
declaration: $ => seq(choice($.parserDeclaration, $.parserTypeDeclaration, $.type_declaration, $.function_declaration, $.control_declaration, $.extern_declaration)),
|
declaration: $ => seq(choice($.parserDeclaration, $.parserTypeDeclaration, $.type_declaration, $.function_declaration, $.control_declaration, $.extern_declaration)),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ bool() main;
|
|||||||
(bool)
|
(bool)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
(arguments)
|
||||||
(identifier)
|
(identifier)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user