Compare commits

..

5 Commits

Author SHA1 Message Date
Will Hawkins ef6b07b54a Make Formatter Happy
Continuous Integration / Grammar Tests (push) Successful in 3m52s
Continuous Integration / Library Format Tests (push) Successful in 4m52s
Continuous Integration / Library Tests (push) Successful in 7m32s
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-06-15 23:37:48 -04:00
Will Hawkins aa12974dd6 compiler: Flesh Out CST Visitor Framework
As a use case, use it to implement text serialization
of the CST.

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-06-15 23:37:14 -04:00
Will Hawkins d22776b018 compiler: Refactor Language Element Tags
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-06-15 21:16:52 -04:00
Will Hawkins d7022725ed compiler: Refactor Type Parsers
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-06-15 21:16:23 -04:00
Will Hawkins 6c23cf7458 Refactor P4Compiler to P4Parser
Continuous Integration / Grammar Tests (push) Successful in 36s
Continuous Integration / Library Format Tests (push) Successful in 1m46s
Continuous Integration / Library Tests (push) Successful in 4m23s
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-06-12 23:23:54 -04:00
25 changed files with 1787 additions and 1637 deletions
+5 -5
View File
@@ -10,8 +10,8 @@ let package = Package(
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",
@@ -40,7 +40,7 @@ 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"),
@@ -70,14 +70,14 @@ let package = Package(
.target(
name: "P4CodeGen",
dependencies: [
"P4Compiler", "Common",
"P4Parser", "Common",
//.product(name: "SwiftProtobuf", package: "swift-protobuf"),
]
),
.testTarget(
name: "Tests",
dependencies: [
"P4Compiler", "P4CodeGen", "Macros", "TreeSitterExtensions", "Common",
"P4Parser", "P4CodeGen", "Macros", "TreeSitterExtensions", "Common",
.product(name: "SystemPackage", package: "swift-system"),
],
//swiftSettings: [.enableExperimentalFeature("CodeItemMacros"), .unsafeFlags(["-Xfrontend", "-dump-macro-expansions"])],
@@ -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 P4Compiler
let p4_program_with_control_decl = """
control simple() {
action a() {
}
table t {
key = {
true: exact;
}
}
};
"""
/*
// snippet.include
let flter = { (tipe: P4QualifiedType) -> Bool in
switch tipe.baseType(){
case let c as Control: c.name == "simple"
default: false
}
}
if case .Ok(let program) = SpecialCompilers.ProgramCompiler.Compile(p4_program_with_control_decl) {
print(program.InstancesWithTypes(flter))
}
*/
-45
View File
@@ -1,45 +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 P4Compiler
let p4_program_with_struct_decl = """
struct agg {
int x;
};
"""
/*
// snippet.include
let flter = { (tipe: P4Type) -> Bool in
switch tipe {
case let c as P4Struct: c.name == "agg"
default: false
}
}
if case .Ok(let program) = SpecialCompilers.ProgramCompiler.Compile(p4_program_with_struct_decl) {
print(program.TypesWithTypes(flter))
}
*/
+6 -6
View File
@@ -439,7 +439,7 @@ public struct CliTestDeclarationMacro: PeerMacro, Sendable {
}
}
public enum DeriveCompilableStatement: MemberMacro {
public enum DeriveParsableStatement: MemberMacro {
public static func expansion(
of: AttributeSyntax, providingMembersOf type: some DeclGroupSyntax, conformingTo: [TypeSyntax],
in: some MacroExpansionContext
@@ -447,10 +447,10 @@ public enum DeriveCompilableStatement: MemberMacro {
let implementation = DeclSyntax(
"""
public static func CompileStatement(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.AnStatement> {
return switch Compile(node: node, withContext: context) {
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)
}
@@ -465,6 +465,6 @@ struct P4Macros: CompilerPlugin {
var providingMacros: [Macro.Type] = [
RequireResult.self, RequireErrorResult.self, UseOkResult.self, UseErrorResult.self,
RequireNodeType.self, SkipUnlessNodeType.self, SkipUnlessNodesTypes.self, RequireNodesType.self,
MustOr.self, CliTestDeclarationMacro.self, DeriveCompilableStatement.self,
MustOr.self, CliTestDeclarationMacro.self, DeriveParsableStatement.self,
]
}
+198 -37
View File
@@ -16,53 +16,102 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import Common
import P4Compiler
import P4Parser
public struct TextSerializer {
public struct CSTTextSerializer {
public init() {}
}
public struct TextSerializerContext {
public struct CSTTextSerializerContext {
public let serialized: String
public let indents: Int
public init(_ serialized: String = "") {
public init(_ serialized: String = "", _ indents: Int = 0) {
self.serialized = serialized
self.indents = indents
}
public func append(_ a: String) -> TextSerializerContext {
return TextSerializerContext(self.serialized + a)
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 TextSerializer: ASTVisitor<TextSerializerContext> {
extension CSTTextSerializer: CSTVisitor<CSTTextSerializerContext> {
public func visit(
node: P4Compiler.AST.BinaryOperatorExpression, driver: P4Compiler.ASTVisitorDriver,
context: TextSerializerContext
) -> Common.Result<TextSerializerContext> {
return .Ok(context.append("Binary Operator Expression"))
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: P4Compiler.AST.Literal, driver: P4Compiler.ASTVisitorDriver,
context: TextSerializerContext
) -> Common.Result<TextSerializerContext> {
return .Ok(context.append("Literal Expression"))
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: P4Compiler.AST.Identifier, driver: P4Compiler.ASTVisitorDriver,
context: TextSerializerContext
) -> Common.Result<TextSerializerContext> {
return .Ok(context.append("Identifier Expression"))
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: P4Compiler.AST.Parser, driver: P4Compiler.ASTVisitorDriver, context: TextSerializerContext
) -> Common.Result<TextSerializerContext> {
var context = context.append("Identifier Expression")
for s in node.states.states {
switch driver.visit(state: s, visitor: self, context: context) {
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)
}
@@ -72,24 +121,136 @@ extension TextSerializer: ASTVisitor<TextSerializerContext> {
}
public func visit(
node: P4Compiler.AST.ParserStateDirectTransition, driver: P4Compiler.ASTVisitorDriver,
context: TextSerializerContext
) -> Common.Result<TextSerializerContext> {
return .Ok(context.append("State: Direct Transition"))
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: P4Compiler.AST.ParserStateNoTransition, driver: P4Compiler.ASTVisitorDriver,
context: TextSerializerContext
) -> Common.Result<TextSerializerContext> {
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: P4Compiler.AST.ParserStateSelectTransition, driver: P4Compiler.ASTVisitorDriver,
context: TextSerializerContext
) -> Common.Result<TextSerializerContext> {
return .Ok(context.append("State: Direct Transition"))
}
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())
}
}
-71
View File
@@ -1,71 +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 SwiftTreeSitter
import TreeSitterExtensions
import TreeSitterP4
public protocol CompilableValue {
static func CompileValue(withValue value: String) -> Result<P4DataValue>
}
public protocol MaybeCompilableType {
static func MaybeCompileType(
type: SwiftTreeSitter.Node, withContext: ASTCompilerContext
) -> Result<AST.Tipe?>
}
public protocol CompilableType {
static func CompileType(
type: SwiftTreeSitter.Node, withContext: ASTCompilerContext
) -> Result<AST.Tipe>
}
public protocol CompilableExpression {
static func CompileExpression(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.AnExpression>
}
public protocol Compilable<C> {
associatedtype C
static func Compile(
node: Node, withContext context: ASTCompilerContext
) -> Result<C>
}
public protocol CompilableStatement {
static func CompileStatement(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.AnStatement>
}
public protocol ASTVisitor<T> {
associatedtype T
func visit(node: AST.BinaryOperatorExpression, driver: ASTVisitorDriver, context: T) -> Result<T>
func visit(node: AST.Literal, driver: ASTVisitorDriver, context: T) -> Result<T>
func visit(node: AST.Identifier, driver: ASTVisitorDriver, context: T) -> Result<T>
func visit(node: AST.Parser, driver: ASTVisitorDriver, context: T) -> Result<T>
func visit(
node: AST.ParserStateDirectTransition, driver: ASTVisitorDriver, context: T
) -> Result<T>
func visit(node: AST.ParserStateNoTransition, driver: ASTVisitorDriver, context: T) -> Result<T>
func visit(
node: AST.ParserStateSelectTransition, driver: ASTVisitorDriver, context: T
) -> Result<T>
}
-240
View File
@@ -1,240 +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 SwiftTreeSitter
import TreeSitterExtensions
import TreeSitterP4
extension AST.TransitionStatement: Compilable {
public static func Compile(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.AnState> {
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."
))
}
guard let stmts = context.lexical_context_statements else {
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError:
"Cannot parse a transition statement without statements of the containing state."))
}
#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 = AST.Identifier.CompileExpression(
node: next_node, withContext: context)
switch maybe_parsed_next_state_id {
case .Ok(let next_state_id):
return .Ok(
AST.ParserStateDirectTransition(
name: (state_identifier),
withNextStateIdentifier: next_state_id as! AST.Identifier, withStatements: stmts))
case .Error(let e):
return .Error(e)
}
}
// We know that the next node is a select expression.
return
switch AST.SelectExpression.CompileExpression(node: next_node, withContext: context)
{
case .Ok(let tse):
.Ok(
AST.ParserStateSelectTransition(
name: state_identifier, withTransitionExpression: tse as! AST.SelectExpression,
withStatements: stmts,
)
)
case .Error(let e): .Error(e)
}
}
}
public struct SpecialCompilers {
public struct Statements {
static func Compile(
node: Node, withContext context: ASTCompilerContext
) -> Result<[AST.AnStatement]> {
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: [AST.AnStatement] = Array()
node.enumerateNamedChildren { node in
switch AST.Statement.Compile(
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(parsed_s)
}
}
static func CompileParserBody(
withName name: AST.Identifier, withParameters parameters: AST.ParameterList, node: Node,
withContext context: ASTCompilerContext
) -> Result<AST.Parser> {
var parser = AST.Parser(withName: name, withParameters: parameters)
// Build a state from each one listed.
var errors: (any Errorable)? = .none
/// 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 AST.ParserState.Compile(
node: parser_state,
withContext: context)
{
case Result.Ok(let state):
// All states are instances inside the parser.
parser.states = parser.states.append(state: state)
case Result.Error(let e):
errors =
if let errors = errors {
errors.append(error: e)
} else {
e
}
}
}
if let errors = errors {
return .Error(errors)
}
return .Ok(parser)
}
public struct ProgramCompiler {
public static func Compile(_ source: String) -> Result<AST.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 = AST.Program()
// Set up a context for parsing.
let compilation_context = ASTCompilerContext()
var errors: (any Errorable)? = .none
// Try to parse all top-level declarations.
result?.rootNode?.enumerateNamedChildren { (declaration_node: Node) in
let declaration_parsers: [String: CompilableStatement.Type] = [
"declaration": AST.Declaration.self,
"instantiation": AST.Instantiation.self,
]
if let parser = declaration_parsers[declaration_node.nodeType!] {
let r = parser.CompileStatement(node: declaration_node, withContext: compilation_context)
switch r {
case .Ok(let compiled):
program.statements = program.statements + [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(program)
}
}
}
-420
View File
@@ -1,420 +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 SwiftTreeSitter
import TreeSitterExtensions
import TreeSitterP4
extension AST.BlockStatement: Compilable {
public typealias C = AST.BlockStatement
public static func Compile(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.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.Compile(
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(AST.BlockStatement([]))
}
}
@deriveCompilableStatement
extension AST.BlockStatement: CompilableStatement {}
extension AST.ConditionalStatement: Compilable {
public typealias C = AST.ConditionalStatement
public static func Compile(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.ConditionalStatement> {
#RequireNodeType<Node, AST.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) = AST.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) = AST.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<AST.AnStatement>? =
if let elss = node.child(at: 6) {
.some(
AST.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(
AST.ConditionalStatement(condition: condition, withThen: thenns, andElse: elss))
}
return .Ok(AST.ConditionalStatement(condition: condition, withThen: thenns))
}
}
@deriveCompilableStatement
extension AST.ConditionalStatement: CompilableStatement {}
extension AST.VariableDeclarationStatement: Compilable {
public typealias C = AST.VariableDeclarationStatement
public static func Compile(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.VariableDeclarationStatement> {
#RequireNodeType<Node, AST.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) = AST.Identifier.CompileExpression(
node: variablename, withContext: context)
else {
return Result.Error(
Error(withMessage: "Could not parse variable name"))
}
guard
case .Ok(let declaration_p4_type) = AST.Types.CompileType(type: typeref, withContext: context)
else {
return Result.Error(
Error(withMessage: "Could not parse a P4 type from \(typeref.text!)"))
}
var initializer: AST.AnExpression? = .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 = AST.Expression.Compile(
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(
AST.VariableDeclarationStatement(
identifier: parsed_variablename as! AST.Identifier, withType: declaration_p4_type,
withInitializer: initializer),
)
}
}
@deriveCompilableStatement
extension AST.VariableDeclarationStatement: CompilableStatement {}
extension AST.ExpressionStatement: Compilable {
public typealias C = AST.ExpressionStatement
public static func Compile(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.ExpressionStatement> {
#RequireNodeType<Node, (P4Statement)>(
node: node, type: "expressionStatement", nice_type_name: "expression statement")
let expression_node = node.child(at: 0)!
return switch AST.Expression.Compile(node: expression_node, withContext: context) {
case .Ok(let expression): .Ok(AST.ExpressionStatement(expression))
case .Error(let e): .Error(e)
}
}
}
@deriveCompilableStatement
extension AST.ExpressionStatement: CompilableStatement {}
extension AST.ParserAssignmentStatement: Compilable {
public typealias C = AST.ParserAssignmentStatement
public static func Compile(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.ParserAssignmentStatement> {
#RequireNodeType<Node, AST.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 = AST.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 = AST.Expression.Compile(node: lvalue_node, withContext: context)
guard case .Ok(let lvalue) = maybe_parsed_lvalue else {
return Result.Error(maybe_parsed_lvalue.error()!)
}
return Result.Ok(
AST.ParserAssignmentStatement(
withLValue: lvalue,
withValue: rvalue
))
}
}
@deriveCompilableStatement
extension AST.ParserAssignmentStatement: CompilableStatement {}
extension AST.ReturnStatement: Compilable {
public typealias C = AST.ReturnStatement
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext
) -> Result<AST.ReturnStatement> {
#RequireNodeType<Node, AST.ReturnStatement>(
node: node, type: "return_statement", nice_type_name: "return statement")
let expression_node = node.child(at: 1)!
return switch AST.Expression.Compile(node: expression_node, withContext: context) {
case .Ok(let result): .Ok(AST.ReturnStatement(result))
case .Error(let e): .Error(e)
}
}
}
@deriveCompilableStatement
extension AST.ReturnStatement: CompilableStatement {}
extension AST.ApplyStatement: Compilable {
public typealias C = AST.ApplyStatement
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext
) -> Result<AST.ApplyStatement> {
#RequireNodeType<Node, AST.ApplyStatement>(
node: node, type: "apply_statement", nice_type_name: "apply statement")
let expression_node = node.child(at: 1)!
return switch AST.BlockStatement.Compile(node: expression_node, withContext: context) {
case .Ok(let statement):
.Ok(AST.ApplyStatement(statement))
case .Error(let e): .Error(e)
}
}
}
@deriveCompilableStatement
extension AST.ApplyStatement: CompilableStatement {}
extension AST.Instantiation: Compilable {
public typealias C = AST.Instantiation
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext
) -> Result<AST.Instantiation> {
let expression = node
#RequireNodeType<Node, AST.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<AST.Instantiation>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing function call component")))
let maybe_instantiated_type_name = AST.Identifier.CompileExpression(
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<AST.Instantiation>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing instantiation component")))
let maybe_argument_list = AST.ArgumentList.Compile(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<AST.Instantiation>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing instantiation name")))
let name = AST.Identifier.CompileExpression(node: current_node!, withContext: context)
guard case .Ok(let name) = name else {
return .Error(name.error()!)
}
return .Ok(
AST.Instantiation(
named: name as! AST.Identifier, withType: instantiated_type_name as! AST.Identifier,
withArguments: arguments))
}
}
@deriveCompilableStatement
extension AST.Instantiation: CompilableStatement {}
-98
View File
@@ -1,98 +0,0 @@
// p4rse, Copyright 2026, Will Hawkins
//
// This file is part of p4rse.
//
// This file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import Common
import SwiftTreeSitter
import TreeSitterExtensions
import TreeSitterP4
extension P4Boolean: MaybeCompilableType {
public static func MaybeCompileType(
type: SwiftTreeSitter.Node, withContext: ASTCompilerContext
) -> Common.Result<(AST.Tipe)?> {
return type.text == "bool" ? .Ok(AST.Tipe(P4QualifiedType(P4Boolean()))) : .Ok(.none)
}
}
extension P4Int: MaybeCompilableType {
public static func MaybeCompileType(
type: SwiftTreeSitter.Node, withContext: ASTCompilerContext
) -> Common.Result<(AST.Tipe)?> {
// Drill down, as appropriate.
let base_type_node = type.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: base_type_node, type: "baseType")
let type_node = base_type_node.child(at: 0)!
#SkipUnlessNodeType<SwiftTreeSitter.Node>(
node: type_node, type: "int_type")
var walker = Walker(node: type_node)
var int_node: Node? = .none
#MustOr(
result: int_node, thing: walker.getNext(),
or: Result<AST.Tipe?>.Error(
ErrorWithLocation(
sourceLocation: type_node.toSourceLocation(),
withError: "Missing elements in int type declaration")))
// Move passed the keyword.
walker.next()
if let bit_width_node = walker.getNext() {
guard let bit_width = Int(bit_width_node.child(at: 1)!.text!),
bit_width != 0
else {
return .Error(
ErrorWithLocation(
sourceLocation: bit_width_node.toSourceLocation(),
withError: "Could not parse \(bit_width_node.text!) into integer"))
}
return .Ok(AST.Tipe(P4QualifiedType(P4Int(BitWidth.Width(bit_width)))))
}
return .Ok(AST.Tipe(P4QualifiedType(P4Int(BitWidth.Infinite))))
}
}
extension P4String: MaybeCompilableType {
public static func MaybeCompileType(
type: SwiftTreeSitter.Node, withContext: ASTCompilerContext
) -> Common.Result<(AST.Tipe)?> {
return type.text == "string" ? .Ok(AST.Tipe(P4QualifiedType(P4String()))) : .Ok(.none)
}
}
extension AST.Types: CompilableType {
public static func CompileType(
type: SwiftTreeSitter.Node, withContext context: ASTCompilerContext
) -> Result<AST.Tipe> {
let type_parsers: [MaybeCompilableType.Type] = [
P4Boolean.self, P4Int.self, P4String.self, /*P4Struct.self,*/
]
for type_parser in type_parsers {
switch type_parser.MaybeCompileType(type: type, withContext: context) {
case .Ok(.some(let type)): return .Ok(type)
case .Ok(.none): continue
case .Error(let e): return .Error(e)
}
}
return Result.Error(Error(withMessage: "Type name not recognized"))
}
}
-71
View File
@@ -1,71 +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 ASTVisitorDriver {
public init() {}
public func visit<T>(
expression: any AST.AnExpression, visitor: any ASTVisitor<T>, context: T
) -> Result<T> {
return switch expression {
case let e as AST.BinaryOperatorExpression:
visitor.visit(node: e, driver: self, context: context)
case let e as AST.Literal: visitor.visit(node: e, driver: self, context: context)
default: .Error(Error(withMessage: "AST Expression Element Is Not Visitable"))
}
}
public func visit<T>(
state: any AST.AnState, visitor: any ASTVisitor<T>, context: T
) -> Result<T> {
return switch state {
case let s as AST.ParserStateDirectTransition:
visitor.visit(node: s, driver: self, context: context)
case let s as AST.ParserStateNoTransition:
visitor.visit(node: s, driver: self, context: context)
case let s as AST.ParserStateSelectTransition:
visitor.visit(node: s, driver: self, context: context)
default: .Error(Error(withMessage: "AST State Element (\(state)) Is Not Visitable"))
}
}
public func visit<T>(
statement: any AST.AnStatement, visitor: any ASTVisitor<T>, context: T
) -> Result<T> {
return switch statement {
case let s as AST.Parser: visitor.visit(node: s, driver: self, context: context)
default: .Error(Error(withMessage: "AST Statement Element Is Not Visitable"))
}
}
public func visit<T>(
program: AST.Program, visitor: any ASTVisitor<T>, context: T
) -> Result<T> {
var context = context
for s in program.statements {
switch visit(statement: s, visitor: visitor, context: context) {
case .Ok(let c): context = c
case .Error(let e): return .Error(e)
}
}
return .Ok(context)
}
}
@@ -17,24 +17,36 @@
import Common
extension P4Value: AST.AnExpression {}
extension P4Value: CST.Categories.Expression {}
public struct AST {
public struct CST {
public protocol AnExpression {}
public protocol AnStatement {}
public protocol AnState {}
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 {}
}
public struct Statement {}
struct Expression {}
public struct VariableDeclarationStatement: AnStatement {
public var initializer: AnExpression?
public var identifier: AST.Identifier
public var tipe: AST.Tipe
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: AST.Tipe, withInitializer initializer: AnExpression?
identifier: Identifier, withType tipe: CST.Tipe,
withInitializer initializer: Categories.Expression?
) {
self.identifier = identifier
self.initializer = initializer
@@ -42,20 +54,20 @@ public struct AST {
}
}
public struct ConditionalStatement: AnStatement {
public var condition: AnExpression
public var thenn: AnStatement
public var elss: AnStatement?
public struct ConditionalStatement: Categories.Statement {
public var condition: Categories.Expression
public var thenn: Categories.Statement
public var elss: Categories.Statement?
public init(condition: AnExpression, withThen thenn: AnStatement) {
public init(condition: Categories.Expression, withThen thenn: Categories.Statement) {
self.condition = condition
self.thenn = thenn
self.elss = .none
}
public init(
condition: AnExpression, withThen thenn: AnStatement,
andElse elss: AnStatement
condition: Categories.Expression, withThen thenn: Categories.Statement,
andElse elss: Categories.Statement
) {
self.condition = condition
self.thenn = thenn
@@ -63,28 +75,28 @@ public struct AST {
}
}
public struct BlockStatement: AnStatement {
public var statements: [AnStatement]
public struct BlockStatement: Categories.Statement {
public var statements: Statements
public init(_ statements: [AnStatement]) {
public init(_ statements: Statements) {
self.statements = statements
}
}
public struct ReturnStatement: AnStatement {
public let value: AnExpression
public struct ReturnStatement: Categories.Statement {
public let value: Categories.Expression
public init(_ value: AnExpression) {
public init(_ value: Categories.Expression) {
self.value = value
}
}
public struct ApplyStatement: AnStatement {
public let body: AST.BlockStatement?
public struct ApplyStatement: Categories.Statement {
public let body: CST.BlockStatement?
public init() { self.body = .none }
public init(_ body: AST.BlockStatement) {
public init(_ body: CST.BlockStatement) {
self.body = body
}
}
@@ -95,14 +107,14 @@ public struct AST {
+ "\(self.name) with parameters \(self.params) and body \(String(describing: self.body))"
}
public var body: AST.BlockStatement?
public var params: AST.ParameterList
public var name: AST.Identifier
public var body: CST.BlockStatement?
public var params: CST.ParameterList
public var name: CST.Identifier
public init(
named name: AST.Identifier = AST.Identifier(Common.Identifier(name: "")),
named name: CST.Identifier = CST.Identifier(Common.Identifier(name: "")),
withParameters parameters: ParameterList = ParameterList([]),
withBody body: AST.BlockStatement? = .none
withBody body: CST.BlockStatement? = .none
) {
self.name = name
self.params = parameters
@@ -112,8 +124,8 @@ public struct AST {
}
public struct Actions: CustomStringConvertible {
public let actions: [AST.Action]
public init(withActions actions: [AST.Action]) {
public let actions: [CST.Action]
public init(withActions actions: [CST.Action]) {
self.actions = actions
}
@@ -130,10 +142,10 @@ public struct AST {
}
public struct TableKeyEntry: CustomStringConvertible {
public let key: AST.KeysetExpression
public let match_type: AST.TableKeyMatchType
public let key: CST.KeysetExpression
public let match_type: CST.TableKeyMatchType
public init(_ key: AST.KeysetExpression, _ match: AST.TableKeyMatchType) {
public init(_ key: CST.KeysetExpression, _ match: CST.TableKeyMatchType) {
self.key = key
self.match_type = match
}
@@ -144,9 +156,9 @@ public struct AST {
}
public struct TableKeys: CustomStringConvertible {
public let keys: [AST.TableKeyEntry]
public let keys: [CST.TableKeyEntry]
public init(withEntries entries: [AST.TableKeyEntry]) {
public init(withEntries entries: [CST.TableKeyEntry]) {
self.keys = entries
}
public init() {
@@ -162,8 +174,8 @@ public struct AST {
}
public struct TableActionsProperty: CustomStringConvertible {
public let actions: [AST.Identifier]
public init(_ actions: [AST.Identifier] = []) {
public let actions: [CST.Identifier]
public init(_ actions: [CST.Identifier] = []) {
self.actions = actions
}
@@ -176,9 +188,9 @@ public struct AST {
}
public struct TablePropertyList: CustomStringConvertible {
public let actions: AST.TableActionsProperty
public let keys: AST.TableKeys
public init(withActions actions: AST.TableActionsProperty, withKeys keys: AST.TableKeys) {
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
}
@@ -189,13 +201,13 @@ public struct AST {
}
public struct Table: CustomStringConvertible {
public let properties: AST.TablePropertyList
let name: AST.Identifier
public let entries: [(P4Value, AST.Identifier)]
public let properties: CST.TablePropertyList
let name: CST.Identifier
public let entries: [(P4Value, CST.Identifier)]
public init(
withName name: AST.Identifier, withPropertyList property_list: AST.TablePropertyList,
withEntries entries: [(P4Value, AST.Identifier)] = []
withName name: CST.Identifier, withPropertyList property_list: CST.TablePropertyList,
withEntries entries: [(P4Value, CST.Identifier)] = []
) {
self.name = name
self.properties = property_list
@@ -210,39 +222,37 @@ public struct AST {
/// 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, AST.Identifier)) -> Table {
public func update(addEntry entry: (P4Value, CST.Identifier)) -> Table {
return Table(
withName: self.name, withPropertyList: self.properties, withEntries: self.entries + [entry])
}
}
public protocol AnDeclaration: AnStatement {}
public struct Declaration {}
public struct Control: CustomStringConvertible, AnDeclaration {
public struct Control: CustomStringConvertible, Categories.Declaration {
public var description: String {
return "Control named \(self._name) \(self.parameters) \(self.actions) \(self.table)"
}
public let actions: AST.Actions
public let table: AST.Table
let _parameters: AST.ParameterList
let _name: AST.Identifier
let apply: AST.ApplyStatement
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: AST.ParameterList {
public var parameters: CST.ParameterList {
_parameters
}
public var name: AST.Identifier {
public var name: CST.Identifier {
_name
}
public init(
named: AST.Identifier, withParameters parameters: AST.ParameterList,
withTable table: AST.Table,
withActions actions: AST.Actions, withApply apply: AST.ApplyStatement
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
@@ -251,7 +261,7 @@ public struct AST {
self.apply = apply
}
public func updateTable(addEntry entry: (P4Value, AST.Identifier)) -> Control {
public func updateTable(addEntry entry: (P4Value, CST.Identifier)) -> Control {
let table = self.table.update(addEntry: entry)
return Control(
@@ -265,28 +275,28 @@ public struct AST {
}
public struct ExternDeclaration: AnDeclaration {
public let declaration: AST.AnDeclaration
public struct ExternDeclaration: Categories.Declaration {
public let declaration: CST.Categories.Declaration
public init(_ declaration: AST.AnDeclaration) {
public init(_ declaration: CST.Categories.Declaration) {
self.declaration = declaration
}
}
public struct FunctionDeclaration: AnDeclaration {
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: AST.BlockStatement?
public var params: AST.ParameterList
public var name: AST.Identifier
public var tipe: AST.Tipe
public var body: CST.BlockStatement?
public var params: CST.ParameterList
public var name: CST.Identifier
public var tipe: CST.Tipe
public init(
named name: AST.Identifier, ofType type: AST.Tipe,
withParameters parameters: AST.ParameterList,
withBody body: AST.BlockStatement?
named name: CST.Identifier, ofType type: CST.Tipe,
withParameters parameters: CST.ParameterList,
withBody body: CST.BlockStatement?
) {
self.name = name
self.tipe = type
@@ -295,23 +305,23 @@ public struct AST {
}
}
public struct StructDeclaration: AnDeclaration {
public let fields: [AST.VariableDeclarationStatement]
public let identifier: AST.Identifier
public init(_ id: AST.Identifier, _ fields: [AST.VariableDeclarationStatement]) {
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: AnStatement {
public let name: AST.Identifier
public var tipe: AST.Identifier
public let arguments: AST.ArgumentList
public struct Instantiation: Categories.Statement {
public let name: CST.Identifier
public var tipe: CST.Identifier
public let arguments: CST.ArgumentList
public init(
named name: AST.Identifier, withType tipe: AST.Identifier,
withArguments arguments: AST.ArgumentList
named name: CST.Identifier, withType tipe: CST.Identifier,
withArguments arguments: CST.ArgumentList
) {
self.name = name
self.arguments = arguments
@@ -319,56 +329,56 @@ public struct AST {
}
}
public struct ExpressionStatement: AnStatement {
public let expression: AnExpression
public struct ExpressionStatement: Categories.Statement {
public let expression: Categories.Expression
public init(_ expr: AnExpression) {
public init(_ expr: Categories.Expression) {
self.expression = expr
}
}
public struct Identifier: AnExpression {
public struct Identifier: Categories.Expression {
public let id: Common.Identifier
public init(_ id: Common.Identifier) {
self.id = id
}
}
public struct Literal: AnExpression {
public struct Literal: Categories.Expression {
public let literal: P4Value
public init(_ literal: P4Value) {
self.literal = literal
}
}
public enum KeysetExpression: AnExpression {
public enum KeysetExpression: Categories.Expression {
case Default
case Value(AnExpression)
case Value(Categories.Expression)
}
public struct SelectCaseExpression: AnExpression {
public let key: AST.KeysetExpression
public let next_state_identifier: AST.Identifier
public struct SelectCaseExpression: Categories.Expression {
public let key: CST.KeysetExpression
public let next_state_identifier: CST.Identifier
public init(withKey key: AST.KeysetExpression, withNextState next_state_id: AST.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: AnExpression {
public let selector: AnExpression
public let case_expressions: [AST.SelectCaseExpression]
public struct SelectExpression: Categories.Expression {
public let selector: Categories.Expression
public let case_expressions: [CST.SelectCaseExpression]
public init(
withSelector selector: AnExpression,
withSelectCaseExpressions sces: [AST.SelectCaseExpression]
withSelector selector: Categories.Expression,
withSelectCaseExpressions sces: [CST.SelectCaseExpression]
) {
self.selector = selector
self.case_expressions = sces
}
public func append_checked_sce(sce: AST.SelectCaseExpression) -> AST.SelectExpression {
public func append_checked_sce(sce: CST.SelectCaseExpression) -> CST.SelectExpression {
var new_cses = self.case_expressions
new_cses.append(sce)
return SelectExpression(
@@ -390,15 +400,15 @@ public struct AST {
case Or
}
public struct BinaryOperatorExpression: AnExpression {
public let left: AnExpression
public let right: AnExpression
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: AnExpression,
withRhs rhs: AnExpression
withLhs lhs: Categories.Expression,
withRhs rhs: Categories.Expression
) {
self.type = tipe
self.left = lhs
@@ -406,30 +416,30 @@ public struct AST {
}
}
public struct ArrayAccessExpression: AnExpression {
public let indexor: AnExpression
public let name: AnExpression
public struct ArrayAccessExpression: Categories.Expression {
public let indexor: Categories.Expression
public let name: Categories.Expression
public init(
withName name: AnExpression,
withIndexor indexor: AnExpression
withName name: Categories.Expression,
withIndexor indexor: Categories.Expression
) {
self.name = name
self.indexor = indexor
}
}
public struct FieldAccessExpression: AnExpression {
public struct FieldAccessExpression: Categories.Expression {
public let field: Identifier
public let strct: AnExpression
public let strct: Categories.Expression
public init(withStruct strct: AnExpression, withField field: Identifier) {
public init(withStruct strct: Categories.Expression, withField field: Identifier) {
self.strct = strct
self.field = field
}
}
public struct FunctionCall: AnExpression {
public struct FunctionCall: Categories.Expression {
public let callee: Identifier
public let arguments: ArgumentList
@@ -443,12 +453,12 @@ public struct AST {
public struct LocalElement {}
public struct ParserAssignmentStatement: AnStatement {
public let lvalue: AnExpression
public let value: AnExpression
public struct ParserAssignmentStatement: Categories.Statement {
public let lvalue: Categories.Expression
public let value: Categories.Expression
public init(
withLValue lvalue: AnExpression, withValue value: AnExpression
withLValue lvalue: Categories.Expression, withValue value: Categories.Expression
) {
self.lvalue = lvalue
self.value = value
@@ -458,7 +468,7 @@ public struct AST {
/// A P4 Parser State
public class ParserState {
let name: Identifier
public let statements: [AnStatement]
public let statements: CST.Statements?
public var description: String {
return "Parser State named \(self.name)"
@@ -468,12 +478,12 @@ public struct AST {
return self.name
}
public func getStatements() -> [AnStatement] {
public func getStatements() -> CST.Statements? {
return self.statements
}
/// Construct a ParserState
public init(_ name: Identifier, _ statements: [AnStatement] = Array()) {
public init(_ name: Identifier, _ statements: CST.Statements? = .none) {
self.name = name
self.statements = statements
}
@@ -484,34 +494,34 @@ public struct AST {
/// Only defined to define Compilable extension.
public struct TransitionStatement {}
public class ParserStateDirectTransition: ParserState, AnState {
public class ParserStateDirectTransition: ParserState, Categories.State {
public let next_state_identifier: Identifier?
public init(
name: Identifier, withNextStateIdentifier next_state_id: Identifier,
withStatements stmts: [AnStatement] = Array(),
withStatements stmts: CST.Statements? = .none
) {
self.next_state_identifier = next_state_id
super.init(name, stmts)
}
}
public class ParserStateNoTransition: ParserState, AnState {
public class ParserStateNoTransition: ParserState, Categories.State {
/// Construct a ParserState
public init(
name: Identifier, withStatements stmts: [AnStatement] = Array(),
name: Identifier, withStatements stmts: CST.Statements? = .none
) {
super.init(name, stmts)
}
}
public class ParserStateSelectTransition: ParserState, AnState {
public class ParserStateSelectTransition: ParserState, Categories.State {
public let te: SelectExpression
public init(
name: Identifier, withTransitionExpression te: SelectExpression,
withStatements stmts: [AnStatement] = Array()
withStatements stmts: CST.Statements? = .none
) {
self.te = te
super.init(name, stmts)
@@ -519,17 +529,17 @@ public struct AST {
}
public struct ParserStates {
public var states: [AnState] = Array()
public var states: [Categories.State] = Array()
public func count() -> Int {
return states.count
}
public init(_ states: [AnState] = Array()) {
public init(_ states: [Categories.State] = Array()) {
self.states = states
}
public func append(state: AnState) -> ParserStates {
public func append(state: Categories.State) -> ParserStates {
var new_states = self.states
new_states.append(state)
return ParserStates(new_states)
@@ -539,7 +549,7 @@ public struct AST {
/// A P4 Parser
///
/// Note: A Parser is a type
public struct Parser: AnDeclaration {
public struct Parser: Categories.Declaration {
public var states: ParserStates
public var name: Identifier
@@ -572,11 +582,11 @@ public struct AST {
}
public struct Parameter: CustomStringConvertible {
public var name: AST.Identifier
public var type: AST.Tipe
public var name: CST.Identifier
public var type: CST.Tipe
public init(
identifier: Identifier, withType type: AST.Tipe
identifier: Identifier, withType type: CST.Tipe
) {
self.name = identifier
self.type = type
@@ -589,18 +599,18 @@ public struct AST {
}
public struct ParameterList: CustomStringConvertible {
public var parameters: [AST.Parameter]
public var parameters: [CST.Parameter]
public init() {
self.parameters = Array()
}
public init(_ parameters: [AST.Parameter]) {
public init(_ parameters: [CST.Parameter]) {
self.parameters = parameters
}
public func addParameter(_ parameter: AST.Parameter) -> AST.ParameterList {
return AST.ParameterList(self.parameters + [parameter])
public func addParameter(_ parameter: CST.Parameter) -> CST.ParameterList {
return CST.ParameterList(self.parameters + [parameter])
}
public var description: String {
@@ -612,57 +622,59 @@ public struct AST {
}
public struct ArgumentList {
public let arguments: [AST.Argument]
public let arguments: [CST.Argument]
public init(_ arguments: [AST.Argument] = []) {
public init(_ arguments: [CST.Argument] = []) {
self.arguments = arguments
}
public func addArgument(_ argument: AST.Argument) -> AST.ArgumentList {
public func addArgument(_ argument: CST.Argument) -> CST.ArgumentList {
return ArgumentList(self.arguments + [argument])
}
}
public struct Argument {
public let index: Int
public let argument: AST.AnExpression
public let argument: Categories.Expression
public init(_ argument: AST.AnExpression, atIndex index: Int) {
public init(_ argument: Categories.Expression, atIndex index: Int) {
self.argument = argument
self.index = index
}
}
public struct Program {
public var statements: [AnStatement]
public init(_ stmts: [AnStatement] = Array()) {
public var statements: Statements
public init(_ stmts: Statements = Statements([])) {
self.statements = stmts
}
}
public struct Statement {}
}
public struct ASTCompilerContext {
public let lexical_context_name: AST.Identifier?
public let lexical_context_statements: [AST.AnStatement]?
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: AST.Identifier? = .none, _ stmts: [AST.AnStatement]? = .none, _ extern: Bool = false
_ 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: AST.Identifier?) -> ASTCompilerContext {
return ASTCompilerContext(cn, self.lexical_context_statements, self.extern_context)
public func update(withContextName cn: CST.Identifier?) -> CSTCompilerContext {
return CSTCompilerContext(cn, self.lexical_context_statements, self.extern_context)
}
public func update(withContextStatements stmts: [AST.AnStatement]?) -> ASTCompilerContext {
return ASTCompilerContext(self.lexical_context_name, stmts, 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) -> ASTCompilerContext {
return ASTCompilerContext(self.lexical_context_name, self.lexical_context_statements, extern)
public func update(withExtern extern: Bool) -> CSTCompilerContext {
return CSTCompilerContext(self.lexical_context_name, self.lexical_context_statements, extern)
}
}
@@ -26,6 +26,6 @@ extension Node {
}
}
@attached(member, names: named(CompileStatement))
public macro deriveCompilableStatement() =
#externalMacro(module: "Macros", type: "DeriveCompilableStatement")
@attached(member, names: named(ParseStatement))
public macro deriveParsableStatement() =
#externalMacro(module: "Macros", type: "DeriveParsableStatement")
@@ -33,3 +33,25 @@ public func ConfigureP4Parser() -> Result<SwiftTreeSitter.Parser> {
return .Ok(p)
}
public func compile(_ s: String) -> Result<CST.Program> {
let maybe_parser = ConfigureP4Parser()
guard case .Ok(let p) = maybe_parser else {
return .Error(maybe_parser.error()!)
}
let result = p.parse(s)
guard let tree = result,
!tree.isError(lang: p4lang),
!tree.containsMissing(lang: p4lang)
else {
return Result.Error(Error(withMessage: "Could not parse the P4 program"))
}
guard let root = tree.rootNode else {
return Result.Error(Error(withMessage: "No P4 program compiled"))
}
let compilation_context = CSTCompilerContext()
return CST.Program.Parse(node: root, withContext: compilation_context)
}
File diff suppressed because it is too large Load Diff
@@ -19,31 +19,31 @@ import Common
import SwiftTreeSitter
import TreeSitterP4
extension AST.Identifier: CompilableExpression {
public static func CompileExpression(
node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext
) -> Result<AST.AnExpression> {
extension CST.Identifier: ParsableExpression {
public static func ParseExpression(
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
) -> Result<CST.Categories.Expression> {
#RequireNodeType<Node, AST.AnExpression>(
#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(AST.Identifier(Common.Identifier(name: node.text!)))
return .Ok(CST.Identifier(Common.Identifier(name: node.text!)))
}
}
extension P4BooleanValue: CompilableExpression {
public static func CompileExpression(
node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext
) -> Result<AST.AnExpression> {
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, AST.AnExpression>(
#RequireNodeType<Node, CST.Categories.Expression>(
node: node, type: "booleanLiteralExpression", nice_type_name: "Boolean Literal Expression")
if node.text == "false" {
return .Ok(AST.Literal(P4Value(P4BooleanValue(withValue: false))))
return .Ok(CST.Literal(P4Value(P4BooleanValue(withValue: false))))
} else if node.text == "true" {
return .Ok(AST.Literal(P4Value(P4BooleanValue(withValue: true))))
return .Ok(CST.Literal(P4Value(P4BooleanValue(withValue: true))))
}
return .Error(
@@ -53,13 +53,13 @@ extension P4BooleanValue: CompilableExpression {
}
}
extension P4IntValue: CompilableExpression {
public static func CompileExpression(
node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext
) -> Result<AST.AnExpression> {
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, AST.AnExpression>(
#RequireNodesType<Node, CST.Categories.Expression>(
nodes: node, type: ["integer", "integer_elaborated"],
nice_type_names: ["Integer", "Elaborated Integer"])
@@ -93,7 +93,7 @@ extension P4IntValue: CompilableExpression {
}
if let parsed_int = Int(value_source) {
return .Ok(AST.Literal(P4Value(P4IntValue(withValue: parsed_int, andWidth: bit_width))))
return .Ok(CST.Literal(P4Value(P4IntValue(withValue: parsed_int, andWidth: bit_width))))
} else {
return .Error(
ErrorWithLocation(
@@ -103,43 +103,43 @@ extension P4IntValue: CompilableExpression {
}
}
extension P4StringValue: CompilableExpression {
public static func CompileExpression(
node: SwiftTreeSitter.Node, withContext scopes: ASTCompilerContext
) -> Result<AST.AnExpression> {
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, AST.AnExpression>(
#RequireNodeType<Node, CST.Categories.Expression>(
node: node, type: "string_literal", nice_type_name: "String Literal")
return .Ok(AST.Literal(P4Value(P4StringValue(withValue: node.text!))))
return .Ok(CST.Literal(P4Value(P4StringValue(withValue: node.text!))))
}
}
extension AST.Expression: Compilable {
public static func Compile(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.AnExpression> {
#RequireNodeType<Node, AST.AnExpression>(
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, AST.AnExpression>(
#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 AST.Expression.Compile(node: expression_node.child(at: 1)!, withContext: context)
return CST.Expression.Parse(node: expression_node.child(at: 1)!, withContext: context)
}
let expression_parsers: [CompilableExpression.Type] = [
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, AST.Identifier.self,
AST.BinaryOperatorExpression.self, AST.ArrayAccessExpression.self,
AST.FieldAccessExpression.self,
AST.FunctionCall.self,
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.CompileExpression(
switch candidate_expression_parser.ParseExpression(
node: expression_node, withContext: context)
{
case .Ok(let parsed): return .Ok(parsed)
@@ -151,37 +151,37 @@ extension AST.Expression: Compilable {
}
}
extension AST.KeysetExpression: CompilableExpression {
public static func CompileExpression(
node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext
) -> Result<AST.AnExpression> {
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, AST.AnExpression>(
#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(AST.KeysetExpression.Default)
return .Ok(CST.KeysetExpression.Default)
}
// Compile the expression:
let maybe_compiled_set_expression = AST.Expression.Compile(
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(AST.KeysetExpression.Value(compiled_expression))
return .Ok(CST.KeysetExpression.Value(compiled_expression))
}
}
extension AST.SelectExpression: CompilableExpression {
public static func CompileExpression(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.AnExpression> {
#RequireNodeType<Node, (AST.SelectExpression, ASTCompilerContext)>(
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),
@@ -201,7 +201,7 @@ extension AST.SelectExpression: CompilableExpression {
withError: "Could not find select expression body"))
}
let maybe_selector = AST.Expression.Compile(node: selector_node, withContext: context)
let maybe_selector = CST.Expression.Parse(node: selector_node, withContext: context)
guard case .Ok(let selector) = maybe_selector else {
return .Error(
ErrorWithLocation(
@@ -211,14 +211,14 @@ extension AST.SelectExpression: CompilableExpression {
))
}
var sces: [AST.SelectCaseExpression] = Array()
var sces: [CST.SelectCaseExpression] = Array()
var sces_errors: (any Errorable)? = .none
select_body_node.enumerateNamedChildren { current_node in
let maybe_parsed_cse = AST.SelectCaseExpression.CompileExpression(
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! AST.SelectCaseExpression)
case .Ok(let parsed_cse): sces.append(parsed_cse as! CST.SelectCaseExpression)
case .Error(let e):
sces_errors =
if let sces_errors = sces_errors {
@@ -234,17 +234,17 @@ extension AST.SelectExpression: CompilableExpression {
}
return .Ok(
AST.SelectExpression(withSelector: selector, withSelectCaseExpressions: sces),
CST.SelectExpression(withSelector: selector, withSelectCaseExpressions: sces),
)
}
}
extension AST.SelectCaseExpression: CompilableExpression {
public static func CompileExpression(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.AnExpression> {
extension CST.SelectCaseExpression: ParsableExpression {
public static func ParseExpression(
node: Node, withContext context: CSTCompilerContext
) -> Result<CST.Categories.Expression> {
#RequireNodeType<Node, AST.AnExpression>(
#RequireNodeType<Node, CST.Categories.Expression>(
node: node, type: "selectCase", nice_type_name: "Select Case")
guard let keysetexpression_node = node.child(at: 0),
@@ -259,34 +259,34 @@ extension AST.SelectCaseExpression: CompilableExpression {
return Result.Error(Error(withMessage: "Missing target state in select case"))
}
let maybe_parsed_keysetexpression = AST.KeysetExpression.CompileExpression(
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! AST.KeysetExpression
let keysetexpression = maybe_keysetexpression as! CST.KeysetExpression
let maybe_parsed_targetstate = AST.Identifier.CompileExpression(
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(
AST.SelectCaseExpression(
withKey: keysetexpression, withNextState: targetstate as! AST.Identifier)
CST.SelectCaseExpression(
withKey: keysetexpression, withNextState: targetstate as! CST.Identifier)
)
}
}
extension AST.BinaryOperatorExpression: CompilableExpression {
public static func CompileExpression(
node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext
) -> Result<AST.AnExpression> {
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, AST.AnExpression>(
#RequireNodeType<Node, CST.Categories.Expression>(
node: expression, type: "binaryOperatorExpression",
nice_type_name: "Binary Operator Expression")
let binary_operator_expression_node = expression.child(at: 0)!
@@ -295,20 +295,20 @@ extension AST.BinaryOperatorExpression: CompilableExpression {
var current_node: Node? = .none
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnExpression>.Error(
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, AST.AnExpression>(
#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<AST.AnExpression>.Error(
or: Result<CST.Categories.Expression>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing LHS for binary operator expression")))
@@ -318,7 +318,7 @@ extension AST.BinaryOperatorExpression: CompilableExpression {
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnExpression>.Error(
or: Result<CST.Categories.Expression>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing binary operator for binary operator expression")))
@@ -326,69 +326,69 @@ extension AST.BinaryOperatorExpression: CompilableExpression {
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnExpression>.Error(
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 = AST.Expression.Compile(
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 = AST.Expression.Compile(
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, AST.BinaryOperatorExpressionType)] = [
let evaluators: [String: (String, P4QualifiedType, CST.BinaryOperatorExpressionType)] = [
"binaryEqualOperatorExpression": (
"Binary Equal", P4QualifiedType(P4Boolean()),
AST.BinaryOperatorExpressionType.Eq
CST.BinaryOperatorExpressionType.Eq
),
"binaryLessThanOperatorExpression": (
"Binary Less Than", P4QualifiedType(P4Boolean()),
AST.BinaryOperatorExpressionType.Lt
CST.BinaryOperatorExpressionType.Lt
),
"binaryLessThanEqualOperatorExpression": (
"Binary Less Than Or Equal", P4QualifiedType(P4Boolean()),
AST.BinaryOperatorExpressionType.Lte
CST.BinaryOperatorExpressionType.Lte
),
"binaryGreaterThanOperatorExpression": (
"Binary Greater Than", P4QualifiedType(P4Boolean()),
AST.BinaryOperatorExpressionType.Gt
CST.BinaryOperatorExpressionType.Gt
),
"binaryGreaterThanEqualOperatorExpression": (
"Binary Greater Than Or Equal", P4QualifiedType(P4Boolean()),
AST.BinaryOperatorExpressionType.Gte
CST.BinaryOperatorExpressionType.Gte
),
"binaryAndOperatorExpression": (
"Binary Or", P4QualifiedType(P4Boolean()),
AST.BinaryOperatorExpressionType.And
CST.BinaryOperatorExpressionType.And
),
"binaryOrOperatorExpression": (
"Binary And", P4QualifiedType(P4Boolean()),
AST.BinaryOperatorExpressionType.Or
CST.BinaryOperatorExpressionType.Or
),
"binaryAddOperatorExpression": (
"Binary Add", P4QualifiedType(P4Int()),
AST.BinaryOperatorExpressionType.Add
CST.BinaryOperatorExpressionType.Add
),
"binarySubtractOperatorExpression": (
"Binary Subtract", P4QualifiedType(P4Int()),
AST.BinaryOperatorExpressionType.Subtract
CST.BinaryOperatorExpressionType.Subtract
),
"binaryMultiplyOperatorExpression": (
"Binary Multiply", P4QualifiedType(P4Int()),
AST.BinaryOperatorExpressionType.Multiply
CST.BinaryOperatorExpressionType.Multiply
),
"binaryDivideOperatorExpression": (
"Binary Divide", P4QualifiedType(P4Int()),
AST.BinaryOperatorExpressionType.Divide
CST.BinaryOperatorExpressionType.Divide
),
]
@@ -398,19 +398,19 @@ extension AST.BinaryOperatorExpression: CompilableExpression {
}
return .Ok(
AST.BinaryOperatorExpression(
CST.BinaryOperatorExpression(
withType: selected_evaluator.2,
withLhs: left_hand_side, withRhs: right_hand_side))
}
}
extension AST.ArrayAccessExpression: CompilableExpression {
public static func CompileExpression(
node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext
) -> Result<AST.AnExpression> {
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, AST.AnExpression>(
#RequireNodeType<Node, CST.Categories.Expression>(
node: expression, type: "arrayAccessExpression", nice_type_name: "Array Access Expression")
let array_access_expression_node = expression
@@ -419,11 +419,11 @@ extension AST.ArrayAccessExpression: CompilableExpression {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnExpression>.Error(
or: Result<CST.Categories.Expression>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Malformed array access expression")))
#RequireNodeType<Node, AST.AnExpression>(
#RequireNodeType<Node, CST.Categories.Expression>(
node: current_node!, type: "expression",
nice_type_name: "array identifier expression")
let array_access_identifier_node = current_node!
@@ -431,7 +431,7 @@ extension AST.ArrayAccessExpression: CompilableExpression {
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnExpression>.Error(
or: Result<CST.Categories.Expression>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing [ for array access expression")))
@@ -440,42 +440,42 @@ extension AST.ArrayAccessExpression: CompilableExpression {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnExpression>.Error(
or: Result<CST.Categories.Expression>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing indexor expression for array access expression")))
#RequireNodeType<Node, AST.AnExpression>(
#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 = AST.Expression.Compile(
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 = AST.Expression.Compile(
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(
AST.ArrayAccessExpression(
CST.ArrayAccessExpression(
withName: array_identifier, withIndexor: array_indexor))
}
}
extension AST.FieldAccessExpression: CompilableExpression {
public static func CompileExpression(
node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext
) -> Result<AST.AnExpression> {
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, AST.AnExpression>(
#RequireNodeType<Node, CST.Categories.Expression>(
node: expression, type: "fieldAccessExpression", nice_type_name: "Array Access Expression")
let field_access_expression_node = expression
@@ -485,11 +485,11 @@ extension AST.FieldAccessExpression: CompilableExpression {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnExpression>.Error(
or: Result<CST.Categories.Expression>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Malformed field access expression")))
#RequireNodeType<Node, AST.AnExpression>(
#RequireNodeType<Node, CST.Categories.Expression>(
node: current_node!, type: "expression",
nice_type_name: "struct identifier expression")
let struct_identifier_node = current_node!
@@ -497,7 +497,7 @@ extension AST.FieldAccessExpression: CompilableExpression {
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnExpression>.Error(
or: Result<CST.Categories.Expression>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing . for field access expression")))
@@ -505,44 +505,44 @@ extension AST.FieldAccessExpression: CompilableExpression {
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnExpression>.Error(
or: Result<CST.Categories.Expression>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing field name for field access expression")))
#RequireNodeType<Node, AST.AnExpression>(
#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 = AST.Expression.Compile(
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 = AST.Identifier.CompileExpression(
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(
AST.FieldAccessExpression(
CST.FieldAccessExpression(
withStruct: struct_identifier,
withField: field_name as! AST.Identifier))
withField: field_name as! CST.Identifier))
}
}
extension AST.FunctionCall: CompilableExpression {
public static func CompileExpression(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.AnExpression> {
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, AST.AnExpression>(
#RequireNodeType<Node, CST.Categories.Expression>(
node: expression, type: "function_call", nice_type_name: "Function Call")
var walker = Walker(node: expression)
@@ -550,11 +550,11 @@ extension AST.FunctionCall: CompilableExpression {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnExpression>.Error(
or: Result<CST.Categories.Expression>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing function call component")))
let maybe_callee_name = AST.Identifier.CompileExpression(
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()!)
@@ -563,15 +563,15 @@ extension AST.FunctionCall: CompilableExpression {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnExpression>.Error(
or: Result<CST.Categories.Expression>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing function call component")))
let maybe_argument_list = AST.ArgumentList.Compile(node: current_node!, withContext: context)
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(AST.FunctionCall(callee_name as! AST.Identifier, withArguments: arguments))
return .Ok(CST.FunctionCall(callee_name as! CST.Identifier, withArguments: arguments))
}
}
@@ -20,55 +20,13 @@ import SwiftTreeSitter
import TreeSitterExtensions
import TreeSitterP4
extension AST.Statement: Compilable {
public typealias C = AST.AnStatement
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: ASTCompilerContext
) -> Result<AST.AnStatement> {
if node.nodeType != "parserStatement" && node.nodeType != "statement" {
return Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing expected parser statement")
)
}
let statement = node.child(at: 0)!
let statementParsers: [String: CompilableStatement.Type] = [
"assignmentStatement": AST.ParserAssignmentStatement.self,
"expressionStatement": AST.ExpressionStatement.self,
"variableDeclaration": AST.VariableDeclarationStatement.self,
"conditionalStatement": AST.ConditionalStatement.self,
"blockStatement": AST.BlockStatement.self,
"return_statement": AST.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.CompileStatement(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)"))
}
}
}
extension AST.LocalElements: Compilable {
public typealias C = AST.AnStatement
public static func Compile(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.AnStatement> {
let localElementsParsers: [String: CompilableStatement.Type] = [
"variableDeclaration": AST.VariableDeclarationStatement.self
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 {
@@ -78,7 +36,7 @@ extension AST.LocalElements: Compilable {
withError: "Unparseable statement type (\(node.nodeType ?? "Unknown Statement Type"))"))
}
switch parser.CompileStatement(node: node, withContext: context) {
switch parser.ParseStatement(node: node, withContext: context) {
case Result.Ok(let parsed):
return Result.Ok(parsed)
case Result.Error(let e):
@@ -87,11 +45,11 @@ extension AST.LocalElements: Compilable {
}
}
extension AST.ParserState: Compilable {
public typealias C = AST.AnState
public static func Compile(
node: Node, withContext context: ASTCompilerContext
) -> Result<AST.AnState> {
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
@@ -107,7 +65,7 @@ extension AST.ParserState: Compilable {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnState>.Error(
or: Result<CST.Categories.State>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing elements in parser state declaration")))
@@ -125,12 +83,12 @@ extension AST.ParserState: Compilable {
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnState>.Error(
or: Result<CST.Categories.State>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing elements in parser state declaration")))
let maybe_state_identifier = AST.Identifier.CompileExpression(
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()!)
@@ -141,16 +99,16 @@ extension AST.ParserState: Compilable {
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnState>.Error(
or: Result<CST.Categories.State>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing body of state declaration")
))
var errors: (any Errorable)? = .none
var parsed_s: [AST.AnStatement] = Array()
var parsed_s: CST.Statements? = .none
if current_node!.nodeType == "parserStatements" {
switch SpecialCompilers.Statements.Compile(
switch CST.Statements.Parse(
node: current_node!, withContext: context)
{
case .Ok(let state_statements):
@@ -172,14 +130,14 @@ extension AST.ParserState: Compilable {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<AST.AnState>.Error(
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! AST.Identifier))
let updated_context = context.update(withContextName: (state_identifier as! CST.Identifier))
.update(withContextStatements: parsed_s)
return AST.TransitionStatement.Compile(node: current_node!, withContext: updated_context)
return CST.TransitionStatement.Parse(node: current_node!, withContext: updated_context)
}
}
+72
View File
@@ -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)))
}
}
+566
View File
@@ -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)"))
}
}
}
+99
View File
@@ -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"))
}
}
+92
View File
@@ -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>
}
+112
View File
@@ -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)
}
}
+32 -7
View File
@@ -22,9 +22,9 @@ import SwiftTreeSitter
import Testing
import TreeSitter
import TreeSitterP4
import P4CodeGen
@testable import P4Compiler
@testable import P4Parser
@testable import P4CodeGen
@Test func test_text_serializer() async throws {
let simple_parser_declaration = """
@@ -42,9 +42,34 @@ import P4CodeGen
};
"""
let program = try #UseOkResult(SpecialCompilers.ProgramCompiler.Compile(simple_parser_declaration))
let v = TextSerializer()
let c = TextSerializerContext();
let vd = ASTVisitorDriver();
#expect(#RequireOkResult((vd.visit(program: program, visitor: v, context: c))))
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)
}
@@ -17,7 +17,6 @@
import Foundation
import Macros
import P4Compiler
import SwiftTreeSitter
import SystemPackage
import Testing
@@ -25,6 +24,7 @@ import TreeSitter
import TreeSitterP4
@testable import Common
@testable import P4Parser
@Test func test_preprocessor() async throws {
let sm = SourceManager(["./TestData/Sources/"])
@@ -32,7 +32,7 @@ import TreeSitterP4
let file = FilePath.init(stringLiteral: "simple.p4")
let source = try! (#UseOkResult(prep.preprocess(file)))
#expect(#RequireOkResult(SpecialCompilers.ProgramCompiler.Compile(source.getSource())))
#expect(#RequireOkResult(compile(source.getSource())))
#expect(source.getLocations().getPath() == file)
}
@@ -42,7 +42,7 @@ import TreeSitterP4
let file = FilePath.init(stringLiteral: "simple.p4")
let source = try! (#UseOkResult(prep.preprocess(file)))
#expect(#RequireOkResult(SpecialCompilers.ProgramCompiler.Compile(source.getSource())))
#expect(#RequireOkResult(compile(source.getSource())))
#expect(source.getLocations().getPath() == file)
}
@@ -54,7 +54,7 @@ import TreeSitterP4
#expect(#RequireOkResult(prep.preprocess(file)))
let source = try! (#UseOkResult(prep.preprocess(file)))
#expect(#RequireOkResult(SpecialCompilers.ProgramCompiler.Compile(source.getSource())))
#expect(#RequireOkResult(compile(source.getSource())))
#expect(source.getLocations().getPath() == file)
}
@@ -66,7 +66,7 @@ import TreeSitterP4
#expect(#RequireOkResult(prep.preprocess(file)))
let source = try! (#UseOkResult(prep.preprocess(file)))
#expect(#RequireOkResult(SpecialCompilers.ProgramCompiler.Compile(source.getSource())))
#expect(#RequireOkResult(compile(source.getSource())))
#expect(source.getLocations().getPath() == file)
}