Refactor P4Compiler to P4Parser
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
@@ -0,0 +1,675 @@
|
||||
// p4rse, Copyright 2026, Will Hawkins
|
||||
//
|
||||
// This file is part of p4rse.
|
||||
//
|
||||
// This file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Common
|
||||
|
||||
extension P4Value: CST.AnExpression {}
|
||||
|
||||
public struct CST {
|
||||
|
||||
public protocol AnExpression {}
|
||||
public protocol Statement {}
|
||||
public protocol AnState {}
|
||||
|
||||
struct Expression {}
|
||||
|
||||
public struct Statements {
|
||||
public let statements: [Statement]
|
||||
|
||||
public init(_ s: [Statement]) {
|
||||
self.statements = s
|
||||
}
|
||||
}
|
||||
|
||||
public struct VariableDeclarationStatement: Statement {
|
||||
public var initializer: AnExpression?
|
||||
public var identifier: CST.Identifier
|
||||
public var tipe: CST.Tipe
|
||||
|
||||
public init(
|
||||
identifier: Identifier, withType tipe: CST.Tipe, withInitializer initializer: AnExpression?
|
||||
) {
|
||||
self.identifier = identifier
|
||||
self.initializer = initializer
|
||||
self.tipe = tipe
|
||||
}
|
||||
}
|
||||
|
||||
public struct ConditionalStatement: Statement {
|
||||
public var condition: AnExpression
|
||||
public var thenn: Statement
|
||||
public var elss: Statement?
|
||||
|
||||
public init(condition: AnExpression, withThen thenn: Statement) {
|
||||
self.condition = condition
|
||||
self.thenn = thenn
|
||||
self.elss = .none
|
||||
}
|
||||
|
||||
public init(
|
||||
condition: AnExpression, withThen thenn: Statement,
|
||||
andElse elss: Statement
|
||||
) {
|
||||
self.condition = condition
|
||||
self.thenn = thenn
|
||||
self.elss = elss
|
||||
}
|
||||
}
|
||||
|
||||
public struct BlockStatement: Statement {
|
||||
public var statements: Statements
|
||||
|
||||
public init(_ statements: Statements) {
|
||||
self.statements = statements
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct ReturnStatement: Statement {
|
||||
public let value: AnExpression
|
||||
|
||||
public init(_ value: AnExpression) {
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
|
||||
public struct ApplyStatement: Statement {
|
||||
public let body: CST.BlockStatement?
|
||||
|
||||
public init() { self.body = .none }
|
||||
public init(_ body: CST.BlockStatement) {
|
||||
self.body = body
|
||||
}
|
||||
}
|
||||
|
||||
public struct Action: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "Action: "
|
||||
+ "\(self.name) with parameters \(self.params) and body \(String(describing: self.body))"
|
||||
}
|
||||
|
||||
public var body: CST.BlockStatement?
|
||||
public var params: CST.ParameterList
|
||||
public var name: CST.Identifier
|
||||
|
||||
public init(
|
||||
named name: CST.Identifier = CST.Identifier(Common.Identifier(name: "")),
|
||||
withParameters parameters: ParameterList = ParameterList([]),
|
||||
withBody body: CST.BlockStatement? = .none
|
||||
) {
|
||||
self.name = name
|
||||
self.params = parameters
|
||||
self.body = body
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct Actions: CustomStringConvertible {
|
||||
public let actions: [CST.Action]
|
||||
public init(withActions actions: [CST.Action]) {
|
||||
self.actions = actions
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Actions: "
|
||||
+ actions.map { action in
|
||||
return "\(action)"
|
||||
}.joined(separator: ";")
|
||||
}
|
||||
}
|
||||
|
||||
public enum TableKeyMatchType {
|
||||
case Exact
|
||||
}
|
||||
|
||||
public struct TableKeyEntry: CustomStringConvertible {
|
||||
public let key: CST.KeysetExpression
|
||||
public let match_type: CST.TableKeyMatchType
|
||||
|
||||
public init(_ key: CST.KeysetExpression, _ match: CST.TableKeyMatchType) {
|
||||
self.key = key
|
||||
self.match_type = match
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Table Key Entry: " + "\(self.key): \(self.match_type)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct TableKeys: CustomStringConvertible {
|
||||
public let keys: [CST.TableKeyEntry]
|
||||
|
||||
public init(withEntries entries: [CST.TableKeyEntry]) {
|
||||
self.keys = entries
|
||||
}
|
||||
public init() {
|
||||
self.keys = []
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Table Keys: "
|
||||
+ self.keys.map { key in
|
||||
return "\(key)"
|
||||
}.joined(separator: ";")
|
||||
}
|
||||
}
|
||||
|
||||
public struct TableActionsProperty: CustomStringConvertible {
|
||||
public let actions: [CST.Identifier]
|
||||
public init(_ actions: [CST.Identifier] = []) {
|
||||
self.actions = actions
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Table Actions: "
|
||||
+ self.actions.map { action in
|
||||
return "\(action)"
|
||||
}.joined(separator: ";")
|
||||
}
|
||||
}
|
||||
|
||||
public struct TablePropertyList: CustomStringConvertible {
|
||||
public let actions: CST.TableActionsProperty
|
||||
public let keys: CST.TableKeys
|
||||
public init(withActions actions: CST.TableActionsProperty, withKeys keys: CST.TableKeys) {
|
||||
self.actions = actions
|
||||
self.keys = keys
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Table Property List: \(self.actions) \(self.keys)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct Table: CustomStringConvertible {
|
||||
public let properties: CST.TablePropertyList
|
||||
let name: CST.Identifier
|
||||
public let entries: [(P4Value, CST.Identifier)]
|
||||
|
||||
public init(
|
||||
withName name: CST.Identifier, withPropertyList property_list: CST.TablePropertyList,
|
||||
withEntries entries: [(P4Value, CST.Identifier)] = []
|
||||
) {
|
||||
self.name = name
|
||||
self.properties = property_list
|
||||
self.entries = entries
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Table named: \(self.name) \(self.properties)"
|
||||
}
|
||||
|
||||
/// When the control is evaluated, the value of the x in the table is
|
||||
/// compared to the entries and the match is assocated with an action
|
||||
/// that is invoked when the match occurs!
|
||||
|
||||
public func update(addEntry entry: (P4Value, CST.Identifier)) -> Table {
|
||||
return Table(
|
||||
withName: self.name, withPropertyList: self.properties, withEntries: self.entries + [entry])
|
||||
}
|
||||
}
|
||||
|
||||
public protocol AnDeclaration: Statement {}
|
||||
|
||||
public struct Declaration {}
|
||||
|
||||
public struct Control: CustomStringConvertible, AnDeclaration {
|
||||
public var description: String {
|
||||
return "Control named \(self._name) \(self.parameters) \(self.actions) \(self.table)"
|
||||
}
|
||||
|
||||
public let actions: CST.Actions
|
||||
public let table: CST.Table
|
||||
let _parameters: CST.ParameterList
|
||||
let _name: CST.Identifier
|
||||
let apply: CST.ApplyStatement
|
||||
|
||||
public var parameters: CST.ParameterList {
|
||||
_parameters
|
||||
}
|
||||
|
||||
public var name: CST.Identifier {
|
||||
_name
|
||||
}
|
||||
|
||||
public init(
|
||||
named: CST.Identifier, withParameters parameters: CST.ParameterList,
|
||||
withTable table: CST.Table,
|
||||
withActions actions: CST.Actions, withApply apply: CST.ApplyStatement
|
||||
) {
|
||||
self._name = named
|
||||
self._parameters = parameters
|
||||
self.actions = actions
|
||||
self.table = table
|
||||
self.apply = apply
|
||||
}
|
||||
|
||||
public func updateTable(addEntry entry: (P4Value, CST.Identifier)) -> Control {
|
||||
let table = self.table.update(addEntry: entry)
|
||||
|
||||
return Control(
|
||||
named: self.name, withParameters: self.parameters, withTable: table,
|
||||
withActions: self.actions, withApply: self.apply)
|
||||
}
|
||||
|
||||
public func def() -> P4DataValue? {
|
||||
return .none
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct ExternDeclaration: AnDeclaration {
|
||||
public let declaration: CST.AnDeclaration
|
||||
|
||||
public init(_ declaration: CST.AnDeclaration) {
|
||||
self.declaration = declaration
|
||||
}
|
||||
}
|
||||
|
||||
public struct FunctionDeclaration: AnDeclaration {
|
||||
public var description: String {
|
||||
return "Function named \(self.name) that returns \(self.tipe) with parameters \(self.params)"
|
||||
}
|
||||
|
||||
public var body: CST.BlockStatement?
|
||||
public var params: CST.ParameterList
|
||||
public var name: CST.Identifier
|
||||
public var tipe: CST.Tipe
|
||||
|
||||
public init(
|
||||
named name: CST.Identifier, ofType type: CST.Tipe,
|
||||
withParameters parameters: CST.ParameterList,
|
||||
withBody body: CST.BlockStatement?
|
||||
) {
|
||||
self.name = name
|
||||
self.tipe = type
|
||||
self.params = parameters
|
||||
self.body = body
|
||||
}
|
||||
}
|
||||
|
||||
public struct StructDeclaration: AnDeclaration {
|
||||
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: Statement {
|
||||
public let name: CST.Identifier
|
||||
public var tipe: CST.Identifier
|
||||
public let arguments: CST.ArgumentList
|
||||
|
||||
public init(
|
||||
named name: CST.Identifier, withType tipe: CST.Identifier,
|
||||
withArguments arguments: CST.ArgumentList
|
||||
) {
|
||||
self.name = name
|
||||
self.arguments = arguments
|
||||
self.tipe = tipe
|
||||
}
|
||||
}
|
||||
|
||||
public struct ExpressionStatement: Statement {
|
||||
public let expression: AnExpression
|
||||
|
||||
public init(_ expr: AnExpression) {
|
||||
self.expression = expr
|
||||
}
|
||||
}
|
||||
|
||||
public struct Identifier: AnExpression {
|
||||
public let id: Common.Identifier
|
||||
public init(_ id: Common.Identifier) {
|
||||
self.id = id
|
||||
}
|
||||
}
|
||||
|
||||
public struct Literal: AnExpression {
|
||||
public let literal: P4Value
|
||||
public init(_ literal: P4Value) {
|
||||
self.literal = literal
|
||||
}
|
||||
}
|
||||
|
||||
public enum KeysetExpression: AnExpression {
|
||||
case Default
|
||||
case Value(AnExpression)
|
||||
}
|
||||
|
||||
public struct SelectCaseExpression: AnExpression {
|
||||
public let key: CST.KeysetExpression
|
||||
public let next_state_identifier: CST.Identifier
|
||||
|
||||
public init(withKey key: CST.KeysetExpression, withNextState next_state_id: CST.Identifier) {
|
||||
self.key = key
|
||||
self.next_state_identifier = next_state_id
|
||||
}
|
||||
}
|
||||
|
||||
public struct SelectExpression: AnExpression {
|
||||
public let selector: AnExpression
|
||||
public let case_expressions: [CST.SelectCaseExpression]
|
||||
|
||||
public init(
|
||||
withSelector selector: AnExpression,
|
||||
withSelectCaseExpressions sces: [CST.SelectCaseExpression]
|
||||
) {
|
||||
self.selector = selector
|
||||
self.case_expressions = sces
|
||||
}
|
||||
|
||||
public func append_checked_sce(sce: CST.SelectCaseExpression) -> CST.SelectExpression {
|
||||
var new_cses = self.case_expressions
|
||||
new_cses.append(sce)
|
||||
return SelectExpression(
|
||||
withSelector: self.selector, withSelectCaseExpressions: new_cses)
|
||||
}
|
||||
}
|
||||
|
||||
public enum BinaryOperatorExpressionType {
|
||||
case Add
|
||||
case Subtract
|
||||
case Multiply
|
||||
case Divide
|
||||
case Lt
|
||||
case Lte
|
||||
case Gt
|
||||
case Gte
|
||||
case Eq
|
||||
case And
|
||||
case Or
|
||||
}
|
||||
|
||||
public struct BinaryOperatorExpression: AnExpression {
|
||||
public let left: AnExpression
|
||||
public let right: AnExpression
|
||||
public let type: BinaryOperatorExpressionType
|
||||
|
||||
public init(
|
||||
withType tipe: BinaryOperatorExpressionType,
|
||||
withLhs lhs: AnExpression,
|
||||
withRhs rhs: AnExpression
|
||||
) {
|
||||
self.type = tipe
|
||||
self.left = lhs
|
||||
self.right = rhs
|
||||
}
|
||||
}
|
||||
|
||||
public struct ArrayAccessExpression: AnExpression {
|
||||
public let indexor: AnExpression
|
||||
public let name: AnExpression
|
||||
|
||||
public init(
|
||||
withName name: AnExpression,
|
||||
withIndexor indexor: AnExpression
|
||||
) {
|
||||
self.name = name
|
||||
self.indexor = indexor
|
||||
}
|
||||
}
|
||||
|
||||
public struct FieldAccessExpression: AnExpression {
|
||||
public let field: Identifier
|
||||
public let strct: AnExpression
|
||||
|
||||
public init(withStruct strct: AnExpression, withField field: Identifier) {
|
||||
self.strct = strct
|
||||
self.field = field
|
||||
}
|
||||
}
|
||||
|
||||
public struct FunctionCall: AnExpression {
|
||||
public let callee: Identifier
|
||||
public let arguments: ArgumentList
|
||||
|
||||
public init(_ callee: Identifier, withArguments arguments: ArgumentList) {
|
||||
self.callee = callee
|
||||
self.arguments = arguments
|
||||
}
|
||||
}
|
||||
|
||||
public struct LocalElements {}
|
||||
|
||||
public struct LocalElement {}
|
||||
|
||||
public struct ParserAssignmentStatement: Statement {
|
||||
public let lvalue: AnExpression
|
||||
public let value: AnExpression
|
||||
|
||||
public init(
|
||||
withLValue lvalue: AnExpression, withValue value: AnExpression
|
||||
) {
|
||||
self.lvalue = lvalue
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
|
||||
/// A P4 Parser State
|
||||
public class ParserState {
|
||||
let name: Identifier
|
||||
public let statements: CST.Statements?
|
||||
|
||||
public var description: String {
|
||||
return "Parser State named \(self.name)"
|
||||
}
|
||||
|
||||
public func getName() -> Identifier {
|
||||
return self.name
|
||||
}
|
||||
|
||||
public func getStatements() -> CST.Statements? {
|
||||
return self.statements
|
||||
}
|
||||
|
||||
/// Construct a ParserState
|
||||
public init(_ name: Identifier, _ statements: CST.Statements? = .none) {
|
||||
self.name = name
|
||||
self.statements = statements
|
||||
}
|
||||
}
|
||||
|
||||
/// TransitionStatement
|
||||
///
|
||||
/// Only defined to define Compilable extension.
|
||||
public struct TransitionStatement {}
|
||||
|
||||
public class ParserStateDirectTransition: ParserState, AnState {
|
||||
public let next_state_identifier: Identifier?
|
||||
|
||||
public init(
|
||||
name: Identifier, withNextStateIdentifier next_state_id: Identifier,
|
||||
withStatements stmts: CST.Statements? = .none
|
||||
) {
|
||||
self.next_state_identifier = next_state_id
|
||||
super.init(name, stmts)
|
||||
}
|
||||
}
|
||||
|
||||
public class ParserStateNoTransition: ParserState, AnState {
|
||||
/// Construct a ParserState
|
||||
public init(
|
||||
name: Identifier, withStatements stmts: CST.Statements? = .none
|
||||
) {
|
||||
super.init(name, stmts)
|
||||
}
|
||||
}
|
||||
|
||||
public class ParserStateSelectTransition: ParserState, AnState {
|
||||
|
||||
public let te: SelectExpression
|
||||
|
||||
public init(
|
||||
name: Identifier, withTransitionExpression te: SelectExpression,
|
||||
withStatements stmts: CST.Statements? = .none
|
||||
) {
|
||||
self.te = te
|
||||
super.init(name, stmts)
|
||||
}
|
||||
}
|
||||
|
||||
public struct ParserStates {
|
||||
public var states: [AnState] = Array()
|
||||
|
||||
public func count() -> Int {
|
||||
return states.count
|
||||
}
|
||||
|
||||
public init(_ states: [AnState] = Array()) {
|
||||
self.states = states
|
||||
}
|
||||
|
||||
public func append(state: AnState) -> ParserStates {
|
||||
var new_states = self.states
|
||||
new_states.append(state)
|
||||
return ParserStates(new_states)
|
||||
}
|
||||
}
|
||||
|
||||
/// A P4 Parser
|
||||
///
|
||||
/// Note: A Parser is a type
|
||||
public struct Parser: AnDeclaration {
|
||||
public var states: ParserStates
|
||||
|
||||
public var name: Identifier
|
||||
public var parameters: ParameterList
|
||||
|
||||
public init(withName name: Identifier) {
|
||||
self.states = ParserStates()
|
||||
self.parameters = ParameterList()
|
||||
self.name = name
|
||||
}
|
||||
|
||||
public init(withName name: Identifier, withParameters parameters: ParameterList) {
|
||||
self.states = ParserStates()
|
||||
self.parameters = parameters
|
||||
self.name = name
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Parser \(self.name) with parameters: \(parameters) and states: \(self.states)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct Types {}
|
||||
|
||||
public struct Tipe {
|
||||
public let tipe: P4QualifiedType
|
||||
public init(_ tipe: P4QualifiedType) {
|
||||
self.tipe = tipe
|
||||
}
|
||||
}
|
||||
|
||||
public struct Parameter: CustomStringConvertible {
|
||||
public var name: CST.Identifier
|
||||
public var type: CST.Tipe
|
||||
|
||||
public init(
|
||||
identifier: Identifier, withType type: CST.Tipe
|
||||
) {
|
||||
self.name = identifier
|
||||
self.type = type
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "Parameter: \(self.name) with type \(self.type)"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct ParameterList: CustomStringConvertible {
|
||||
public var parameters: [CST.Parameter]
|
||||
|
||||
public init() {
|
||||
self.parameters = Array()
|
||||
}
|
||||
|
||||
public init(_ parameters: [CST.Parameter]) {
|
||||
self.parameters = parameters
|
||||
}
|
||||
|
||||
public func addParameter(_ parameter: CST.Parameter) -> CST.ParameterList {
|
||||
return CST.ParameterList(self.parameters + [parameter])
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
let parameters = self.parameters.map { parameter in
|
||||
parameter.description
|
||||
}.joined(separator: ";")
|
||||
return "Parameter list: \(parameters)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct ArgumentList {
|
||||
public let arguments: [CST.Argument]
|
||||
|
||||
public init(_ arguments: [CST.Argument] = []) {
|
||||
self.arguments = arguments
|
||||
}
|
||||
|
||||
public func addArgument(_ argument: CST.Argument) -> CST.ArgumentList {
|
||||
return ArgumentList(self.arguments + [argument])
|
||||
}
|
||||
}
|
||||
|
||||
public struct Argument {
|
||||
public let index: Int
|
||||
public let argument: CST.AnExpression
|
||||
|
||||
public init(_ argument: CST.AnExpression, atIndex index: Int) {
|
||||
self.argument = argument
|
||||
self.index = index
|
||||
}
|
||||
}
|
||||
|
||||
public struct Program {
|
||||
public var statements: Statements
|
||||
public init(_ stmts: Statements = Statements([])) {
|
||||
self.statements = stmts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct CSTCompilerContext {
|
||||
public let lexical_context_name: CST.Identifier?
|
||||
public let lexical_context_statements: CST.Statements?
|
||||
public let extern_context: Bool
|
||||
|
||||
public init(
|
||||
_ name: CST.Identifier? = .none, _ stmts: CST.Statements? = .none, _ extern: Bool = false
|
||||
) {
|
||||
self.lexical_context_name = name
|
||||
self.lexical_context_statements = stmts
|
||||
self.extern_context = extern
|
||||
}
|
||||
|
||||
public func update(withContextName cn: CST.Identifier?) -> CSTCompilerContext {
|
||||
return CSTCompilerContext(cn, self.lexical_context_statements, self.extern_context)
|
||||
}
|
||||
|
||||
public func update(withContextStatements stmts: CST.Statements?) -> CSTCompilerContext {
|
||||
return CSTCompilerContext(self.lexical_context_name, stmts, self.extern_context)
|
||||
}
|
||||
|
||||
public func update(withExtern extern: Bool) -> CSTCompilerContext {
|
||||
return CSTCompilerContext(self.lexical_context_name, self.lexical_context_statements, extern)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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 Node {
|
||||
public func toSourceLocation() -> SourceLocation {
|
||||
return SourceLocation(self.range.location, self.range.length)
|
||||
}
|
||||
}
|
||||
|
||||
@attached(member, names: named(ParseStatement))
|
||||
public macro deriveParsableStatement() =
|
||||
#externalMacro(module: "Macros", type: "DeriveParsableStatement")
|
||||
@@ -0,0 +1,57 @@
|
||||
// p4rse, Copyright 2026, Will Hawkins
|
||||
//
|
||||
// This file is part of p4rse.
|
||||
//
|
||||
// This file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Common
|
||||
import SwiftTreeSitter
|
||||
import TreeSitterExtensions
|
||||
import TreeSitterP4
|
||||
|
||||
let p4lang = Language(tree_sitter_p4())
|
||||
|
||||
public func ConfigureP4Parser() -> Result<SwiftTreeSitter.Parser> {
|
||||
let p = SwiftTreeSitter.Parser.init()
|
||||
|
||||
do {
|
||||
try p.setLanguage(p4lang)
|
||||
} catch {
|
||||
return Result.Error(Error(withMessage: "Could not configure the P4 parser"))
|
||||
}
|
||||
|
||||
return .Ok(p)
|
||||
}
|
||||
|
||||
public func compile(_ s: String) -> Result<CST.Program> {
|
||||
let maybe_parser = ConfigureP4Parser()
|
||||
guard case .Ok(let p) = maybe_parser else {
|
||||
return .Error(maybe_parser.error()!)
|
||||
}
|
||||
|
||||
let result = p.parse(s)
|
||||
guard let tree = result,
|
||||
!tree.isError(lang: p4lang),
|
||||
!tree.containsMissing(lang: p4lang)
|
||||
else {
|
||||
return Result.Error(Error(withMessage: "Could not parse the P4 program"))
|
||||
}
|
||||
|
||||
guard let root = tree.rootNode else {
|
||||
return Result.Error(Error(withMessage: "No P4 program compiled"))
|
||||
}
|
||||
|
||||
let compilation_context = CSTCompilerContext()
|
||||
return CST.Program.Parse(node: root, withContext: compilation_context)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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 CompilationFeedback {
|
||||
public let feedback: String
|
||||
|
||||
static func markup(
|
||||
_ source: SourceCode, _ errors: [any Errorable], _ formatter: any Formattable
|
||||
) -> String {
|
||||
errors.map { $0.format(formatter, source) }.joined(separator: "\n")
|
||||
}
|
||||
|
||||
public init(_ source: SourceCode, _ errors: [any Errorable], _ formatter: any Formattable) {
|
||||
self.feedback = Self.markup(source, errors, formatter)
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,577 @@
|
||||
// p4rse, Copyright 2026, Will Hawkins
|
||||
//
|
||||
// This file is part of p4rse.
|
||||
//
|
||||
// This file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Common
|
||||
import SwiftTreeSitter
|
||||
import TreeSitterP4
|
||||
|
||||
extension CST.Identifier: ParsableExpression {
|
||||
public static func ParseExpression(
|
||||
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.AnExpression> {
|
||||
|
||||
#RequireNodeType<Node, CST.AnExpression>(
|
||||
node: node, type: "identifier", nice_type_name: "Identifier")
|
||||
|
||||
/// TODO: If there is a value here, then we can make this a compile-time constant!
|
||||
return .Ok(CST.Identifier(Common.Identifier(name: node.text!)))
|
||||
}
|
||||
}
|
||||
|
||||
extension P4BooleanValue: ParsableExpression {
|
||||
public static func ParseExpression(
|
||||
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.AnExpression> {
|
||||
let node = node.child(at: 0)!
|
||||
#RequireNodeType<Node, CST.AnExpression>(
|
||||
node: node, type: "booleanLiteralExpression", nice_type_name: "Boolean Literal Expression")
|
||||
|
||||
if node.text == "false" {
|
||||
return .Ok(CST.Literal(P4Value(P4BooleanValue(withValue: false))))
|
||||
} else if node.text == "true" {
|
||||
return .Ok(CST.Literal(P4Value(P4BooleanValue(withValue: true))))
|
||||
}
|
||||
|
||||
return .Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Failed to parse boolean literal: \(node.text!)"))
|
||||
}
|
||||
}
|
||||
|
||||
extension P4IntValue: ParsableExpression {
|
||||
public static func ParseExpression(
|
||||
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.AnExpression> {
|
||||
let node = node.child(at: 0)!
|
||||
|
||||
#RequireNodesType<Node, CST.AnExpression>(
|
||||
nodes: node, type: ["integer", "integer_elaborated"],
|
||||
nice_type_names: ["Integer", "Elaborated Integer"])
|
||||
|
||||
var bit_width: BitWidth = BitWidth.Infinite
|
||||
let value_source: String
|
||||
if node.nodeType == "integer_elaborated" {
|
||||
let re = /([0-9]+)([ws])([\-0-9]+)/
|
||||
let integer_components = node.text!.matches(of: re)
|
||||
|
||||
if integer_components.isEmpty || integer_components.count > 1 {
|
||||
return .Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Failed to parse elaborated integer: \(node.text!)"))
|
||||
}
|
||||
|
||||
let width_source = "\(integer_components[0].1)"
|
||||
guard let width = Int(width_source) else {
|
||||
return .Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Failed to parse width from elaborated integer: \(width_source)"))
|
||||
}
|
||||
|
||||
/// TODO: Handle signed vs. unsigned.
|
||||
|
||||
bit_width = BitWidth.Width(width)
|
||||
value_source = "\(integer_components[0].3)"
|
||||
} else {
|
||||
value_source = node.text!
|
||||
}
|
||||
|
||||
if let parsed_int = Int(value_source) {
|
||||
return .Ok(CST.Literal(P4Value(P4IntValue(withValue: parsed_int, andWidth: bit_width))))
|
||||
} else {
|
||||
return .Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Failed to parse integer: \(node.text!)"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension P4StringValue: ParsableExpression {
|
||||
public static func ParseExpression(
|
||||
node: SwiftTreeSitter.Node, withContext scopes: CSTCompilerContext
|
||||
) -> Result<CST.AnExpression> {
|
||||
let node = node.child(at: 0)!
|
||||
#RequireNodeType<Node, CST.AnExpression>(
|
||||
node: node, type: "string_literal", nice_type_name: "String Literal")
|
||||
return .Ok(CST.Literal(P4Value(P4StringValue(withValue: node.text!))))
|
||||
}
|
||||
}
|
||||
|
||||
extension CST.Expression: Parsable {
|
||||
public static func Parse(
|
||||
node: Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.AnExpression> {
|
||||
#RequireNodeType<Node, CST.AnExpression>(
|
||||
node: node, type: "expression", nice_type_name: "expression")
|
||||
|
||||
let expression_node = node.child(at: 0)!
|
||||
#RequireNodesType<Node, CST.AnExpression>(
|
||||
nodes: expression_node, type: ["grouped_expression", "simple_expression"],
|
||||
nice_type_names: ["grouped expression", "simple expression"])
|
||||
|
||||
// If this is a grouped expression, recurse!
|
||||
if expression_node.nodeType == "grouped_expression" {
|
||||
return CST.Expression.Parse(node: expression_node.child(at: 1)!, withContext: context)
|
||||
}
|
||||
|
||||
let expression_parsers: [ParsableExpression.Type] = [
|
||||
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, CST.Identifier.self,
|
||||
CST.BinaryOperatorExpression.self, CST.ArrayAccessExpression.self,
|
||||
CST.FieldAccessExpression.self,
|
||||
CST.FunctionCall.self,
|
||||
]
|
||||
|
||||
for candidate_expression_parser in expression_parsers {
|
||||
switch candidate_expression_parser.ParseExpression(
|
||||
node: expression_node, withContext: context)
|
||||
{
|
||||
case .Ok(let parsed): return .Ok(parsed)
|
||||
case .Error(let e): return .Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Error(Error(withMessage: "\(node.range): Could not parse into expression"))
|
||||
}
|
||||
}
|
||||
|
||||
extension CST.KeysetExpression: ParsableExpression {
|
||||
public static func ParseExpression(
|
||||
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.AnExpression> {
|
||||
let keyset_expression_node = node.child(at: 0)!
|
||||
|
||||
#RequireNodesType<Node, CST.AnExpression>(
|
||||
nodes: keyset_expression_node, type: ["expression", "default_keyset"],
|
||||
nice_type_names: ["expression", "default keyset"])
|
||||
|
||||
// If there is a default keyset, that's easy!
|
||||
if keyset_expression_node.nodeType == "default_keyset" {
|
||||
return .Ok(CST.KeysetExpression.Default)
|
||||
}
|
||||
|
||||
// Compile the expression:
|
||||
let maybe_compiled_set_expression = CST.Expression.Parse(
|
||||
node: keyset_expression_node, withContext: context)
|
||||
guard case .Ok(let compiled_expression) = maybe_compiled_set_expression else {
|
||||
return .Error(maybe_compiled_set_expression.error()!)
|
||||
}
|
||||
|
||||
return .Ok(CST.KeysetExpression.Value(compiled_expression))
|
||||
}
|
||||
}
|
||||
|
||||
extension CST.SelectExpression: ParsableExpression {
|
||||
public static func ParseExpression(
|
||||
node: Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.AnExpression> {
|
||||
#RequireNodeType<Node, (CST.SelectExpression, CSTCompilerContext)>(
|
||||
node: node, type: "selectExpression", nice_type_name: "parser select expression")
|
||||
|
||||
guard let selector_node = node.child(at: 2),
|
||||
selector_node.nodeType == "expression"
|
||||
else {
|
||||
return .Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(), withError: "Could not find selector expression"))
|
||||
}
|
||||
|
||||
guard let select_body_node = node.child(at: 5),
|
||||
select_body_node.nodeType == "selectBody"
|
||||
else {
|
||||
return .Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Could not find select expression body"))
|
||||
}
|
||||
|
||||
let maybe_selector = CST.Expression.Parse(node: selector_node, withContext: context)
|
||||
guard case .Ok(let selector) = maybe_selector else {
|
||||
return .Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: selector_node.toSourceLocation(),
|
||||
withError:
|
||||
"Could not parse transition select expression selector expression: \(maybe_selector.error()!)"
|
||||
))
|
||||
}
|
||||
|
||||
var sces: [CST.SelectCaseExpression] = Array()
|
||||
var sces_errors: (any Errorable)? = .none
|
||||
|
||||
select_body_node.enumerateNamedChildren { current_node in
|
||||
let maybe_parsed_cse = CST.SelectCaseExpression.ParseExpression(
|
||||
node: current_node, withContext: context)
|
||||
switch maybe_parsed_cse {
|
||||
case .Ok(let parsed_cse): sces.append(parsed_cse as! CST.SelectCaseExpression)
|
||||
case .Error(let e):
|
||||
sces_errors =
|
||||
if let sces_errors = sces_errors {
|
||||
sces_errors.append(error: Error(withMessage: "\(maybe_parsed_cse.error()!)"))
|
||||
} else {
|
||||
e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let sces_errors = sces_errors {
|
||||
return .Error(ErrorWithLabel("Error(s) parsing select cases", sces_errors))
|
||||
}
|
||||
|
||||
return .Ok(
|
||||
CST.SelectExpression(withSelector: selector, withSelectCaseExpressions: sces),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension CST.SelectCaseExpression: ParsableExpression {
|
||||
public static func ParseExpression(
|
||||
node: Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.AnExpression> {
|
||||
|
||||
#RequireNodeType<Node, CST.AnExpression>(
|
||||
node: node, type: "selectCase", nice_type_name: "Select Case")
|
||||
|
||||
guard let keysetexpression_node = node.child(at: 0),
|
||||
keysetexpression_node.nodeType == "keysetExpression"
|
||||
else {
|
||||
return Result.Error(Error(withMessage: "Missing keyset expression in select case"))
|
||||
}
|
||||
|
||||
guard let targetstate_node = node.child(at: 2),
|
||||
targetstate_node.nodeType == "identifier"
|
||||
else {
|
||||
return Result.Error(Error(withMessage: "Missing target state in select case"))
|
||||
}
|
||||
|
||||
let maybe_parsed_keysetexpression = CST.KeysetExpression.ParseExpression(
|
||||
node: keysetexpression_node, withContext: context)
|
||||
guard case Result.Ok(let maybe_keysetexpression) = maybe_parsed_keysetexpression else {
|
||||
return Result.Error(maybe_parsed_keysetexpression.error()!)
|
||||
}
|
||||
|
||||
let keysetexpression = maybe_keysetexpression as! CST.KeysetExpression
|
||||
|
||||
let maybe_parsed_targetstate = CST.Identifier.ParseExpression(
|
||||
node: targetstate_node, withContext: context)
|
||||
guard case .Ok(let targetstate) = maybe_parsed_targetstate else {
|
||||
return Result.Error(maybe_parsed_targetstate.error()!)
|
||||
}
|
||||
|
||||
return .Ok(
|
||||
CST.SelectCaseExpression(
|
||||
withKey: keysetexpression, withNextState: targetstate as! CST.Identifier)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension CST.BinaryOperatorExpression: ParsableExpression {
|
||||
public static func ParseExpression(
|
||||
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.AnExpression> {
|
||||
let expression = node.child(at: 0)!
|
||||
|
||||
#RequireNodeType<Node, CST.AnExpression>(
|
||||
node: expression, type: "binaryOperatorExpression",
|
||||
nice_type_name: "Binary Operator Expression")
|
||||
let binary_operator_expression_node = expression.child(at: 0)!
|
||||
var walker = Walker(node: binary_operator_expression_node)
|
||||
|
||||
var current_node: Node? = .none
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnExpression>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(), withError: "Malformed binary operator expression"
|
||||
)))
|
||||
|
||||
/// TODO: This macro cannot handle new lines in the arrays
|
||||
// swift-format-ignore
|
||||
#RequireNodesType<Node, CST.AnExpression>(
|
||||
nodes: binary_operator_expression_node,
|
||||
type: ["binaryEqualOperatorExpression", "binaryLessThanOperatorExpression", "binaryLessThanEqualOperatorExpression", "binaryGreaterThanOperatorExpression", "binaryGreaterThanEqualOperatorExpression", "binaryAndOperatorExpression", "binaryOrOperatorExpression", "binaryAddOperatorExpression", "binarySubtractOperatorExpression", "binaryMultiplyOperatorExpression", "binaryDivideOperatorExpression"],
|
||||
nice_type_names: [ "binary equal operator", "binary less than operator", "binary less than or equal to operator", "binary greater than operator", "binary greater than or equal to operator", "binary and operator", "binary or operator", "binary add operator", "binary subtract operator", "binary multiply operator", "binary divide operator"])
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnExpression>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Missing LHS for binary operator expression")))
|
||||
|
||||
let left_hand_side_raw = current_node!
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnExpression>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Missing binary operator for binary operator expression")))
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnExpression>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Missing RHS for binary operator expression")))
|
||||
|
||||
let right_hand_side_raw = current_node!
|
||||
|
||||
let maybe_left_hand_side = CST.Expression.Parse(
|
||||
node: left_hand_side_raw, withContext: context)
|
||||
guard case Result.Ok(let left_hand_side) = maybe_left_hand_side else {
|
||||
return Result.Error(maybe_left_hand_side.error()!)
|
||||
}
|
||||
|
||||
let maybe_right_hand_side = CST.Expression.Parse(
|
||||
node: right_hand_side_raw, withContext: context)
|
||||
guard case Result.Ok(let right_hand_side) = maybe_right_hand_side else {
|
||||
return Result.Error(maybe_right_hand_side.error()!)
|
||||
}
|
||||
|
||||
let evaluators: [String: (String, P4QualifiedType, CST.BinaryOperatorExpressionType)] = [
|
||||
"binaryEqualOperatorExpression": (
|
||||
"Binary Equal", P4QualifiedType(P4Boolean()),
|
||||
CST.BinaryOperatorExpressionType.Eq
|
||||
),
|
||||
"binaryLessThanOperatorExpression": (
|
||||
"Binary Less Than", P4QualifiedType(P4Boolean()),
|
||||
CST.BinaryOperatorExpressionType.Lt
|
||||
),
|
||||
"binaryLessThanEqualOperatorExpression": (
|
||||
"Binary Less Than Or Equal", P4QualifiedType(P4Boolean()),
|
||||
CST.BinaryOperatorExpressionType.Lte
|
||||
),
|
||||
"binaryGreaterThanOperatorExpression": (
|
||||
"Binary Greater Than", P4QualifiedType(P4Boolean()),
|
||||
CST.BinaryOperatorExpressionType.Gt
|
||||
),
|
||||
"binaryGreaterThanEqualOperatorExpression": (
|
||||
"Binary Greater Than Or Equal", P4QualifiedType(P4Boolean()),
|
||||
CST.BinaryOperatorExpressionType.Gte
|
||||
),
|
||||
"binaryAndOperatorExpression": (
|
||||
"Binary Or", P4QualifiedType(P4Boolean()),
|
||||
CST.BinaryOperatorExpressionType.And
|
||||
),
|
||||
"binaryOrOperatorExpression": (
|
||||
"Binary And", P4QualifiedType(P4Boolean()),
|
||||
CST.BinaryOperatorExpressionType.Or
|
||||
),
|
||||
"binaryAddOperatorExpression": (
|
||||
"Binary Add", P4QualifiedType(P4Int()),
|
||||
CST.BinaryOperatorExpressionType.Add
|
||||
),
|
||||
"binarySubtractOperatorExpression": (
|
||||
"Binary Subtract", P4QualifiedType(P4Int()),
|
||||
CST.BinaryOperatorExpressionType.Subtract
|
||||
),
|
||||
"binaryMultiplyOperatorExpression": (
|
||||
"Binary Multiply", P4QualifiedType(P4Int()),
|
||||
CST.BinaryOperatorExpressionType.Multiply
|
||||
),
|
||||
"binaryDivideOperatorExpression": (
|
||||
"Binary Divide", P4QualifiedType(P4Int()),
|
||||
CST.BinaryOperatorExpressionType.Divide
|
||||
),
|
||||
]
|
||||
|
||||
guard let selected_evaluator = evaluators[binary_operator_expression_node.nodeType!] else {
|
||||
return Result.Error(
|
||||
Error(withMessage: "No evaluator for \(binary_operator_expression_node.nodeType!)"))
|
||||
}
|
||||
|
||||
return .Ok(
|
||||
CST.BinaryOperatorExpression(
|
||||
withType: selected_evaluator.2,
|
||||
withLhs: left_hand_side, withRhs: right_hand_side))
|
||||
}
|
||||
}
|
||||
|
||||
extension CST.ArrayAccessExpression: ParsableExpression {
|
||||
public static func ParseExpression(
|
||||
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.AnExpression> {
|
||||
let expression = node.child(at: 0)!
|
||||
|
||||
#RequireNodeType<Node, CST.AnExpression>(
|
||||
node: expression, type: "arrayAccessExpression", nice_type_name: "Array Access Expression")
|
||||
let array_access_expression_node = expression
|
||||
|
||||
var walker = Walker(node: array_access_expression_node)
|
||||
var current_node: Node? = .none
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnExpression>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(), withError: "Malformed array access expression")))
|
||||
|
||||
#RequireNodeType<Node, CST.AnExpression>(
|
||||
node: current_node!, type: "expression",
|
||||
nice_type_name: "array identifier expression")
|
||||
let array_access_identifier_node = current_node!
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnExpression>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Missing [ for array access expression")))
|
||||
|
||||
walker.next()
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnExpression>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Missing indexor expression for array access expression")))
|
||||
|
||||
#RequireNodeType<Node, CST.AnExpression>(
|
||||
node: current_node!, type: "expression",
|
||||
nice_type_name: "array indexor expression")
|
||||
|
||||
let array_access_indexor_node = current_node!
|
||||
|
||||
let maybe_array_identifier = CST.Expression.Parse(
|
||||
node: array_access_identifier_node, withContext: context)
|
||||
guard case Result.Ok(let array_identifier) = maybe_array_identifier else {
|
||||
return Result.Error(maybe_array_identifier.error()!)
|
||||
}
|
||||
|
||||
let maybe_array_indexor = CST.Expression.Parse(
|
||||
node: array_access_indexor_node, withContext: context)
|
||||
guard case Result.Ok(let array_indexor) = maybe_array_indexor else {
|
||||
return Result.Error(maybe_array_indexor.error()!)
|
||||
}
|
||||
|
||||
return .Ok(
|
||||
CST.ArrayAccessExpression(
|
||||
withName: array_identifier, withIndexor: array_indexor))
|
||||
}
|
||||
}
|
||||
|
||||
extension CST.FieldAccessExpression: ParsableExpression {
|
||||
public static func ParseExpression(
|
||||
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.AnExpression> {
|
||||
let expression = node.child(at: 0)!
|
||||
|
||||
#RequireNodeType<Node, CST.AnExpression>(
|
||||
node: expression, type: "fieldAccessExpression", nice_type_name: "Array Access Expression")
|
||||
|
||||
let field_access_expression_node = expression
|
||||
|
||||
var walker = Walker(node: field_access_expression_node)
|
||||
var current_node: Node? = .none
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnExpression>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(), withError: "Malformed field access expression")))
|
||||
|
||||
#RequireNodeType<Node, CST.AnExpression>(
|
||||
node: current_node!, type: "expression",
|
||||
nice_type_name: "struct identifier expression")
|
||||
let struct_identifier_node = current_node!
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnExpression>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Missing . for field access expression")))
|
||||
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnExpression>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Missing field name for field access expression")))
|
||||
|
||||
#RequireNodeType<Node, CST.AnExpression>(
|
||||
node: current_node!, type: "identifier",
|
||||
nice_type_name: "field name")
|
||||
|
||||
let field_name_node = current_node!
|
||||
|
||||
// Make sure that the identifier really identifies a struct.
|
||||
let maybe_struct_identifier = CST.Expression.Parse(
|
||||
node: struct_identifier_node, withContext: context)
|
||||
guard case Result.Ok(let struct_identifier) = maybe_struct_identifier else {
|
||||
return Result.Error(maybe_struct_identifier.error()!)
|
||||
}
|
||||
|
||||
let maybe_field_name = CST.Identifier.ParseExpression(
|
||||
node: field_name_node, withContext: context)
|
||||
guard case Result.Ok(let field_name) = maybe_field_name else {
|
||||
return Result.Error(maybe_field_name.error()!)
|
||||
}
|
||||
|
||||
return .Ok(
|
||||
CST.FieldAccessExpression(
|
||||
withStruct: struct_identifier,
|
||||
withField: field_name as! CST.Identifier))
|
||||
}
|
||||
}
|
||||
|
||||
extension CST.FunctionCall: ParsableExpression {
|
||||
public static func ParseExpression(
|
||||
node: Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.AnExpression> {
|
||||
|
||||
let expression = node.child(at: 0)!
|
||||
#RequireNodeType<Node, CST.AnExpression>(
|
||||
node: expression, type: "function_call", nice_type_name: "Function Call")
|
||||
|
||||
var walker = Walker(node: expression)
|
||||
var current_node: Node? = .none
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnExpression>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(), withError: "Missing function call component")))
|
||||
|
||||
let maybe_callee_name = CST.Identifier.ParseExpression(
|
||||
node: current_node!, withContext: context)
|
||||
guard case .Ok(let callee_name) = maybe_callee_name else {
|
||||
return Result.Error(maybe_callee_name.error()!)
|
||||
}
|
||||
walker.next()
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnExpression>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(), withError: "Missing function call component")))
|
||||
|
||||
let maybe_argument_list = CST.ArgumentList.Parse(node: current_node!, withContext: context)
|
||||
|
||||
guard case .Ok(let arguments) = maybe_argument_list else {
|
||||
return .Error(maybe_argument_list.error()!)
|
||||
}
|
||||
return .Ok(CST.FunctionCall(callee_name as! CST.Identifier, withArguments: arguments))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// p4rse, Copyright 2026, Will Hawkins
|
||||
//
|
||||
// This file is part of p4rse.
|
||||
//
|
||||
// This file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Common
|
||||
import SwiftTreeSitter
|
||||
import TreeSitterExtensions
|
||||
import TreeSitterP4
|
||||
|
||||
extension CST.LocalElements: Parsable {
|
||||
public typealias C = CST.Statement
|
||||
public static func Parse(
|
||||
node: Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.Statement> {
|
||||
let localElementsParsers: [String: ParsableStatement.Type] = [
|
||||
"variableDeclaration": CST.VariableDeclarationStatement.self
|
||||
]
|
||||
|
||||
guard let parser = localElementsParsers[node.nodeType ?? ""] else {
|
||||
return Result.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Unparseable statement type (\(node.nodeType ?? "Unknown Statement Type"))"))
|
||||
}
|
||||
|
||||
switch parser.ParseStatement(node: node, withContext: context) {
|
||||
case Result.Ok(let parsed):
|
||||
return Result.Ok(parsed)
|
||||
case Result.Error(let e):
|
||||
return Result.Error(Error(withMessage: "Failed to parse local element: \(e)"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CST.ParserState: Parsable {
|
||||
public typealias C = CST.AnState
|
||||
public static func Parse(
|
||||
node: Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.AnState> {
|
||||
var walker = Walker(node: node)
|
||||
|
||||
var current_node: Node? = .none
|
||||
|
||||
guard let node_type = node.nodeType,
|
||||
node_type == "parserState"
|
||||
else {
|
||||
return Result.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Did not find a parser state declaration"))
|
||||
}
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnState>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Missing elements in parser state declaration")))
|
||||
|
||||
if current_node!.nodeType == "annotations" {
|
||||
return Result.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: current_node!.toSourceLocation(),
|
||||
withError: "Annotations in parser state are not yet handled."))
|
||||
|
||||
// Would increment here.
|
||||
}
|
||||
|
||||
// Skip the keyword state
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnState>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Missing elements in parser state declaration")))
|
||||
|
||||
let maybe_state_identifier = CST.Identifier.ParseExpression(
|
||||
node: current_node!, withContext: context)
|
||||
guard case Result.Ok(let state_identifier) = maybe_state_identifier else {
|
||||
return Result.Error(maybe_state_identifier.error()!)
|
||||
}
|
||||
|
||||
walker.next()
|
||||
// Skip the '{'
|
||||
walker.next()
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnState>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(), withError: "Missing body of state declaration")
|
||||
))
|
||||
|
||||
var errors: (any Errorable)? = .none
|
||||
var parsed_s: CST.Statements? = .none
|
||||
|
||||
if current_node!.nodeType == "parserStatements" {
|
||||
switch CST.Statements.Parse(
|
||||
node: current_node!, withContext: context)
|
||||
{
|
||||
case .Ok(let state_statements):
|
||||
parsed_s = state_statements
|
||||
case .Error(let error):
|
||||
errors =
|
||||
if let errors = errors {
|
||||
errors.append(error: error)
|
||||
} else {
|
||||
error
|
||||
}
|
||||
}
|
||||
walker.next()
|
||||
}
|
||||
|
||||
if let errors = errors {
|
||||
return .Error(errors)
|
||||
}
|
||||
|
||||
#MustOr(
|
||||
result: current_node, thing: walker.getNext(),
|
||||
or: Result<CST.AnState>.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Missing transition statement of state declaration")))
|
||||
|
||||
let updated_context = context.update(withContextName: (state_identifier as! CST.Identifier))
|
||||
.update(withContextStatements: parsed_s)
|
||||
|
||||
return CST.TransitionStatement.Parse(node: current_node!, withContext: updated_context)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// p4rse, Copyright 2026, Will Hawkins
|
||||
//
|
||||
// This file is part of p4rse.
|
||||
//
|
||||
// This file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Common
|
||||
import SwiftTreeSitter
|
||||
|
||||
extension CST.Program: Parsable {
|
||||
public typealias C = CST.Program
|
||||
public static func Parse(
|
||||
node: Node, withContext context: CSTCompilerContext
|
||||
) -> Common.Result<CST.Program> {
|
||||
|
||||
var statements: [CST.Statement] = Array()
|
||||
|
||||
var errors: (any Errorable)? = .none
|
||||
|
||||
// Try to parse all top-level declarations.
|
||||
node.enumerateNamedChildren { (declaration_node: Node) in
|
||||
let declaration_parsers: [String: ParsableStatement.Type] = [
|
||||
"declaration": CST.Declaration.self,
|
||||
"instantiation": CST.Instantiation.self,
|
||||
]
|
||||
|
||||
if let parser = declaration_parsers[declaration_node.nodeType!] {
|
||||
let r = parser.ParseStatement(node: declaration_node, withContext: context)
|
||||
switch r {
|
||||
case .Ok(let compiled):
|
||||
statements.append(compiled)
|
||||
case .Error(let e):
|
||||
errors =
|
||||
if let errors = errors {
|
||||
errors.append(error: e)
|
||||
} else {
|
||||
e
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
let e = ErrorWithLocation(
|
||||
sourceLocation: declaration_node.toSourceLocation(),
|
||||
withError:
|
||||
"\(declaration_node.nodeType!) cannot be at a P4 program top level")
|
||||
errors =
|
||||
if let errors = errors {
|
||||
errors.append(error: e)
|
||||
} else {
|
||||
e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let errors = errors {
|
||||
return .Error(errors)
|
||||
}
|
||||
return Result.Ok(CST.Program(CST.Statements(statements)))
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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 struct SpecialParsers {
|
||||
public struct Statement {}
|
||||
}
|
||||
|
||||
extension SpecialParsers.Statement: Parsable {
|
||||
public typealias C = CST.Statement
|
||||
public static func Parse(
|
||||
node: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.Statement> {
|
||||
|
||||
if node.nodeType != "parserStatement" && node.nodeType != "statement" {
|
||||
return Result.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(), withError: "Missing expected parser statement")
|
||||
)
|
||||
}
|
||||
|
||||
let statement = node.child(at: 0)!
|
||||
|
||||
let statementParsers: [String: ParsableStatement.Type] = [
|
||||
"assignmentStatement": CST.ParserAssignmentStatement.self,
|
||||
"expressionStatement": CST.ExpressionStatement.self,
|
||||
"variableDeclaration": CST.VariableDeclarationStatement.self,
|
||||
"conditionalStatement": CST.ConditionalStatement.self,
|
||||
"blockStatement": CST.BlockStatement.self,
|
||||
"return_statement": CST.ReturnStatement.self,
|
||||
]
|
||||
guard let parser = statementParsers[statement.nodeType ?? ""] else {
|
||||
return Result.Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: statement.toSourceLocation(),
|
||||
withError:
|
||||
"Unparseable statement type (\(statement.nodeType ?? "Unknown Statement Type"))"))
|
||||
}
|
||||
switch parser.ParseStatement(node: statement, withContext: context) {
|
||||
case Result.Ok(let parsed):
|
||||
return .Ok(parsed)
|
||||
case Result.Error(let e):
|
||||
return .Error(
|
||||
ErrorWithLocation(
|
||||
sourceLocation: node.toSourceLocation(),
|
||||
withError: "Failed to parse a statement element: \(e)"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,524 @@
|
||||
// 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) = SpecialParsers.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.Statement>? =
|
||||
if let elss = node.child(at: 6) {
|
||||
.some(
|
||||
SpecialParsers.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.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 = 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.Statement] = Array()
|
||||
|
||||
node.enumerateNamedChildren { node in
|
||||
switch SpecialParsers.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.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."
|
||||
))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// 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
|
||||
) -> Common.Result<(CST.Tipe)?> {
|
||||
return type.text == "bool" ? .Ok(CST.Tipe(P4QualifiedType(P4Boolean()))) : .Ok(.none)
|
||||
}
|
||||
}
|
||||
|
||||
extension P4Int: MaybeParsableType {
|
||||
public static func MaybeParseType(
|
||||
type: SwiftTreeSitter.Node, withContext: CSTCompilerContext
|
||||
) -> Common.Result<(CST.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<CST.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(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
|
||||
) -> Common.Result<(CST.Tipe)?> {
|
||||
return type.text == "string" ? .Ok(CST.Tipe(P4QualifiedType(P4String()))) : .Ok(.none)
|
||||
}
|
||||
}
|
||||
|
||||
extension CST.Types: ParsableType {
|
||||
public static func ParseType(
|
||||
type: SwiftTreeSitter.Node, withContext context: CSTCompilerContext
|
||||
) -> Result<CST.Tipe> {
|
||||
let type_parsers: [MaybeParsableType.Type] = [
|
||||
P4Boolean.self, P4Int.self, P4String.self, /*P4Struct.self,*/
|
||||
]
|
||||
for type_parser in type_parsers {
|
||||
switch type_parser.MaybeParseType(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"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// 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.AnExpression>
|
||||
}
|
||||
|
||||
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.Statement>
|
||||
}
|
||||
|
||||
public protocol CSTVisitor<T> {
|
||||
associatedtype 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>
|
||||
|
||||
func visit(node: CST.Parser, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||
func visit(
|
||||
node: CST.ParserStateDirectTransition, driver: CSTVisitorDriver, context: T
|
||||
) -> Result<T>
|
||||
func visit(node: CST.ParserStateNoTransition, driver: CSTVisitorDriver, context: T) -> Result<T>
|
||||
func visit(
|
||||
node: CST.ParserStateSelectTransition, driver: CSTVisitorDriver, context: T
|
||||
) -> Result<T>
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// p4rse, Copyright 2026, Will Hawkins
|
||||
//
|
||||
// This file is part of p4rse.
|
||||
//
|
||||
// This file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Common
|
||||
|
||||
public struct CSTVisitorDriver {
|
||||
public init() {}
|
||||
|
||||
public func visit<T>(
|
||||
expression: any CST.AnExpression, visitor: any CSTVisitor<T>, context: T
|
||||
) -> Result<T> {
|
||||
return switch expression {
|
||||
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 Is Not Visitable"))
|
||||
}
|
||||
}
|
||||
|
||||
public func visit<T>(
|
||||
state: any CST.AnState, 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"))
|
||||
}
|
||||
}
|
||||
|
||||
public func visit<T>(
|
||||
statement: any CST.Statement, visitor: any CSTVisitor<T>, context: T
|
||||
) -> Result<T> {
|
||||
return switch statement {
|
||||
case let s as CST.Parser: visitor.visit(node: s, driver: self, context: context)
|
||||
default: .Error(Error(withMessage: "AST Statement Element Is Not Visitable"))
|
||||
}
|
||||
}
|
||||
|
||||
public func visit<T>(
|
||||
program: CST.Program, visitor: any CSTVisitor<T>, context: T
|
||||
) -> Result<T> {
|
||||
|
||||
var context = context
|
||||
for s in program.statements.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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// p4rse, Copyright 2026, Will Hawkins
|
||||
//
|
||||
// This file is part of p4rse.
|
||||
//
|
||||
// This file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Common
|
||||
import SwiftTreeSitter
|
||||
import TreeSitterP4
|
||||
|
||||
public struct Walker {
|
||||
var currentChildIdx: Int
|
||||
let childCount: Int
|
||||
let node: Node
|
||||
|
||||
public init(node: Node) {
|
||||
self.currentChildIdx = 0
|
||||
self.childCount = node.childCount
|
||||
self.node = node
|
||||
}
|
||||
|
||||
public mutating func next() {
|
||||
self.currentChildIdx += 1
|
||||
}
|
||||
|
||||
public func getNext() -> Node? {
|
||||
// If it is safe, then return the node!
|
||||
if self.currentChildIdx < self.childCount {
|
||||
return self.node.child(at: self.currentChildIdx)!
|
||||
}
|
||||
return .none
|
||||
}
|
||||
|
||||
public func overUntil(n: Int, todo: (Node) -> Result<()>) -> Result<()> {
|
||||
for currentChildIdx in currentChildIdx..<n {
|
||||
let currentChild = node.child(at: currentChildIdx)!
|
||||
if case Result.Error(let e) = todo(currentChild) {
|
||||
return Result<()>.Error(e)
|
||||
}
|
||||
}
|
||||
return Result.Ok(())
|
||||
}
|
||||
|
||||
public func try_map<T>(
|
||||
n: Int, onlyNamed: Bool = false, todo: (Node) -> Result<T>
|
||||
) -> ([T], [any Errorable]) {
|
||||
var errors: [any Errorable] = Array()
|
||||
var results: [T] = Array()
|
||||
|
||||
for currentChildIdx in currentChildIdx..<n {
|
||||
let currentChild = node.child(at: currentChildIdx)!
|
||||
if onlyNamed && !currentChild.isNamed {
|
||||
continue
|
||||
}
|
||||
switch todo(currentChild) {
|
||||
case .Ok(let r): results.append(r)
|
||||
case .Error(let e): errors.append(e)
|
||||
}
|
||||
}
|
||||
return (results, errors)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user