Refactor Runtime

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
Will Hawkins
2026-02-19 22:50:19 -05:00
parent 3693bdc02d
commit d9c8c5aeb0
17 changed files with 897 additions and 439 deletions
+82 -6
View File
@@ -18,6 +18,7 @@
open class ProgramExecution: CustomStringConvertible {
public var scopes: Scopes = Scopes()
var error: Error?
var debug: DebugLevel = DebugLevel.Error
public init() {}
@@ -38,10 +39,44 @@ open class ProgramExecution: CustomStringConvertible {
npe.error = error
return npe
}
public func getDebugLevel() -> DebugLevel {
return self.debug
}
public func setDebugLevel(_ dl: DebugLevel) -> ProgramExecution {
let pe = self
pe.debug = dl
return pe
}
open func isDone() -> Bool {
return false
}
open func setDone() -> ProgramExecution {
// For a bare ProgramExecution, setDone is a noop.
return self
}
public func enter_scope() -> ProgramExecution {
let new_pe = self
new_pe.scopes = self.scopes.enter()
return new_pe
}
public func exit_scope() -> ProgramExecution {
let new_pe = self
new_pe.scopes = self.scopes.exit()
return new_pe
}
}
public struct Scope: CustomStringConvertible{
public struct Scope: CustomStringConvertible, Equatable {
var variables: [Variable] = Array()
public init() {}
@@ -59,6 +94,26 @@ public struct Scope: CustomStringConvertible{
}
}
public func set(identifier: Identifier, value: P4Value) -> Scope? {
var updated = false
var updated_scope: [Variable] = Array()
for v in variables {
if v == identifier && v.value_type.type().eq(rhs: value.type()) {
updated = true
updated_scope.append(Variable(name: v.name, withValue: value, isConstant: false))
} else {
updated_scope.append(v)
}
}
var new_scope = Scope()
new_scope.variables = updated_scope
return if updated {
new_scope
} else {
.none
}
}
public func lookup(identifier: Identifier) -> Variable? {
for v in variables {
if v == identifier {
@@ -75,17 +130,26 @@ public struct Scope: CustomStringConvertible{
}
}
public struct Scopes: CustomStringConvertible {
public struct Scopes: CustomStringConvertible, Equatable {
var scopes: [Scope] = Array()
public init() {}
public mutating func enter() {
scopes.append(Scope())
init(withScopes scopes: [Scope]) {
self.scopes = scopes
}
public mutating func exit() {
let _ = scopes.popLast()
public func enter() -> Scopes {
var new_scopes = scopes
new_scopes.append(Scope())
return Scopes(withScopes: new_scopes)
}
public func exit() -> Scopes {
var old_scopes = scopes
_ = old_scopes.popLast()
return Scopes(withScopes: old_scopes)
}
public var description: String {
@@ -125,4 +189,16 @@ public struct Scopes: CustomStringConvertible {
scopes.count
}
}
public func set(identifier: Identifier, value: P4Value) -> Scopes {
var new_scopes: [Scope] = Array()
for scope in self.scopes {
if let updated_scope = scope.set(identifier: identifier, value: value) {
new_scopes.append(updated_scope)
} else {
new_scopes.append(scope)
}
}
return Scopes(withScopes: new_scopes)
}
}
+53 -17
View File
@@ -54,15 +54,20 @@ public class Variable: Identifier {
/// A base for all instances of P4 types
open class P4ValueBase<T: P4Type>: P4Value {
public init() {}
public func type() -> P4Type {
return T.create()
}
public func eq(rhs: P4Value) -> Bool {
return false
}
public var description: String {
"Value of \(self.type()) type"
}
}
extension P4ValueBase: EvaluatableExpression {
@@ -85,6 +90,17 @@ public struct P4Struct: P4Type {
public init() {
self.name = ""
}
public var description: String {
return "Struct \(self.name)"
}
public func eq(rhs: P4Type) -> Bool {
return if let struct_rhs = rhs as? P4Struct {
struct_rhs.name == self.name
} else {
false
}
}
}
/// The field of a P4 struct
@@ -111,6 +127,15 @@ public struct P4Boolean: P4Type {
public static func create() -> any P4Type {
return P4Boolean()
}
public var description: String {
return "Boolean"
}
public func eq(rhs: P4Type) -> Bool {
return switch rhs {
case is P4Boolean: true
default: false
}
}
}
/// An instance of a P4 boolean
@@ -126,14 +151,26 @@ public class P4BooleanValue: P4ValueBase<P4Boolean> {
}
return self.value == bool_rhs.value
}
}
public override var description: String {
"\(self.value ? "true" : "false") of \(self.type()) type"
}
}
/// A P4 int type
public struct P4Int: P4Type {
public static func create() -> any P4Type {
return P4Int()
}
public var description: String {
return "Int"
}
public func eq(rhs: P4Type) -> Bool {
return switch rhs {
case is P4Int: true
default: false
}
}
}
/// An instance of a P4 integer
@@ -148,6 +185,9 @@ public class P4IntValue: P4ValueBase<P4Int> {
}
return self.value == int_rhs.value
}
public override var description: String {
"\(self.value) of \(self.type()) type"
}
}
/// A P4 string type
@@ -155,6 +195,15 @@ public struct P4String: P4Type {
public static func create() -> any P4Type {
return P4String()
}
public var description: String {
return "String"
}
public func eq(rhs: P4Type) -> Bool {
return switch rhs {
case is P4String: true
default: false
}
}
}
/// An instance of a P4 string
public class P4StringValue: P4ValueBase<P4String> {
@@ -168,22 +217,9 @@ public class P4StringValue: P4ValueBase<P4String> {
}
return self.value == string_rhs.value
}
}
/// A P4 value (with a type)
public struct Value: CustomStringConvertible, Equatable {
public var type: P4Type
public var value: P4Value
public init(withValue value: P4Value, andType type: P4Type) {
self.value = value
self.type = type
}
public var description: String {
return "\(self.value) of \(self.type)"
}
public static func == (lhs: Value, rhs: Value) -> Bool {
return lhs.value.eq(rhs: rhs.value)
public override var description: String {
"\(self.value) of \(self.type()) type"
}
}
+3 -2
View File
@@ -31,11 +31,12 @@ public protocol EvaluatableParserStatement {
func evaluate(execution: ProgramExecution) -> ProgramExecution
}
public protocol P4Type {
public protocol P4Type: CustomStringConvertible {
static func create() -> P4Type
func eq(rhs: P4Type) -> Bool
}
public protocol P4Value {
public protocol P4Value: CustomStringConvertible {
func type() -> P4Type
func eq(rhs: P4Value) -> Bool
}
+46
View File
@@ -15,6 +15,52 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
public enum DebugLevel {
case Trace
case Verbose
case Debug
case Error
func value() -> UInt8 {
return switch self {
case DebugLevel.Trace: 3
case DebugLevel.Verbose: 2
case DebugLevel.Debug: 1
case DebugLevel.Error: 0
}
}
public func isTrace() -> Bool {
if self.value() >= DebugLevel.Trace.value() {
return true
}
return false
}
public func isVerbose() -> Bool {
if self.value() >= DebugLevel.Verbose.value() {
return true
}
return false
}
public func isDebug() -> Bool {
if self.value() >= DebugLevel.Debug.value() {
return true
}
return false
}
public func isError() -> Bool {
return true
}
public var description: String {
return switch self {
case DebugLevel.Trace: "Trace"
case DebugLevel.Verbose: "Verbose"
case DebugLevel.Debug: "Debug"
case DebugLevel.Error: "Error"
}
}
}
public struct Error: Equatable {
public private(set) var msg: String
+30 -54
View File
@@ -25,6 +25,16 @@ public struct LocalElement {
}
public struct ParserAssignmentStatement {
public let lvalue: Identifier
public let value: P4Value
public init(withLValue lvalue: Identifier, withValue value: P4Value) {
self.lvalue = lvalue
self.value = value
}
}
public struct KeysetExpression {
public let key: EvaluatableExpression
public let next_state_name: String
@@ -63,7 +73,6 @@ public struct ParserTransitionSelectExpression {
return ParserTransitionSelectExpression(
withSelector: self.selector, withKeysetExpressions: new_kse)
}
}
public struct ParserTransitionStatement {
@@ -93,7 +102,7 @@ public struct VariableDeclarationStatement {
}
}
public class ParserState: Equatable, CustomStringConvertible {
public class ParserState: Equatable, CustomStringConvertible, Comparable {
public private(set) var state_name: String
public private(set) var local_elements: [EvaluatableParserStatement]
@@ -101,6 +110,13 @@ public class ParserState: Equatable, CustomStringConvertible {
public private(set) var transition: ParserTransitionStatement?
public private(set) var next_state: ParserState?
public static func < (lhs: ParserState, rhs: ParserState) -> Bool {
// If lhs transitions to rhs, then return true. Otherwise, return false.
// TODO!!
return false
}
public var description: String {
return "Name: \(state_name)"
}
@@ -112,13 +128,13 @@ public class ParserState: Equatable, CustomStringConvertible {
/// Construct a ParserState
public init(
name: String, withLocalElements localElements: [EvaluatableParserStatement]?,
withStatements statements: [EvaluatableParserStatement]?,
withStatements stmts: [EvaluatableParserStatement]?,
withTransition transitionStatement: ParserTransitionStatement
) {
state_name = name
transition = transitionStatement
local_elements = localElements ?? Array()
self.statements = statements ?? Array()
statements = stmts ?? Array()
}
public func semantic_check(states: ParserStates) -> Bool {
@@ -126,22 +142,6 @@ public class ParserState: Equatable, CustomStringConvertible {
return self == accept || self == reject
}
if let transition_select_expression = transition.transition_expression {
var updated_tse = ParserTransitionSelectExpression(
withSelector: transition_select_expression.selector, withKeysetExpressions: [])
for kse in transition_select_expression.keyset_expressions {
guard let next_state = states.find(withName: kse.next_state_name) else {
return false
}
let new_kse = KeysetExpression(
withKey: kse.key, withNextStateName: kse.next_state_name, withNextState: next_state)
updated_tse = updated_tse.append_checked_kse(kse: new_kse)
}
self.transition = ParserTransitionStatement(withTransitionExpression: updated_tse)
return true
}
if let next_state_name = transition.next_state_name,
let next_state = states.find(withName: next_state_name)
{
@@ -174,6 +174,9 @@ public class ParserState: Equatable, CustomStringConvertible {
}
}
nonisolated(unsafe) public let accept: ParserState = ParserState(name: "accept")
nonisolated(unsafe) public let reject: ParserState = ParserState(name: "reject")
public struct ParserStates {
public var states: [ParserState] = Array()
@@ -221,8 +224,6 @@ public struct ParserStates {
}
}
nonisolated(unsafe) public let accept: ParserState = ParserState(name: "accept")
nonisolated(unsafe) public let reject: ParserState = ParserState(name: "reject")
public struct Parser: P4Type {
public var states: ParserStates
@@ -250,41 +251,16 @@ public struct Parser: P4Type {
public func semantic_check() -> Result<()> {
return self.states.semantic_check()
}
}
public class ParserInstance: ProgramExecution {
public var state: ParserState
private init(state: ParserState) {
self.state = state
super.init()
public var description: String {
return "Parser"
}
public static func create(_ parser: Parser) -> Result<ParserInstance> {
var augmented_parser = Parser(withName: parser.name)
augmented_parser.states = parser.states.append(state: accept).append(state: reject)
if case .Error(let e) = augmented_parser.semantic_check() {
return .Error(e)
public func eq(rhs: P4Type) -> Bool {
return if let parser_rhs = rhs as? Parser {
self.name == parser_rhs.name
} else {
false
}
guard let start_state = augmented_parser.findStartState() else {
return Result.Error(Error(withMessage: "Could not find the start state"))
}
let new = ParserInstance(state: start_state)
return Result.Ok(new)
}
public func transition(toNextState state: ParserState) -> ParserInstance {
let next = self
next.state = state
return next
}
public override var description: String {
return "Execution: \(super.description)\nCurrent State: \(state)"
}
}
+9 -8
View File
@@ -31,10 +31,10 @@ public struct UseOkResult: ExpressionMacro {
return """
{
if case Result.Ok(let __runtime) = \(argument) {
return __runtime
} else {
print("Oh no")
switch \(argument) {
case Result.Ok(let __good): return __good
case Result.Error(let __error):
print("Unexpected result: \\(__error)")
throw Require.Error.UnexpectedResult
}
}()
@@ -61,10 +61,11 @@ public struct RequireResult: ExpressionMacro {
return """
{
if case Result.Ok(_) = \(argument) {
true
} else {
false
switch \(argument) {
case Result.Ok(let _): return true
case Result.Error(let __error):
print("Unexpected result: \\(__error)")
return false
}
}()
"""
+5 -7
View File
@@ -15,19 +15,17 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// expression: $ => choice($.identifier, $.integer, $.true, $.false, $.string_literal), // Very limited.
import Common
import SwiftTreeSitter
import TreeSitterP4
protocol ParseableEvaluatableExpression {
static func parse(node: Node, inTree tree: MutableTree) -> Result<EvaluatableExpression?>
static func parse(node: Node, inTree tree: MutableTree, withScopes scopes: Scopes) -> Result<EvaluatableExpression?>
}
extension Identifier: ParseableEvaluatableExpression {
static func parse(
node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree
node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree, withScopes scopes: Scopes
) -> Result<EvaluatableExpression?> {
guard
@@ -52,7 +50,7 @@ extension Identifier: ParseableEvaluatableExpression {
extension P4BooleanValue: ParseableEvaluatableExpression {
static func parse(
node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree
node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree, withScopes scopes: Scopes
) -> Result<EvaluatableExpression?> {
guard
@@ -91,14 +89,14 @@ extension P4BooleanValue: ParseableEvaluatableExpression {
}
struct Expression {
public static func Parse(node: Node, inTree: MutableTree) -> Result<EvaluatableExpression> {
public static func Parse(node: Node, inTree: MutableTree, withScopes scopes: Scopes) -> Result<EvaluatableExpression> {
let localElementsParsers: [ParseableEvaluatableExpression.Type] = [
P4BooleanValue.self, Identifier.self,
]
for le_parser in localElementsParsers {
if case Result.Ok(.some(let parsed)) = le_parser.parse(
node: node, inTree: inTree)
node: node, inTree: inTree, withScopes: scopes)
{
return .Ok(parsed)
}
+214 -139
View File
@@ -25,13 +25,15 @@ import TreeSitterP4
let p4lang = Language(tree_sitter_p4())
public protocol ParseableParserStatement {
static func Parse(node: Node, inTree tree: MutableTree) -> Result<EvaluatableParserStatement?>
static func Parse(
node: Node, inTree tree: MutableTree, withScope scopes: Scopes
) -> Result<(EvaluatableParserStatement?, Scopes)>
}
extension ExpressionStatement: ParseableParserStatement {
public static func Parse(
node: Node, inTree tree: MutableTree
) -> Result<EvaluatableParserStatement?> {
node: Node, inTree tree: MutableTree, withScope scopes: Scopes
) -> Result<(EvaluatableParserStatement?, Scopes)> {
guard
let parser_state_query = try? SwiftTreeSitter.Query(
language: p4lang,
@@ -39,36 +41,99 @@ extension ExpressionStatement: ParseableParserStatement {
"(expressionStatement (expression) @expression)"
).data(using: String.Encoding.utf8)!)
else {
return Result.Ok(.none)
return Result.Ok((.none, scopes))
}
let qr = parser_state_query.execute(node: node, in: tree)
let query_result = qr.next()!
guard let query_result = qr.next() else {
return Result.Ok((.none, scopes))
}
let expression_capture = query_result.captures(named: "expression")
if !expression_capture.isEmpty {
// TODO: Actually create an ExpressionStatement
return Result.Ok(ExpressionStatement())
return Result.Ok((ExpressionStatement(), scopes))
}
return Result.Ok((.none, scopes))
}
}
extension ParserAssignmentStatement: ParseableParserStatement {
public static func Parse(
node: Node, inTree tree: MutableTree, withScope scopes: Scopes
) -> Result<(EvaluatableParserStatement?, Scopes)> {
guard
let parser_state_query = try? SwiftTreeSitter.Query(
language: p4lang,
data: String(
"(assignmentStatement (expression) @lvalue (assignment) (expression) @value)"
).data(using: String.Encoding.utf8)!)
else {
return Result.Ok((.none, scopes))
}
let qr = parser_state_query.execute(node: node, in: tree)
let parser_declaration = qr.next()!
let lvalue_capture = parser_declaration.captures(named: "lvalue")
let value_capture = parser_declaration.captures(named: "value")
// There must be a type name and a variable name
guard !lvalue_capture.isEmpty,
!value_capture.isEmpty,
let lvalue_expression_raw = lvalue_capture[0].node.text,
let value_capture_raw = value_capture[0].node.text
else {
return Result.Error(
Error(withMessage: "Could not parse a parser assignment statement"))
}
let lvalue_identifier = Identifier(name: lvalue_expression_raw)
let value =
if !value_capture_raw.isEmpty {
value_capture_raw
} else {
""
}
guard case Result.Ok(let declared_value) = scopes.evaluate(identifier: lvalue_identifier) else {
return Result.Error(
Error(withMessage: "Cannot assign to variable not in scope"))
}
return switch Parser.ParseValueType(type: declared_value.type(), withValue: value) {
case Result.Ok(let value_type):
Result.Ok(
(ParserAssignmentStatement(withLValue: lvalue_identifier, withValue: value_type), scopes))
case Result.Error(let e):
Result.Error(
Error(
withMessage:
"\(declared_value) has type \(declared_value.type()) but rvalue has mismatched type (\(e))"))
}
return Result.Ok(.none)
}
}
extension VariableDeclarationStatement: ParseableParserStatement {
public static func Parse(
node: Node, inTree tree: MutableTree
) -> Result<EvaluatableParserStatement?> {
node: Node, inTree tree: MutableTree, withScope scopes: Scopes
) -> Result<(EvaluatableParserStatement?, Scopes)> {
guard
let parser_state_query = try? SwiftTreeSitter.Query(
language: p4lang,
data: String(
"((annotations)? (typeRef) @type-name variable_name: (identifier) @identifier ((assignment) (expression) @value)?)"
"(variableDeclaration (annotations)? (typeRef) @type-name variable_name: (identifier) @identifier ((assignment) (expression) @value)?)"
).data(using: String.Encoding.utf8)!)
else {
return Result.Ok(.none)
return Result.Ok((.none, scopes))
}
let qr = parser_state_query.execute(node: node, in: tree)
let parser_declaration = qr.next()!
guard let parser_declaration = qr.next() else {
return .Ok((.none, scopes))
}
let type_name_capture = parser_declaration.captures(named: "type-name")
let variable_name_capture = parser_declaration.captures(named: "identifier")
@@ -80,7 +145,8 @@ extension VariableDeclarationStatement: ParseableParserStatement {
let variable_name = variable_name_capture[0].node.text,
let type_name = type_name_capture[0].node.text
else {
return Result.Error(Error(withMessage: "Could not parse a parser declaration"))
return Result.Error(
Error(withMessage: "Could not parse a parser variable declaration statement"))
}
let value =
@@ -90,11 +156,21 @@ extension VariableDeclarationStatement: ParseableParserStatement {
""
}
return switch Parser.ParseValueType(type: type_name, withValue: value) {
guard case .Ok(let p4_type) = Parser.ParseBasicType(type: type_name) else {
return Result.Error(
Error(withMessage: "Could not parse a P4 type from \(type_name)"))
}
return switch Parser.ParseValueType(type: p4_type, withValue: value) {
case Result.Ok(let value_type):
// This scope should have an additional variable in scope.
Result.Ok(
(
VariableDeclarationStatement(
withVariable: Variable(name: variable_name, withValue: value_type, isConstant: false)))
withVariable: Variable(name: variable_name, withValue: value_type, isConstant: false)),
scopes.declare(
variable: Variable(name: variable_name, withValue: value_type, isConstant: false))
))
case Result.Error(let e):
Result.Error(e)
}
@@ -102,13 +178,23 @@ extension VariableDeclarationStatement: ParseableParserStatement {
}
public struct Parser {
static func ParseValueType(type: String, withValue value: String) -> Result<P4Value> {
static func ParseBasicType(type: String) -> Result<P4Type> {
if type == "bool" {
return .Ok(P4Boolean.create())
} else if type == "string" {
return .Ok(P4String.create())
} else if type == "int" {
return .Ok(P4Int.create())
}
return Result.Error(Error(withMessage: "Type name not recognized"))
}
static func ParseValueType(type: P4Type, withValue value: String) -> Result<P4Value> {
if type.eq(rhs: P4Boolean.create()) {
// Default
if value == "" {
return .Ok(P4BooleanValue(withValue: false))
}
if value == "true" {
return .Ok(P4BooleanValue(withValue: true))
} else if value == "false" {
@@ -116,10 +202,10 @@ public struct Parser {
}
return .Error(Error(withMessage: "Cannot convert \(value) into boolean value"))
} else if type == "string" {
} else if type.eq(rhs: P4String.create()) {
return .Ok(P4StringValue.init(withValue: value))
} else if type == "int" {
} else if type.eq(rhs: P4Int.create()) {
// Default
if value == "" {
return .Ok(P4IntValue.init(withValue: 0))
@@ -137,101 +223,51 @@ public struct Parser {
public struct P4Parser {
static func LocalElements(
node: Node, inTree tree: MutableTree
) -> Result<[EvaluatableParserStatement]> {
guard
let parser_le_statement_query = try? SwiftTreeSitter.Query(
language: p4lang,
data: String(
"(parserLocalElement) @parser-local-element"
).data(using: String.Encoding.utf8)!)
else {
return Result.Error(Error(withMessage: "Could not compile the tree sitter query"))
}
node: Node, inTree tree: MutableTree, withScope scopes: Scopes
) -> Result<(EvaluatableParserStatement, Scopes)> {
let localElementsParsers: [ParseableParserStatement.Type] = [
VariableDeclarationStatement.self
]
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: EvaluatableParserStatement? = .none
for le_parser in localElementsParsers {
if case Result.Ok(.some(let parsed)) = le_parser.Parse(
node: raw_le_statement_capture[0].node, inTree: tree)
for local_element_parser in localElementsParsers {
if case Result.Ok((.some(let parsed), let parsed_updated_scopes)) =
local_element_parser.Parse(
node: node, inTree: tree, withScope: scopes)
{
parsed_le_statement = parsed
break
return Result.Ok((parsed, parsed_updated_scopes))
}
}
if let le_statement = parsed_le_statement {
localElements.append(le_statement)
} else {
// There were no parseable statements.
return Result.Error(
Error(withMessage: "Failed to parse a local element: \(raw_le_statement)"))
}
}
return Result.Ok(localElements)
Error(
withMessage:
"Failed to parse any local elements from specified local elements: \(node.text!)")
)
}
static func Statements(
node: Node, inTree tree: MutableTree
) -> Result<[EvaluatableParserStatement]> {
guard
let parser_statement_query = try? SwiftTreeSitter.Query(
language: p4lang,
data: String(
"(parserStatement) @parser-statement"
).data(using: String.Encoding.utf8)!)
else {
return Result.Error(Error(withMessage: "Could not compile the tree sitter query"))
}
node: Node, inTree tree: MutableTree, withScope scopes: Scopes
) -> Result<(EvaluatableParserStatement, Scopes)> {
let statementParsers: [ParseableParserStatement.Type] = [
ExpressionStatement.self, VariableDeclarationStatement.self,
ExpressionStatement.self, VariableDeclarationStatement.self, ParserAssignmentStatement.self
]
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: EvaluatableParserStatement? = .none
// Iterate through statement parsers and give each one a chance.
for parser in statementParsers {
if case Result.Ok(.some(let parsed)) = parser.Parse(
node: raw_statement_capture[0].node, inTree: tree)
if case Result.Ok((.some(let parsed), let updatedScopes)) = parser.Parse(
node: node, inTree: tree, withScope: scopes)
{
parsed_statement = parsed
break
}
return .Ok((parsed, updatedScopes))
}
if let statement = parsed_statement {
statements.append(statement)
} else {
// There were no parseable statements.
}
return Result.Error(
Error(withMessage: "Failed to parse a statement element: \(raw_statement)"))
}
}
return Result.Ok(statements)
Error(withMessage: "Failed to parse a statement element: \(node.text!)"))
}
static func TransitionKeysetExpression(
node: Node, inTree tree: MutableTree
) -> Result<[KeysetExpression]> {
node: Node, inTree tree: MutableTree, withScopes scopes: Scopes
) -> Result<([KeysetExpression], Scopes)> {
guard
let transition_selection_expression_query = try? SwiftTreeSitter.Query(
language: p4lang,
@@ -248,24 +284,25 @@ public struct Parser {
for expression in qr {
let next_state_name = expression.captures[1].node.text!
if case .Error(let e) = Expression.Parse(node: expression.captures[0].node, inTree: tree)
if case .Error(let e) = Expression.Parse(
node: expression.captures[0].node, inTree: tree, withScopes: scopes
)
.map(block: { expression in
kses.append(
KeysetExpression(
withKey: expression, withNextStateName: next_state_name))
return .Ok(expression)
})
{
}) {
return .Error(e)
}
}
return .Ok(kses)
return .Ok((kses, scopes))
}
static func TransitionSelectExpression(
node: Node, inTree tree: MutableTree
) -> Result<ParserTransitionStatement> {
node: Node, inTree tree: MutableTree, withScope scopes: Scopes
) -> Result<(ParserTransitionStatement?, Scopes)> {
guard
let transition_selection_expression_query = try? SwiftTreeSitter.Query(
language: p4lang,
@@ -285,21 +322,26 @@ public struct Parser {
let selector = query_result.captures(named: "selector")
let body = query_result.captures(named: "body")
return Expression.Parse(node: selector[0].node, inTree: tree).map { expression in
return switch TransitionKeysetExpression(node: body[0].node, inTree: tree) {
case .Ok(let kse):
Result<ParserTransitionStatement>.Ok(
return Expression.Parse(node: selector[0].node, inTree: tree, withScopes: scopes).map {
expression in
return
switch TransitionKeysetExpression(node: body[0].node, inTree: tree, withScopes: scopes)
{
case .Ok((let kse, let newScopes)):
Result<(ParserTransitionStatement?, Scopes)>.Ok(
(
ParserTransitionStatement(
withTransitionExpression: ParserTransitionSelectExpression(
withSelector: expression, withKeysetExpressions: kse)))
case .Error(let e): Result<ParserTransitionStatement>.Error(e)
withSelector: expression, withKeysetExpressions: kse)), newScopes
))
case .Error(let e): Result.Error(e)
}
}
}
static func TransitionStatement(
node: Node, inTree tree: MutableTree
) -> Result<ParserTransitionStatement> {
node: Node, inTree tree: MutableTree, withScope scopes: Scopes
) -> Result<(ParserTransitionStatement?, Scopes)> {
guard
let next_state_query = try? SwiftTreeSitter.Query(
language: p4lang,
@@ -314,18 +356,21 @@ public struct Parser {
if let next_state_result = qr.next() {
let transition_capture = next_state_result.captures(named: "next-state")
return .Ok(ParserTransitionStatement(withNextState: transition_capture[0].node.text!))
return .Ok(
(ParserTransitionStatement(withNextState: transition_capture[0].node.text!), scopes))
}
return TransitionSelectExpression(node: node, inTree: tree)
return TransitionSelectExpression(node: node, inTree: tree, withScope: scopes)
}
static func State(node: Node, inTree tree: MutableTree) -> Result<ParserState> {
static func State(
node: Node, inTree tree: MutableTree, withScopes scopes: Scopes
) -> Result<(ParserState, Scopes)> {
guard
let parser_state_query = try? SwiftTreeSitter.Query(
language: p4lang,
data: String(
"(parserState (state) (identifier) @state-name (parserLocalElements)? @state-local-elements (parserStatements)? @state-statements (parserTransitionStatement) @transition)"
"(parserState (state) (identifier) @state-name (parserLocalElements ((parserLocalElement) @state-local-element (semicolon))*)* (parserStatements ((parserStatement) @state-statement (semicolon))*)* (parserTransitionStatement) @transition)"
).data(using: String.Encoding.utf8)!)
else {
return Result.Error(Error(withMessage: "Could not compile the tree sitter query"))
@@ -337,54 +382,74 @@ public struct Parser {
let transition_capture = parser_declaration.captures(named: "transition")
let state_name_capture = parser_declaration.captures(named: "state-name")
let state_le_capture = parser_declaration.captures(named: "state-local-elements")
let statements_capture = parser_declaration.captures(named: "state-statements")
let state_le_capture = parser_declaration.captures(named: "state-local-element")
let statements_capture = parser_declaration.captures(named: "state-statement")
// There must be a state name and there must be a transition statement.
// TODO: Now that scopes are involved, doing this out of order will not work!
guard !state_name_capture.isEmpty,
!transition_capture.isEmpty,
let parsed_state_name = state_name_capture[0].node.text,
case .Ok(let transition_statement) = TransitionStatement(
node: transition_capture[0].node, inTree: tree)
case .Ok((let transition_statement, (var newStateScopes))) = TransitionStatement(
node: transition_capture[0].node, inTree: tree, withScope: scopes)
else {
return Result.Error(Error(withMessage: "Could not parse a parser declaration"))
}
let maybe_parsed_les =
if !state_le_capture.isEmpty {
LocalElements(node: state_le_capture[0].node, inTree: tree)
} else {
Result.Ok([EvaluatableParserStatement]())
var parsed_les: [EvaluatableParserStatement] = Array()
var parse_err: Error? = .none
for state_le in state_le_capture {
state_le.node.enumerateChildren { node in
switch LocalElements(
node: node, inTree: tree, withScope: newStateScopes)
{
case .Ok((let le, let le_parsed_scopes)):
newStateScopes = le_parsed_scopes
parsed_les.append(le)
case .Error(let e):
parse_err = e
}
}
}
guard case Result<[EvaluatableParserStatement]>.Ok(let parsed_les) = maybe_parsed_les else {
return Result.Error(maybe_parsed_les.error()!)
if let parse_err = parse_err {
return Result.Error(parse_err)
}
let maybe_parsed_statements =
var parsed_s: [EvaluatableParserStatement] = Array()
if !statements_capture.isEmpty {
Statements(node: statements_capture[0].node, inTree: tree)
} else {
Result.Ok([EvaluatableParserStatement]())
for statement in statements_capture {
statement.node.enumerateChildren { node in
switch Statements(
node: node, inTree: tree, withScope: newStateScopes)
{
case .Ok((let le, let le_parsed_scopes)):
newStateScopes = le_parsed_scopes
parsed_s.append(le)
case .Error(let e):
parse_err = e
}
}
}
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!
if let parse_err = parse_err {
return Result.Error(parse_err)
}
return Result.Ok(
(
ParserState(
name: parsed_state_name, withLocalElements: parsed_les,
withStatements: parsed_statements,
withTransition: transition_statement))
withStatements: parsed_s,
withTransition: transition_statement!), newStateScopes
))
}
}
static func Parser(
withName name: Identifier, node: Node, inTree tree: MutableTree
) -> Result<Lang.Parser> {
withName name: Identifier, node: Node, inTree tree: MutableTree, withScopes scopes: Scopes
) -> Result<(Lang.Parser, Scopes)> {
guard
let parser_state_query = try? SwiftTreeSitter.Query(
language: p4lang,
@@ -405,10 +470,14 @@ public struct Parser {
var error: Error? = .none
var parser_scopes = scopes
// TODO: Assert that there is only one.
captures[0].node.enumerateChildren { parser_state in
switch P4Parser.State(node: parser_state, inTree: tree) {
case Result.Ok(let state): parser.states = parser.states.append(state: state)
switch P4Parser.State(node: parser_state, inTree: tree, withScopes: scopes) {
case Result.Ok(let (state, new_parser_scopes)):
parser.states = parser.states.append(state: state)
parser_scopes = new_parser_scopes
case Result.Error(let e): error = e
}
}
@@ -416,7 +485,8 @@ public struct Parser {
if let error = error {
return .Error(error)
}
return Result.Ok(parser)
return Result.Ok((parser, parser_scopes))
}
public static func Program(_ source: String) -> Result<Program> {
@@ -449,14 +519,19 @@ public struct Parser {
var program = Lang.Program()
// Set up a lexical scope for parsing.
var program_scope = Scopes().enter()
let parser_qc = parser_declaration_query.execute(in: tree)
for parser_declaration in parser_qc {
switch Parser(
withName: Identifier(name: parser_declaration.nodes[0].text!),
node: parser_declaration.nodes[1], inTree: tree)
node: parser_declaration.nodes[1], inTree: tree, withScopes: program_scope)
{
case Result.Ok(let parser): program.types.append(parser)
case Result.Ok((let parser, let new_program_scope)):
program.types.append(parser)
program_scope = new_program_scope
case Result.Error(let error): return Result.Error(error)
}
}
+178 -29
View File
@@ -18,48 +18,197 @@
import Common
import Lang
extension ParserState: EvaluatableParserTransition {
public func evaluate(execution: ProgramExecution) -> (ParserState, ProgramExecution) {
var currentExecution = execution
extension ParserAssignmentStatement: EvaluatableParserStatement {
public func evaluate(execution: ProgramExecution) -> ProgramExecution {
let updated_scopes = execution.scopes.set(identifier: self.lvalue, value: self.value)
execution.scopes = updated_scopes
return execution
}
}
public struct ParserStateDirectTransition: ParserStateInstance {
public let currrent_state: ParserState
public let next_state: ParserStateInstance
public func execute(
program: Common.ProgramExecution
) -> (any ParserStateInstance, Common.ProgramExecution) {
var program = program.exit_scope()
program = program.enter_scope()
for local_element in currrent_state.local_elements {
program = local_element.evaluate(execution: program)
}
for statement in currrent_state.statements {
program = statement.evaluate(execution: program)
}
return (self.next_state, program)
}
public func done() -> Bool {
return false
}
public func current() -> Lang.ParserState {
return currrent_state
}
}
public struct ParserStateNoTransition: ParserStateInstance {
public let currrent_state: ParserState
public func execute(
program: Common.ProgramExecution
) -> (any ParserStateInstance, Common.ProgramExecution) {
let program = program.exit_scope()
return (self, program)
}
public func done() -> Bool {
return true
}
public func current() -> Lang.ParserState {
return currrent_state
}
}
public struct ParserStateSelectTransition: ParserStateInstance {
public func execute(
program: Common.ProgramExecution
) -> (any ParserStateInstance, Common.ProgramExecution) {
// Otherwise, we exit the scope from the previous state and enter a new one!
var program = program.exit_scope()
program = program.enter_scope()
// First, evaluate the local elements.
for local_element in local_elements {
currentExecution = local_element.evaluate(execution: currentExecution)
for local_element in currrent_state.local_elements {
program = local_element.evaluate(execution: program)
}
// Then, evaluate the statements.
for statement in statements {
currentExecution = statement.evaluate(execution: currentExecution)
for statement in currrent_state.statements {
program = statement.evaluate(execution: program)
}
if direct_transition() {
return (next_state!, currentExecution)
}
if let transition_expression = self.transition,
let transition_select_expression = transition_expression.transition_expression
{
return transition_select_expression.evaluate(execution: currentExecution)
}
return (reject, currentExecution)
}
}
extension ParserTransitionSelectExpression: EvaluatableParserTransition {
func evaluate(execution: Common.ProgramExecution) -> (Lang.ParserState, Common.ProgramExecution) {
// First, evaluate the selector.
switch self.selector.evaluate(execution: execution) {
switch self.selector.evaluate(execution: program) {
case .Ok(let selector_value):
for kse in self.keyset_expressions {
if case .Ok(let kse_key) = kse.key.evaluate(execution: execution),
for (key, target) in zip(self.keys, self.states) {
if case .Ok(let kse_key) = key.evaluate(execution: program),
kse_key.eq(rhs: selector_value)
{
return (kse.next_state!, execution)
return (target, program)
}
}
case .Error(let e): return (reject, execution.setError(error: e))
return (
self, program.setError(error: Error(withMessage: "No selector key matched")).setDone()
)
case .Error(let e): return (self, program.setError(error: e))
}
}
public func done() -> Bool {
return false
}
public func current() -> Lang.ParserState {
return currrent_state
}
public let keys: [any EvaluatableExpression]
public let states: [ParserStateInstance]
public let selector: any EvaluatableExpression
public let currrent_state: ParserState
}
extension ParserState: Compilable {
public typealias ToCompile = (ParserState, [String: ParserStateInstance])
public typealias Compiled = ParserStateInstance
public static func compile(_ parser: ToCompile) -> Result<Compiled> {
let (state, current) = parser
if state == accept || state == reject {
return .Ok(ParserStateNoTransition(currrent_state: state))
}
if state.direct_transition(),
let transition_statement = state.transition {
return .Ok(
ParserStateDirectTransition(
currrent_state: state, next_state: current[transition_statement.next_state_name!]!))
}
if let transition_select_statement = state.transition,
let transition_select_expression = transition_select_statement.transition_expression {
var keys: Array<any EvaluatableExpression> = Array()
var states: Array<any ParserStateInstance> = Array()
for kse in transition_select_expression.keyset_expressions {
guard let next_state = current[kse.next_state_name] else {
return .Error(Error(withMessage: "Cannot find \(kse.next_state_name) as transition target"))
}
keys.append(kse.key)
states.append(next_state)
}
return .Ok(ParserStateSelectTransition(keys: keys, states: states, selector: transition_select_expression.selector, currrent_state: state))
}
return .Error(Error(withMessage: "Invalid parser state: No meaningful transition"))
}
}
extension ParserStates: Compilable {
public typealias ToCompile = ParserStates
public typealias Compiled = ParserStateInstance
public static func compile(_ parser: ToCompile) -> Result<Compiled> {
var compiled_states = [String: ParserStateInstance]()
compiled_states["accept"] = ParserStateNoTransition(currrent_state: accept)
compiled_states["reject"] = ParserStateNoTransition(currrent_state: reject)
// TODO: We assume that states are in transition-order!
for state in parser.states {
switch ParserState.compile((state, compiled_states)) {
case .Ok(let compiled): compiled_states[state.state_name] = compiled
case .Error(let e): return .Error(e)
}
}
// Now, find the start state:
if let start_state = compiled_states["start"] {
return .Ok(start_state)
} else {
return .Error(Error(withMessage: "No start state defined"))
}
}
}
public class ParserInstance: ProgramExecution {
let start_state: ParserStateInstance
public init(_ _start_state: ParserStateInstance) {
self.start_state = _start_state
}
public override var description: String {
return "Execution: \(super.description)\nStart State: \(start_state)"
}
}
extension ParserInstance: Compilable {
public typealias ToCompile = Parser
public typealias Compiled = ParserInstance
public static func compile(_ parser: ToCompile) -> Result<Compiled> {
return switch ParserStates.compile(parser.states) {
case .Ok(let start_state): Result.Ok(ParserInstance(start_state))
case .Error(let e): Result.Error(e)
}
return (reject, execution)
}
}
+17 -1
View File
@@ -19,7 +19,7 @@ import Common
import Lang
protocol EvaluatableParserTransition {
func evaluate(execution: ProgramExecution) -> (ParserState, ProgramExecution)
func program(execution: ProgramExecution) -> (ParserState, ProgramExecution)
}
protocol EvaluatableParserTransitionStatement {
@@ -29,3 +29,19 @@ protocol EvaluatableParserTransitionStatement {
public protocol Execution {
func execute() -> (ParserState, ProgramExecution)
}
public protocol Compilable {
associatedtype ToCompile
associatedtype Compiled
static func compile(_: ToCompile) -> Result<Compiled>
}
public protocol ParserStateInstance {
func execute(program: ProgramExecution) -> (ParserStateInstance, ProgramExecution)
func done() -> Bool
func current() -> ParserState
}
public protocol ParserExecution {
func execute() -> (ParserState, ProgramExecution)
}
+8 -9
View File
@@ -20,7 +20,7 @@ import Lang
/// The runtime for a parser
public class ParserRuntime: CustomStringConvertible {
var parser: ParserInstance
public var parser: ParserInstance
init(execution: ParserInstance) {
self.parser = execution
@@ -31,7 +31,7 @@ public class ParserRuntime: CustomStringConvertible {
return switch program.starting_parser() {
case .Ok(let parser):
switch ParserInstance.create(parser) {
switch ParserInstance.compile(parser) {
case .Ok(let execution): .Ok(Runtime.ParserRuntime(execution: execution))
case .Error(let error): .Error(error)
}
@@ -40,8 +40,7 @@ public class ParserRuntime: CustomStringConvertible {
}
/// Run the P4 parser on a given packet
public func run(input: Packet) -> Result<(ParserState, ProgramExecution)> {
parser.scopes.enter()
public func run() -> Result<(ParserState, ProgramExecution)> {
return .Ok(parser.execute())
}
@@ -51,15 +50,15 @@ public class ParserRuntime: CustomStringConvertible {
}
/// Instances of parsers are executable
extension ParserInstance: Execution {
extension ParserInstance: ParserExecution {
public func execute() -> (ParserState, ProgramExecution) {
var execution = self as ProgramExecution
var state = self.state
var c = self.start_state
// Evaluate until the state is either accept or reject.
while state != accept && state != reject {
(state, execution) = state.evaluate(execution: execution)
while !c.done() && !execution.hasError() {
(c, execution) = c.execute(program: execution)
}
return (state, execution)
return (c.current(), execution)
}
}
+1 -22
View File
@@ -26,28 +26,6 @@ import TreeSitterP4
@testable import Parser
@Test func test_simple_parser() async throws {
let simple_parser_declaration = """
parser main_parser() {
state start {
transition start;
}
};
"""
let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
#expect(parser.states.count() == 1)
let state = try! #require(parser.states.find(withName: "start"))
#expect(state.state_name == "start")
#expect(state.statements.count == 0)
#expect(#RequireOkResult(parser.states.semantic_check()))
let next_state = try! #require(state.next_state)
#expect(next_state == state)
}
@Test func test_simple_parser_syntax_error() async throws {
let simple_parser_declaration = """
parser main_parser() {
@@ -87,6 +65,7 @@ import TreeSitterP4
parser main_parser() {
state start {
true;
false;
transition start;
}
};
+9 -74
View File
@@ -38,14 +38,8 @@ import TreeSitterP4
"""
let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
#expect(#RequireOkResult(Runtime.ParserRuntime.create(program: program)))
let runtime = try #UseOkResult(Runtime.ParserRuntime.create(program: program))
guard case Common.Result.Ok(let (state_result, _)) = runtime.run(input: Packet())
else {
assert(false)
}
let (state_result, _) = try! #UseOkResult(runtime.run())
// We should be in the accept state.
#expect(state_result == Lang.accept)
@@ -62,15 +56,8 @@ import TreeSitterP4
"""
let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
#expect(#RequireOkResult(Runtime.ParserRuntime.create(program: program)))
let runtime = try #UseOkResult(Runtime.ParserRuntime.create(program: program))
guard case Common.Result.Ok(let (state_result, _)) = runtime.run(input: Packet())
else {
assert(false)
}
let (state_result, _) = try! #UseOkResult(runtime.run())
// We should be in the accept state.
#expect(state_result == Lang.reject)
}
@@ -87,55 +74,13 @@ import TreeSitterP4
"""
let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
#expect(
#RequireErrorResult<ParserRuntime>(
Error(withMessage: "Could not find the start state"),
Error(withMessage: "No start state defined"),
Runtime.ParserRuntime.create(program: program)))
}
@Test func test_simple_local_element_variable_declaration() async throws {
let simple_parser_declaration = """
parser main_parser() {
state start {
bool b = false;
string s = "testing";
true;
false;
transition reject;
}
};
"""
let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
let runtime = try #UseOkResult(Runtime.ParserRuntime.create(program: program))
// This seems awkward to me!
// TODO: Is there a better way?
guard case Common.Result.Ok(let (state_result, execution_result)) = runtime.run(input: Packet())
else {
assert(false)
}
// There should be 1 scope.
#expect(execution_result.scopes.count == 1)
guard let scope = execution_result.scopes.current else {
assert(false)
}
// We should be in the accept state.
#expect(state_result == Lang.reject)
// There are two variables declared.
#expect(scope.count == 2)
// Check the names/values of the variables in scope.
let b = try #require(scope.lookup(identifier: Identifier(name: "b")))
let s = try #require(scope.lookup(identifier: Identifier(name: "s")))
#expect(b.value_type.eq(rhs: P4BooleanValue(withValue: false)))
#expect(s.value_type.eq(rhs: P4StringValue(withValue: "\"testing\"")))
}
@Test func test_simple_parser_with_transition_select_expression() async throws {
let simple_parser_declaration = """
parser main_parser() {
@@ -148,18 +93,14 @@ import TreeSitterP4
};
"""
let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
#expect(#RequireOkResult(program.find_parser(withName: Identifier(name: "main_parser"))))
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
let runtime = try #UseOkResult(Runtime.ParserRuntime.create(program: program))
let (state_result, _) = try! #UseOkResult(runtime.run())
#expect(parser.states.count() == 1)
let runtime = try #UseOkResult(Runtime.ParserRuntime.create(program: program))
guard case Common.Result.Ok(let (state_result, _)) = runtime.run(input: Packet())
else {
assert(false)
}
#expect(state_result == Lang.accept)
}
@@ -176,16 +117,10 @@ import TreeSitterP4
"""
let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
#expect(#RequireOkResult(program.find_parser(withName: Identifier(name: "main_parser"))))
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
let runtime = try #UseOkResult(Runtime.ParserRuntime.create(program: program))
let (state_result, _) = try! #UseOkResult(runtime.run())
#expect(parser.states.count() == 1)
let runtime = try #UseOkResult(Runtime.ParserRuntime.create(program: program))
guard case Common.Result.Ok(let (state_result, _)) = runtime.run(input: Packet())
else {
assert(false)
}
#expect(state_result == Lang.reject)
}
+173
View File
@@ -0,0 +1,173 @@
// p4rse, Copyright 2026, Will Hawkins
//
// This file is part of p4rse.
//
// This file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import Common
import Foundation
import Lang
import Macros
import Runtime
import SwiftTreeSitter
import Testing
import TreeSitter
import TreeSitterP4
@testable import Parser
@Test func test_simple_local_element_variable_declaration() async throws {
let simple_parser_declaration = """
parser main_parser() {
state start {
bool b = false;
string s = "testing";
true;
false;
true;
transition reject;
}
};
"""
let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
let runtime = try #UseOkResult(Runtime.ParserRuntime.create(program: program))
let (state_result, execution_result) = try! #UseOkResult(runtime.run())
#expect(execution_result.scopes.count == 1)
guard let scope = execution_result.scopes.current else {
assert(false)
}
// We should be in the accept state.
#expect(state_result == Lang.reject)
// There are two variables declared.
#expect(scope.count == 2)
// Check the names/values of the variables in scope.
let b = try #require(scope.lookup(identifier: Identifier(name: "b")))
let s = try #require(scope.lookup(identifier: Identifier(name: "s")))
#expect(b.value_type.eq(rhs: P4BooleanValue(withValue: false)))
#expect(s.value_type.eq(rhs: P4StringValue(withValue: "\"testing\"")))
}
@Test func test_simple_scope() async throws {
let simple_parser_declaration = """
parser main_parser() {
state starts {
bool where_to = false;
int va = 5;
transition accept;
}
state start {
bool where_to = true;
transition select (where_to) {
false: reject;
true: starts;
};
}
};
"""
let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
let runtime = try #UseOkResult(Runtime.ParserRuntime.create(program: program))
let (state_result, execution_result) = try! #UseOkResult(runtime.run())
#expect(state_result == Lang.accept)
#expect(execution_result.scopes.count == 1)
let scope = try! #require(execution_result.scopes.current)
#expect(scope.count == 2)
let va = try #require(scope.lookup(identifier: Identifier(name: "va")))
let where_to = try #require(scope.lookup(identifier: Identifier(name: "where_to")))
#expect(where_to.value_type.eq(rhs: P4BooleanValue(withValue: false)))
#expect(va.value_type.eq(rhs: P4IntValue(withValue: 5)))
}
@Test func test_simple_scope2() async throws {
let simple_parser_declaration = """
parser main_parser() {
state starts {
bool where_to = false;
int va = 5;
transition accept;
}
state start {
bool where_to = true;
where_to = false;
transition select (where_to) {
false: reject;
true: starts;
};
}
};
"""
let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
let runtime = try #UseOkResult(Runtime.ParserRuntime.create(program: program))
let (state_result, execution_result) = try! #UseOkResult(runtime.run())
#expect(parser.states.count() == 2)
#expect(state_result == Lang.reject)
#expect(execution_result.scopes.count == 1)
let scope = try! #require(execution_result.scopes.current)
#expect(scope.count == 1)
let where_to = try #require(scope.lookup(identifier: Identifier(name: "where_to")))
#expect(where_to.value_type.eq(rhs: P4BooleanValue(withValue: false)))
}
@Test func test_simple_assignment() async throws {
let simple_parser_declaration = """
parser main_parser() {
state start {
bool where_to = true;
string where_from = "here";
where_to = false;
where_from = "there";
transition select (true) {
false: reject;
true: accept;
};
}
};
"""
let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser")))
let runtime = try #UseOkResult(Runtime.ParserRuntime.create(program: program))
let (state_result, execution_result) = try! #UseOkResult(runtime.run())
#expect(parser.states.count() == 1)
#expect(state_result == Lang.accept)
#expect(execution_result.scopes.count == 1)
let scope = try! #require(execution_result.scopes.current)
#expect(scope.count == 2)
let where_to = try #require(scope.lookup(identifier: Identifier(name: "where_to")))
#expect(where_to.value_type.eq(rhs: P4BooleanValue(withValue: false)))
let where_from = try #require(scope.lookup(identifier: Identifier(name: "where_from")))
#expect(where_from.value_type.eq(rhs: P4StringValue(withValue: "\"there\"")))
}
+3 -2
View File
@@ -75,14 +75,15 @@ export default grammar({
// General statements
statements: $ => repeat1(seq($.statement, $.semicolon)),
statement: $ => choice($.conditionalStatement, $.blockStatement, $.expressionStatement),// Limited, so far.
statement: $ => choice($.conditionalStatement, $.blockStatement, $.expressionStatement, $.assignmentStatement),// Limited, so far.
blockStatement: $ => seq(optional($.annotations), '{', optional($.statements), '}'),
conditionalStatement: $ => choice(prec(1, seq($.if, '(', $.expression, ')', $.statement)), prec(2, seq($.if, '(', $.expression, ')', $.statement, $.else, $.statement))),
expressionStatement: $=> $.expression,
assignmentStatement: $=> seq($.expression, $.assignment, $.expression),
// Parser statements
parserStatements: $ => repeat1(seq($.parserStatement, $.semicolon)),
parserStatement: $ => choice($.conditionalStatement, $.parserBlockStatement, $.expressionStatement), // Limited, so far.
parserStatement: $ => choice($.conditionalStatement, $.parserBlockStatement, $.expressionStatement, $.assignmentStatement), // Limited, so far.
parserBlockStatement: $ => seq(optional($.annotations), '{', $.parserStatements, '}'),
parserTransitionStatement: $ => seq($.transition, $.transitionSelectionExpression),
+15 -6
View File
@@ -1,7 +1,16 @@
parser simple() {
state start {
bool l;
transition accept;
parser main_parser() {
state next_state {
transition reject;
}
}
state not_next_state {
transition reject;
}
state start {
transition starts;
}
state starts {
transition select (se) {
true: next_state;
};
}
};
+24 -36
View File
@@ -106,15 +106,12 @@ parser simple() {
)
=========================
Simple If Statement (With Body)
Simple Assignment Statement (To Identifier)
=========================
parser simple() {
state start {
if (true) {
true;
} else {
false;
};
string l = "testing";
l = "five";
transition accept;
}
};
@@ -131,41 +128,32 @@ parser simple() {
(parserState
(state)
(identifier)
(parserLocalElements
(parserLocalElement
(variableDeclaration
(typeRef
(baseType
(string)
)
)
(identifier)
(assignment)
(expression
(string_literal)
)
)
)
(semicolon)
)
(parserStatements
(parserStatement
(conditionalStatement
(if)
(assignmentStatement
(expression
(true)
(identifier)
)
(statement
(blockStatement
(statements
(statement
(expressionStatement
(assignment)
(expression
(true)
)
)
)
(semicolon)
)
)
)
(else)
(statement
(blockStatement
(statements
(statement
(expressionStatement
(expression
(false)
)
)
)
(semicolon)
)
)
(string_literal)
)
)
)