Compare commits
75 Commits
a84e778e79
...
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 | |||
| 041009a22e | |||
| 0cb50e2e2f | |||
| 7fc319d9bc | |||
| 0c8b9e88cf | |||
| e53c32f802 | |||
| 5845cb75cc | |||
| 24b0f0284a | |||
| a1908cc077 | |||
| 382f3de00a | |||
| 8f9fbb86bf | |||
| 16a798cc39 | |||
| 017d5670c0 | |||
| d60465e669 | |||
| b3ca30541a | |||
| 022dc94fde | |||
| 7a2c55cc51 | |||
| 3cff82fd5c | |||
| a7d8fd1304 | |||
| cbebcae20a | |||
| 979fa69ab8 | |||
| 12fa43d9f9 | |||
| f1f20e96a2 | |||
| 49eef16c19 | |||
| fccaf1aa92 | |||
| 73b4f54bbe | |||
| 0e2b13be93 | |||
| f0f7a660a6 | |||
| a0c6b7730c | |||
| d28ccd79e4 | |||
| 355e62d61d | |||
| 754102d601 | |||
| fac1cc4948 | |||
| 7308115d85 | |||
| ce35761efa | |||
| d9133cf6a9 | |||
| d6da7a519e | |||
| 3f0444a8da | |||
| df7a1ac15b | |||
| 8552199ebe | |||
| a657db0b42 | |||
| dd0bbbe479 | |||
| 7a36ca32dd | |||
| 783aac26c7 | |||
| e4d6daa8fe | |||
| 333270deee | |||
| 73779d8074 | |||
| 3d4d26d07b | |||
| 168d48fa7c | |||
| b49ec104e9 | |||
| 5cfe5532a2 | |||
| 7c660b2b0c |
@@ -5,11 +5,13 @@ name: Continuous Integration
|
||||
jobs:
|
||||
grammar-tests:
|
||||
name: Grammar Tests
|
||||
runs-on: macos-latest
|
||||
runs-on: ubuntu-build
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: tree-sitter/setup-action@v2
|
||||
with:
|
||||
tree-sitter-ref: "master"
|
||||
# Because our tree-sitter code is in a subdirectory,
|
||||
# and working-directory does not apply to uses, we
|
||||
# are forced to specify calls to the CI ourselves.
|
||||
@@ -20,32 +22,37 @@ jobs:
|
||||
working-directory: ./tree-sitter-p4
|
||||
library-tests:
|
||||
name: Library Tests
|
||||
runs-on: macos-latest
|
||||
runs-on: ubuntu-build
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: tree-sitter/setup-action@v2
|
||||
with:
|
||||
tree-sitter-ref: "master"
|
||||
- uses: swift-actions/setup-swift@v3
|
||||
with:
|
||||
swift-version: "6.2.4"
|
||||
swift-version: "main-snapshot"
|
||||
skip-verify-signature: true
|
||||
# Because our tree-sitter code is in a subdirectory,
|
||||
# and working-directory does not apply to uses, we
|
||||
# are forced to specify calls to the CI ourselves.
|
||||
# See https://github.com/orgs/community/discussions/25742
|
||||
- run: tree-sitter generate
|
||||
working-directory: ./tree-sitter-p4
|
||||
- run: swift build
|
||||
- run: swift test
|
||||
library-format-tests:
|
||||
name: Library Format Tests
|
||||
runs-on: macos-latest
|
||||
runs-on: ubuntu-build
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: tree-sitter/setup-action@v2
|
||||
with:
|
||||
tree-sitter-ref: "master"
|
||||
- uses: swift-actions/setup-swift@v3
|
||||
with:
|
||||
swift-version: "6.2.4"
|
||||
swift-version: "main-snapshot"
|
||||
skip-verify-signature: true
|
||||
- run: tree-sitter generate
|
||||
working-directory: ./tree-sitter-p4
|
||||
- run: ./ci/format.sh
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
on: [workflow_call]
|
||||
|
||||
name: Install Treesitter
|
||||
|
||||
jobs:
|
||||
install-treesitter:
|
||||
name: Install Treesitter
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: env
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: tree-sitter/setup-action@v2
|
||||
with:
|
||||
install-cli: true
|
||||
- run: ls -latr /opt/hostedtoolcache/tree-sitter/cli/bin/
|
||||
generate-grammar:
|
||||
name: Install Treesitter
|
||||
runs-on: ubuntu-latest
|
||||
needs: install-treesitter
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: ls -latr /opt/hostedtoolcache/tree-sitter/cli/bin/
|
||||
- run: /opt/hostedtoolcache/tree-sitter/cli/bin/tree-sitter generate
|
||||
working-directory: ./tree-sitter-p4
|
||||
|
||||
+22
-4
@@ -1,13 +1,22 @@
|
||||
{
|
||||
"originHash" : "3969417c2a67000e225174da55741dc4261b615b990ae4ce381417f06c5e9099",
|
||||
"originHash" : "87d6d60d6a77921d74305adbe82f77e088a6e06d44cb3db30aa4d25c864181fd",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "swift-argument-parser",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-argument-parser",
|
||||
"state" : {
|
||||
"revision" : "626b5b7b2f45e1b0b1c6f4a309296d1d21d7311b",
|
||||
"version" : "1.7.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-docc-plugin",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-docc-plugin",
|
||||
"state" : {
|
||||
"revision" : "3e4f133a77e644a5812911a0513aeb7288b07d06",
|
||||
"version" : "1.4.5"
|
||||
"revision" : "647c708be89f834fa6a6d4945442793a77ddf5b6",
|
||||
"version" : "1.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -28,13 +37,22 @@
|
||||
"version" : "602.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-system",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-system",
|
||||
"state" : {
|
||||
"revision" : "7c6ad0fc39d0763e0b699210e4124afd5041c5df",
|
||||
"version" : "1.6.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-tree-sitter",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/tree-sitter/swift-tree-sitter",
|
||||
"state" : {
|
||||
"branch" : "main",
|
||||
"revision" : "769f4770fe8197e2f4ce810375e9b21398ae36d5"
|
||||
"revision" : "f97df585296977d8fcaf644cbde567151d1367b8"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+25
-20
@@ -5,25 +5,21 @@ import CompilerPluginSupport
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "p4rse",
|
||||
name: "p4ce",
|
||||
platforms: [.iOS(.v17), .macOS(.v13)],
|
||||
products: [
|
||||
// Products define the executables and libraries a package produces, making them visible to other packages.
|
||||
.library(
|
||||
name: "P4Compiler",
|
||||
targets: ["P4Compiler"]
|
||||
name: "P4Parser",
|
||||
targets: ["P4Parser"]
|
||||
),
|
||||
.library(
|
||||
name: "Common",
|
||||
targets: ["Common"]
|
||||
),
|
||||
.library(
|
||||
name: "P4Lang",
|
||||
targets: ["P4Lang"]
|
||||
),
|
||||
.library(
|
||||
name: "P4Runtime",
|
||||
targets: ["P4Runtime"]
|
||||
name: "P4CodeGen",
|
||||
targets: ["P4CodeGen"]
|
||||
),
|
||||
],
|
||||
dependencies: [
|
||||
@@ -31,6 +27,9 @@ let package = Package(
|
||||
.package(url: "https://github.com/tree-sitter/swift-tree-sitter", revision: "main"),
|
||||
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.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-system", from: "1.6.1"),
|
||||
//.package(url: "https://github.com/apple/swift-protobuf.git", from: "1.27.0"),
|
||||
],
|
||||
targets: [
|
||||
.macro(
|
||||
@@ -41,15 +40,13 @@ let package = Package(
|
||||
],
|
||||
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")]),
|
||||
.target(
|
||||
name: "P4Compiler",
|
||||
name: "P4Parser",
|
||||
dependencies: [
|
||||
.product(name: "SwiftTreeSitter", package: "swift-tree-sitter"),
|
||||
.product(name: "SwiftTreeSitterLayer", package: "swift-tree-sitter"),
|
||||
.product(name: "TreeSitterP4", package: "tree-sitter-p4"),
|
||||
.target(name: "TreeSitterExtensions"),
|
||||
.target(name: "Common"),
|
||||
.target(name: "P4Lang"),
|
||||
.target(name: "P4Runtime"),
|
||||
],
|
||||
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")],
|
||||
),
|
||||
@@ -62,20 +59,28 @@ let package = Package(
|
||||
),
|
||||
.target(
|
||||
name: "Common",
|
||||
dependencies: ["Macros"],
|
||||
dependencies: [
|
||||
"Macros",
|
||||
.product(name: "SystemPackage", package: "swift-system"),
|
||||
.product(name: "SwiftTreeSitter", package: "swift-tree-sitter"),
|
||||
|
||||
],
|
||||
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")],
|
||||
),
|
||||
.target(
|
||||
name: "P4Lang",
|
||||
dependencies: ["Common"]
|
||||
),
|
||||
.target(
|
||||
name: "P4Runtime",
|
||||
dependencies: ["P4Lang", "Common"]
|
||||
name: "P4CodeGen",
|
||||
dependencies: [
|
||||
"P4Parser", "Common",
|
||||
//.product(name: "SwiftProtobuf", package: "swift-protobuf"),
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
name: "Tests",
|
||||
dependencies: ["P4Compiler", "P4Runtime", "P4Lang", "Macros", "TreeSitterExtensions", "Common"],
|
||||
dependencies: [
|
||||
"P4Parser", "P4CodeGen", "Macros", "TreeSitterExtensions", "Common",
|
||||
.product(name: "SystemPackage", package: "swift-system"),
|
||||
],
|
||||
//swiftSettings: [.enableExperimentalFeature("CodeItemMacros"), .unsafeFlags(["-Xfrontend", "-dump-macro-expansions"])],
|
||||
swiftSettings: [.enableExperimentalFeature("CodeItemMacros")],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,12 +1,43 @@
|
||||
## P4CE: P4 Continuous Evolution
|
||||
## 🏎️ GP4: Generalized P4
|
||||
|
||||
_P4CE_[^pronounce] is a P4 parser and runtime written in Swift that supports an _evolved_ version of P4.
|
||||
"P4 is a high-level language for programming protocol-independent packet processors"[^1] and it is awesome. The language is robust and includes many features that make writing
|
||||
|
||||
[^pronounce]: The acronym is pronounced "p force".
|
||||
- _packet_ parsing pipelines easy to write,
|
||||
- _packet_ transformation pipelines easy to write,
|
||||
- _packet_ routing pipelines easy to write.
|
||||
|
||||
### Evolved How?
|
||||
The adjacent ecosystem is great, too: There are now myriad tools available that make it possible to write applications to configure the _packet_ parsing, transformation and routing pipelines written in P4.
|
||||
|
||||
Coming soon.
|
||||
For example, a developer could use P4 to write code that parses raw bytes received from the network into a structured representation of headers/payloads used to transmit those bytes. A developer could use P4 to write a tool that designates the fields of the parsed packet that a network administrator could name when definining configuration for modifying (or not) a parsed packet and defining configuration about how (even _if_) to route the packet. In such a scenario, the system administrator might use a CLI and write
|
||||
|
||||
```console
|
||||
PI CLI> table_add ipv4_lpm 10.0.0.1/24 => set_nhop 10.0.0.1 1
|
||||
```
|
||||
|
||||
which the system running the P4 code written by the developer could read when making a routing decision. Later, if the system administrator wanted to change the way packets were routed, they could use the CLI and write
|
||||
|
||||
|
||||
```console
|
||||
PI CLI> table_add ipv4_lpm 10.0.0.1/24 => set_nhop 10.0.0.2 1
|
||||
```
|
||||
|
||||
and the system running the P4 code written by the developer would immediately start to route _packets_ differently.
|
||||
|
||||
So, why is _packet_ highlighted?
|
||||
|
||||
|
||||
Because we believe that thinking about P4 in exactly the way described above -- after dropping the word _packet_ -- makes it a perfect system for building general-purpose parsing, transformation and routing pipelines. Although there are many ways such tools could be used, we believe that a generalized P4 system would be a perfect candidate for writing online, streaming ETL pipelines (c.f., [Apache Kafka](https://kafka.apache.org/)) or log filter/classification pipelines (c.f., [Sigma](https://sigmahq.io/)).
|
||||
|
||||
Our goal is to build generalized P4 (GP4): The best of P4 and a little more.
|
||||
|
||||
Please join us!
|
||||
|
||||
[^1]: Pat Bosshart, Dan Daly, Glen Gibb, Martin Izzard, Nick McKeown, Jennifer Rexford, Cole Schlesinger, Dan Talayco, Amin Vahdat, George Varghese, and David Walker. 2014. P4: programming protocol-independent packet processors. SIGCOMM Comput. Commun. Rev. 44, 3 (July 2014), 87–95. https://doi.org/10.1145/2656877.2656890
|
||||
|
||||
### Benefits
|
||||
|
||||
1. Reuse the extensive existing work from the P4 community.
|
||||
2. ... more coming soon.
|
||||
|
||||
### Status
|
||||
|
||||
@@ -85,11 +116,14 @@ We will try to maintain the following headline format for commit messages:
|
||||
where `<component>` is one of:
|
||||
|
||||
1. `grammar`: For the tree-sitter-based grammar.
|
||||
2. `compiler`: For the Swift-based P4 compiler of tree-sitter-based-parser parsed programs into AST.
|
||||
3. `runtime`: For the Swift-based P4 interpreter.
|
||||
4. `common`: For any Swift-based components common to the entire project (and macros).
|
||||
5. `documentation`: For any documentation updates.
|
||||
6. `testing`: For Swift-based tests.
|
||||
1. `compiler`: For the Swift-based P4 compiler of tree-sitter-based-parser parsed programs into AST.
|
||||
1. `language`: For the Swift-based AST of a compiled P4 program.
|
||||
1. `runtime`: For the Swift-based P4 interpreter.
|
||||
1. `common`: For any Swift-based components common to the entire project (and macros).
|
||||
1. `documentation`: For any documentation updates.
|
||||
1. `testing`: For Swift-based tests.
|
||||
1. `cli`: For Cli components.
|
||||
1. `codegen`: For code generation components.
|
||||
|
||||
where `<subcomponent>` can be more free-form and `<change>` is a pithy description of the changes in the commit.
|
||||
|
||||
|
||||
@@ -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: P4Type) -> Bool in
|
||||
switch tipe.dataType(){
|
||||
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: P4DataType) -> 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))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
// 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 ArgumentParser
|
||||
import Common
|
||||
import P4Compiler
|
||||
import P4Runtime
|
||||
import SystemPackage
|
||||
|
||||
@main
|
||||
struct Cli: ParsableCommand {
|
||||
@Flag(help: "Disable ANSI-stylized output.") var plain: Int
|
||||
|
||||
static let configuration = CommandConfiguration(
|
||||
abstract: "P4CE compiler, interpreter and debugger.",
|
||||
subcommands: [Preprocess.self, Compile.self, CodeGen.self])
|
||||
}
|
||||
|
||||
struct CliOptions: ParsableArguments {
|
||||
@ArgumentParser.Argument(help: "File to compile.") // Have to be explicit because Common has an Argument, too!
|
||||
var path: String
|
||||
|
||||
@Option(name: [.customShort("I")], help: "Search paths.")
|
||||
var search: [String] = []
|
||||
}
|
||||
|
||||
extension Cli {
|
||||
struct Preprocess: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(abstract: "Compile P4CE code.")
|
||||
|
||||
@ParentCommand var parent: Cli
|
||||
|
||||
@OptionGroup var options: CliOptions
|
||||
|
||||
mutating func run() {
|
||||
let sm = SourceManager(options.search.map { FilePath($0) })
|
||||
let prep = SourceCodePreprocessor(sm)
|
||||
let file = FilePath(options.path)
|
||||
|
||||
let formatter: any Formattable =
|
||||
if parent.plain != 0 {
|
||||
FormatterPlain()
|
||||
} else {
|
||||
FormatterAnsi()
|
||||
}
|
||||
|
||||
let maybe_source = prep.preprocess(file)
|
||||
guard case .Ok(let source) = maybe_source else {
|
||||
print(ErrorWithLabel("Preprocessor Error", maybe_source.error()!).format(formatter))
|
||||
return
|
||||
}
|
||||
|
||||
print(source.getSource())
|
||||
}
|
||||
}
|
||||
}
|
||||
extension Cli {
|
||||
struct Compile: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(abstract: "Compile P4CE code.")
|
||||
|
||||
@ParentCommand var parent: Cli
|
||||
|
||||
@OptionGroup var options: CliOptions
|
||||
|
||||
mutating func run() {
|
||||
let sm = SourceManager(options.search.map { FilePath($0) })
|
||||
let prep = SourceCodePreprocessor(sm)
|
||||
let file = FilePath(options.path)
|
||||
|
||||
let formatter: any Formattable =
|
||||
if parent.plain != 0 {
|
||||
FormatterPlain()
|
||||
} else {
|
||||
FormatterAnsi()
|
||||
}
|
||||
|
||||
let maybe_source = prep.preprocess(file)
|
||||
guard case .Ok(let source) = maybe_source else {
|
||||
print(ErrorWithLabel("Preprocessor Error", maybe_source.error()!).format(formatter))
|
||||
return
|
||||
}
|
||||
|
||||
let maybe_program = SpecialCompilers.ProgramCompiler.Compile(source.getSource())
|
||||
guard case .Ok(_) = maybe_program else {
|
||||
let feedback = CompilationFeedback(source, [maybe_program.error()!], formatter)
|
||||
print(feedback.feedback)
|
||||
return
|
||||
}
|
||||
|
||||
print(
|
||||
formatter.formatWithStyle(
|
||||
"Success", Style(StyleColor.Green, [StyleFormat.Underline])))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Cli {
|
||||
struct CodeGen: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(abstract: "Generate P4CE code.")
|
||||
|
||||
@ParentCommand var parent: Cli
|
||||
|
||||
@OptionGroup var options: CliOptions
|
||||
|
||||
mutating func run() {
|
||||
let sm = SourceManager(options.search.map { FilePath($0) })
|
||||
let prep = SourceCodePreprocessor(sm)
|
||||
let file = FilePath(options.path)
|
||||
|
||||
let formatter: any Formattable =
|
||||
if parent.plain != 0 {
|
||||
FormatterPlain()
|
||||
} else {
|
||||
FormatterAnsi()
|
||||
}
|
||||
|
||||
let maybe_source = prep.preprocess(file)
|
||||
guard case .Ok(let source) = maybe_source else {
|
||||
print(ErrorWithLabel("Preprocessor Error", maybe_source.error()!).format(formatter))
|
||||
return
|
||||
}
|
||||
|
||||
let maybe_program = SpecialCompilers.ProgramCompiler.Compile(source.getSource())
|
||||
guard case .Ok(let program) = maybe_program else {
|
||||
print(ErrorWithLabel("Compiler Error", maybe_source.error()!).format(formatter))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
let maybe_codegen = P4Runtime.CodeGenerator().codeGen(program)
|
||||
guard case .Ok(let codegen) = maybe_codegen else {
|
||||
let formatter = FormatterAnsi()
|
||||
print(ErrorWithLabel("Code Generation Error", maybe_codegen.error()!).format(formatter))
|
||||
return
|
||||
}
|
||||
|
||||
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: P4Type
|
||||
|
||||
public init(
|
||||
identifier: Identifier, withType type: P4Type
|
||||
) {
|
||||
self.name = identifier
|
||||
self.type = type
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Parameter: \(self.name) with type \(self.type)"
|
||||
}
|
||||
|
||||
/// Calculate whether the `argument` is compatible with this parameter.
|
||||
public func compatible(_ argument: Argument) -> Bool {
|
||||
let arg_type = argument.argument.type()
|
||||
|
||||
// If the parameter is (in)out, then the argument must be an lvalue.
|
||||
if let param_direction = self.type.direction(),
|
||||
param_direction == Direction.In || param_direction == Direction.InOut
|
||||
{
|
||||
if !(argument.argument is EvaluatableLValueExpression) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return arg_type.dataType().eq(rhs: self.type.dataType())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct ParameterList: CustomStringConvertible, Equatable {
|
||||
public static func == (lhs: ParameterList, rhs: ParameterList) -> Bool {
|
||||
if lhs.parameters.count != rhs.parameters.count {
|
||||
return false
|
||||
}
|
||||
|
||||
return 0
|
||||
== zip(lhs.parameters, rhs.parameters).count { (lparam, rparam) in
|
||||
return lparam != rparam
|
||||
}
|
||||
}
|
||||
|
||||
public var parameters: [Parameter]
|
||||
|
||||
public init() {
|
||||
self.parameters = Array()
|
||||
}
|
||||
|
||||
public init(_ parameters: [Parameter]) {
|
||||
self.parameters = parameters
|
||||
}
|
||||
|
||||
public func addParameter(_ parameter: Parameter) -> ParameterList {
|
||||
return ParameterList(self.parameters + [parameter])
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
let parameters = self.parameters.map { parameter in
|
||||
parameter.description
|
||||
}.joined(separator: ";")
|
||||
return "Parameter list: \(parameters)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct ArgumentList {
|
||||
public let arguments: [Argument]
|
||||
|
||||
public init(_ arguments: [Argument] = []) {
|
||||
self.arguments = arguments
|
||||
}
|
||||
|
||||
public func compatible(_ parameters: ParameterList) -> Result<()> {
|
||||
if self.arguments.count != parameters.parameters.count {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage:
|
||||
"\(self.arguments.count) arguments found but \(parameters.parameters.count) required"))
|
||||
}
|
||||
|
||||
for (arg, param) in zip(self.arguments, parameters.parameters) {
|
||||
let arg_index = arg.index
|
||||
let arg_type = arg.argument.type()
|
||||
if !param.compatible(arg) {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage:
|
||||
"Argument \(arg_index)'s type (\(arg_type)) is incompatible with the parameter type (\(param.type))"
|
||||
))
|
||||
}
|
||||
}
|
||||
return .Ok(())
|
||||
}
|
||||
|
||||
public func addArgument(_ argument: Argument) -> ArgumentList {
|
||||
return ArgumentList(self.arguments + [argument])
|
||||
}
|
||||
|
||||
public func count() -> Int {
|
||||
return self.arguments.count
|
||||
}
|
||||
}
|
||||
|
||||
public struct Argument {
|
||||
public let index: Int
|
||||
public let argument: EvaluatableExpression
|
||||
|
||||
public init(_ argument: EvaluatableExpression, atIndex index: Int) {
|
||||
self.argument = argument
|
||||
self.index = index
|
||||
}
|
||||
}
|
||||
@@ -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<P4Type>
|
||||
|
||||
/// Scopes that resolve variable identifiers to their types.
|
||||
public typealias VarTypeScopes = Scopes<P4Type>
|
||||
|
||||
/// A scope that resolves type identifiers to their types.
|
||||
public typealias TypeTypeScope = Scope<P4DataType>
|
||||
|
||||
/// Scopes that resolve type identifiers to their types.
|
||||
public typealias TypeTypeScopes = Scopes<P4DataType>
|
||||
@@ -0,0 +1,164 @@
|
||||
// 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 Error: Errorable, Equatable, CustomStringConvertible {
|
||||
public func format(_ formatter: any Formattable) -> String {
|
||||
return self._msg
|
||||
}
|
||||
|
||||
public func msg() -> String {
|
||||
return self._msg
|
||||
}
|
||||
|
||||
public func append(error: any Errorable) -> any Errorable {
|
||||
return Errors(self, error)
|
||||
}
|
||||
|
||||
let _msg: String
|
||||
|
||||
public init(withMessage msg: String) {
|
||||
self._msg = msg
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return self._msg
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
public func trimmingPostfix(_ in_: String.Element) -> String {
|
||||
return String(self.reversed().drop { $0 == in_ }.reversed())
|
||||
}
|
||||
}
|
||||
|
||||
public struct ErrorWithLocation: Errorable, Equatable, CustomStringConvertible {
|
||||
|
||||
public func format(_ formatter: any Formattable, _ sc: SourceCode) -> String {
|
||||
guard let (fp, source, prior, after) = sc.getSourceSnippet(location: self.location, context: 5)
|
||||
else {
|
||||
return self.format(formatter)
|
||||
}
|
||||
|
||||
let prior_snipped = prior.trimmingPrefix(["\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))
|
||||
+ "In \(include_list), there was an error: \n..." + prior_snipped
|
||||
+ formatter.formatWithStyle(source, Style(.none, [StyleFormat.Underline])) + after_snipped
|
||||
+ "...\n"
|
||||
+ self._msg
|
||||
}
|
||||
|
||||
public func format(_ formatter: any Formattable) -> String {
|
||||
let bold_red = Style(StyleColor.Red, [StyleFormat.Bold])
|
||||
let formatted_location = formatter.formatWithStyle(self.location.description, bold_red)
|
||||
return formatted_location + ": " + self._msg
|
||||
}
|
||||
|
||||
public func msg() -> String {
|
||||
return self.description
|
||||
}
|
||||
|
||||
public func append(error: any Errorable) -> any Errorable {
|
||||
return Errors(self, error)
|
||||
}
|
||||
|
||||
let _msg: String
|
||||
|
||||
let location: SourceLocation
|
||||
|
||||
public init(sourceLocation location: SourceLocation, withError msg: String) {
|
||||
self._msg = msg
|
||||
self.location = location
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "\(self.location): \(self._msg)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct Errors: Errorable, CustomStringConvertible {
|
||||
public func format(_ formatter: any Formattable) -> String {
|
||||
self.errors.map { error in
|
||||
error.format(formatter)
|
||||
}.joined(separator: "\n")
|
||||
}
|
||||
|
||||
public func msg() -> String {
|
||||
return self.description
|
||||
}
|
||||
|
||||
public func append(error: any Errorable) -> any Errorable {
|
||||
return Errors(self.errors + [error])
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return self.errors.map { error in
|
||||
return error.msg()
|
||||
}.joined(separator: ";")
|
||||
}
|
||||
|
||||
public let errors: [any Errorable]
|
||||
|
||||
init(_ errors: [any Errorable]) {
|
||||
self.errors = errors
|
||||
}
|
||||
|
||||
public init(_ e1: any Errorable, _ e2: any Errorable) {
|
||||
self.errors = [e1, e2]
|
||||
}
|
||||
}
|
||||
|
||||
public struct ErrorWithLabel: Errorable {
|
||||
let label: String
|
||||
let error: any Errorable
|
||||
|
||||
public func format(_ formatter: any Formattable) -> String {
|
||||
let green = Style(StyleColor.Green)
|
||||
let formatted_label = formatter.formatWithStyle(self.label, green)
|
||||
return formatted_label + ": " + self.error.format(formatter)
|
||||
}
|
||||
|
||||
public init(_ label: String, _ error: any Errorable) {
|
||||
self.label = label
|
||||
self.error = error
|
||||
}
|
||||
|
||||
public func format() -> String {
|
||||
return self.description
|
||||
}
|
||||
|
||||
public func msg() -> String {
|
||||
return self.description
|
||||
}
|
||||
|
||||
public func append(error: any Errorable) -> any Errorable {
|
||||
return Errors(self, error)
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "\(self.label): \(self.error.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: Error?
|
||||
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() -> Error? {
|
||||
return self.error
|
||||
}
|
||||
|
||||
public func setError(error: Error) -> 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() -> P4Type
|
||||
func parameters() -> ParameterList
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// 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 enum StyleColor {
|
||||
case Red
|
||||
case Blue
|
||||
case Green
|
||||
}
|
||||
|
||||
public enum StyleFormat {
|
||||
case Underline
|
||||
case Bold
|
||||
}
|
||||
|
||||
public struct Style: Equatable {
|
||||
let color: StyleColor?
|
||||
let format: [StyleFormat]
|
||||
|
||||
public init(_ color: StyleColor?, _ format: [StyleFormat] = []) {
|
||||
self.color = color
|
||||
self.format = format
|
||||
}
|
||||
|
||||
public func update(setColor color: StyleColor) -> Style {
|
||||
return Style(color, self.format)
|
||||
}
|
||||
|
||||
public func removeColor() -> Style {
|
||||
return Style(nil, self.format)
|
||||
}
|
||||
|
||||
public func update(addFormat format: StyleFormat) -> Style {
|
||||
return if self.format.contains(format) {
|
||||
Style(self.color, self.format)
|
||||
} else {
|
||||
Style(self.color, self.format + [format])
|
||||
}
|
||||
}
|
||||
|
||||
public func update(removeFormat format: StyleFormat) -> Style {
|
||||
let new_format = self.format.filter { existing_format in
|
||||
existing_format != format
|
||||
}
|
||||
return Style(self.color, new_format)
|
||||
}
|
||||
|
||||
public func getColor() -> StyleColor? {
|
||||
return self.color
|
||||
}
|
||||
|
||||
public func getFormat() -> [StyleFormat] {
|
||||
return self.format
|
||||
}
|
||||
}
|
||||
|
||||
public struct FormatterPlain: Formattable {
|
||||
public init() {}
|
||||
public func formatWithStyle(_ value: String, _ style: Style) -> String {
|
||||
return value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct FormatterDelimited: Formattable {
|
||||
let start: String
|
||||
let end: String
|
||||
|
||||
public init(_ start: String, _ end: String) {
|
||||
self.start = start
|
||||
self.end = end
|
||||
}
|
||||
|
||||
public func formatWithStyle(_ value: String, _ style: Style) -> String {
|
||||
return self.start + value + self.end
|
||||
}
|
||||
}
|
||||
|
||||
public struct FormatterAnsi: Formattable {
|
||||
|
||||
public init() {}
|
||||
|
||||
let startFormat: String = "\u{1B}["
|
||||
let resetFormat: String = "\u{1B}[0m"
|
||||
|
||||
let colorMap = [
|
||||
StyleColor.Red: "31",
|
||||
StyleColor.Green: "32",
|
||||
StyleColor.Blue: "34",
|
||||
]
|
||||
|
||||
let styleMap = [
|
||||
StyleFormat.Underline: "4",
|
||||
StyleFormat.Bold: "1",
|
||||
]
|
||||
|
||||
public func formatWithStyle(_ value: String, _ style: Style) -> String {
|
||||
let color =
|
||||
if let color = style.getColor() {
|
||||
self.colorMap[color]!
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
let style = style.getFormat().map { format in
|
||||
String(self.styleMap[format]!)
|
||||
}.joined(separator: ";")
|
||||
|
||||
if color.isEmpty && style.isEmpty {
|
||||
return value
|
||||
}
|
||||
|
||||
let code = startFormat + color + ((!color.isEmpty && !style.isEmpty) ? ";" : "") + style + "m"
|
||||
|
||||
return code + value + resetFormat
|
||||
}
|
||||
}
|
||||
@@ -50,14 +50,14 @@ public class Identifier: CustomStringConvertible, Comparable, Hashable {
|
||||
|
||||
/// A P4 identifier
|
||||
public class TypedIdentifier: Identifier {
|
||||
public var type: P4Type
|
||||
public var type: P4QualifiedType
|
||||
|
||||
public init(name: String, withType type: P4Type) {
|
||||
public init(name: String, withType type: P4QualifiedType) {
|
||||
self.type = type
|
||||
super.init(name: name)
|
||||
}
|
||||
|
||||
public init(id: Identifier, withType type: P4Type) {
|
||||
public init(id: Identifier, withType type: P4QualifiedType) {
|
||||
self.type = type
|
||||
super.init(id: id)
|
||||
}
|
||||
@@ -107,7 +107,7 @@ public struct P4StructFields: Sequence, CustomStringConvertible, Equatable {
|
||||
}.joined(separator: ",")
|
||||
}
|
||||
|
||||
public func get_field_type(_ field: Identifier) -> P4Type? {
|
||||
public func get_field_type(_ field: Identifier) -> P4QualifiedType? {
|
||||
if let found_field = self.fields.makeIterator().first(where: { current in
|
||||
return current.name == field.name
|
||||
}) {
|
||||
@@ -135,7 +135,7 @@ public struct P4StructFields: Sequence, CustomStringConvertible, Equatable {
|
||||
}
|
||||
|
||||
/// The type for a P4 struct
|
||||
public struct P4Struct: P4DataType {
|
||||
public struct P4Struct: P4Type {
|
||||
|
||||
public let name: Identifier
|
||||
public let fields: P4StructFields
|
||||
@@ -154,7 +154,7 @@ public struct P4Struct: P4DataType {
|
||||
return "Struct \(self.name) with fields: \(self.fields)"
|
||||
}
|
||||
|
||||
public func eq(rhs: P4DataType) -> Bool {
|
||||
public func eq(rhs: P4Type) -> Bool {
|
||||
return if let struct_rhs = rhs as? P4Struct {
|
||||
struct_rhs.name == self.name
|
||||
} else {
|
||||
@@ -162,14 +162,14 @@ public struct P4Struct: P4DataType {
|
||||
}
|
||||
}
|
||||
|
||||
public func def() -> any P4DataValue {
|
||||
public func def() -> P4DataValue? {
|
||||
return P4StructValue(withType: self)
|
||||
}
|
||||
}
|
||||
|
||||
/// An instance of a P4 struct
|
||||
public class P4StructValue: P4DataValue {
|
||||
public func type() -> P4DataType {
|
||||
public func type() -> P4Type {
|
||||
return self.stype
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ public class P4StructValue: P4DataValue {
|
||||
}
|
||||
|
||||
// Now that we know that the field names match, do the values match?
|
||||
if !op(left_field_value.dataValue(), right_field_value.dataValue()) {
|
||||
if !op(left_field_value?.dataValue(), right_field_value?.dataValue()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -297,19 +297,22 @@ public class P4StructValue: P4DataValue {
|
||||
}
|
||||
|
||||
public let stype: P4Struct
|
||||
public let values: [P4Value]
|
||||
public let values: [P4Value?]
|
||||
|
||||
public convenience init(withType type: P4Struct) {
|
||||
self.init(withType: type, andInitializers: [])
|
||||
}
|
||||
|
||||
public init(withType type: P4Struct, andInitializers initializers: [P4Value?]) {
|
||||
let values = zip(0..<type.fields.count(), type.fields.fields).map { (index, field) in
|
||||
let values: [P4Value?] = zip(0..<type.fields.count(), type.fields.fields).map {
|
||||
(index, field) in
|
||||
// If there is an initializer for the field, then use it.
|
||||
if index < initializers.count, let initializer = initializers[index] {
|
||||
initializer
|
||||
} else {
|
||||
// Otherwise, set a default!
|
||||
// Otherwise, try to set a default!
|
||||
// Note: If the field type does not have a default, then the value
|
||||
// will be a none. Pretty cool!
|
||||
field.type.def()
|
||||
}
|
||||
}
|
||||
@@ -350,25 +353,25 @@ public class P4StructValue: P4DataValue {
|
||||
}
|
||||
|
||||
/// A P4 boolean type
|
||||
public struct P4Boolean: P4DataType {
|
||||
public struct P4Boolean: P4Type {
|
||||
public init() {}
|
||||
public var description: String {
|
||||
return "Boolean"
|
||||
}
|
||||
public func eq(rhs: P4DataType) -> Bool {
|
||||
public func eq(rhs: P4Type) -> Bool {
|
||||
return switch rhs {
|
||||
case is P4Boolean: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
public func def() -> any P4DataValue {
|
||||
public func def() -> P4DataValue? {
|
||||
return P4BooleanValue(withValue: false)
|
||||
}
|
||||
}
|
||||
|
||||
/// An instance of a P4 boolean
|
||||
public class P4BooleanValue: P4DataValue {
|
||||
public func type() -> any P4DataType {
|
||||
public func type() -> any P4Type {
|
||||
return P4Boolean()
|
||||
}
|
||||
|
||||
@@ -421,32 +424,47 @@ public class P4BooleanValue: P4DataValue {
|
||||
}
|
||||
}
|
||||
|
||||
public enum BitWidth: Equatable {
|
||||
case Infinite
|
||||
case Width(Int)
|
||||
}
|
||||
|
||||
/// A P4 int type
|
||||
public struct P4Int: P4DataType {
|
||||
public init() {}
|
||||
public struct P4Int: P4Type {
|
||||
let width: BitWidth
|
||||
|
||||
public init(_ width: BitWidth = BitWidth.Infinite) {
|
||||
self.width = width
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Int"
|
||||
return "Int (width: \(self.width))"
|
||||
}
|
||||
public func eq(rhs: P4DataType) -> Bool {
|
||||
|
||||
public func eq(rhs: P4Type) -> Bool {
|
||||
return switch rhs {
|
||||
case is P4Int: true
|
||||
case let rrhs as P4Int: rrhs.width == self.width
|
||||
default: false
|
||||
}
|
||||
}
|
||||
public func def() -> any P4DataValue {
|
||||
|
||||
public func def() -> P4DataValue? {
|
||||
return P4IntValue(withValue: 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// An instance of a P4 integer
|
||||
public class P4IntValue: P4DataValue {
|
||||
public func type() -> P4DataType {
|
||||
return P4Int()
|
||||
|
||||
let int_type: P4Int
|
||||
|
||||
public func type() -> P4Type {
|
||||
return int_type
|
||||
}
|
||||
|
||||
let value: Int
|
||||
public init(withValue value: Int) {
|
||||
public init(withValue value: Int, andWidth width: BitWidth = BitWidth.Infinite) {
|
||||
self.int_type = P4Int(width)
|
||||
self.value = value
|
||||
}
|
||||
|
||||
@@ -495,24 +513,24 @@ public class P4IntValue: P4DataValue {
|
||||
}
|
||||
|
||||
/// A P4 string type
|
||||
public struct P4String: P4DataType {
|
||||
public struct P4String: P4Type {
|
||||
public init() {}
|
||||
public var description: String {
|
||||
return "String"
|
||||
}
|
||||
public func eq(rhs: any P4DataType) -> Bool {
|
||||
public func eq(rhs: any P4Type) -> Bool {
|
||||
return switch rhs {
|
||||
case is P4String: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
public func def() -> any P4DataValue {
|
||||
public func def() -> P4DataValue? {
|
||||
return P4StringValue(withValue: "")
|
||||
}
|
||||
}
|
||||
/// An instance of a P4 string
|
||||
public class P4StringValue: P4DataValue {
|
||||
public func type() -> any P4DataType {
|
||||
public func type() -> any P4Type {
|
||||
return P4String()
|
||||
}
|
||||
|
||||
@@ -565,14 +583,14 @@ public class Packet {
|
||||
}
|
||||
|
||||
/// A P4 array type
|
||||
public struct P4Array: P4DataType {
|
||||
public init(withValueType vtype: P4Type) {
|
||||
public struct P4Array: P4Type {
|
||||
public init(withValueType vtype: P4QualifiedType) {
|
||||
self.vtype = vtype
|
||||
}
|
||||
|
||||
let vtype: P4Type
|
||||
let vtype: P4QualifiedType
|
||||
|
||||
public func value_type() -> P4Type {
|
||||
public func value_type() -> P4QualifiedType {
|
||||
return self.vtype
|
||||
}
|
||||
|
||||
@@ -580,28 +598,28 @@ public struct P4Array: P4DataType {
|
||||
return "Array"
|
||||
}
|
||||
|
||||
public func eq(rhs: any P4DataType) -> Bool {
|
||||
public func eq(rhs: any P4Type) -> Bool {
|
||||
return switch rhs {
|
||||
case is P4Array: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
public func def() -> P4DataValue {
|
||||
public func def() -> P4DataValue? {
|
||||
return P4ArrayValue(withType: self.vtype, withValue: [])
|
||||
}
|
||||
}
|
||||
|
||||
/// An instance of a P4 array
|
||||
public class P4ArrayValue: P4DataValue {
|
||||
public func type() -> any P4DataType {
|
||||
public func type() -> any P4Type {
|
||||
return P4Array(withValueType: self.vtype)
|
||||
}
|
||||
|
||||
let value: [P4Value]
|
||||
let vtype: P4Type
|
||||
let vtype: P4QualifiedType
|
||||
|
||||
public init(withType type: P4Type, withValue value: [P4Value]) {
|
||||
public init(withType type: P4QualifiedType, withValue value: [P4Value]) {
|
||||
self.vtype = type
|
||||
self.value = value
|
||||
}
|
||||
@@ -663,14 +681,14 @@ public class P4ArrayValue: P4DataValue {
|
||||
}
|
||||
|
||||
/// A P4 set type
|
||||
public struct P4Set: P4DataType {
|
||||
public init(withSetType stype: P4Type) {
|
||||
public struct P4Set: P4Type {
|
||||
public init(withSetType stype: P4QualifiedType) {
|
||||
self.stype = stype
|
||||
}
|
||||
|
||||
let stype: P4Type
|
||||
let stype: P4QualifiedType
|
||||
|
||||
public func set_type() -> P4Type {
|
||||
public func set_type() -> P4QualifiedType {
|
||||
return self.stype
|
||||
}
|
||||
|
||||
@@ -678,22 +696,25 @@ public struct P4Set: P4DataType {
|
||||
return "P4Set"
|
||||
}
|
||||
|
||||
public func eq(rhs: any P4DataType) -> Bool {
|
||||
public func eq(rhs: any P4Type) -> Bool {
|
||||
return switch rhs {
|
||||
// If rhs is a set type, then they are the same if the types in the set are the same.
|
||||
case let srhs as P4Set: srhs.eq(rhs: self.stype.dataType())
|
||||
case let srhs as P4Set: srhs.eq(rhs: self.stype.baseType())
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
public func def() -> P4DataValue {
|
||||
return P4SetValue(withValue: P4Value(self.stype.dataType().def(), self.stype))
|
||||
public func def() -> P4DataValue? {
|
||||
if let base_type_default = self.stype.baseType().def() {
|
||||
return P4SetValue(withValue: P4Value(base_type_default, self.stype))
|
||||
}
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
||||
/// An instance of a P4 set
|
||||
public class P4SetValue: P4DataValue {
|
||||
public func type() -> any P4DataType {
|
||||
public func type() -> any P4Type {
|
||||
return P4Set(withSetType: self.value.type())
|
||||
}
|
||||
|
||||
@@ -744,13 +765,13 @@ public class P4SetValue: P4DataValue {
|
||||
}
|
||||
|
||||
public class P4SetDefaultValue: P4DataValue {
|
||||
public func type() -> P4DataType {
|
||||
public func type() -> P4Type {
|
||||
return P4Set(withSetType: self.stype)
|
||||
}
|
||||
|
||||
let stype: P4Type
|
||||
let stype: P4QualifiedType
|
||||
|
||||
public init(withType type: P4Type) {
|
||||
public init(withType type: P4QualifiedType) {
|
||||
self.stype = type
|
||||
}
|
||||
|
||||
@@ -776,15 +797,15 @@ public class P4SetDefaultValue: P4DataValue {
|
||||
}
|
||||
}
|
||||
|
||||
public struct P4HitMiss: P4DataType {
|
||||
public func eq(rhs: any P4DataType) -> Bool {
|
||||
public struct P4HitMiss: P4Type {
|
||||
public func eq(rhs: any P4Type) -> Bool {
|
||||
return switch rhs {
|
||||
case is P4HitMiss: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
public func def() -> any P4DataValue {
|
||||
public func def() -> P4DataValue? {
|
||||
return P4TableHitMissValue.Miss
|
||||
}
|
||||
|
||||
@@ -794,7 +815,7 @@ public struct P4HitMiss: P4DataType {
|
||||
}
|
||||
|
||||
public enum P4TableHitMissValue: P4DataValue, Equatable, Comparable, CustomStringConvertible {
|
||||
public func type() -> any P4DataType {
|
||||
public func type() -> any P4Type {
|
||||
return P4HitMiss()
|
||||
}
|
||||
|
||||
@@ -52,27 +52,27 @@ public enum Direction: Equatable, CustomStringConvertible {
|
||||
}
|
||||
}
|
||||
|
||||
public enum P4TypeAttribute: Equatable {
|
||||
public enum P4TypeQualifier: Equatable {
|
||||
case Direction(Direction)
|
||||
case Readonly // Not yet used -- here to keep Swift warnings at bay
|
||||
}
|
||||
|
||||
public struct P4TypeAttributes: CustomStringConvertible {
|
||||
let _attributes: [P4TypeAttribute]
|
||||
public struct P4TypeQualifiers: CustomStringConvertible {
|
||||
let _qualifiers: [P4TypeQualifier]
|
||||
|
||||
public init(_ attributes: [P4TypeAttribute]) {
|
||||
self._attributes = attributes
|
||||
public init(_ qualifiers: [P4TypeQualifier]) {
|
||||
self._qualifiers = qualifiers
|
||||
}
|
||||
|
||||
public func direction() -> Direction? {
|
||||
let result = _attributes.firstIndex { attribute in
|
||||
let result = _qualifiers.firstIndex { attribute in
|
||||
return switch attribute {
|
||||
case .Direction(_): true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
return result.flatMap { index in
|
||||
return switch _attributes[index] {
|
||||
return switch _qualifiers[index] {
|
||||
case .Direction(let d): d
|
||||
default: Optional<Direction>.none
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public struct P4TypeAttributes: CustomStringConvertible {
|
||||
}
|
||||
|
||||
public func readOnly() -> Bool {
|
||||
return _attributes.contains { attribute in
|
||||
return _qualifiers.contains { attribute in
|
||||
return switch attribute {
|
||||
case .Readonly: true
|
||||
default: false
|
||||
@@ -88,45 +88,45 @@ public struct P4TypeAttributes: CustomStringConvertible {
|
||||
}
|
||||
}
|
||||
|
||||
public func update(removeAttribute attributeToRemove: P4TypeAttribute) -> P4TypeAttributes {
|
||||
var new_attributes = self._attributes
|
||||
public func update(removeAttribute attributeToRemove: P4TypeQualifier) -> P4TypeQualifiers {
|
||||
var new_attributes = self._qualifiers
|
||||
new_attributes.removeAll { item in
|
||||
return item == attributeToRemove
|
||||
}
|
||||
return P4TypeAttributes(new_attributes)
|
||||
return P4TypeQualifiers(new_attributes)
|
||||
}
|
||||
|
||||
public func update(addAttribute attributeToAdd: P4TypeAttribute) -> P4TypeAttributes {
|
||||
return P4TypeAttributes(self._attributes + [attributeToAdd])
|
||||
public func update(addAttribute attributeToAdd: P4TypeQualifier) -> P4TypeQualifiers {
|
||||
return P4TypeQualifiers(self._qualifiers + [attributeToAdd])
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return self._attributes.map { attribute in
|
||||
return "\(attribute)"
|
||||
return self._qualifiers.map { qualifier in
|
||||
return "\(qualifier)"
|
||||
}.joined(separator: ",")
|
||||
}
|
||||
|
||||
public static func ReadOnly() -> P4TypeAttributes {
|
||||
return P4TypeAttributes([P4TypeAttribute.Readonly])
|
||||
public static func ReadOnly() -> P4TypeQualifiers {
|
||||
return P4TypeQualifiers([P4TypeQualifier.Readonly])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct P4Type: CustomStringConvertible {
|
||||
let _attributes: P4TypeAttributes
|
||||
let _data_type: P4DataType
|
||||
public struct P4QualifiedType: CustomStringConvertible {
|
||||
let _attributes: P4TypeQualifiers
|
||||
let base_type: P4Type
|
||||
|
||||
public init(_ type: P4DataType, _ attributes: P4TypeAttributes = P4TypeAttributes([])) {
|
||||
public init(_ base_type: P4Type, _ attributes: P4TypeQualifiers = P4TypeQualifiers([])) {
|
||||
self._attributes = attributes
|
||||
self._data_type = type
|
||||
self.base_type = base_type
|
||||
}
|
||||
|
||||
public func update(removeAttribute attribute: P4TypeAttribute) -> P4Type {
|
||||
return P4Type(self._data_type, self._attributes.update(removeAttribute: attribute))
|
||||
public func update(removeAttribute attribute: P4TypeQualifier) -> P4QualifiedType {
|
||||
return P4QualifiedType(self.base_type, self._attributes.update(removeAttribute: attribute))
|
||||
}
|
||||
|
||||
public func update(addAttribute attribute: P4TypeAttribute) -> P4Type {
|
||||
return P4Type(self._data_type, self._attributes.update(addAttribute: attribute))
|
||||
public func update(addAttribute attribute: P4TypeQualifier) -> P4QualifiedType {
|
||||
return P4QualifiedType(self.base_type, self._attributes.update(addAttribute: attribute))
|
||||
}
|
||||
|
||||
public func direction() -> Direction? {
|
||||
@@ -137,17 +137,20 @@ public struct P4Type: CustomStringConvertible {
|
||||
return self._attributes.readOnly()
|
||||
}
|
||||
|
||||
public func dataType() -> P4DataType {
|
||||
return self._data_type
|
||||
public func baseType() -> P4Type {
|
||||
return self.base_type
|
||||
}
|
||||
|
||||
public func def() -> P4Value {
|
||||
return P4Value(self._data_type.def(), self)
|
||||
public func def() -> P4Value? {
|
||||
if let default_value = self.base_type.def() {
|
||||
return P4Value(default_value, self)
|
||||
}
|
||||
return .none
|
||||
}
|
||||
|
||||
public func eq(_ rhs: P4Type) -> Bool {
|
||||
public func eq(_ rhs: P4QualifiedType) -> Bool {
|
||||
return self.direction() == rhs.direction() && self.readOnly() == self.readOnly()
|
||||
&& self.dataType().eq(rhs: rhs.dataType())
|
||||
&& self.baseType().eq(rhs: rhs.baseType())
|
||||
}
|
||||
|
||||
public func assignable() -> TypeCheckResults {
|
||||
@@ -163,8 +166,8 @@ public struct P4Type: CustomStringConvertible {
|
||||
return TypeCheckResults.Ok
|
||||
}
|
||||
|
||||
public func assignableFromType(_ rhs: P4Type) -> TypeCheckResults {
|
||||
if !self.dataType().eq(rhs: rhs.dataType()) {
|
||||
public func assignableFromType(_ rhs: P4QualifiedType) -> TypeCheckResults {
|
||||
if !self.baseType().eq(rhs: rhs.baseType()) {
|
||||
return TypeCheckResults.IncompatibleTypes
|
||||
}
|
||||
|
||||
@@ -181,8 +184,8 @@ public struct P4Type: CustomStringConvertible {
|
||||
return TypeCheckResults.Ok
|
||||
}
|
||||
|
||||
public static func ReadOnly(_ type: P4DataType) -> P4Type {
|
||||
return P4Type(type, P4TypeAttributes.ReadOnly())
|
||||
public static func ReadOnly(_ type: P4Type) -> P4QualifiedType {
|
||||
return P4QualifiedType(type, P4TypeQualifiers.ReadOnly())
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
@@ -190,17 +193,17 @@ public struct P4Type: CustomStringConvertible {
|
||||
if !attributes_description.isEmpty {
|
||||
attributes_description += " "
|
||||
}
|
||||
return "\(attributes_description)\(self._data_type)"
|
||||
return "\(attributes_description)\(self.base_type)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct P4Value: CustomStringConvertible {
|
||||
let _value: P4DataValue
|
||||
let _type: P4Type
|
||||
let _type: P4QualifiedType
|
||||
|
||||
public init(_ value: P4DataValue, _ type: P4Type? = .none) {
|
||||
public init(_ value: P4DataValue, _ type: P4QualifiedType? = .none) {
|
||||
self._value = value
|
||||
self._type = type != nil ? type! : P4Type(value.type())
|
||||
self._type = type != nil ? type! : P4QualifiedType(value.type())
|
||||
}
|
||||
|
||||
public func update(withNewValue value: P4DataValue) -> Result<P4Value> {
|
||||
@@ -212,7 +215,7 @@ public struct P4Value: CustomStringConvertible {
|
||||
return "Value: \(self._value) of type \(self._type)"
|
||||
}
|
||||
|
||||
public func type() -> P4Type {
|
||||
public func type() -> P4QualifiedType {
|
||||
return self._type
|
||||
}
|
||||
|
||||
@@ -15,32 +15,20 @@
|
||||
// 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 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() -> P4Type
|
||||
public protocol P4Type: CustomStringConvertible {
|
||||
func eq(rhs: any P4Type) -> Bool
|
||||
func def() -> P4DataValue?
|
||||
func instantiable() -> Bool
|
||||
}
|
||||
|
||||
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)
|
||||
extension P4Type {
|
||||
public func instantiable() -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
public protocol P4DataType: CustomStringConvertible {
|
||||
func eq(rhs: any P4DataType) -> Bool
|
||||
func def() -> P4DataValue
|
||||
}
|
||||
|
||||
public protocol P4DataValue: CustomStringConvertible {
|
||||
func type() -> any P4DataType
|
||||
func type() -> any P4Type
|
||||
func eq(rhs: P4DataValue) -> Bool
|
||||
func lt(rhs: P4DataValue) -> Bool
|
||||
func lte(rhs: P4DataValue) -> Bool
|
||||
@@ -48,32 +36,35 @@ public protocol P4DataValue: CustomStringConvertible {
|
||||
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 Errorable: CustomStringConvertible {
|
||||
func format(_ formatter: Formattable) -> String
|
||||
func format(_ formatter: Formattable, _ sc: SourceCode) -> String
|
||||
func msg() -> String
|
||||
func append(error: any Errorable) -> any Errorable
|
||||
func eq(_ rhs: any Errorable) -> Bool
|
||||
}
|
||||
|
||||
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)
|
||||
extension Errorable {
|
||||
public func eq(_ rhs: any Errorable) -> Bool {
|
||||
return self.msg() == rhs.msg()
|
||||
}
|
||||
|
||||
extension ProgramExecutionEvaluator {
|
||||
public func ExecuteStatements(
|
||||
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution
|
||||
) -> (ControlFlow, ProgramExecution) {
|
||||
return ExecuteStatements(statements, inExecution: execution, .none)
|
||||
public func format(_ formatter: Formattable, _ sc: SourceCode) -> String {
|
||||
return self.format(formatter)
|
||||
}
|
||||
}
|
||||
|
||||
public protocol Formattable {
|
||||
func formatWithStyle(_ value: String, _ style: Style) -> String
|
||||
}
|
||||
|
||||
public protocol P4Statement {
|
||||
}
|
||||
|
||||
public protocol P4Expression {
|
||||
}
|
||||
|
||||
extension P4Expression {
|
||||
}
|
||||
|
||||
extension P4Expression {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// 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 enum Result<OKT>: Equatable {
|
||||
case Ok(OKT)
|
||||
case Error(any Errorable)
|
||||
|
||||
public static func == (lhs: Result, rhs: Result) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (Ok, Ok):
|
||||
return true
|
||||
case (Error(let le), Error(let re)):
|
||||
return le.eq(re)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public func ok() -> Bool {
|
||||
switch self {
|
||||
case .Ok(_): true
|
||||
case .Error(_): false
|
||||
}
|
||||
}
|
||||
public func error() -> (any Errorable)? {
|
||||
if case Result.Error(let e) = self {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func map<T>(block: (OKT) -> Result<T>) -> Result<T> {
|
||||
switch self {
|
||||
case .Ok(let ok): return block(ok)
|
||||
case .Error(let e): return .Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
public func map_err(block: (any Errorable) -> Result) -> Result {
|
||||
switch self {
|
||||
case .Ok(let ok): return .Ok(ok)
|
||||
case .Error(let e): return block(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Result: CustomStringConvertible where OKT: CustomStringConvertible {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case Result.Error(let e):
|
||||
return e.msg()
|
||||
case Result.Ok(let o):
|
||||
return "Ok: \(o)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
// 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 SystemPackage
|
||||
|
||||
/// Represent a location in a post-preprocessed piece of P4 source code.
|
||||
public struct SourceLocation: Equatable, CustomStringConvertible {
|
||||
|
||||
public let range: Range<Int>
|
||||
|
||||
public init(_ start: Int, _ extent: Int) {
|
||||
self.range = start..<(start + extent)
|
||||
}
|
||||
|
||||
public init(_ range: Range<Int>) {
|
||||
self.range = range
|
||||
}
|
||||
|
||||
public func contains(_ other: SourceLocation) -> Bool {
|
||||
return self.range.contains(other.range)
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "{\(self.range.lowerBound), \(self.self.range.upperBound - self.range.lowerBound)}"
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent search paths for P4 code that can be accessed with relative paths.
|
||||
public struct SourceManager {
|
||||
let paths: [FilePath]
|
||||
|
||||
/// Create a `SourceManager`
|
||||
///
|
||||
/// Any relative `FilePath`s in `paths` will be absolutized
|
||||
/// if a `FileManager` is given.
|
||||
///
|
||||
/// parameters:
|
||||
/// - paths: The include paths searched for files with relative paths.
|
||||
/// - fm: An optional instance of a `FileManager` that will be used to
|
||||
/// convert relative paths in `paths` to absolute paths.
|
||||
public init(_ paths: [FilePath], _ fm: FileManager? = .none) {
|
||||
|
||||
// If the user gives a file manager, we will convert relative paths
|
||||
// to absolute paths. Otherwise, we do not.
|
||||
guard let fm else {
|
||||
self.paths = paths
|
||||
return
|
||||
}
|
||||
|
||||
// There is a file manager, so we should try to absolutize any
|
||||
// relative paths
|
||||
self.paths = paths.map {
|
||||
if !$0.isAbsolute {
|
||||
return FilePath(fm.currentDirectoryPath + "/" + $0.string).lexicallyNormalized()
|
||||
}
|
||||
return $0
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `FilePath` of `file` in search paths.
|
||||
///
|
||||
/// Only if `file` is relative will the search paths be searched.
|
||||
///
|
||||
/// parameters:
|
||||
/// - file: A file to look for in the search paths.
|
||||
public func firstExisting(_ file: FilePath) -> FilePath? {
|
||||
if file.isAbsolute {
|
||||
return file
|
||||
}
|
||||
|
||||
let fm = FileManager()
|
||||
for path in self.paths {
|
||||
let combined = path.pushing(file).lexicallyNormalized()
|
||||
if fm.fileExists(atPath: combined.string) {
|
||||
return combined
|
||||
}
|
||||
}
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent preprocessed P4 code
|
||||
///
|
||||
/// The preprocessed code has metadata to recover the paths of any
|
||||
/// code generated by a preprocessor directive.
|
||||
public struct FileSourceLocation: Equatable, CustomStringConvertible {
|
||||
let location: SourceLocation
|
||||
let path: FilePath
|
||||
let whence: Int?
|
||||
let nested: [FileSourceLocation]
|
||||
|
||||
public init(
|
||||
_ location: SourceLocation, _ path: FilePath, _ whence: Int? = .none,
|
||||
_ nested: [FileSourceLocation] = Array()
|
||||
) {
|
||||
self.location = location
|
||||
self.path = path
|
||||
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 {
|
||||
return self.location
|
||||
}
|
||||
|
||||
public func getPath() -> FilePath {
|
||||
return self.path
|
||||
}
|
||||
|
||||
public func getNestedLocations() -> [FileSourceLocation] {
|
||||
return self.nested
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "\(self.path): \(self.location) (Nested: "
|
||||
+ self.nested.map({ location in
|
||||
return "\(location)"
|
||||
}).joined(separator: ",") + ")"
|
||||
}
|
||||
|
||||
public func pathForLocation(_ location: Int) -> [FileSourceLocation]? {
|
||||
|
||||
let queried_location = SourceLocation(location, 1)
|
||||
if !self.location.contains(queried_location) {
|
||||
return .none
|
||||
}
|
||||
|
||||
for nested in self.nested {
|
||||
if nested.location.contains(queried_location) {
|
||||
return [self] + nested.pathForLocation(location)!
|
||||
}
|
||||
}
|
||||
|
||||
return [self]
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent preprocessed P4 code.
|
||||
public struct SourceCode {
|
||||
let code: String
|
||||
let manager: SourceManager
|
||||
let locations: FileSourceLocation
|
||||
|
||||
public init(_ contents: String, _ manager: SourceManager, _ locations: FileSourceLocation) {
|
||||
self.code = contents
|
||||
self.manager = manager
|
||||
self.locations = locations
|
||||
}
|
||||
|
||||
public func getManager() -> SourceManager {
|
||||
return self.manager
|
||||
}
|
||||
|
||||
static func do_annotate(
|
||||
_ contents: String, _ manager: SourceManager, _ locations: FileSourceLocation,
|
||||
_ formatter: Formattable, _ style: Style
|
||||
) -> String {
|
||||
var result = ""
|
||||
|
||||
// Keep track of the start of any gap between nested locations.
|
||||
var gap_start = contents.startIndex
|
||||
|
||||
// contents are devoid of any preceding source code, but the locations do not know that. So,
|
||||
// when we use a range from locations we must adjust appropriately.
|
||||
let offset = locations.location.range.lowerBound
|
||||
|
||||
for nested in locations.getNestedLocations() {
|
||||
let nested_start = contents.index(
|
||||
contents.startIndex, offsetBy: nested.location.range.lowerBound - offset)
|
||||
let nested_end = contents.index(
|
||||
contents.startIndex, offsetBy: nested.location.range.upperBound - offset)
|
||||
|
||||
// Add in any gap.
|
||||
result += contents[gap_start..<nested_start]
|
||||
|
||||
// Handle this range (recursively)
|
||||
result += do_annotate(
|
||||
"\(contents[nested_start ..< nested_end])", manager, nested, formatter, style)
|
||||
|
||||
// Adjust where the next gap will start.
|
||||
gap_start = nested_end
|
||||
}
|
||||
|
||||
// Is there anything left?
|
||||
let remainder = contents[gap_start...]
|
||||
|
||||
return formatter.formatWithStyle(result + remainder, style)
|
||||
}
|
||||
|
||||
public func getSource(
|
||||
annotated: Bool = false, formatter: Formattable = FormatterDelimited("<", ">"),
|
||||
initialStyle: Style = Style(StyleColor.Red)
|
||||
) -> String {
|
||||
if annotated {
|
||||
return SourceCode.do_annotate(
|
||||
self.code, self.manager, self.locations, formatter, initialStyle)
|
||||
}
|
||||
return self.code
|
||||
}
|
||||
|
||||
public func getSourceSnippet(
|
||||
location: SourceLocation, context: Int = 0
|
||||
) -> ([FileSourceLocation], String, String, String)? {
|
||||
guard let paths = self.pathForLocation(location.range.lowerBound) else {
|
||||
return .none
|
||||
}
|
||||
let lower = String.UTF8View.Index(utf16Offset: location.range.lowerBound, in: self.code)
|
||||
let upper = String.UTF8View.Index(utf16Offset: location.range.upperBound, in: self.code)
|
||||
let prior_start =
|
||||
if location.range.lowerBound - context >= 0 {
|
||||
String.UTF8View.Index(utf16Offset: location.range.lowerBound - context, in: self.code)
|
||||
} else {
|
||||
String.UTF8View.Index(utf16Offset: location.range.lowerBound, in: self.code)
|
||||
}
|
||||
let after_end =
|
||||
if location.range.upperBound + context < self.code.count {
|
||||
String.UTF8View.Index(utf16Offset: location.range.upperBound + context, in: self.code)
|
||||
} else {
|
||||
String.UTF8View.Index(utf16Offset: location.range.upperBound, in: self.code)
|
||||
}
|
||||
|
||||
let result = String(self.code.utf16[lower..<upper])!
|
||||
let prior = String(self.code.utf16[prior_start..<lower])!
|
||||
let after = String(self.code.utf16[upper...after_end])!
|
||||
return (paths, result, prior, after)
|
||||
}
|
||||
|
||||
public func getLocations() -> FileSourceLocation {
|
||||
return self.locations
|
||||
}
|
||||
|
||||
public func pathForLocation(_ location: Int) -> [FileSourceLocation]? {
|
||||
return self.locations.pathForLocation(location)
|
||||
}
|
||||
}
|
||||
|
||||
func do_preprocess(
|
||||
_ file: FilePath, _ manager: SourceManager, _ starting: Int = 0
|
||||
) -> Result<(FileSourceLocation, String)> {
|
||||
// First (1) match group has the name of the include file.
|
||||
let re = /\#include[\s]*<([^\s>]*)>/
|
||||
|
||||
do {
|
||||
var locations: [FileSourceLocation] = Array()
|
||||
|
||||
// First, try to find the file.
|
||||
guard let included_path = manager.firstExisting(file) else {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage:
|
||||
"Could not open \(file) for preprocessing"))
|
||||
}
|
||||
|
||||
let orig = try String.init(
|
||||
contentsOf: URL(filePath: included_path.string), encoding: String.defaultCStringEncoding)
|
||||
var expanded = ""
|
||||
var oloc = orig.startIndex
|
||||
|
||||
for match in orig.matches(of: re) {
|
||||
|
||||
if oloc != match.range.lowerBound {
|
||||
expanded += String(orig[oloc..<match.range.lowerBound])
|
||||
}
|
||||
oloc = match.range.upperBound
|
||||
|
||||
// By calling ourselves recursively, the include being processed will
|
||||
// be _completely_ expanded (including any nested includes).
|
||||
switch do_preprocess(FilePath("\(match.1)"), manager, starting + expanded.count) {
|
||||
case .Ok((let location, let recursively_expanded)):
|
||||
// Recombine what was before and after the include being processed
|
||||
// with the expanded text.
|
||||
expanded += recursively_expanded
|
||||
// Remember the location (and those it has nested) that were found in
|
||||
// the expanded text.
|
||||
locations.append(location.update(whence: match.range.lowerBound.utf16Offset(in: orig)))
|
||||
case .Error(let e):
|
||||
return .Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that we got it all!
|
||||
expanded += orig[oloc...]
|
||||
|
||||
return .Ok(
|
||||
(
|
||||
FileSourceLocation(
|
||||
SourceLocation(starting..<(starting + expanded.count)), file, .none, locations), expanded
|
||||
))
|
||||
|
||||
} catch (let e) {
|
||||
return .Error(Error(withMessage: "\(e)"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Preprocess P4 code.
|
||||
public struct SourceCodePreprocessor {
|
||||
let manager: SourceManager
|
||||
|
||||
public init(_ manager: SourceManager) {
|
||||
self.manager = manager
|
||||
}
|
||||
|
||||
public func preprocess(_ file: FilePath) -> Result<SourceCode> {
|
||||
switch do_preprocess(file, self.manager) {
|
||||
case .Ok((let location, let contents)):
|
||||
return .Ok(SourceCode(contents, self.manager, location))
|
||||
case .Error(let e): return .Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,18 +61,6 @@ public enum DebugLevel {
|
||||
}
|
||||
}
|
||||
|
||||
public struct Error: Equatable, CustomStringConvertible {
|
||||
public private(set) var msg: String
|
||||
|
||||
public init(withMessage msg: String) {
|
||||
self.msg = msg
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return self.msg
|
||||
}
|
||||
}
|
||||
|
||||
public struct Nothing: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "Nothing"
|
||||
@@ -81,64 +69,27 @@ public struct Nothing: CustomStringConvertible {
|
||||
public init() {}
|
||||
}
|
||||
|
||||
public enum Result<OKT>: Equatable {
|
||||
case Ok(OKT)
|
||||
case Error(Error)
|
||||
|
||||
public static func == (lhs: Result, rhs: Result) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (Ok, Ok):
|
||||
return true
|
||||
case (Error(let le), Error(let re)):
|
||||
return le.msg == re.msg
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public func ok() -> Bool {
|
||||
switch self {
|
||||
case .Ok(_): true
|
||||
case .Error(_): false
|
||||
}
|
||||
}
|
||||
public func error() -> Error? {
|
||||
if case Result.Error(let e) = self {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func map<T>(block: (OKT) -> Result<T>) -> Result<T> {
|
||||
switch self {
|
||||
case .Ok(let ok): return block(ok)
|
||||
case .Error(let e): return .Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Result: CustomStringConvertible where OKT: CustomStringConvertible {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case Result.Error(let e):
|
||||
return e.msg
|
||||
case Result.Ok(let o):
|
||||
return "Ok: \(o)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func Map<T, U>(input: T, block: (T) -> U) -> U {
|
||||
return block(input)
|
||||
}
|
||||
|
||||
public func Fold<T, A>(input: [T], initial: A, block: (T, A) -> A) -> A {
|
||||
var result = initial
|
||||
for i in input {
|
||||
result = block(i, result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@freestanding(expression) public macro RequireOkResult<T>(_: Result<T>) -> Bool =
|
||||
#externalMacro(module: "Macros", type: "RequireResult")
|
||||
@freestanding(expression) public macro RequireErrorResult<T>(_: Error, _: Result<T>) -> Bool =
|
||||
@freestanding(expression) public macro RequireErrorResult<T>(
|
||||
_: any Errorable, _: Result<T>
|
||||
) -> Bool =
|
||||
#externalMacro(module: "Macros", type: "RequireErrorResult")
|
||||
@freestanding(expression) public macro UseOkResult<T>(_: Result<T>) -> T =
|
||||
#externalMacro(module: "Macros", type: "UseOkResult")
|
||||
@freestanding(expression) public macro UseErrorResult<T>(_: Result<T>) -> Error =
|
||||
@freestanding(expression) public macro UseErrorResult<T>(_: Result<T>) -> any Errorable =
|
||||
#externalMacro(module: "Macros", type: "UseErrorResult")
|
||||
@freestanding(codeItem) public macro RequireNodeType<N, T>(
|
||||
node: N, type: String, nice_type_name: String
|
||||
@@ -150,6 +101,8 @@ public func Map<T, U>(input: T, block: (T) -> U) -> U {
|
||||
#externalMacro(module: "Macros", type: "RequireNodesType")
|
||||
@freestanding(codeItem) public macro SkipUnlessNodeType<N>(node: N, type: String) =
|
||||
#externalMacro(module: "Macros", type: "SkipUnlessNodeType")
|
||||
@freestanding(codeItem) public macro SkipUnlessNodesTypes<N>(node: N, types: [String]) =
|
||||
#externalMacro(module: "Macros", type: "SkipUnlessNodesTypes")
|
||||
|
||||
@freestanding(codeItem) public macro MustOr<E, N>(result: E, thing: E?, or: N) =
|
||||
#externalMacro(module: "Macros", type: "MustOr")
|
||||
|
||||
+213
-5
@@ -125,10 +125,14 @@ public struct RequireErrorResult: ExpressionMacro {
|
||||
{
|
||||
let __expected_error = \(expected_error)
|
||||
let __actual_error = \(error_producer)
|
||||
if case Result.Error(__expected_error) = __actual_error {
|
||||
if case Result.Error(let __found_error) = __actual_error {
|
||||
if !__expected_error.eq(__found_error) {
|
||||
print("Expected Error: \\(__expected_error) but got Error: \\(__found_error)")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
print("Expected Error: \\(__expected_error) but got Error: \\(__actual_error)")
|
||||
print("Expected error, but got Ok")
|
||||
return false
|
||||
}
|
||||
}()
|
||||
@@ -156,7 +160,7 @@ public struct RequireNodeType: CodeItemMacro {
|
||||
"""
|
||||
if \(node_to_check).nodeType != \(expected_type) {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: \(node_to_check), withError: "\(raw: error_message)"))
|
||||
ErrorWithLocation(sourceLocation: \(node_to_check).toSourceLocation(), withError: "\(raw: error_message)"))
|
||||
}
|
||||
""")
|
||||
]
|
||||
@@ -198,7 +202,7 @@ public struct RequireNodesType: CodeItemMacro {
|
||||
"""
|
||||
if \(raw: ifs) {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: \(node_to_check), withError: "\(raw: error_message)"))
|
||||
ErrorWithLocation(sourceLocation: \(node_to_check).toSourceLocation(), withError: "\(raw: error_message)"))
|
||||
}
|
||||
""")
|
||||
]
|
||||
@@ -225,6 +229,38 @@ public struct SkipUnlessNodeType: CodeItemMacro {
|
||||
}
|
||||
}
|
||||
|
||||
public struct SkipUnlessNodesTypes: CodeItemMacro {
|
||||
public static func expansion(
|
||||
of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext
|
||||
) throws -> [CodeBlockItemSyntax] {
|
||||
|
||||
let arguments = node.arguments.indices
|
||||
var arg_index = arguments.startIndex
|
||||
|
||||
let node_to_check = node.arguments[arg_index].expression
|
||||
|
||||
arg_index = arguments.index(after: arg_index)
|
||||
guard let expected_types = node.arguments[arg_index].expression.as(ArrayExprSyntax.self) else {
|
||||
throw MacroError(withMessage: "Node(s) to check must be in an array")
|
||||
}
|
||||
|
||||
arg_index = arguments.index(after: arg_index)
|
||||
|
||||
let ifs = expected_types.elements.map { l in
|
||||
"\(node_to_check).nodeType != \(l.expression)"
|
||||
}.joined(separator: " && ")
|
||||
|
||||
return [
|
||||
CodeBlockItemSyntax(
|
||||
"""
|
||||
if \(raw: ifs) {
|
||||
return Result.Ok(.none)
|
||||
}
|
||||
""")
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
public struct MustOr: CodeItemMacro {
|
||||
public static func expansion(
|
||||
of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext
|
||||
@@ -253,10 +289,182 @@ public struct MustOr: CodeItemMacro {
|
||||
}
|
||||
}
|
||||
|
||||
public struct CliTestDeclarationMacro: PeerMacro, Sendable {
|
||||
|
||||
// NOTE: Taken from swift-testing.
|
||||
|
||||
/// Get an expression initializing an instance of ``SourceLocation`` from an
|
||||
/// arbitrary syntax node.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - node: The syntax node for which an instance of ``SourceLocation`` is
|
||||
/// needed.
|
||||
/// - context: The macro context in which the expression is being parsed.
|
||||
///
|
||||
/// - Returns: An expression value that initializes an instance of
|
||||
/// ``SourceLocation`` for `node`.
|
||||
static func createSourceLocationExpr(
|
||||
of node: some SyntaxProtocol, context: some MacroExpansionContext
|
||||
) -> ExprSyntax {
|
||||
if node.isProtocol((any FreestandingMacroExpansionSyntax).self) {
|
||||
// Freestanding macro expressions can just use __here()
|
||||
// directly and do not need to talk to the macro context to get source
|
||||
// location info.
|
||||
return "Testing.SourceLocation.__here()"
|
||||
}
|
||||
|
||||
// Get the equivalent source location in both `#fileID` and `#filePath` modes.
|
||||
guard let fileIDSourceLoc: AbstractSourceLocation = context.location(of: node),
|
||||
let filePathSourceLoc: AbstractSourceLocation = context.location(
|
||||
of: node, at: .afterLeadingTrivia, filePathMode: .filePath)
|
||||
else {
|
||||
return "Testing.SourceLocation.__here()"
|
||||
}
|
||||
|
||||
return
|
||||
"Testing.SourceLocation(__uncheckedFileID: \(fileIDSourceLoc.file), filePath: \(filePathSourceLoc.file), line: \(fileIDSourceLoc.line), column: \(fileIDSourceLoc.column))"
|
||||
}
|
||||
|
||||
/// Get an expression initializing an instance of `__SourceBounds` from two
|
||||
/// arbitrary syntax nodesvalues.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - lowerBoundNode: The syntax node representing the lower bound. The start
|
||||
/// of this node (after leading trivia) is used.
|
||||
/// - upperBoundNode: The syntax node representing the upper bound. The end of
|
||||
/// this node (before trailing trivia) is used.
|
||||
/// - context: The macro context in which the expression is being parsed.
|
||||
///
|
||||
/// - Returns: An expression value that initializes an instance of
|
||||
/// `__SourceBounds`.
|
||||
///
|
||||
/// The resulting source bounds instance represents (approximately):
|
||||
///
|
||||
/// ```swift
|
||||
/// lowerBoundNode.positionAfterSkippingLeadingTrivia ..< upperBoundNode.endPositionBeforeTrailingTrivia
|
||||
/// ```
|
||||
static func createSourceBoundsExpr(
|
||||
from lowerBoundNode: some SyntaxProtocol, to upperBoundNode: some SyntaxProtocol,
|
||||
in context: some MacroExpansionContext
|
||||
) -> ExprSyntax {
|
||||
let lowerBoundExpr = createSourceLocationExpr(of: lowerBoundNode, context: context)
|
||||
let upperBoundExpr: ExprSyntax =
|
||||
if let upperBoundSourceLoc = context.location(
|
||||
of: upperBoundNode, at: .beforeTrailingTrivia, filePathMode: .fileID)
|
||||
{
|
||||
"(\(upperBoundSourceLoc.line), \(upperBoundSourceLoc.column))"
|
||||
} else {
|
||||
"(.max, .max)"
|
||||
}
|
||||
return
|
||||
"Testing.__SourceBounds(__uncheckedLowerBound: \(lowerBoundExpr), upperBound: \(upperBoundExpr))"
|
||||
}
|
||||
|
||||
// NOTE: End of what was taken from swift-testing
|
||||
|
||||
private static func doc_shrink(_ from: String) -> String {
|
||||
return from.replacing(Regex(#/^.*\/\/\/[\s]+/#), with: "")
|
||||
}
|
||||
|
||||
public static func expansion(
|
||||
of node: AttributeSyntax,
|
||||
providingPeersOf declaration: some DeclSyntaxProtocol,
|
||||
in context: some MacroExpansionContext
|
||||
) throws -> [DeclSyntax] {
|
||||
|
||||
let test_name = declaration.cast(FunctionDeclSyntax.self).name
|
||||
|
||||
let cli_test_expected_output = node.leadingTrivia.filter({ $0.isComment }).map({
|
||||
doc_shrink("\($0)")
|
||||
}).joined(separator: "\\n")
|
||||
|
||||
let cli_test_driver_thunk_name = context.makeUniqueName("_thunk_")
|
||||
|
||||
let (expected_decl, expected_label) =
|
||||
if cli_test_expected_output.isEmpty {
|
||||
("let expected = \"\(test_name)\"", "withExpectedPath:")
|
||||
} else {
|
||||
("let expected = \"\(cli_test_expected_output)\"", "withExpected:")
|
||||
}
|
||||
|
||||
let cli_test_driver_thunk: DeclSyntax = """
|
||||
@Sendable private func \(cli_test_driver_thunk_name)() async throws {
|
||||
\(raw: expected_decl)
|
||||
|
||||
_ = unsafe try await Testing.__requiringUnsafe(
|
||||
Testing.__requiringTry(
|
||||
Testing.__requiringAwait(swiftCliTestRunner(\(test_name), \(raw: expected_label) expected))))
|
||||
}
|
||||
"""
|
||||
|
||||
let source_bounds = createSourceBoundsExpr(from: node, to: declaration, in: context)
|
||||
let cli_test_driver_generator_name = context.makeUniqueName("_generator_")
|
||||
let cli_test_driver_generator: DeclSyntax = """
|
||||
@Sendable private func \(cli_test_driver_generator_name)() async -> Testing.Test {
|
||||
return .__function(
|
||||
named: "\(test_name)",
|
||||
in: nil as Swift.Never.Type?,
|
||||
xcTestCompatibleSelector: Testing.__xcTestCompatibleSelector("\(test_name)"),
|
||||
traits: [],
|
||||
sourceBounds: \(source_bounds),
|
||||
parameters: [],
|
||||
testFunction: \(cli_test_driver_thunk_name)
|
||||
)
|
||||
}
|
||||
"""
|
||||
|
||||
#if os(macOS)
|
||||
let section = "__DATA_CONST,__swift5_tests"
|
||||
#else
|
||||
let section = "swift5_tests"
|
||||
#endif
|
||||
|
||||
let cli_test_driver_content_record_name = context.makeUniqueName("testContentRecord")
|
||||
let cli_test_driver_cr: DeclSyntax = """
|
||||
@section("\(raw: section)")
|
||||
@used
|
||||
private nonisolated let \(cli_test_driver_content_record_name): Testing.__TestContentRecord = (
|
||||
0x7465_7374, /* indicate a test */
|
||||
0,
|
||||
{ outValue, type, _, _ in
|
||||
Testing.Test.__store(\(cli_test_driver_generator_name), into: outValue, asTypeAt: type)
|
||||
},
|
||||
0,
|
||||
0
|
||||
)
|
||||
"""
|
||||
return [
|
||||
cli_test_driver_thunk, cli_test_driver_generator, cli_test_driver_cr,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
struct P4Macros: CompilerPlugin {
|
||||
var providingMacros: [Macro.Type] = [
|
||||
RequireResult.self, RequireErrorResult.self, UseOkResult.self, UseErrorResult.self,
|
||||
RequireNodeType.self, SkipUnlessNodeType.self, RequireNodesType.self, MustOr.self,
|
||||
RequireNodeType.self, SkipUnlessNodeType.self, SkipUnlessNodesTypes.self, RequireNodesType.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,374 +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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing '(' in parameter list component")))
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(ParameterList, CompilerContext)>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing parameter list component")))
|
||||
|
||||
return parameter_list_compiler(node: current_node!, withContext: context)
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
ErrorOnNode(
|
||||
node: direction_node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing parameter declaration component")))
|
||||
|
||||
// Annotation?
|
||||
if current_node!.nodeType == "annotations" {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: current_node!,
|
||||
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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing parameter declaration component")))
|
||||
|
||||
if current_node!.nodeType != "typeRef" {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Did not find type name for parameter declaration"))
|
||||
}
|
||||
|
||||
guard
|
||||
case .Ok(let parameter_type) = Types.CompileType(type: 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(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing parameter declaration component")))
|
||||
|
||||
if current_node!.nodeType != "identifier" {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Did not find identifier for parameter statement"))
|
||||
}
|
||||
|
||||
guard
|
||||
case .Ok(let parameter_name) = Identifier.Compile(node: 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: P4TypeAttribute.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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing '(' in argument list component")))
|
||||
|
||||
walker.next()
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(ArgumentList, CompilerContext)>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing argument list component")))
|
||||
|
||||
return argument_list_compiler(node: current_node!, withContext: context)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,162 +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)
|
||||
}
|
||||
|
||||
public func ErrorOnNode(node: Node, withError error: String) -> Error {
|
||||
return Error(withMessage: "\(node.range): \(error)")
|
||||
}
|
||||
|
||||
/// 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: P4Type?
|
||||
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: P4Type?, withExtern extern: Bool,
|
||||
withExterns externs: TypeTypeScopes, withFFIs foreigns: [P4FFI]
|
||||
) {
|
||||
instances = _instances
|
||||
types = _types
|
||||
expected_type = expectation
|
||||
extern_context = extern
|
||||
self.externs = externs
|
||||
ffis = foreigns
|
||||
}
|
||||
|
||||
/// Update a compiler context
|
||||
///
|
||||
/// Create a new compiler context based on the current 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: P4Type?) -> 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)
|
||||
}
|
||||
}
|
||||
@@ -1,947 +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 Declaration: CompilableDeclaration {
|
||||
public static func Compile(
|
||||
node: Node, withContext context: CompilerContext
|
||||
) -> Result<(Declaration, CompilerContext)?> {
|
||||
|
||||
let declaration_compilers: [String: CompilableDeclaration.Type] = [
|
||||
"function_declaration": FunctionDeclaration.self,
|
||||
"control_declaration": Control.self,
|
||||
"type_declaration": P4Struct.self,
|
||||
/// ASSUME: Type declarations are struct declarations.
|
||||
"extern_declaration": ExternDeclaration.self,
|
||||
]
|
||||
|
||||
guard let declaration_compiler = declaration_compilers[node.nodeType!] else {
|
||||
return .Ok(.none)
|
||||
}
|
||||
|
||||
return declaration_compiler.Compile(node: node, withContext: context)
|
||||
}
|
||||
}
|
||||
|
||||
extension FunctionDeclaration: CompilableDeclaration {
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(Declaration, CompilerContext)?> {
|
||||
let function_declaration_node = node
|
||||
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
|
||||
node: function_declaration_node, type: "function_declaration",
|
||||
nice_type_name: "Function Declaration")
|
||||
|
||||
var walker = Walker(node: function_declaration_node)
|
||||
var context = context
|
||||
|
||||
var current_node: Node? = .none
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: function_declaration_node, withError: "Missing function declaration component")))
|
||||
|
||||
let maybe_function_type = Types.CompileType(type: current_node!, withContext: context)
|
||||
guard case .Ok(let function_type) = maybe_function_type else {
|
||||
return .Error(maybe_function_type.error()!)
|
||||
}
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: function_declaration_node, withError: "Missing function declaration component")))
|
||||
|
||||
let maybe_function_name = Identifier.Compile(node: current_node!, withContext: context)
|
||||
guard case .Ok(let function_name) = maybe_function_name else {
|
||||
return .Error(maybe_function_name.error()!)
|
||||
}
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: function_declaration_node, withError: "Missing function declaration component")))
|
||||
|
||||
let maybe_function_parameters = ParameterList.Compile(node: current_node!, withContext: context)
|
||||
guard case .Ok((let function_parameters, let updated_context)) = maybe_function_parameters
|
||||
else {
|
||||
return .Error(maybe_function_parameters.error()!)
|
||||
}
|
||||
context = updated_context
|
||||
|
||||
var function_body: BlockStatement? = .none
|
||||
|
||||
walker.next()
|
||||
if let body = walker.getNext() {
|
||||
// Add the parameters into scope.
|
||||
var function_scope = context.instances.enter()
|
||||
for parameter in function_parameters.parameters {
|
||||
function_scope = function_scope.declare(
|
||||
identifier: parameter.name, withValue: parameter.type)
|
||||
}
|
||||
|
||||
let maybe_function_body = BlockStatement.Compile(
|
||||
node: body,
|
||||
withContext: context.update(newInstances: function_scope).update(
|
||||
newExpectation: function_type))
|
||||
|
||||
guard case .Ok((let parsed_function_body, _)) = maybe_function_body else {
|
||||
return .Error(maybe_function_body.error()!)
|
||||
}
|
||||
function_body = (parsed_function_body as! BlockStatement)
|
||||
} else {
|
||||
|
||||
// If we are in an extern context, no body is okay!
|
||||
|
||||
if !context.extern_context {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: function_declaration_node, withError: "Missing function declaration component"))
|
||||
}
|
||||
}
|
||||
|
||||
let function_declaration = Declaration(
|
||||
TypedIdentifier(
|
||||
id: function_name,
|
||||
withType: P4Type(
|
||||
FunctionDeclaration(
|
||||
named: function_name, ofType: function_type, withParameters: function_parameters,
|
||||
withBody: function_body))))
|
||||
|
||||
// Do not use the updated context returned by parsing the body
|
||||
// and do not use the function_scope, either.
|
||||
// And, do not update the context if we are compiling in an
|
||||
// extern context.
|
||||
return .Ok(
|
||||
(
|
||||
function_declaration,
|
||||
context.extern_context
|
||||
? context
|
||||
: context.update(
|
||||
newTypes: context.types.declare(
|
||||
identifier: function_name, withValue: function_declaration.identifier.type.dataType())
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
extension P4Struct: CompilableDeclaration {
|
||||
static public func Compile(
|
||||
node: Node, withContext context: CompilerContext
|
||||
) -> Result<(Declaration, CompilerContext)?> {
|
||||
let struct_declaration_node = node.child(at: 0)!
|
||||
var walker = Walker(node: struct_declaration_node)
|
||||
var currentNode: Node? = .none
|
||||
|
||||
#SkipUnlessNodeType(node: struct_declaration_node, type: "struct_declaration")
|
||||
|
||||
// Skip the keyword struct
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: currentNode, thing: walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: struct_declaration_node, withError: "Missing function declaration component")))
|
||||
|
||||
// The name of the struct type.
|
||||
let maybe_struct_identifier = Identifier.Compile(
|
||||
node: currentNode!, withContext: context)
|
||||
guard case Result.Ok(let struct_identifier) = maybe_struct_identifier else {
|
||||
return Result.Error(maybe_struct_identifier.error()!)
|
||||
}
|
||||
walker.next()
|
||||
|
||||
// Skip the '{'
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: currentNode, thing: walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: struct_declaration_node, withError: "Missing function declaration component")))
|
||||
|
||||
// If there are no fields, it will be a "}"
|
||||
if currentNode!.nodeType == "}" {
|
||||
let struc = Declaration(
|
||||
TypedIdentifier(
|
||||
id: struct_identifier,
|
||||
withType: P4Type(P4Struct(withName: struct_identifier, andFields: P4StructFields([])))))
|
||||
return Result.Ok(
|
||||
(
|
||||
struc,
|
||||
context.extern_context
|
||||
? context
|
||||
: context.update(
|
||||
newTypes: context.types.declare(
|
||||
identifier: struct_identifier, withValue: struc.identifier.type.dataType()))
|
||||
))
|
||||
}
|
||||
|
||||
var parse_errs: [Error] = Array()
|
||||
var current_context = context
|
||||
var parsed_fields: [P4StructFieldIdentifier] = Array()
|
||||
|
||||
if currentNode!.nodeType == "struct_declaration_fields" {
|
||||
currentNode!.enumerateNamedChildren { declaration_field in
|
||||
switch VariableDeclarationStatement.Compile(
|
||||
node: declaration_field, withContext: current_context)
|
||||
{
|
||||
case .Ok((let declaration, let updated_context)):
|
||||
let variable_declaration = declaration as! VariableDeclarationStatement
|
||||
parsed_fields.append(
|
||||
P4StructFieldIdentifier(
|
||||
id: variable_declaration.identifier, withType: variable_declaration.initializer.type()
|
||||
))
|
||||
current_context = updated_context
|
||||
case .Error(let e): parse_errs.append(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !parse_errs.isEmpty {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage: "Error(s) parsing select cases: "
|
||||
+ (parse_errs.map { error in
|
||||
return "\(error.msg)"
|
||||
}.joined(separator: ";"))))
|
||||
}
|
||||
|
||||
let declared_struct = Declaration(
|
||||
TypedIdentifier(
|
||||
id: struct_identifier,
|
||||
withType: P4Type(
|
||||
P4Struct(
|
||||
withName: struct_identifier, andFields: P4StructFields(parsed_fields)))))
|
||||
return .Ok(
|
||||
(
|
||||
declared_struct,
|
||||
current_context.extern_context
|
||||
? current_context
|
||||
: current_context.update(
|
||||
newTypes: current_context.types.declare(
|
||||
identifier: struct_identifier, withValue: declared_struct.identifier.type.dataType()))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
extension P4Lang.Parser: CompilableDeclaration {
|
||||
public static func Compile(
|
||||
node: Node, withContext context: CompilerContext
|
||||
) -> Result<(Declaration, CompilerContext)?> {
|
||||
let parser_node = node
|
||||
#SkipUnlessNodeType<Node>(node: parser_node, type: "parserDeclaration")
|
||||
var current_context = context
|
||||
|
||||
var walker = Walker(node: parser_node)
|
||||
var current_node: Node? = .none
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: parser_node, withError: "Missing elements of parser declaration")))
|
||||
|
||||
if current_node!.nodeType != "parserType" {
|
||||
return .Error(
|
||||
ErrorOnNode(node: current_node!, withError: "Missing type for parser declaration"))
|
||||
}
|
||||
|
||||
let type_node = current_node
|
||||
var parser_name: Common.Identifier? = .none
|
||||
|
||||
/// TODO: Handle parser parameter lists.
|
||||
var parameter_list = ParameterList()
|
||||
|
||||
do {
|
||||
var type_node_walker = Walker(node: type_node!)
|
||||
var type_node_child: Node? = .none
|
||||
|
||||
#MustOr(
|
||||
result: type_node_child, thing: type_node_walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: parser_node, withError: "Missing elements of parser type in parser declaration")))
|
||||
|
||||
if type_node_child!.nodeType == "annotations" {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: type_node_child!, withError: "Annotations in parser type are not yet handled."))
|
||||
// Will increment indexes here.
|
||||
}
|
||||
|
||||
type_node_walker.next()
|
||||
#MustOr(
|
||||
result: type_node_child, thing: type_node_walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: type_node_child!, withError: "Missing name in parser type declaration")))
|
||||
|
||||
switch Identifier.Compile(node: type_node_child!, withContext: current_context) {
|
||||
case .Ok(let id): parser_name = id
|
||||
case .Error(let e):
|
||||
return .Error(e)
|
||||
}
|
||||
|
||||
type_node_walker.next()
|
||||
#MustOr(
|
||||
result: type_node_child, thing: type_node_walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: type_node_child!, withError: "Missing parser parameters")))
|
||||
|
||||
switch ParameterList.Compile(node: type_node_child!, withContext: current_context) {
|
||||
case .Ok(let (parsed_parameter_list, updated_context)):
|
||||
parameter_list = parsed_parameter_list
|
||||
current_context = updated_context
|
||||
case .Error(let e):
|
||||
return .Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Now, let's put the parameters into scope.
|
||||
for parameter in parameter_list.parameters {
|
||||
current_context = current_context.update(
|
||||
newInstances: current_context.instances.declare(
|
||||
identifier: parameter.name, withValue: parameter.type))
|
||||
}
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: parser_node, withError: "Missing parser declaration component")))
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: parser_node, withError: "Missing elements of parser declaration")))
|
||||
|
||||
if current_node!.nodeType == "parserLocalElements" {
|
||||
return .Error(
|
||||
ErrorOnNode(node: current_node!, withError: "Parser Local Elements are not yet handled."))
|
||||
// Will increment indexes here.
|
||||
}
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: parser_node, withError: "Missing body of parser declaration")))
|
||||
|
||||
if current_node!.nodeType != "parserStates" {
|
||||
return .Error(Error(withMessage: "Missing parser states in parser declaration"))
|
||||
}
|
||||
|
||||
switch Parser.Compile(
|
||||
withName: parser_name!, withParameters: parameter_list, node: current_node!,
|
||||
withContext: current_context)
|
||||
{
|
||||
case Result.Ok((let parser, let updated_context)):
|
||||
let parser_declaration = Declaration(
|
||||
TypedIdentifier(id: parser.name, withType: P4Type(parser)))
|
||||
// Create a new context with the name of the parser that was just compiled in scope.
|
||||
return .Ok(
|
||||
(
|
||||
parser_declaration,
|
||||
context.extern_context
|
||||
? context
|
||||
: context.update(
|
||||
newInstances: updated_context.instances.declare(
|
||||
identifier: parser.name, withValue: parser_declaration.identifier.type))
|
||||
))
|
||||
case Result.Error(let error): return .Error(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Control: CompilableDeclaration {
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(Declaration, CompilerContext)?> {
|
||||
|
||||
#SkipUnlessNodeType<Node>(node: node, type: "control_declaration")
|
||||
|
||||
var walker = Walker(node: node)
|
||||
var current_node: Node? = .none
|
||||
|
||||
var local_context = context
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing control declaration component")))
|
||||
|
||||
guard
|
||||
case .Ok(let control_name) = Identifier.Compile(
|
||||
node: current_node!, withContext: local_context)
|
||||
else {
|
||||
return Result.Error(
|
||||
Error(withMessage: "Could not parse a parameter name from \(current_node!.text!)"))
|
||||
}
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing control declaration component")))
|
||||
|
||||
let maybe_control_parameters = ParameterList.Compile(
|
||||
node: current_node!, withContext: local_context)
|
||||
guard case .Ok((let control_parameters, let updated_context)) = maybe_control_parameters
|
||||
else {
|
||||
return .Error(maybe_control_parameters.error()!)
|
||||
}
|
||||
local_context = updated_context
|
||||
|
||||
// Before continuing, make sure to put the parameters into context.
|
||||
var control_scope = local_context.instances.enter()
|
||||
for parameter in control_parameters.parameters {
|
||||
control_scope = control_scope.declare(
|
||||
identifier: parameter.name, withValue: parameter.type)
|
||||
}
|
||||
local_context = local_context.update(newInstances: control_scope)
|
||||
|
||||
walker.next()
|
||||
// Skip the '{'
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(Declaration, CompilerContext)?>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing control declaration component")))
|
||||
|
||||
var actions: [Action] = Array()
|
||||
var tables: [Table] = Array()
|
||||
var apply: ApplyStatement? = .none
|
||||
|
||||
// Because the final child
|
||||
// is the '}'.
|
||||
// \/\/
|
||||
let body_parse_results = walker.overUntil(n: node.childCount - 1) { current_node in
|
||||
if current_node.nodeType == "action_declaration" {
|
||||
let maybe_action_declaration = Action.Compile(
|
||||
node: current_node, withContext: local_context)
|
||||
guard
|
||||
case .Ok((let action_declaration, let updated_context)) = maybe_action_declaration
|
||||
else {
|
||||
return .Error(maybe_action_declaration.error()!)
|
||||
}
|
||||
actions.append(action_declaration)
|
||||
local_context = updated_context
|
||||
// Now, add the declaration into the context.
|
||||
local_context = local_context.update(
|
||||
newTypes: local_context.types.declare(
|
||||
identifier: action_declaration.name, withValue: action_declaration))
|
||||
} else if current_node.nodeType == "table_declaration" {
|
||||
let maybe_table_declaration = Table.Compile(
|
||||
node: current_node, withContext: local_context)
|
||||
guard
|
||||
case .Ok((let table_declaration, let updated_context)) = maybe_table_declaration
|
||||
else {
|
||||
return .Error(maybe_table_declaration.error()!)
|
||||
}
|
||||
tables.append(table_declaration)
|
||||
local_context = updated_context
|
||||
} else if current_node.nodeType == "apply_statement" {
|
||||
// When we see an apply, that is it for the actions and the tables.
|
||||
let maybe_apply_statement = ApplyStatement.Compile(
|
||||
node: current_node, withContext: local_context)
|
||||
guard
|
||||
case .Ok((let apply_statement, let updated_context)) = maybe_apply_statement
|
||||
else {
|
||||
return .Error(maybe_apply_statement.error()!)
|
||||
}
|
||||
local_context = updated_context
|
||||
apply = (apply_statement as! ApplyStatement)
|
||||
|
||||
// The apply is the last thing in a control declaration.
|
||||
// But, that is handled by the compiler.
|
||||
} else {
|
||||
return .Error(
|
||||
ErrorOnNode(node: current_node, withError: "Uknown node type in control declaration"))
|
||||
}
|
||||
return .Ok(())
|
||||
}
|
||||
|
||||
if case .Error(let e) = body_parse_results {
|
||||
return .Error(e)
|
||||
}
|
||||
|
||||
// There should only be a single table!
|
||||
/// TODO: Check the semantics here.
|
||||
if tables.count > 1 {
|
||||
/// TODO: Make this error message better.
|
||||
/// IDEA: Add a "compilation context" for the error message into the `CompilationContext`
|
||||
// that can be retrieved to make the error messages nicer.
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "More than one table in control declaration"))
|
||||
}
|
||||
|
||||
// Check to make sure that there is an apply.
|
||||
guard let apply = apply else {
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "Missing apply in control declaration"))
|
||||
}
|
||||
|
||||
let declared_control =
|
||||
Declaration(
|
||||
TypedIdentifier(
|
||||
id: control_name,
|
||||
withType: P4Type(
|
||||
Control(
|
||||
named: control_name, withParameters: control_parameters, withTable: tables[0],
|
||||
withActions: Actions(withActions: actions), withApply: apply))))
|
||||
|
||||
// Don't forget to add the newly declared Control to the instance that we were given
|
||||
// (and not the one that we entered to do the parsing of this Control).
|
||||
return .Ok(
|
||||
(
|
||||
declared_control,
|
||||
context.extern_context
|
||||
? context
|
||||
: context.update(
|
||||
newInstances: context.instances.declare(
|
||||
identifier: control_name, withValue: declared_control.identifier.type))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
extension Action: Compilable {
|
||||
public typealias T = Action
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(P4Lang.Action, CompilerContext)> {
|
||||
#RequireNodeType<Node, (P4DataType, CompilerContext)>(
|
||||
node: node, type: "action_declaration", nice_type_name: "Action Declaration")
|
||||
|
||||
var walker = Walker(node: node)
|
||||
var current_node: Node? = .none
|
||||
var current_context = context
|
||||
|
||||
// Skip action keyword
|
||||
walker.next()
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(P4Lang.Action, CompilerContext)>.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing action declaration component"))
|
||||
)
|
||||
|
||||
guard
|
||||
case .Ok(let action_name) = Identifier.Compile(
|
||||
node: current_node!, withContext: current_context)
|
||||
else {
|
||||
return Result.Error(
|
||||
Error(withMessage: "Could not parse an action name from \(current_node!.text!)"))
|
||||
}
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(P4Lang.Action, CompilerContext)>.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing action declaration component"))
|
||||
)
|
||||
|
||||
let maybe_action_parameters = ParameterList.Compile(
|
||||
node: current_node!, withContext: current_context)
|
||||
guard case .Ok((let action_parameters, let updated_context)) = maybe_action_parameters
|
||||
else {
|
||||
return .Error(maybe_action_parameters.error()!)
|
||||
}
|
||||
current_context = updated_context
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(P4Lang.Action, CompilerContext)>.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing action declaration component"))
|
||||
)
|
||||
|
||||
// Add the parameters into scope.
|
||||
var function_scope = context.instances.enter()
|
||||
for parameter in action_parameters.parameters {
|
||||
function_scope = function_scope.declare(
|
||||
identifier: parameter.name, withValue: parameter.type)
|
||||
}
|
||||
|
||||
let maybe_action_body = BlockStatement.Compile(
|
||||
node: current_node!, withContext: context.update(newInstances: function_scope))
|
||||
guard case .Ok((let action_body, _)) = maybe_action_body else {
|
||||
return .Error(maybe_action_body.error()!)
|
||||
}
|
||||
|
||||
/// TODO: Actions cannot contain switches!
|
||||
|
||||
return .Ok(
|
||||
(
|
||||
Action(
|
||||
named: action_name, withParameters: action_parameters,
|
||||
withBody: (action_body as! BlockStatement)),
|
||||
current_context
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
extension TableKeyEntry: Compilable {
|
||||
public typealias T = TableKeyEntry
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(P4Lang.TableKeyEntry, CompilerContext)> {
|
||||
|
||||
#RequireNodeType<Node, (P4DataType, CompilerContext)>(
|
||||
node: node, type: "table_key_entry", nice_type_name: "Table Key Entry")
|
||||
|
||||
var walker = Walker(node: node)
|
||||
|
||||
var current_node: Node? = .none
|
||||
|
||||
let current_context = context
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(P4Lang.TableKeyEntry, CompilerContext)>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing table key entry declaration component")))
|
||||
|
||||
let maybe_keyset_expression = KeysetExpression.compile(
|
||||
node: current_node!, withContext: current_context)
|
||||
guard case .Ok(let keyset_expression) = maybe_keyset_expression else {
|
||||
return Result.Error(maybe_keyset_expression.error()!)
|
||||
}
|
||||
|
||||
walker.next()
|
||||
// Skip the ':'
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(P4Lang.TableKeyEntry, CompilerContext)>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing table key entry declaration component")))
|
||||
|
||||
let maybe_match_type = TableKeyMatchType.Compile(
|
||||
node: current_node!, withContext: current_context)
|
||||
guard case .Ok((let match_type, _)) = maybe_match_type else {
|
||||
return .Error(maybe_match_type.error()!)
|
||||
}
|
||||
|
||||
return .Ok((TableKeyEntry(keyset_expression as! KeysetExpression, match_type), current_context))
|
||||
}
|
||||
}
|
||||
|
||||
extension TableKeyMatchType: Compilable {
|
||||
public typealias T = TableKeyMatchType
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(P4Lang.TableKeyMatchType, CompilerContext)> {
|
||||
#RequireNodeType<Node, (TableKeyMatchType, CompilerContext)>(
|
||||
node: node, type: "table_key_match_type", nice_type_name: "Table Key Match Type")
|
||||
|
||||
if node.text! == "exact" {
|
||||
return .Ok((TableKeyMatchType.Exact, context))
|
||||
}
|
||||
return .Error(ErrorOnNode(node: node, withError: "\(node.text!) is not a valid match type)"))
|
||||
}
|
||||
}
|
||||
|
||||
extension TableKeys: Compilable {
|
||||
public typealias T = TableKeys
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(P4Lang.TableKeys, CompilerContext)> {
|
||||
#RequireNodeType<Node, (TableKeyMatchType, CompilerContext)>(
|
||||
node: node, type: "table_keys", nice_type_name: "Table Keys")
|
||||
|
||||
var walker = Walker(node: node)
|
||||
// Skip the
|
||||
// keys = {
|
||||
// 1 2 3
|
||||
walker.next() // 1
|
||||
walker.next() // 2
|
||||
walker.next() // 3
|
||||
|
||||
var current_node: Node? = .none
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(P4Lang.TableKeys, CompilerContext)>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing table keys declaration component in control declaration"))
|
||||
)
|
||||
|
||||
let (keys, errors) = walker.try_map(n: node.childCount - 1, onlyNamed: true) { current_node in
|
||||
return switch TableKeyEntry.Compile(node: current_node, withContext: context) {
|
||||
case .Ok((let keyset_expression, _)): .Ok(keyset_expression)
|
||||
case .Error(let e): .Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
if !errors.isEmpty {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: node,
|
||||
withError: "Error(s) parsing table key: "
|
||||
+ (errors.map { error in
|
||||
return "\(error.msg)"
|
||||
}.joined(separator: ";"))))
|
||||
}
|
||||
|
||||
return .Ok((TableKeys(withEntries: keys), context))
|
||||
}
|
||||
}
|
||||
|
||||
extension TableActionsProperty: Compilable {
|
||||
public typealias T = TableActionsProperty
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(TableActionsProperty, CompilerContext)> {
|
||||
#RequireNodeType<Node, (TableActionsProperty, CompilerContext)>(
|
||||
node: node, type: "table_actions", nice_type_name: "Table Actions")
|
||||
|
||||
var walker = Walker(node: node)
|
||||
// Skip the
|
||||
// actions = {
|
||||
// 1 2 3
|
||||
walker.next() // 1
|
||||
walker.next() // 2
|
||||
walker.next() // 3
|
||||
|
||||
var current_node: Node? = .none
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(TableActionsProperty, CompilerContext)>.Error(
|
||||
ErrorOnNode(
|
||||
node: node,
|
||||
withError: "Missing table actions declaration component in control declaration"))
|
||||
)
|
||||
|
||||
let (actions, errors) = walker.try_map(n: node.childCount - 1, onlyNamed: true) {
|
||||
current_node in
|
||||
switch Identifier.Compile(node: current_node, withContext: context) {
|
||||
case .Ok(let listed_action):
|
||||
switch context.types.lookup(identifier: listed_action) {
|
||||
case .Ok(let maybe_action):
|
||||
if maybe_action.eq(rhs: Action()) {
|
||||
return .Ok(TypedIdentifier(id: listed_action, withType: P4Type(maybe_action)))
|
||||
}
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "\(listed_action) does not name an action"))
|
||||
case .Error(let e): return .Error(e)
|
||||
}
|
||||
case .Error(let e): return .Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
if !errors.isEmpty {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: node,
|
||||
withError: "Error(s) parsing table actions: "
|
||||
+ (errors.map { error in
|
||||
return "\(error.msg)"
|
||||
}.joined(separator: ";"))))
|
||||
}
|
||||
|
||||
return .Ok((TableActionsProperty(actions), context))
|
||||
}
|
||||
}
|
||||
extension TablePropertyList: Compilable {
|
||||
public typealias T = TablePropertyList
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(P4Lang.TablePropertyList, CompilerContext)> {
|
||||
|
||||
#RequireNodeType<Node, (P4DataType, CompilerContext)>(
|
||||
node: node, type: "table_property_list", nice_type_name: "Table Property List")
|
||||
|
||||
var current_context = context
|
||||
|
||||
var keys: [TableKeys] = Array()
|
||||
var actions: [TableActionsProperty] = Array()
|
||||
var errors: [Error] = Array()
|
||||
|
||||
node.enumerateNamedChildren { child in
|
||||
if child.nodeType == "table_keys" {
|
||||
switch TableKeys.Compile(node: child, withContext: current_context) {
|
||||
case .Ok((let table_key, let updated_context)):
|
||||
current_context = updated_context
|
||||
keys.append(table_key)
|
||||
case .Error(let e): errors.append(e)
|
||||
}
|
||||
} else if child.nodeType == "table_actions" {
|
||||
switch TableActionsProperty.Compile(node: child, withContext: current_context) {
|
||||
case .Ok((let table_action_property, let updated_context)):
|
||||
current_context = updated_context
|
||||
actions.append(table_action_property)
|
||||
case .Error(let e): errors.append(e)
|
||||
}
|
||||
} else {
|
||||
errors.append(
|
||||
ErrorOnNode(node: child, withError: "Uknown node type in control declaration"))
|
||||
}
|
||||
}
|
||||
|
||||
if !errors.isEmpty {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: node,
|
||||
withError: "Error(s) parsing property list: "
|
||||
+ (errors.map { error in
|
||||
return "\(error.msg)"
|
||||
}.joined(separator: ";"))))
|
||||
}
|
||||
|
||||
// There should be only one table keys!
|
||||
if keys.count > 1 {
|
||||
// Todo: Make this error message better.
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "More than one key set in table property list"))
|
||||
}
|
||||
|
||||
// There should be only one table actions!
|
||||
if actions.count > 1 {
|
||||
// Todo: Make this error message better.
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "More than one actions in table property list"))
|
||||
}
|
||||
if actions.isEmpty {
|
||||
actions.append(TableActionsProperty())
|
||||
}
|
||||
|
||||
return .Ok((TablePropertyList(withActions: actions[0], withKeys: keys[0]), current_context))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension Table: Compilable {
|
||||
public typealias T = Table
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(P4Lang.Table, CompilerContext)> {
|
||||
|
||||
let table_declaration_node = node
|
||||
#RequireNodeType<Node, (P4DataType, CompilerContext)>(
|
||||
node: table_declaration_node, type: "table_declaration", nice_type_name: "Table Declaration")
|
||||
|
||||
var walker = Walker(node: table_declaration_node)
|
||||
|
||||
var current_node: Node? = .none
|
||||
|
||||
let current_context = context
|
||||
|
||||
walker.next() // Skip the XXX?
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(P4Lang.Table, CompilerContext)>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing table declaration component")))
|
||||
|
||||
guard
|
||||
case .Ok(let table_name) = Identifier.Compile(
|
||||
node: current_node!, withContext: current_context)
|
||||
else {
|
||||
return Result.Error(
|
||||
Error(withMessage: "Could not parse a table name from \(current_node!.text!)"))
|
||||
}
|
||||
|
||||
walker.next()
|
||||
// Skip the '{'
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(P4Lang.Table, CompilerContext)>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing table declaration component")))
|
||||
|
||||
let maybe_table_property_list = TablePropertyList.Compile(
|
||||
node: current_node!, withContext: current_context)
|
||||
guard case .Ok((let table_property_list, _)) = maybe_table_property_list else {
|
||||
return Result.Error(maybe_table_property_list.error()!)
|
||||
}
|
||||
|
||||
return .Ok(
|
||||
(Table(withName: table_name, withPropertyList: table_property_list), current_context))
|
||||
}
|
||||
}
|
||||
|
||||
extension ExternDeclaration: CompilableDeclaration {
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(Declaration, CompilerContext)?> {
|
||||
let extern_declaration_node = node
|
||||
#RequireNodeType<Node, (Declaration, CompilerContext)>(
|
||||
node: extern_declaration_node, type: "extern_declaration",
|
||||
nice_type_name: "Extern Declaration")
|
||||
let declaration_node = extern_declaration_node.child(at: 1)!
|
||||
#RequireNodeType<Node, (Declaration, CompilerContext)>(
|
||||
node: declaration_node, type: "declaration", nice_type_name: "Declaration")
|
||||
let declarationed_node = declaration_node.child(at: 0)!
|
||||
|
||||
let maybe_declared = Declaration.Compile(
|
||||
node: declarationed_node, withContext: context.update(newExtern: true))
|
||||
|
||||
guard case .Ok(let maybe_declared) = maybe_declared else {
|
||||
return .Error(maybe_declared.error()!)
|
||||
}
|
||||
|
||||
guard case .some((let declared, _)) = maybe_declared else {
|
||||
return .Ok(.none)
|
||||
}
|
||||
|
||||
// Before we are okay with this declaration, it must already be registered as an extern
|
||||
// with the matching "stuff".
|
||||
|
||||
let found_ffi = context.ffis.first { ffi in
|
||||
ffi.type().dataType().eq(rhs: declared.identifier.type.dataType())
|
||||
}
|
||||
|
||||
guard let found_ffi = found_ffi else {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: declarationed_node,
|
||||
withError:
|
||||
"Could not find a foreign function that matches the extern declaration (\(declared))"))
|
||||
}
|
||||
|
||||
let extern_declaration = Declaration(extern: declared, ffi: found_ffi)
|
||||
|
||||
return .Ok(
|
||||
(
|
||||
extern_declaration,
|
||||
context.update(
|
||||
newExterns: context.externs.declare(
|
||||
identifier: declared.identifier, withValue: extern_declaration))
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1,755 +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(ErrorOnNode(node: node, 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(
|
||||
ErrorOnNode(node: node, 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)!
|
||||
#SkipUnlessNodeType<SwiftTreeSitter.Node>(node: node, type: "integer")
|
||||
if let parsed_int = Int(node.text!) {
|
||||
return .Ok(P4Value(P4IntValue(withValue: parsed_int)))
|
||||
} else {
|
||||
return .Error(ErrorOnNode(node: node, 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(ErrorOnNode(node: node, withError: "Could not find selector expression"))
|
||||
}
|
||||
|
||||
guard let select_body_node = node.child(at: 5),
|
||||
select_body_node.nodeType == "selectBody"
|
||||
else {
|
||||
return .Error(ErrorOnNode(node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: selector_node,
|
||||
withError:
|
||||
"Could not parse transition select expression selector expression: \(maybe_selector.error()!)"
|
||||
))
|
||||
}
|
||||
|
||||
var sces: [SelectCaseExpression] = Array()
|
||||
var sces_errors: [Error] = Array()
|
||||
|
||||
select_body_node.enumerateNamedChildren { current_node in
|
||||
let maybe_parsed_cse = SelectCaseExpression.compile(
|
||||
node: current_node, withContext: context.update(newExpectation: selector.type()))
|
||||
if case .Ok(let parsed_cse) = maybe_parsed_cse {
|
||||
sces.append(parsed_cse as! SelectCaseExpression)
|
||||
} else {
|
||||
sces_errors.append(Error(withMessage: "\(maybe_parsed_cse.error()!)"))
|
||||
}
|
||||
}
|
||||
|
||||
if !sces_errors.isEmpty {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage: "Error(s) parsing select cases: "
|
||||
+ (sces_errors.map { error in
|
||||
return "\(error.msg)"
|
||||
}.joined(separator: ";"))))
|
||||
}
|
||||
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(
|
||||
ErrorOnNode(node: keysetexpression_node, withError: "Missing expected keyset expression"))
|
||||
}
|
||||
|
||||
let keysetexpression = maybe_keysetexpression as! KeysetExpression
|
||||
|
||||
if case .Error(let e) = keysetexpression.compatible(type: context.expected_type!) {
|
||||
return .Error(ErrorOnNode(node: keysetexpression_node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing binary operator for binary operator expression")))
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<EvaluatableExpression?>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing RHS for binary operator expression")))
|
||||
|
||||
let right_hand_side_raw = current_node!
|
||||
|
||||
let maybe_left_hand_side = Expression.Compile(node: left_hand_side_raw, withContext: context)
|
||||
guard case Result.Ok(let left_hand_side) = maybe_left_hand_side else {
|
||||
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, P4Type, BinaryOperatorChecker?, BinaryOperatorEvaluator)] = [
|
||||
"binaryEqualOperatorExpression": (
|
||||
"Binary Equal", P4Type(P4Boolean()), Optional<BinaryOperatorChecker>.none,
|
||||
binary_equal_operator_evaluator
|
||||
),
|
||||
"binaryLessThanOperatorExpression": (
|
||||
"Binary Less Than", P4Type(P4Boolean()), Optional<BinaryOperatorChecker>.none,
|
||||
binary_lt_operator_evaluator
|
||||
),
|
||||
"binaryLessThanEqualOperatorExpression": (
|
||||
"Binary Less Than Or Equal", P4Type(P4Boolean()), Optional<BinaryOperatorChecker>.none,
|
||||
binary_lte_operator_evaluator
|
||||
),
|
||||
"binaryGreaterThanOperatorExpression": (
|
||||
"Binary Greater Than", P4Type(P4Boolean()), Optional<BinaryOperatorChecker>.none,
|
||||
binary_gt_operator_evaluator
|
||||
),
|
||||
"binaryGreaterThanEqualOperatorExpression": (
|
||||
"Binary Greater Than Or Equal", P4Type(P4Boolean()), Optional<BinaryOperatorChecker>.none,
|
||||
binary_gte_operator_evaluator
|
||||
),
|
||||
"binaryAndOperatorExpression": (
|
||||
"Binary Or", P4Type(P4Boolean()), binary_and_or_operator_checker,
|
||||
binary_and_operator_evaluator
|
||||
),
|
||||
"binaryOrOperatorExpression": (
|
||||
"Binary And", P4Type(P4Boolean()), binary_and_or_operator_checker,
|
||||
binary_or_operator_evaluator
|
||||
),
|
||||
"binaryAddOperatorExpression": (
|
||||
"Binary Add", P4Type(P4Int()), binary_int_math_operator_checker,
|
||||
binary_add_operator_evaluator
|
||||
),
|
||||
"binarySubtractOperatorExpression": (
|
||||
"Binary Subtract", P4Type(P4Int()), binary_int_math_operator_checker,
|
||||
binary_subtract_operator_evaluator
|
||||
),
|
||||
"binaryMultiplyOperatorExpression": (
|
||||
"Binary Multiply", P4Type(P4Int()), binary_int_math_operator_checker,
|
||||
binary_multiply_operator_evaluator
|
||||
),
|
||||
"binaryDivideOperatorExpression": (
|
||||
"Binary Divide", P4Type(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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing [ for array access expression")))
|
||||
|
||||
walker.next()
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<EvaluatableExpression?>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing 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.dataType() as? P4Array else {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: array_access_identifier_node,
|
||||
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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing . for field access expression")))
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<EvaluatableExpression?>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing 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().dataType() as? P4Struct else {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: struct_identifier_node,
|
||||
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(
|
||||
ErrorOnNode(
|
||||
node: field_name_node,
|
||||
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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(node: current_node!, 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.dataType() {
|
||||
case is FunctionDeclaration: Result.Ok((.none, callee))
|
||||
default:
|
||||
.Error(ErrorOnNode(node: current_node!, withError: "\(callee_name) is not a function"))
|
||||
}
|
||||
default: .Error(e)
|
||||
}
|
||||
} else {
|
||||
maybe_callee
|
||||
}
|
||||
|
||||
guard case .Ok(let callee) = maybe_callee else {
|
||||
return .Error(maybe_callee.error()!)
|
||||
}
|
||||
|
||||
walker.next()
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<EvaluatableExpression?>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing function call component")))
|
||||
|
||||
let maybe_argument_list = ArgumentList.Compile(node: current_node!, withContext: context)
|
||||
|
||||
guard case .Ok((let arguments, _)) = maybe_argument_list else {
|
||||
return .Error(maybe_argument_list.error()!)
|
||||
}
|
||||
|
||||
// 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().dataType() as! FunctionDeclaration).params)
|
||||
default: Optional<ParameterList>.none
|
||||
}
|
||||
|
||||
guard case .some(let params) = params else {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: node,
|
||||
withError: "Could not lookup the parameters for the called function (\(callee_name))"))
|
||||
}
|
||||
|
||||
if case .Error(let e) = arguments.compatible(params) {
|
||||
return .Error(e)
|
||||
}
|
||||
|
||||
// All good!
|
||||
|
||||
return switch callee {
|
||||
case (.some(let callee), .none): .Ok(FunctionCall(callee, withArguments: arguments))
|
||||
case (.none, .some(let callee)): .Ok(FunctionCall(callee.ffi!, withArguments: arguments))
|
||||
default:
|
||||
Result.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Unexpected error occurred calling function named (\(callee_name))"
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,302 +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(
|
||||
ErrorOnNode(
|
||||
node: node,
|
||||
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(ErrorOnNode(node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: statement,
|
||||
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(
|
||||
ErrorOnNode(node: node, 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(
|
||||
ErrorOnNode(node: node, withError: "Could not find transition select expression"))
|
||||
}
|
||||
|
||||
guard let next_node = tse_node.child(at: 0) else {
|
||||
return .Error(
|
||||
ErrorOnNode(
|
||||
node: node,
|
||||
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(ErrorOnNode(node: node, withError: "Did not find expected statements"))
|
||||
}
|
||||
|
||||
var parse_errs: [Error] = Array()
|
||||
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):
|
||||
parse_errs.append(e)
|
||||
}
|
||||
}
|
||||
|
||||
if !parse_errs.isEmpty {
|
||||
return Result.Error(
|
||||
Error(
|
||||
withMessage: parse_errs.map { err in
|
||||
return String(err.msg)
|
||||
}.joined(separator: ";")))
|
||||
}
|
||||
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(
|
||||
ErrorOnNode(node: node, withError: "Did not find a parser state declaration"))
|
||||
}
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing elements in parser state declaration")))
|
||||
|
||||
if current_node!.nodeType == "annotations" {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: current_node!, 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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing body of state declaration")))
|
||||
|
||||
var parse_errs: [Error] = Array()
|
||||
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):
|
||||
parse_errs.append(error)
|
||||
}
|
||||
walker.next()
|
||||
}
|
||||
|
||||
if !parse_errs.isEmpty {
|
||||
return Result.Error(
|
||||
Error(
|
||||
withMessage: parse_errs.map { err in
|
||||
return String(err.msg)
|
||||
}.joined(separator: ";")))
|
||||
}
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
|
||||
ErrorOnNode(
|
||||
node: node, withError: "Missing transition statement of state declaration")))
|
||||
|
||||
return TransitionStatement.Compile(
|
||||
node: 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: Error? = .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,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
|
||||
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: [Error] = Array()
|
||||
|
||||
// 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.append(e)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If none of the declaration parsers chose to parse, that's an error, too!
|
||||
if !found_parser {
|
||||
errors.append(
|
||||
ErrorOnNode(
|
||||
node: specific_declaration_node, withError: "Could not find parser for declaration node"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if !errors.isEmpty {
|
||||
return Result.Error(
|
||||
Error(
|
||||
withMessage: errors.map { error in
|
||||
return error.msg
|
||||
}.joined(separator: ";")))
|
||||
}
|
||||
|
||||
// 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<P4DataType?>
|
||||
}
|
||||
|
||||
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,345 +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(
|
||||
ErrorOnNode(node: node, withError: "Malformed block statement")))
|
||||
|
||||
if current_node!.nodeType != "{" {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: current_node!, withError: "Missing { on block statement"))
|
||||
}
|
||||
|
||||
var statements: [EvaluatableStatement] = Array()
|
||||
var parse_err: Error? = .none
|
||||
var current_context = context
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<(EvaluatableStatement, CompilerContext)>.Error(
|
||||
ErrorOnNode(node: node, withError: "Malformed block statement")))
|
||||
|
||||
if current_node!.nodeType == "statements" {
|
||||
switch Parser.Statements.Compile(
|
||||
node: 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(
|
||||
ErrorOnNode(node: node, withError: "Malformed block statement")))
|
||||
|
||||
if current_node!.nodeType != "}" {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: current_node!, 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(
|
||||
ErrorOnNode(node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: node, 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 = declaration_p4_type.def()
|
||||
// If there is an initializer, it must be an expression.
|
||||
if let rvalue = maybe_rvalue {
|
||||
guard rvalue.nodeType == "expression" else {
|
||||
return Result.Error(
|
||||
ErrorOnNode(
|
||||
node: node,
|
||||
withError: "initial value for declaration statement is not an expression"))
|
||||
}
|
||||
|
||||
let maybe_parsed_rvalue = Expression.Compile(node: rvalue, 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 rvalue with type \(parsed_rvalue.type())"
|
||||
))
|
||||
}
|
||||
}
|
||||
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(
|
||||
ErrorOnNode(node: node, withError: "Missing lvalue in assignment statement"))
|
||||
}
|
||||
|
||||
guard let rvalue_node = node.child(at: 2),
|
||||
rvalue_node.nodeType == "expression"
|
||||
else {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, 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(
|
||||
ErrorOnNode(
|
||||
node: lvalue_node,
|
||||
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().dataType().eq(rhs: context.expected_type!.dataType()) {
|
||||
.Ok((ReturnStatement(result), context))
|
||||
} else {
|
||||
.Error(
|
||||
ErrorOnNode(
|
||||
node: node,
|
||||
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,84 +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.P4DataType)?> {
|
||||
return type.text == "bool" ? .Ok(P4Boolean()) : .Ok(.none)
|
||||
}
|
||||
}
|
||||
|
||||
extension P4Int: CompilableType {
|
||||
public static func CompileType(
|
||||
type: SwiftTreeSitter.Node, withContext: CompilerContext
|
||||
) -> Common.Result<(any Common.P4DataType)?> {
|
||||
return type.text == "int" ? .Ok(P4Int()) : .Ok(.none)
|
||||
}
|
||||
}
|
||||
|
||||
extension P4String: CompilableType {
|
||||
public static func CompileType(
|
||||
type: SwiftTreeSitter.Node, withContext: CompilerContext
|
||||
) -> Common.Result<(any Common.P4DataType)?> {
|
||||
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.P4DataType)?> {
|
||||
|
||||
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<P4Type> {
|
||||
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(P4Type(type))
|
||||
case .Ok(.none): continue
|
||||
case .Error(let e): return .Error(e)
|
||||
}
|
||||
}
|
||||
return Result.Error(Error(withMessage: "Type name not recognized"))
|
||||
}
|
||||
}
|
||||
@@ -1,298 +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, P4DataType, P4DataValue {
|
||||
public func type() -> any Common.P4DataType {
|
||||
return self
|
||||
}
|
||||
|
||||
public func eq(rhs: any Common.P4DataValue) -> Bool {
|
||||
return switch rhs {
|
||||
case let arhs as Action: self.name == arhs.name
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
public func eq(rhs: any Common.P4DataType) -> Bool {
|
||||
return switch rhs {
|
||||
case is Action: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
public func lt(rhs: any Common.P4DataValue) -> Bool {
|
||||
switch rhs {
|
||||
case let arhs as Action: return self.name < arhs.name
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
public func lte(rhs: any Common.P4DataValue) -> Bool {
|
||||
switch rhs {
|
||||
case let arhs as Action: return self.name <= arhs.name
|
||||
default: return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public func gt(rhs: any Common.P4DataValue) -> Bool {
|
||||
switch rhs {
|
||||
case let arhs as Action: return self.name > arhs.name
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
public func gte(rhs: any Common.P4DataValue) -> Bool {
|
||||
switch rhs {
|
||||
case let arhs as Action: return self.name >= arhs.name
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
public func def() -> any Common.P4DataValue {
|
||||
return Action()
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Action: "
|
||||
+ "\(self.name) with parameters \(self.params) and body \(String(describing: self.body))"
|
||||
}
|
||||
|
||||
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: P4DataType, P4DataValue, Equatable, CustomStringConvertible {
|
||||
public static func == (lhs: Control, rhs: Control) -> Bool {
|
||||
// Two "bare" controls are always equal.
|
||||
return true
|
||||
}
|
||||
|
||||
public func eq(rhs: any Common.P4DataType) -> Bool {
|
||||
return switch rhs {
|
||||
case is Control: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
public func type() -> any Common.P4DataType {
|
||||
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() -> any P4DataValue {
|
||||
return Control(
|
||||
named: Identifier(name: ""),
|
||||
withParameters: ParameterList(),
|
||||
withTable: Table(
|
||||
withName: Identifier(name: "empty"),
|
||||
withPropertyList: TablePropertyList(
|
||||
withActions: TableActionsProperty(), withKeys: TableKeys())),
|
||||
withActions: Actions(withActions: []), withApply: ApplyStatement())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
public struct Declaration: P4DataType {
|
||||
public let identifier: TypedIdentifier
|
||||
public let extern: Bool
|
||||
public let ffi: P4FFI?
|
||||
|
||||
public init(_ id: TypedIdentifier) {
|
||||
identifier = id
|
||||
ffi = .none
|
||||
self.extern = false
|
||||
}
|
||||
|
||||
public init(extern: Declaration, ffi: P4FFI) {
|
||||
identifier = extern.identifier
|
||||
self.ffi = ffi
|
||||
self.extern = true
|
||||
}
|
||||
|
||||
public func eq(rhs: any Common.P4DataType) -> Bool {
|
||||
return switch rhs {
|
||||
case let rrhs as Declaration:
|
||||
self.identifier.type.dataType().eq(rhs: rrhs.identifier.type.dataType())
|
||||
&& self.extern == rrhs.extern
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
public func def() -> any Common.P4DataValue {
|
||||
/// TODO: Is a default of the extern'd type the right way to go?
|
||||
return self.identifier.type.dataType().def()
|
||||
}
|
||||
|
||||
public func type() -> any Common.P4DataType {
|
||||
return self
|
||||
}
|
||||
public var description: String {
|
||||
return "Extern \(self.identifier)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct ExternDeclaration {}
|
||||
|
||||
public struct FunctionDeclaration: P4DataType, P4DataValue {
|
||||
public func type() -> any Common.P4DataType {
|
||||
return self
|
||||
}
|
||||
|
||||
public func eq(rhs: any Common.P4DataType) -> 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 P4DataType)
|
||||
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() -> any Common.P4DataValue {
|
||||
return FunctionDeclaration(
|
||||
named: Identifier(name: ""), ofType: P4Type(P4Boolean()),
|
||||
withParameters: ParameterList([]),
|
||||
withBody: .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: P4Type
|
||||
|
||||
public init(
|
||||
named name: Identifier, ofType type: P4Type, withParameters parameters: ParameterList,
|
||||
withBody body: BlockStatement?
|
||||
) {
|
||||
self.name = name
|
||||
self.tipe = type
|
||||
self.params = parameters
|
||||
self.body = body
|
||||
}
|
||||
}
|
||||
@@ -1,147 +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: P4Type) -> Result<()> {
|
||||
if let key_type = self.key.type().dataType() 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, P4Type, (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: P4DataType
|
||||
|
||||
public init(_ callee: FunctionDeclaration, withArguments arguments: ArgumentList) {
|
||||
self.callee = (callee, .none)
|
||||
self.arguments = arguments
|
||||
self.return_type = callee.tipe.dataType()
|
||||
}
|
||||
|
||||
public init(_ callee: P4FFI, withArguments arguments: ArgumentList) {
|
||||
self.callee = (.none, callee)
|
||||
self.arguments = arguments
|
||||
/// ASSUME: That the FFI has been checked and the type is always a function declaration.
|
||||
self.return_type = (callee.type().dataType() as! FunctionDeclaration).tipe.dataType()
|
||||
}
|
||||
}
|
||||
@@ -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: P4DataType, 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.P4DataType) -> Bool {
|
||||
return switch rhs {
|
||||
case is ParserState: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
public func type() -> any Common.P4DataType {
|
||||
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() -> any P4DataValue {
|
||||
return ParserState()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.P4DataType) -> Bool {
|
||||
return switch rhs {
|
||||
case is ParserState: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
public override func type() -> any Common.P4DataType {
|
||||
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: P4DataType, P4DataValue {
|
||||
public func type() -> any Common.P4DataType {
|
||||
return self
|
||||
}
|
||||
|
||||
public func eq(rhs: any Common.P4DataType) -> 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() -> any P4DataValue {
|
||||
return Parser(withName: Identifier(name: ""))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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: [P4DataType] = Array()
|
||||
public var externs: [P4DataType] = Array()
|
||||
public var instances: [P4Type] = Array()
|
||||
|
||||
/// Type of closure for filtering results from ``Program/InstancesWithTypes(_:)``
|
||||
public typealias TypeFilter = (P4Type) -> Bool
|
||||
/// Type of closure for filtering results from ``Program/TypesWithTypes(_:)``
|
||||
public typealias DataTypeFilter = (P4DataType) -> Bool
|
||||
|
||||
/// Retrieve global instances in the compiled P4 program.
|
||||
public func InstancesWithTypes() -> [P4Type] {
|
||||
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) -> [P4Type] {
|
||||
return self.instances.filter { instance in
|
||||
filter(instance)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve global types in the compiled P4 program.
|
||||
public func TypesWithTypes() -> [P4DataType] {
|
||||
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) -> [P4DataType] {
|
||||
return self.types.filter { instance in
|
||||
filter(instance)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve extern types in the compiled P4 program.
|
||||
public func Externs() -> [P4DataType] {
|
||||
return self.externs
|
||||
}
|
||||
|
||||
/// Retrieve extern types declared in the compiled P4 program.
|
||||
///
|
||||
/// Use the given filter to select which of the extern types
|
||||
/// declared in the compiled P4 program to retrieve.
|
||||
///
|
||||
/// If the compiled P4 program (from the source in the
|
||||
/// string `p4_program_with_struct_decl`) has two extern structs declared and
|
||||
/// you only want to select the one named `agg`, you could
|
||||
/// use a filter like
|
||||
///
|
||||
/// @Snippet(path: "use-program-typeswithtypes", slice: "include")
|
||||
///
|
||||
public func Externs(_ filter: DataTypeFilter) -> [P4DataType] {
|
||||
return self.externs.filter { instance in
|
||||
filter(instance)
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the program's main parser
|
||||
///
|
||||
/// Note: For now, the main parser is expected to be named main_parser.
|
||||
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.dataType() 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
|
||||
}
|
||||
}
|
||||
@@ -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,13 +16,16 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Common
|
||||
import SwiftTreeSitter
|
||||
import TreeSitterExtensions
|
||||
import TreeSitterP4
|
||||
|
||||
public struct AttributedP4Type {
|
||||
public let type: P4DataType
|
||||
public let attributes: P4Type
|
||||
|
||||
public init(_ type: P4DataType, _ attributes: P4Type) {
|
||||
self.type = type
|
||||
self.attributes = attributes
|
||||
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)
|
||||
}
|
||||
@@ -16,3 +16,18 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Common
|
||||
|
||||
public struct CompilationFeedback {
|
||||
public let feedback: String
|
||||
|
||||
static func markup(
|
||||
_ source: SourceCode, _ errors: [any Errorable], _ formatter: any Formattable
|
||||
) -> String {
|
||||
errors.map { $0.format(formatter, source) }.joined(separator: "\n")
|
||||
}
|
||||
|
||||
public init(_ source: SourceCode, _ errors: [any Errorable], _ formatter: any Formattable) {
|
||||
self.feedback = Self.markup(source, errors, formatter)
|
||||
}
|
||||
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -54,8 +54,8 @@ public struct Walker {
|
||||
|
||||
public func try_map<T>(
|
||||
n: Int, onlyNamed: Bool = false, todo: (Node) -> Result<T>
|
||||
) -> ([T], [Error]) {
|
||||
var errors: [Error] = Array()
|
||||
) -> ([T], [any Errorable]) {
|
||||
var errors: [any Errorable] = Array()
|
||||
var results: [T] = Array()
|
||||
|
||||
for currentChildIdx in currentChildIdx..<n {
|
||||
@@ -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() -> P4Type {
|
||||
return P4Type(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() -> P4Type {
|
||||
return P4Type(ParserState())
|
||||
}
|
||||
}
|
||||
|
||||
// Variables are evaluatable because they can be looked up by identifiers.
|
||||
extension TypedIdentifier: EvaluatableExpression {
|
||||
public func type() -> P4Type {
|
||||
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().dataType().eq(rhs: P4Boolean()) && right.type().dataType().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().dataType().eq(rhs: P4Int()) && right.type().dataType().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() -> P4Type {
|
||||
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() -> P4Type {
|
||||
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() -> P4Type {
|
||||
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() -> P4Type {
|
||||
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() -> P4Type {
|
||||
return P4Type(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().dataType().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().dataType().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, P4Type.ReadOnly(accept.type())))
|
||||
execution = execution.declare(
|
||||
identifier: AsInstantiatedParserState(reject.state()).state,
|
||||
withValue: P4Value(reject, P4Type.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().dataType().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 @@
|
||||
Success
|
||||
@@ -0,0 +1,13 @@
|
||||
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 {
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
state start {
|
||||
Testing ts;
|
||||
ts.yesno = true;
|
||||
ts.count = 5;
|
||||
transition select (ts.count == 5) {
|
||||
true: accept;
|
||||
false: reject;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
parser main_parser() {
|
||||
#include <annotate-parser-state.p4>
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
bool yesno;
|
||||
int count;
|
||||
@@ -0,0 +1,4 @@
|
||||
struct Testing {
|
||||
#include <annotate-struct-body.p4>
|
||||
};
|
||||
#include <annotate-parser.p4>
|
||||
@@ -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,9 @@
|
||||
state start {
|
||||
Testing ts;
|
||||
ts.yesno = true;
|
||||
ts.count = 5;
|
||||
transition select (ts.count == 5) {
|
||||
true: accept;
|
||||
false: reject;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
parser main_parser() {
|
||||
#include <file-loc-parser-state.p4>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
struct Testing {
|
||||
bool yesno;
|
||||
int count;
|
||||
};
|
||||
#include <file-loc-parser.p4>
|
||||
@@ -0,0 +1,2 @@
|
||||
#include <nested-split-a.p4>
|
||||
#include <nested-split-b.p4>
|
||||
@@ -0,0 +1 @@
|
||||
state start {
|
||||
@@ -0,0 +1,5 @@
|
||||
transition select (false) {
|
||||
true: reject;
|
||||
false: reject;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
parser main_parser() {
|
||||
#include <nested-split-2.p4>
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
#include <testing-split-oneline-a.p4>#include <testing-split-oneline-b.p4>
|
||||
@@ -0,0 +1 @@
|
||||
#include <testing-split-a.p4>
|
||||
@@ -0,0 +1 @@
|
||||
#include <unfound.p4>
|
||||
@@ -0,0 +1 @@
|
||||
#include <testing.p4>
|
||||
@@ -0,0 +1,3 @@
|
||||
parser main_parser() {
|
||||
state start {
|
||||
#include <testing-split-b.p4>
|
||||
@@ -0,0 +1,6 @@
|
||||
transition select (false) {
|
||||
true: reject;
|
||||
false: reject;
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
parser main_parser() {
|
||||
state start {
|
||||
transition
|
||||
@@ -0,0 +1,6 @@
|
||||
select (false) {
|
||||
true: reject;
|
||||
false: reject;
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
parser main_parser() {
|
||||
state start {
|
||||
transition select (false) {
|
||||
true: reject;
|
||||
false: reject;
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
Error: In action-parameters-wrong-order.p4, there was an error:
|
||||
...ion a(inout bool aa, int ax, inout bool ay) {...
|
||||
All parameters with direction must precede directionless parameters
|
||||
@@ -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
|
||||
@@ -0,0 +1,8 @@
|
||||
parser main_parser() {
|
||||
state start {
|
||||
transition select (false) {
|
||||
true: reject;
|
||||
false: reject;
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
Preprocessor Error: Could not open simple.p for preprocessing
|
||||
@@ -0,0 +1 @@
|
||||
Success
|
||||
@@ -0,0 +1,117 @@
|
||||
// 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 SystemPackage
|
||||
import Testing
|
||||
|
||||
private let TestGoldenPath: FilePath.Component = FilePath.Component("TestData")
|
||||
private let TestGoldenExtension: String = ".golden"
|
||||
|
||||
typealias Accessor =
|
||||
@convention(c) (
|
||||
_ outValue: UnsafeMutableRawPointer,
|
||||
_ type: UnsafeRawPointer,
|
||||
_ hint: UnsafeRawPointer?,
|
||||
_ reserved: UInt
|
||||
) -> CBool
|
||||
|
||||
typealias TestContentRecord = (
|
||||
kind: UInt32,
|
||||
reserved1: UInt32,
|
||||
accessor: Accessor?,
|
||||
context: UInt,
|
||||
reserved2: UInt
|
||||
)
|
||||
|
||||
func findPathEnv() -> [URL]? {
|
||||
for (key, value) in ProcessInfo.processInfo.environment {
|
||||
if key == "PATH" {
|
||||
return value.split(separator: ":").map { URL(filePath: "\($0)") }
|
||||
}
|
||||
}
|
||||
|
||||
return .none
|
||||
}
|
||||
|
||||
func findInPath(_ what: String) -> URL? {
|
||||
guard let bin_paths = findPathEnv() else {
|
||||
return .none
|
||||
}
|
||||
|
||||
let fm = FileManager()
|
||||
for bin_path in bin_paths {
|
||||
let sought = bin_path.appending(path: what)
|
||||
if fm.fileExists(atPath: sought.path) {
|
||||
return sought
|
||||
}
|
||||
}
|
||||
|
||||
return .none
|
||||
}
|
||||
|
||||
func swiftPath() -> URL? {
|
||||
return findInPath("swift")
|
||||
}
|
||||
|
||||
func swiftRun(withArgs args: [String]) throws -> String? {
|
||||
let path = swiftPath()!
|
||||
let child = Process()
|
||||
let so = Pipe()
|
||||
|
||||
child.standardOutput = so
|
||||
child.executableURL = path
|
||||
child.arguments =
|
||||
[
|
||||
"run",
|
||||
"--ignore-lock" /* --ignore-lock needs to be early because it is a "location option". */,
|
||||
"--skip-build",
|
||||
] + args
|
||||
|
||||
try! child.run()
|
||||
|
||||
let output =
|
||||
switch child.standardOutput {
|
||||
case let so as Pipe:
|
||||
String(data: try! so.fileHandleForReading.readToEnd()!, encoding: String.Encoding.ascii)
|
||||
default: Optional<String>.none
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func swiftCliTestRunner(_ arg_gen: () -> [String], withExpected expected: String) async throws {
|
||||
let args = arg_gen()
|
||||
|
||||
let run_output = try! swiftRun(withArgs: args)
|
||||
|
||||
#expect(run_output == expected)
|
||||
}
|
||||
|
||||
func swiftCliTestRunner(_ arg_gen: () -> [String], withExpectedPath expected_path: String) async throws {
|
||||
let args = arg_gen()
|
||||
|
||||
let run_output = try! swiftRun(withArgs: args)
|
||||
|
||||
let expected_source_absolute = FilePath(FileManager().currentDirectoryPath).appending(TestGoldenPath).appending(expected_path + TestGoldenExtension)
|
||||
|
||||
let expected_output = try! String(contentsOfFile: expected_source_absolute.string)
|
||||
|
||||
#expect(run_output == expected_output)
|
||||
}
|
||||
|
||||
@attached(peer) public macro CliTest() =
|
||||
#externalMacro(module: "Macros", type: "CliTestDeclarationMacro")
|
||||
@@ -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: P4Type(P4Array(withValueType: P4Type(P4Int()))))
|
||||
var test_values = VarValueScopes().enter()
|
||||
test_values = test_values.declare(
|
||||
identifier: Identifier(name: "ta"),
|
||||
withValue: P4Value(P4ArrayValue(withType: P4Type(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: P4Type(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: P4Type(P4Array(withValueType: P4Type(P4Int()))))
|
||||
var test_values = VarValueScopes().enter()
|
||||
|
||||
test_values = test_values.declare(
|
||||
identifier: Identifier(name: "ta"),
|
||||
withValue: P4Value(P4ArrayValue(withType: P4Type(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: P4Type(P4Array(withValueType: P4Type(P4Int()))))
|
||||
var test_values = VarValueScopes().enter()
|
||||
test_values = test_values.declare(
|
||||
identifier: Identifier(name: "ta"),
|
||||
withValue: P4Value(P4ArrayValue(withType: P4Type(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: P4Type(P4Array(withValueType: P4Type(P4Int()))))
|
||||
var test_values = VarValueScopes().enter()
|
||||
test_values = test_values.declare(
|
||||
identifier: Identifier(name: "ta"),
|
||||
withValue: P4Value(P4ArrayValue(withType: P4Type(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: P4Type(P4Array(withValueType: P4Type(P4Array(withValueType: P4Type(P4Int()))))))
|
||||
var test_values = VarValueScopes().enter()
|
||||
|
||||
let nested = P4Value(P4ArrayValue(
|
||||
withType: P4Type(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: P4Type(P4Array(withValueType: P4Type(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: P4Type(P4Array(withValueType: P4Type(P4Int()))))
|
||||
var test_values = VarValueScopes().enter()
|
||||
test_values = test_values.declare(
|
||||
identifier: Identifier(name: "ta"),
|
||||
withValue: P4Value(P4ArrayValue(withType: P4Type(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: P4Type(P4Array(withValueType: P4Type(P4Array(withValueType: P4Type(P4Int()))))))
|
||||
var test_values = VarValueScopes().enter()
|
||||
|
||||
let nested = P4Value(P4ArrayValue(
|
||||
withType: P4Type(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: P4Type(P4Array(withValueType: P4Type(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: P4Type(P4Boolean())),
|
||||
P4StructFieldIdentifier(name: "count", withType: P4Type(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: P4Type(P4Boolean())),
|
||||
P4StructFieldIdentifier(name: "count", withType: P4Type(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: P4Type(P4Boolean())),
|
||||
P4StructFieldIdentifier(name: "count", withType: P4Type(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: P4Type(P4Boolean())),
|
||||
P4StructFieldIdentifier(name: "count", withType: P4Type(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: P4Type(P4Boolean())),
|
||||
P4StructFieldIdentifier(name: "count", withType: P4Type(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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,322 +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_control_declaration() async throws {
|
||||
let simple_parser_declaration = """
|
||||
control simple() {
|
||||
action a() {
|
||||
}
|
||||
table t {
|
||||
key = {
|
||||
true: exact;
|
||||
}
|
||||
}
|
||||
apply {
|
||||
}
|
||||
};
|
||||
"""
|
||||
let x = { (tipe: P4Type) -> Bool in
|
||||
switch tipe.dataType() {
|
||||
case let c as Control: c.name == "simple"
|
||||
default: false
|
||||
}
|
||||
}
|
||||
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
|
||||
#expect(program.InstancesWithTypes(x).count == 1)
|
||||
}
|
||||
|
||||
@Test func test_simple_control_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: P4Type) -> Bool in
|
||||
switch tipe.dataType() {
|
||||
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: P4Type) -> Bool in
|
||||
switch tipe.dataType() {
|
||||
case let c as Control: c.name == "simple"
|
||||
default: false
|
||||
}
|
||||
}
|
||||
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
|
||||
#expect(program.InstancesWithTypes(x).count == 1)
|
||||
}
|
||||
|
||||
@Test func test_simple_control_declaration_with_misnamed_actions() async throws {
|
||||
let simple_parser_declaration = """
|
||||
control simple() {
|
||||
action a() {
|
||||
}
|
||||
table t {
|
||||
key = {
|
||||
true: exact;
|
||||
}
|
||||
actions = {
|
||||
b;
|
||||
}
|
||||
}
|
||||
apply {
|
||||
}
|
||||
};
|
||||
"""
|
||||
#expect(
|
||||
#RequireErrorResult(
|
||||
Error(
|
||||
withMessage: "{54, 63}: Error(s) parsing property list: {91, 26}: Error(s) parsing table actions: Cannot find b in lexical scope."
|
||||
),
|
||||
Program.Compile(simple_parser_declaration))
|
||||
)
|
||||
}
|
||||
|
||||
@Test func test_simple_control_declaration_with_misnamed_actions2() async throws {
|
||||
let simple_parser_declaration = """
|
||||
control simple() {
|
||||
action a() {
|
||||
}
|
||||
table t {
|
||||
key = {
|
||||
true: exact;
|
||||
}
|
||||
actions = {
|
||||
a;
|
||||
b;
|
||||
}
|
||||
}
|
||||
apply {
|
||||
}
|
||||
};
|
||||
"""
|
||||
#expect(
|
||||
#RequireErrorResult(
|
||||
Error(
|
||||
withMessage: "{54, 72}: Error(s) parsing property list: {91, 35}: Error(s) parsing table actions: Cannot find b in lexical scope."
|
||||
),
|
||||
Program.Compile(simple_parser_declaration))
|
||||
)
|
||||
}
|
||||
|
||||
@Test func test_simple_control_declaration_with_mistyped_actions() async throws {
|
||||
let simple_parser_declaration = """
|
||||
bool a() {
|
||||
return true;
|
||||
};
|
||||
control simple() {
|
||||
table t {
|
||||
key = {
|
||||
true: exact;
|
||||
}
|
||||
actions = {
|
||||
a;
|
||||
}
|
||||
}
|
||||
apply {
|
||||
}
|
||||
};
|
||||
"""
|
||||
#expect(
|
||||
#RequireErrorResult(
|
||||
Error(
|
||||
withMessage: "{64, 63}: Error(s) parsing property list: {101, 26}: Error(s) parsing table actions: {101, 26}: a does not name an action"
|
||||
),
|
||||
Program.Compile(simple_parser_declaration))
|
||||
)
|
||||
}
|
||||
|
||||
@Test func test_simple_control_declaration_with_parameters() async throws {
|
||||
let simple_parser_declaration = """
|
||||
control simple(bool x, bool y) {
|
||||
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"
|
||||
),
|
||||
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))
|
||||
)
|
||||
}
|
||||
@@ -1,384 +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: P4Type) -> Bool in
|
||||
switch tipe.dataType() {
|
||||
case let c as Control: c.name == "simple"
|
||||
default: false
|
||||
}
|
||||
}
|
||||
var control = ((controls[0].dataType() as P4DataType) as! Control)
|
||||
|
||||
// Add entries to the table.
|
||||
control = control.updateTable(
|
||||
addEntry: (
|
||||
P4Value(P4BooleanValue(withValue: true)),
|
||||
TypedIdentifier(name: "a", withType: P4Type(Action()))
|
||||
)
|
||||
).updateTable(
|
||||
addEntry: (
|
||||
P4Value(P4BooleanValue(withValue: false)),
|
||||
TypedIdentifier(name: "b", withType: P4Type(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),
|
||||
P4Type(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: P4Type(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: P4Type) -> Bool in
|
||||
switch tipe.dataType() {
|
||||
case let c as Control: c.name == "simple"
|
||||
default: false
|
||||
}
|
||||
}
|
||||
var control = ((controls[0].dataType() as P4DataType) as! Control)
|
||||
|
||||
// Add entries to the table.
|
||||
control = control.updateTable(
|
||||
addEntry: (
|
||||
P4Value(P4BooleanValue(withValue: true)),
|
||||
TypedIdentifier(name: "a", withType: P4Type(Action()))
|
||||
)
|
||||
).updateTable(
|
||||
addEntry: (
|
||||
P4Value(P4BooleanValue(withValue: false)),
|
||||
TypedIdentifier(name: "b", withType: P4Type(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),
|
||||
P4Type(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: P4Type(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: P4Type) -> Bool in
|
||||
switch tipe.dataType() {
|
||||
case let c as Control: c.name == "simple"
|
||||
default: false
|
||||
}
|
||||
}
|
||||
var control = ((controls[0].dataType() as P4DataType) as! Control)
|
||||
|
||||
// Add entries to the table.
|
||||
control = control.updateTable(
|
||||
addEntry: (
|
||||
P4Value(P4IntValue(withValue: 5)),
|
||||
TypedIdentifier(name: "a", withType: P4Type(Action()))
|
||||
)
|
||||
).updateTable(
|
||||
addEntry: (
|
||||
P4Value(P4IntValue(withValue: 2)),
|
||||
TypedIdentifier(name: "b", withType: P4Type(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),
|
||||
P4Type(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: P4Type(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: P4Type) -> Bool in
|
||||
switch tipe.dataType() {
|
||||
case let c as Control: c.name == "simple"
|
||||
default: false
|
||||
}
|
||||
}
|
||||
var control = ((controls[0].dataType() as P4DataType) as! Control)
|
||||
|
||||
// Add entries to the table.
|
||||
control = control.updateTable(
|
||||
addEntry: (
|
||||
P4Value(P4IntValue(withValue: 1)),
|
||||
TypedIdentifier(name: "a", withType: P4Type(Action()))
|
||||
)
|
||||
).updateTable(
|
||||
addEntry: (
|
||||
P4Value(P4IntValue(withValue: 2)),
|
||||
TypedIdentifier(name: "b", withType: P4Type(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),
|
||||
P4Type(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: P4Type(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: P4Type) -> Bool in
|
||||
switch tipe.dataType() {
|
||||
case let c as Control: c.name == "simple"
|
||||
default: false
|
||||
}
|
||||
}
|
||||
var control = ((controls[0].dataType() as P4DataType) as! Control)
|
||||
|
||||
// Add entries to the table.
|
||||
control = control.updateTable(
|
||||
addEntry: (
|
||||
P4Value(P4BooleanValue(withValue: true)),
|
||||
TypedIdentifier(name: "a", withType: P4Type(Action()))
|
||||
)
|
||||
).updateTable(
|
||||
addEntry: (
|
||||
P4Value(P4IntValue(withValue: 5)),
|
||||
TypedIdentifier(name: "b", withType: P4Type(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),
|
||||
P4Type(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: P4Type(P4Int())), atIndex: 0),
|
||||
Argument(P4Value(P4BooleanValue(withValue: false)), atIndex: 1),
|
||||
Argument(P4Value(P4IntValue(withValue: 5)), atIndex: 2),
|
||||
])))
|
||||
|
||||
// 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))))
|
||||
|
||||
}
|
||||
@@ -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))
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user