From 989c9b121262a50e1d10754cf81ce57a7acf6399 Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Tue, 3 Feb 2026 08:22:58 -0500 Subject: [PATCH] Refactor Refactor into four major components: 1. Common: Things shared among all other components. 2. Lang: P4-language-related components. 3. Runtime: Components required to run a P4 program. 4. Parser: Components for parsing a P4 program from source. Other components: 1. Macros 2. Tests 3. TreeSitterExtensions: Extra tree sitter functionality Signed-off-by: Will Hawkins --- Package.swift | 29 +++- .../Program.swift => Common/Execution.swift} | 57 +++--- .../Types.swift => Common/ProgramTypes.swift} | 40 ++++- Sources/Common/Protocols.swift | 33 ++++ Sources/{P4 => Common}/Support.swift | 2 +- Sources/Lang/Parser.swift | 133 ++++++++++++++ Sources/Lang/Program.swift | 21 +++ .../Execution.swift => Lang/Types.swift} | 2 +- Sources/Macros/Macros.swift | 2 +- Sources/P4/Parser.swift | 163 ------------------ Sources/P4/Runtime.swift | 56 ------ Sources/Parser/Parser.swift | 92 +++++----- Sources/Runtime/Parser.swift | 50 ++++++ Sources/Runtime/Program.swift | 33 ++++ Sources/Runtime/Protocols.swift | 31 ++++ Sources/Runtime/Runtime.swift | 58 +++++++ Sources/TreeSitterExtensions/Extensions.swift | 2 +- Tests/p4lmTests/ParserTests.swift | 5 +- Tests/p4lmTests/RuntimeTests.swift | 20 ++- Tests/p4lmTests/SupportTests.swift | 4 +- 20 files changed, 513 insertions(+), 320 deletions(-) rename Sources/{P4/Program.swift => Common/Execution.swift} (69%) rename Sources/{P4/Types.swift => Common/ProgramTypes.swift} (69%) create mode 100644 Sources/Common/Protocols.swift rename Sources/{P4 => Common}/Support.swift (99%) create mode 100644 Sources/Lang/Parser.swift create mode 100644 Sources/Lang/Program.swift rename Sources/{Parser/Execution.swift => Lang/Types.swift} (99%) delete mode 100644 Sources/P4/Parser.swift delete mode 100644 Sources/P4/Runtime.swift create mode 100644 Sources/Runtime/Parser.swift create mode 100644 Sources/Runtime/Program.swift create mode 100644 Sources/Runtime/Protocols.swift create mode 100644 Sources/Runtime/Runtime.swift diff --git a/Package.swift b/Package.swift index 3b2148d..62edd3c 100644 --- a/Package.swift +++ b/Package.swift @@ -14,8 +14,16 @@ let package = Package( targets: ["Parser"] ), .library( - name: "P4", - targets: ["P4"] + name: "Common", + targets: ["Common"] + ), + .library( + name: "Lang", + targets: ["Lang"] + ), + .library( + name: "Runtime", + targets: ["Runtime"] ), ], dependencies: [ @@ -38,7 +46,8 @@ let package = Package( .product(name: "SwiftTreeSitterLayer", package: "swift-tree-sitter"), .product(name: "TreeSitterP4", package: "tree-sitter-p4"), .target(name: "TreeSitterExtensions"), - .target(name: "P4"), + .target(name: "Common"), + .target(name: "Lang"), ], ), .target( @@ -49,12 +58,20 @@ let package = Package( ], ), .target( - name: "P4", - dependencies: ["Macros", "TreeSitterExtensions"] + name: "Common", + dependencies: ["Macros"] + ), + .target( + name: "Lang", + dependencies: ["Common"] + ), + .target( + name: "Runtime", + dependencies: ["Lang", "Common"] ), .testTarget( name: "ParserTests", - dependencies: ["Parser", "P4", "Macros", "TreeSitterExtensions"] + dependencies: ["Parser", "Runtime", "Lang", "Macros", "TreeSitterExtensions", "Common"] ), ] ) diff --git a/Sources/P4/Program.swift b/Sources/Common/Execution.swift similarity index 69% rename from Sources/P4/Program.swift rename to Sources/Common/Execution.swift index 523f7a6..467918d 100644 --- a/Sources/P4/Program.swift +++ b/Sources/Common/Execution.swift @@ -1,7 +1,7 @@ // 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 @@ -15,42 +15,16 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -public class Identifier: CustomStringConvertible, Equatable { - var name: String +open class ProgramExecution: CustomStringConvertible { + public var scopes: Scopes = Scopes() - public init(name: String) { - self.name = name - } + public init() {} - public var description: String { - return "\(name)" - } - - public static func ==(lhs: Identifier, rhs: Identifier) -> Bool { - return lhs.name == rhs.name + open var description: String { + return "Runtime:\nScopes: \(scopes)" } } -public class Variable: Identifier { - var constant: Bool - var value: ValueType - - public init(name: String, withValue value: ValueType, isConstant constant: Bool) { - self.constant = constant - self.value = value - super.init(name: name) - } - - public override var description: String { - return "\(super.description) = \(value) \(constant ? "(constant)" : "")" - } - - public var value_type: ValueType { - get { - value - } - } -} public struct Scope: CustomStringConvertible{ var variables: [Variable] = Array() @@ -78,6 +52,12 @@ public struct Scope: CustomStringConvertible{ } return .none } + + public mutating func declare(variable: Variable) -> Scope { + var s = self + s.variables.append(variable) + return s + } } public struct Scopes: CustomStringConvertible { @@ -108,14 +88,17 @@ public struct Scopes: CustomStringConvertible { } } + public func declare(variable: Variable) -> Scopes { + var s = self + if var scope = s.scopes.popLast() { + s.scopes.append(scope.declare(variable: variable)) + } + return s + } + public var count: Int { get { scopes.count } } } - -public struct Program { - public var parsers: [P4.Parser] = Array() - public init() {} -} diff --git a/Sources/P4/Types.swift b/Sources/Common/ProgramTypes.swift similarity index 69% rename from Sources/P4/Types.swift rename to Sources/Common/ProgramTypes.swift index dc0ae7d..d7454d1 100644 --- a/Sources/P4/Types.swift +++ b/Sources/Common/ProgramTypes.swift @@ -1,7 +1,7 @@ // 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 @@ -15,6 +15,44 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . + +public class Identifier: CustomStringConvertible, Equatable { + var name: String + + public init(name: String) { + self.name = name + } + + public var description: String { + return "\(name)" + } + + public static func ==(lhs: Identifier, rhs: Identifier) -> Bool { + return lhs.name == rhs.name + } +} + +public class Variable: Identifier { + var constant: Bool + var value: ValueType + + public init(name: String, withValue value: ValueType, isConstant constant: Bool) { + self.constant = constant + self.value = value + super.init(name: name) + } + + public override var description: String { + return "\(super.description) = \(value) \(constant ? "(constant)" : "")" + } + + public var value_type: ValueType { + get { + value + } + } +} + public enum ValueType: CustomStringConvertible, Equatable { case Boolean(Bool) case Int(Int) diff --git a/Sources/Common/Protocols.swift b/Sources/Common/Protocols.swift new file mode 100644 index 0000000..b9aceff --- /dev/null +++ b/Sources/Common/Protocols.swift @@ -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 . + +public protocol EvaluatableExpression { + /// Evaluate an expression for a given execution + /// - Parameters + /// - execution: The execution context in which to evaluate the expression + /// - Returns: The value of expression + func evaluate(execution: ProgramExecution) -> ValueType +} + +public protocol EvaluatableParserStatement { + /// Evaluate a statement for a given execution + /// - Parameters + /// - execution: The execution context in which to evaluate the parser statement + /// - Returns: An updated execution after evaluating the parser statement + func evaluate(execution: ProgramExecution) -> ProgramExecution +} + diff --git a/Sources/P4/Support.swift b/Sources/Common/Support.swift similarity index 99% rename from Sources/P4/Support.swift rename to Sources/Common/Support.swift index d054ce0..22b1f16 100644 --- a/Sources/P4/Support.swift +++ b/Sources/Common/Support.swift @@ -1,7 +1,7 @@ // 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 diff --git a/Sources/Lang/Parser.swift b/Sources/Lang/Parser.swift new file mode 100644 index 0000000..6fb1909 --- /dev/null +++ b/Sources/Lang/Parser.swift @@ -0,0 +1,133 @@ +// 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 . + +import Common + +public struct LocalElements { + +} + +public struct LocalElement { + +} + +public struct ParserTransitionStatement { + public init() {} +} + +public struct VariableDeclarationStatement { + public var variable: Variable + public init(withVariable variable: Variable) { + self.variable = variable + } +} + +public struct ExpressionStatement { + public init() {} +} + +public struct ParserState: Equatable, CustomStringConvertible { + + public private(set) var state_name: String + public private(set) var local_elements: [EvaluatableParserStatement] + public private(set) var statements: [EvaluatableParserStatement] + public private(set) var transition: ParserTransitionStatement? + + public var description: String { + return "Name: \(state_name)" + } + + public static func == (lhs: ParserState, rhs: ParserState) -> Bool { + return lhs.state_name == rhs.state_name + } + + /// Construct a ParserState + public init( + name: String, withLocalElements localElements: [EvaluatableParserStatement]?, + withStatements statements: [EvaluatableParserStatement]?, + withTransition transitionStatement: ParserTransitionStatement + ) { + state_name = name + transition = transitionStatement + local_elements = localElements ?? Array() + self.statements = statements ?? Array() + } + + /// (private) constructor (no transition) + /// + /// accept and reject are the only final states and they are constructed internally. + init(name: String) { + state_name = name + transition = .none + local_elements = Array() + statements = Array() + } +} + +public struct ParserStates { + public var states: [ParserState] = Array() +} + +nonisolated(unsafe) public let accept: ParserState = ParserState(name: "accept") +nonisolated(unsafe) public let reject: ParserState = ParserState(name: "reject") + +public struct Parser { + public var states: [ParserState] = Array() + public var count: Int { + states.count + } + + public init() {} + + public func findStartState() -> ParserState? { + for state in states { + if state.state_name == "start" { + return state + } + } + return .none + } +} + +public class ParserExecution: ProgramExecution { + + private init(state: ParserState) { + self.state = state + super.init() + } + + public static func create(_ parser: Parser) -> Result { + guard let start_state = parser.findStartState() else { + return Result.Error(Error(withMessage: "Could not find the start state")) + } + let new = ParserExecution(state: start_state) + + return Result.Ok(new) + } + + public var state: ParserState + + public func transition(toNextState state: ParserState) -> ParserExecution { + let next = self + next.state = state + return next + } + + public override var description: String { + return "Execution: \(super.description)\nCurrent State: \(state)" + } +} \ No newline at end of file diff --git a/Sources/Lang/Program.swift b/Sources/Lang/Program.swift new file mode 100644 index 0000000..471c5bd --- /dev/null +++ b/Sources/Lang/Program.swift @@ -0,0 +1,21 @@ +// 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 . + +public struct Program { + public var parsers: [Parser] = Array() + public init() {} +} \ No newline at end of file diff --git a/Sources/Parser/Execution.swift b/Sources/Lang/Types.swift similarity index 99% rename from Sources/Parser/Execution.swift rename to Sources/Lang/Types.swift index 79b9de0..aca375f 100644 --- a/Sources/Parser/Execution.swift +++ b/Sources/Lang/Types.swift @@ -1,7 +1,7 @@ // 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 diff --git a/Sources/Macros/Macros.swift b/Sources/Macros/Macros.swift index ff0593b..a0ec8f5 100644 --- a/Sources/Macros/Macros.swift +++ b/Sources/Macros/Macros.swift @@ -1,7 +1,7 @@ // 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 diff --git a/Sources/P4/Parser.swift b/Sources/P4/Parser.swift deleted file mode 100644 index 40f6e31..0000000 --- a/Sources/P4/Parser.swift +++ /dev/null @@ -1,163 +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 . - -public struct LocalElements { - -} - -public struct LocalElement { - -} - -public class ParserExecution: ProgramExecution { - public var state: ParserState - - public init(_ state: ParserState) { - self.state = state - } - - public func transition(toNextState state: ParserState) -> ParserExecution { - let next = self - next.state = state - return next - } - - public override var description: String { - return "Execution: \(super.description)\nCurrent State: \(state)" - } -} - -public protocol Expression { - /// Evaluate an expression for a given execution - /// - Parameters - /// - execution: The execution context in which to evaluate the expression - /// - Returns: The value of expression - func evaluate(execution: ParserExecution) -> Value -} - -public protocol ParserStatement { - /// Evaluate a statement for a given execution - /// - Parameters - /// - execution: The execution context in which to evaluate the parser statement - /// - Returns: An updated execution after evaluating the parser statement - func evaluate(execution: ParserExecution) -> ParserExecution -} - -public struct ParserTransitionStatement: ParserStatement { - public init() {} - public func evaluate(execution: ParserExecution) -> ParserExecution { - return execution - } -} - -public struct VariableDeclarationStatement: ParserStatement { - public var variable: Variable - public init(withVariable variable: Variable) { - self.variable = variable - } - - public func evaluate(execution: ParserExecution) -> ParserExecution { - execution.scopes.scopes[0].variables.append(self.variable) - return execution - } -} - -public struct ExpressionStatement: ParserStatement { - public init() {} - public func evaluate(execution: ParserExecution) -> ParserExecution { - return execution - } -} - -public struct ParserState: Equatable, CustomStringConvertible { - - public private(set) var state_name: String - public private(set) var local_elements: [ParserStatement] - public private(set) var statements: [ParserStatement] - public private(set) var transition: ParserTransitionStatement? - - public var description: String { - return "Name: \(state_name)" - } - - public static func == (lhs: ParserState, rhs: ParserState) -> Bool { - return lhs.state_name == rhs.state_name - } - - /// Construct a ParserState - public init(name: String, withLocalElements localElements: [ParserStatement]?, withStatements statements: [ParserStatement]?, withTransition transitionStatement: ParserTransitionStatement) { - state_name = name - transition = transitionStatement - local_elements = localElements ?? Array() - self.statements = statements ?? Array() - } - - func evaluate(execution: ParserExecution) -> ParserExecution { - var currentExecution = execution - - // First, evaluate the local elements. - for local_element in local_elements { - currentExecution = local_element.evaluate(execution: currentExecution) - } - - // Then, evaluate the statements. - for statement in statements { - currentExecution = statement.evaluate(execution: currentExecution) - } - - return if let transition = transition { - currentExecution.transition(toNextState: accept) - } else { - currentExecution.transition(toNextState: reject) - } - } - - /// (private) constructor (no transition) - /// - /// accept and reject are the only final states and they are constructed internally. - init(name: String) { - state_name = name - transition = .none - local_elements = Array() - statements = Array() - } -} - -public struct ParserStates { - public var states: [ParserState] = Array() -} - -nonisolated(unsafe) public let accept: ParserState = ParserState(name: "accept") -nonisolated(unsafe) public let reject: ParserState = ParserState(name: "reject") - -public struct Parser { - public var states: [ParserState] = Array() - public var count: Int { - states.count - } - - public init() {} - - public func findStartState() -> Optional { - for state in states { - if state.state_name == "start" { - return state - } - } - return .none - } -} diff --git a/Sources/P4/Runtime.swift b/Sources/P4/Runtime.swift deleted file mode 100644 index 581e981..0000000 --- a/Sources/P4/Runtime.swift +++ /dev/null @@ -1,56 +0,0 @@ -// p4rse, Copyright 2026, Will Hawkins -// -// This file is part of p4rse. - -// This file is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -public class ProgramExecution: CustomStringConvertible { - public var scopes: Scopes = Scopes() - - public init() {} - - public var description: String { - return "Runtime:\nScopes: \(scopes)" - } -} - -//public struct ParserRuntime: ProgramRuntime { -public class ParserRuntime: CustomStringConvertible { - var execution: ParserExecution - - init(execution: ParserExecution) { - self.execution = execution - } - - public static func create(program: P4.Parser) -> Result { - // First, find the start state. - guard let start_state = program.findStartState() else { - return Result.Error(Error(withMessage: "Could not find the start state")) - } - return Result.Ok(P4.ParserRuntime(execution: P4.ParserExecution(start_state))) - } - - public func run(input: P4.Packet) -> Result { - execution.scopes.enter() - while execution.state != P4.accept && execution.state != P4.reject { - execution = execution.state.evaluate(execution: execution) - } - return .Ok(execution) - } - - public var description: String { - //return "\(super.description)\nState: \(execution?.description ?? "N/A")\nError: \(error?.description ?? "None")" - return "Runtime:\nExecution: \(execution)" - } -} diff --git a/Sources/Parser/Parser.swift b/Sources/Parser/Parser.swift index a9ca741..7acf744 100644 --- a/Sources/Parser/Parser.swift +++ b/Sources/Parser/Parser.swift @@ -1,7 +1,7 @@ // 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 @@ -15,46 +15,48 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import P4 +import Common +import Lang +import Runtime import SwiftTreeSitter -import TreeSitterP4 import TreeSitterExtensions +import TreeSitterP4 let p4lang = Language(tree_sitter_p4()) public protocol ParseableValueType { - static func Parse(type: String, withValue value: String) -> Result + static func Parse(type: String, withValue value: String) -> Result } // This seems unnecessary because all the value types are in a single enum? -extension P4.ValueType: ParseableValueType { - public static func Parse(type: String, withValue value: String) -> Result { +extension ValueType: ParseableValueType { + public static func Parse(type: String, withValue value: String) -> Result { if type == "bool" { // Default if value == "" { - return .Ok(P4.ValueType.Boolean(false)) + return .Ok(ValueType.Boolean(false)) } if value == "true" { - return .Ok(P4.ValueType.Boolean(true)) + return .Ok(ValueType.Boolean(true)) } else if value == "false" { - return .Ok(P4.ValueType.Boolean(false)) + return .Ok(ValueType.Boolean(false)) } return .Error(Error(withMessage: "Cannot convert \(value) into boolean value")) } else if type == "string" { - return .Ok(P4.ValueType.String(value)) + return .Ok(ValueType.String(value)) } else if type == "int" { // Default if value == "" { - return .Ok(P4.ValueType.Int(0)) + return .Ok(ValueType.Int(0)) } guard let parsed_value = Swift.Int(value) else { return .Error(Error(withMessage: "Cannot convert \(value) into integer value")) } - return .Ok(P4.ValueType.Int(parsed_value)) + return .Ok(ValueType.Int(parsed_value)) } return .Error(Error(withMessage: "Invalid type")) @@ -62,11 +64,13 @@ extension P4.ValueType: ParseableValueType { } public protocol ParseableParserStatement { - static func Parse(node: Node, inTree tree: MutableTree) -> Result + static func Parse(node: Node, inTree tree: MutableTree) -> Result } -extension P4.ExpressionStatement: ParseableParserStatement { - public static func Parse(node: Node, inTree tree: MutableTree) -> Result { +extension ExpressionStatement: ParseableParserStatement { + public static func Parse( + node: Node, inTree tree: MutableTree + ) -> Result { guard let parser_state_query = try? SwiftTreeSitter.Query( language: p4lang, @@ -82,16 +86,16 @@ extension P4.ExpressionStatement: ParseableParserStatement { let expression_capture = query_result.captures(named: "expression") if !expression_capture.isEmpty { // TODO: Actually create an ExpressionStatement - return Result.Ok(P4.ExpressionStatement()) + return Result.Ok(ExpressionStatement()) } - return Result.Ok(.none) - } } -extension P4.VariableDeclarationStatement: ParseableParserStatement { - public static func Parse(node: Node, inTree tree: MutableTree) -> Result { +extension VariableDeclarationStatement: ParseableParserStatement { + public static func Parse( + node: Node, inTree tree: MutableTree + ) -> Result { guard let parser_state_query = try? SwiftTreeSitter.Query( language: p4lang, @@ -125,10 +129,10 @@ extension P4.VariableDeclarationStatement: ParseableParserStatement { "" } - return switch P4.ValueType.Parse(type: type_name, withValue: value) { + return switch ValueType.Parse(type: type_name, withValue: value) { case Result.Ok(let value_type): Result.Ok( - P4.VariableDeclarationStatement( + VariableDeclarationStatement( withVariable: Variable(name: variable_name, withValue: value_type, isConstant: false))) case Result.Error(let e): Result.Error(e) @@ -139,9 +143,10 @@ extension P4.VariableDeclarationStatement: ParseableParserStatement { public struct Parser { public struct P4Parser { + static func LocalElements( node: Node, inTree tree: MutableTree - ) -> Result<[P4.ParserStatement]> { + ) -> Result<[EvaluatableParserStatement]> { guard let parser_le_statement_query = try? SwiftTreeSitter.Query( @@ -154,15 +159,15 @@ public struct Parser { } let localElementsParsers: [ParseableParserStatement.Type] = [ - P4.VariableDeclarationStatement.self + VariableDeclarationStatement.self ] - var localElements: [P4.ParserStatement] = Array() + var localElements: [EvaluatableParserStatement] = Array() let qr = parser_le_statement_query.execute(node: node, in: tree) for raw_le_statement in qr { let raw_le_statement_capture = raw_le_statement.captures(named: "parser-local-element") - var parsed_le_statement: P4.ParserStatement? = .none + var parsed_le_statement: EvaluatableParserStatement? = .none for le_parser in localElementsParsers { if case Result.Ok(.some(let parsed)) = le_parser.Parse( @@ -187,7 +192,7 @@ public struct Parser { static func Statements( node: Node, inTree tree: MutableTree - ) -> Result<[P4.ParserStatement]> { + ) -> Result<[EvaluatableParserStatement]> { guard let parser_statement_query = try? SwiftTreeSitter.Query( @@ -200,16 +205,16 @@ public struct Parser { } let statementParsers: [ParseableParserStatement.Type] = [ - P4.ExpressionStatement.self, P4.VariableDeclarationStatement.self, + ExpressionStatement.self, VariableDeclarationStatement.self, ] - var statements: [P4.ParserStatement] = Array() + var statements: [EvaluatableParserStatement] = Array() let qr = parser_statement_query.execute(node: node, in: tree) for raw_statement in qr { let raw_statement_capture = raw_statement.captures(named: "parser-statement") - var parsed_statement: P4.ParserStatement? = .none + var parsed_statement: EvaluatableParserStatement? = .none // Iterate through statement parsers and give each one a chance. for parser in statementParsers { @@ -234,12 +239,11 @@ public struct Parser { static func TransitionStatement( node: Node, inTree tree: MutableTree - ) -> P4.ParserTransitionStatement? - { - return P4.ParserTransitionStatement() + ) -> ParserTransitionStatement? { + return ParserTransitionStatement() } - static func State(node: Node, inTree tree: MutableTree) -> Result { + static func State(node: Node, inTree tree: MutableTree) -> Result { guard let parser_state_query = try? SwiftTreeSitter.Query( language: p4lang, @@ -273,10 +277,10 @@ public struct Parser { if !state_le_capture.isEmpty { LocalElements(node: state_le_capture[0].node, inTree: tree) } else { - Result.Ok([P4.ParserStatement]()) + Result.Ok([EvaluatableParserStatement]()) } - guard case Result<[P4.ParserStatement]>.Ok(let parsed_les) = maybe_parsed_les else { + guard case Result<[EvaluatableParserStatement]>.Ok(let parsed_les) = maybe_parsed_les else { return Result.Error(maybe_parsed_les.error()!) } @@ -284,22 +288,24 @@ public struct Parser { if !statements_capture.isEmpty { Statements(node: statements_capture[0].node, inTree: tree) } else { - Result.Ok([P4.ParserStatement]()) + Result.Ok([EvaluatableParserStatement]()) } - guard case Result<[P4.ParserStatement]>.Ok(let parsed_statements) = maybe_parsed_statements + guard + case Result<[EvaluatableParserStatement]>.Ok(let parsed_statements) = + maybe_parsed_statements else { return Result.Error(maybe_parsed_statements.error()!) } // TODO: Validate that there is only one! return Result.Ok( - P4.ParserState( + ParserState( name: parsed_state_name, withLocalElements: parsed_les, withStatements: parsed_statements, withTransition: transition_statement)) } } - static func Parser(node: Node, inTree tree: MutableTree) -> Result { + static func Parser(node: Node, inTree tree: MutableTree) -> Result { guard let parser_state_query = try? SwiftTreeSitter.Query( language: p4lang, @@ -311,7 +317,7 @@ public struct Parser { Error(withMessage: "Could not compile the parser state tree sitter query")) } - var parser = P4.Parser() + var parser = Lang.Parser() // Build a state from each one listed. for parser_states in parser_state_query.execute(node: node, in: tree) { @@ -323,7 +329,7 @@ public struct Parser { return Result.Ok(parser) } - public static func Program(_ source: String) -> Result { + public static func Program(_ source: String) -> Result { let p = SwiftTreeSitter.Parser.init() do { @@ -350,7 +356,7 @@ public struct Parser { Error(withMessage: "Could not compile the parser declaration tree sitter query")) } - var program: P4.Program = P4.Program() + var program = Lang.Program() let parser_qc = parser_declaration_query.execute(in: tree) diff --git a/Sources/Runtime/Parser.swift b/Sources/Runtime/Parser.swift new file mode 100644 index 0000000..9b13e96 --- /dev/null +++ b/Sources/Runtime/Parser.swift @@ -0,0 +1,50 @@ +// 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 . + +import Common +import Lang + + + +extension ParserTransitionStatement: EvaluatableParserTransitionStatement { + // TODO: Currently transitions to accept. + func transition(execution: ProgramExecution) -> (ParserState, ProgramExecution) { + return (accept, execution) + } +} + +extension ParserState: EvaluatableParserState { + public func evaluate(execution: ProgramExecution) -> (ParserState, ProgramExecution) { + var currentExecution = execution + + // First, evaluate the local elements. + for local_element in local_elements { + currentExecution = local_element.evaluate(execution: currentExecution) + } + + // Then, evaluate the statements. + for statement in statements { + currentExecution = statement.evaluate(execution: currentExecution) + } + + return if let transition = transition { + transition.transition(execution: currentExecution) + } else { + (reject, currentExecution) + } + } +} diff --git a/Sources/Runtime/Program.swift b/Sources/Runtime/Program.swift new file mode 100644 index 0000000..a594c90 --- /dev/null +++ b/Sources/Runtime/Program.swift @@ -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 . + +import Lang +import Common + +extension VariableDeclarationStatement: EvaluatableParserStatement { + public func evaluate(execution: ProgramExecution) -> ProgramExecution { + let new_scopes = execution.scopes.declare(variable: self.variable) + execution.scopes = new_scopes + return execution + } +} + +extension ExpressionStatement: EvaluatableParserStatement { + public func evaluate(execution: ProgramExecution) -> ProgramExecution { + return execution + } +} \ No newline at end of file diff --git a/Sources/Runtime/Protocols.swift b/Sources/Runtime/Protocols.swift new file mode 100644 index 0000000..098b62c --- /dev/null +++ b/Sources/Runtime/Protocols.swift @@ -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 . + +import Common +import Lang + +protocol EvaluatableParserState { + func evaluate(execution: ProgramExecution) -> (ParserState, ProgramExecution) +} + +protocol EvaluatableParserTransitionStatement { + func transition(execution: ProgramExecution) -> (ParserState, ProgramExecution) +} + +public protocol Execution { + func execute() -> (ParserState, ProgramExecution) +} \ No newline at end of file diff --git a/Sources/Runtime/Runtime.swift b/Sources/Runtime/Runtime.swift new file mode 100644 index 0000000..0a769d1 --- /dev/null +++ b/Sources/Runtime/Runtime.swift @@ -0,0 +1,58 @@ +// 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 . + +import Common +import Lang + +public class ParserRuntime: CustomStringConvertible { + var execution: ParserExecution + + init(execution: ParserExecution) { + self.execution = execution + } + + public static func create(program: Lang.Parser) -> Result { + switch ParserExecution.create(program) { + case .Ok(let execution): return .Ok(Runtime.ParserRuntime(execution: execution)) + case .Error(let error): return .Error(error) + + } + } + + public func run(input: Packet) -> Result<(ParserState, ProgramExecution)> { + execution.scopes.enter() + return .Ok(execution.execute()) + } + + public var description: String { + //return "\(super.description)\nState: \(execution?.description ?? "N/A")\nError: \(error?.description ?? "None")" + return "Runtime:\nExecution: \(execution)" + } +} + +extension ParserExecution: Execution { + public func execute() -> (ParserState, ProgramExecution) { + var execution = self as ProgramExecution + var state = self.state + + // Evaluate until the state is either accept or reject. + while state != accept && state != reject { + (state, execution) = self.state.evaluate(execution: execution) + } + return (state, execution) + } +} diff --git a/Sources/TreeSitterExtensions/Extensions.swift b/Sources/TreeSitterExtensions/Extensions.swift index 1c8840f..643f22e 100644 --- a/Sources/TreeSitterExtensions/Extensions.swift +++ b/Sources/TreeSitterExtensions/Extensions.swift @@ -1,7 +1,7 @@ // 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 diff --git a/Tests/p4lmTests/ParserTests.swift b/Tests/p4lmTests/ParserTests.swift index e27d116..9964525 100644 --- a/Tests/p4lmTests/ParserTests.swift +++ b/Tests/p4lmTests/ParserTests.swift @@ -1,7 +1,7 @@ // 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 @@ -20,7 +20,8 @@ import TreeSitter import SwiftTreeSitter import TreeSitterP4 import Foundation -import P4 +import Runtime +import Common import Macros diff --git a/Tests/p4lmTests/RuntimeTests.swift b/Tests/p4lmTests/RuntimeTests.swift index df8aec3..d001cab 100644 --- a/Tests/p4lmTests/RuntimeTests.swift +++ b/Tests/p4lmTests/RuntimeTests.swift @@ -1,7 +1,7 @@ // 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 @@ -16,7 +16,9 @@ // along with this program. If not, see . import Foundation -import P4 +import Runtime +import Common +import Lang import Macros import SwiftTreeSitter import Testing @@ -36,7 +38,7 @@ import TreeSitterP4 """ let program = try #UseOkResult(Parser.Program(simple_parser_declaration)) - #expect(#RequireOkResult(P4.ParserRuntime.create(program: program.parsers[0]))) + #expect(#RequireOkResult(Runtime.ParserRuntime.create(program: program.parsers[0]))) } @Test func test_simple_runtime_no_start_state() async throws { @@ -53,7 +55,7 @@ import TreeSitterP4 #expect( #RequireErrorResult( Error(withMessage: "Could not find the start state"), - P4.ParserRuntime.create(program: program.parsers[0]))) + Runtime.ParserRuntime.create(program: program.parsers[0]))) } @Test func test_simple_local_element_variable_declaration() async throws { @@ -70,11 +72,11 @@ import TreeSitterP4 """ let program = try #UseOkResult(Parser.Program(simple_parser_declaration)) - let runtime = try #UseOkResult(P4.ParserRuntime.create(program: program.parsers[0])) + let runtime = try #UseOkResult(Runtime.ParserRuntime.create(program: program.parsers[0])) // This seems awkward to me! // TODO: Is there a better way? - guard case P4.Result.Ok(let execution_result) = runtime.run(input: P4.Packet()) else { + guard case Common.Result.Ok(let (state_result, execution_result)) = runtime.run(input: Packet()) else { assert(false) } @@ -85,6 +87,9 @@ import TreeSitterP4 assert(false) } + // We should be in the accept state. + #expect(state_result == Lang.accept) + // There are two variables declared. #expect(scope.count == 2) @@ -93,4 +98,7 @@ import TreeSitterP4 let s = try #require(scope.lookup(identifier: Identifier(name: "s"))) #expect(b.value_type == ValueType.Boolean(false)) #expect(s.value_type == ValueType.String("\"testing\"")) + + + } diff --git a/Tests/p4lmTests/SupportTests.swift b/Tests/p4lmTests/SupportTests.swift index 30ab8e1..7e72b57 100644 --- a/Tests/p4lmTests/SupportTests.swift +++ b/Tests/p4lmTests/SupportTests.swift @@ -1,7 +1,7 @@ // 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 @@ -16,7 +16,7 @@ // along with this program. If not, see . import Foundation -import P4 +import Common import Macros import SwiftTreeSitter import Testing