Start Evaluation

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
Will Hawkins
2026-02-10 03:37:57 -05:00
parent 0dd5ce4be3
commit 3693bdc02d
15 changed files with 635 additions and 119 deletions
+24
View File
@@ -17,12 +17,27 @@
open class ProgramExecution: CustomStringConvertible {
public var scopes: Scopes = Scopes()
var error: Error?
public init() {}
open var description: String {
return "Runtime:\nScopes: \(scopes)"
}
public func hasError() -> Bool {
return self.error != nil
}
public func getError() -> Error? {
return self.error
}
public func setError(error: Error) -> ProgramExecution {
let npe = self
npe.error = error
return npe
}
}
@@ -96,6 +111,15 @@ public struct Scopes: CustomStringConvertible {
return s
}
public func evaluate(identifier: Identifier) -> Result<P4Value> {
for scope in scopes {
if let vari = scope.lookup(identifier: identifier) {
return .Ok(vari.value)
}
}
return .Error(Error(withMessage: "Cannot find \(identifier) in scope."))
}
public var count: Int {
get {
scopes.count
+7
View File
@@ -65,6 +65,12 @@ open class P4ValueBase<T: P4Type>: P4Value {
}
}
extension P4ValueBase: EvaluatableExpression {
public func evaluate(execution: ProgramExecution) -> Result<P4Value> {
return .Ok(self)
}
}
/// The type for a P4 struct
public struct P4Struct: P4Type {
public let name: String
@@ -122,6 +128,7 @@ public class P4BooleanValue: P4ValueBase<P4Boolean> {
}
}
/// A P4 int type
public struct P4Int: P4Type {
public static func create() -> any P4Type {
+1 -1
View File
@@ -20,7 +20,7 @@ public protocol EvaluatableExpression {
/// - Parameters
/// - execution: The execution context in which to evaluate the expression
/// - Returns: The value of expression
func evaluate(execution: ProgramExecution) -> P4Value
func evaluate(execution: ProgramExecution) -> Result<P4Value>
}
public protocol EvaluatableParserStatement {
+7
View File
@@ -53,6 +53,13 @@ public enum Result<OKT>: Equatable {
}
return nil
}
public func map<T>(block: (OKT) -> Result<T>) -> Result<T> {
switch self {
case .Ok(let ok): return block(ok)
case .Error(let e): return .Error(e)
}
}
}
extension Result where OKT: CustomStringConvertible {
View File
+163 -14
View File
@@ -25,8 +25,65 @@ public struct LocalElement {
}
public struct KeysetExpression {
public let key: EvaluatableExpression
public let next_state_name: String
public let next_state: ParserState?
public init(withKey key: EvaluatableExpression, withNextStateName next_state_name: String) {
self.key = key
self.next_state_name = next_state_name
self.next_state = .none
}
public init(
withKey key: EvaluatableExpression, withNextStateName next_state_name: String,
withNextState next_state: ParserState
) {
self.key = key
self.next_state_name = next_state_name
self.next_state = next_state
}
}
public struct ParserTransitionSelectExpression {
public let selector: EvaluatableExpression
public let keyset_expressions: [KeysetExpression]
public init(
withSelector selector: EvaluatableExpression, withKeysetExpressions kses: [KeysetExpression]
) {
self.selector = selector
self.keyset_expressions = kses
}
public func append_checked_kse(kse: KeysetExpression) -> ParserTransitionSelectExpression {
var new_kse = self.keyset_expressions
new_kse.append(kse)
return ParserTransitionSelectExpression(
withSelector: self.selector, withKeysetExpressions: new_kse)
}
}
public struct ParserTransitionStatement {
public init() {}
public let next_state_name: String?
public let transition_expression: ParserTransitionSelectExpression?
public init() {
self.next_state_name = .none
self.transition_expression = .none
}
public init(withTransitionExpression transition_expression: ParserTransitionSelectExpression) {
self.next_state_name = .none
self.transition_expression = transition_expression
}
public init(withNextState next_state_name: String) {
self.next_state_name = next_state_name
self.transition_expression = .none
}
}
public struct VariableDeclarationStatement {
@@ -36,16 +93,13 @@ public struct VariableDeclarationStatement {
}
}
public struct ExpressionStatement {
public init() {}
}
public struct ParserState: Equatable, CustomStringConvertible {
public class 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 private(set) var next_state: ParserState?
public var description: String {
return "Name: \(state_name)"
@@ -67,6 +121,37 @@ public struct ParserState: Equatable, CustomStringConvertible {
self.statements = statements ?? Array()
}
public func semantic_check(states: ParserStates) -> Bool {
guard let transition = transition else {
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)
{
self.next_state = next_state
return true
}
return false
}
/// (private) constructor (no transition)
///
/// accept and reject are the only final states and they are constructed internally.
@@ -76,24 +161,76 @@ public struct ParserState: Equatable, CustomStringConvertible {
local_elements = Array()
statements = Array()
}
public func direct_transition() -> Bool {
return
if let transition = self.transition,
transition.next_state_name != nil
{
true
} else {
false
}
}
}
public struct ParserStates {
public var states: [ParserState] = Array()
public func count() -> Int {
return states.count
}
public func find(withName name: String) -> ParserState? {
for state in states {
if state.state_name == name {
return .some(state)
}
}
return .none
}
public func semantic_check() -> Result<()> {
// Check whether all the states referred to in the transition statements are
// valid.
let errors = states.filter { state in
return !state.semantic_check(states: self)
}.map { state in
return Result<()>.Error(Error(withMessage: "State \(state) has invalid transition"))
}
if !errors.isEmpty {
return errors[0]
}
return .Ok(())
}
public init() {
self.states = Array()
}
private init(withStates states: [ParserState]) {
self.states = states
}
public func append(state: ParserState) -> ParserStates {
var new_states = self.states
new_states.append(state)
return ParserStates(withStates: new_states)
}
}
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: [ParserState] = Array()
public var count: Int {
states.count
}
public var states: ParserStates
public var name: Identifier
public init(withName name: Identifier) {
self.states = ParserStates()
self.name = name
}
@@ -102,24 +239,38 @@ public struct Parser: P4Type {
}
public func findStartState() -> ParserState? {
for state in states {
for state in states.states {
if state.state_name == "start" {
return state
}
}
return .none
}
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 static func create(_ parser: Parser) -> Result<ParserInstance> {
guard let start_state = parser.findStartState() else {
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)
}
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)
@@ -127,8 +278,6 @@ public class ParserInstance: ProgramExecution {
return Result.Ok(new)
}
public var state: ParserState
public func transition(toNextState state: ParserState) -> ParserInstance {
let next = self
next.state = state
+4 -1
View File
@@ -17,6 +17,10 @@
import Common
public struct ExpressionStatement {
public init() {}
}
public struct Program {
public var types: [P4Type] = Array()
@@ -29,7 +33,6 @@ public struct Program {
public func find_parser(withName name: Identifier) -> Result<Parser> {
for type in self.types {
print("type: \(type)")
guard let parser = type as? Parser else {
continue
}
+109
View File
@@ -0,0 +1,109 @@
// 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/>.
// 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?>
}
extension Identifier: ParseableEvaluatableExpression {
static func parse(
node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree
) -> Result<EvaluatableExpression?> {
guard
let parser_statement_query = try? SwiftTreeSitter.Query(
language: p4lang,
data: String(
"(expression (identifier) @identifier)"
).data(using: String.Encoding.utf8)!)
else {
return Result.Error(Error(withMessage: "Could not compile the tree sitter query"))
}
let qr = parser_statement_query.execute(node: node, in: tree)
guard let result = qr.next() else {
return .Ok(.none)
}
return .Ok(Identifier(name: result.captures[0].node.text!))
}
}
extension P4BooleanValue: ParseableEvaluatableExpression {
static func parse(
node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree
) -> Result<EvaluatableExpression?> {
guard
let true_query = try? SwiftTreeSitter.Query(
language: p4lang,
data: String(
"(expression (true))"
).data(using: String.Encoding.utf8)!)
else {
return Result.Error(Error(withMessage: "Could not compile the tree sitter query"))
}
let true_qr = true_query.execute(node: node, in: tree)
if true_qr.next() != nil {
return .Ok(P4BooleanValue(withValue: true))
}
guard
let false_query = try? SwiftTreeSitter.Query(
language: p4lang,
data: String(
"(expression (false))"
).data(using: String.Encoding.utf8)!)
else {
return Result.Error(Error(withMessage: "Could not compile the tree sitter query"))
}
let false_qr = false_query.execute(node: node, in: tree)
if false_qr.next() != nil {
return .Ok(P4BooleanValue(withValue: false))
}
return .Ok(.none)
}
}
struct Expression {
public static func Parse(node: Node, inTree: MutableTree) -> 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)
{
return .Ok(parsed)
}
}
return Result.Error(Error(withMessage: "Could not parse into expression."))
}
}
+110 -9
View File
@@ -229,10 +229,95 @@ public struct Parser {
return Result.Ok(statements)
}
static func TransitionKeysetExpression(
node: Node, inTree tree: MutableTree
) -> Result<[KeysetExpression]> {
guard
let transition_selection_expression_query = try? SwiftTreeSitter.Query(
language: p4lang,
data: String(
"((keysetExpression (expression) @ks) (colon) (identifier) @next-state)"
).data(using: String.Encoding.utf8)!)
else {
return Result.Error(Error(withMessage: "Could not compile the tree sitter query"))
}
let qr = transition_selection_expression_query.execute(node: node, in: tree)
var kses: [KeysetExpression] = Array()
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)
.map(block: { expression in
kses.append(
KeysetExpression(
withKey: expression, withNextStateName: next_state_name))
return .Ok(expression)
})
{
return .Error(e)
}
}
return .Ok(kses)
}
static func TransitionSelectExpression(
node: Node, inTree tree: MutableTree
) -> Result<ParserTransitionStatement> {
guard
let transition_selection_expression_query = try? SwiftTreeSitter.Query(
language: p4lang,
data: String(
"(parserTransitionStatement (transition) (transitionSelectionExpression (selectExpression (select) (expression) @selector (selectBody) @body)))"
).data(using: String.Encoding.utf8)!)
else {
return Result.Error(Error(withMessage: "Could not compile the tree sitter query"))
}
let qr = transition_selection_expression_query.execute(node: node, in: tree)
guard let query_result = qr.next() else {
return .Error(Error(withMessage: "Could not find transition select expression"))
}
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(
ParserTransitionStatement(
withTransitionExpression: ParserTransitionSelectExpression(
withSelector: expression, withKeysetExpressions: kse)))
case .Error(let e): Result<ParserTransitionStatement>.Error(e)
}
}
}
static func TransitionStatement(
node: Node, inTree tree: MutableTree
) -> ParserTransitionStatement? {
return ParserTransitionStatement()
) -> Result<ParserTransitionStatement> {
guard
let next_state_query = try? SwiftTreeSitter.Query(
language: p4lang,
data: String(
"(parserTransitionStatement (transition) (transitionSelectionExpression (identifier) @next-state))"
).data(using: String.Encoding.utf8)!)
else {
return Result.Error(Error(withMessage: "Could not compile the tree sitter query"))
}
let qr = next_state_query.execute(node: node, in: tree)
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 TransitionSelectExpression(node: node, inTree: tree)
}
static func State(node: Node, inTree tree: MutableTree) -> Result<ParserState> {
@@ -259,7 +344,7 @@ public struct Parser {
guard !state_name_capture.isEmpty,
!transition_capture.isEmpty,
let parsed_state_name = state_name_capture[0].node.text,
let transition_statement = TransitionStatement(
case .Ok(let transition_statement) = TransitionStatement(
node: transition_capture[0].node, inTree: tree)
else {
return Result.Error(Error(withMessage: "Could not parse a parser declaration"))
@@ -297,7 +382,9 @@ public struct Parser {
withTransition: transition_statement))
}
}
static func Parser(withName name: Identifier, node: Node, inTree tree: MutableTree) -> Result<Lang.Parser> {
static func Parser(
withName name: Identifier, node: Node, inTree tree: MutableTree
) -> Result<Lang.Parser> {
guard
let parser_state_query = try? SwiftTreeSitter.Query(
language: p4lang,
@@ -312,12 +399,23 @@ public struct Parser {
var parser = Lang.Parser(withName: name)
// Build a state from each one listed.
for parser_states in parser_state_query.execute(node: node, in: tree) {
switch P4Parser.State(node: parser_states.nodes[0], inTree: tree) {
case Result.Ok(let state): parser.states.append(state)
case Result.Error(let error): return Result.Error(error)
let qr = parser_state_query.execute(node: node, in: tree)
let qr_value = qr.next()!
let captures = qr_value.captures(named: "parser-states")
var error: Error? = .none
// 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)
case Result.Error(let e): error = e
}
}
if let error = error {
return .Error(error)
}
return Result.Ok(parser)
}
@@ -354,7 +452,10 @@ public struct Parser {
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) {
switch Parser(
withName: Identifier(name: parser_declaration.nodes[0].text!),
node: parser_declaration.nodes[1], inTree: tree)
{
case Result.Ok(let parser): program.types.append(parser)
case Result.Error(let error): return Result.Error(error)
}
+29 -14
View File
@@ -18,16 +18,7 @@
import Common
import Lang
extension ParserTransitionStatement: EvaluatableParserTransitionStatement {
// TODO: Currently transitions to accept.
func transition(execution: ProgramExecution) -> (ParserState, ProgramExecution) {
return (accept, execution)
}
}
extension ParserState: EvaluatableParserState {
extension ParserState: EvaluatableParserTransition {
public func evaluate(execution: ProgramExecution) -> (ParserState, ProgramExecution) {
var currentExecution = execution
@@ -41,10 +32,34 @@ extension ParserState: EvaluatableParserState {
currentExecution = statement.evaluate(execution: currentExecution)
}
return if let transition = transition {
transition.transition(execution: currentExecution)
} else {
(reject, currentExecution)
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) {
case .Ok(let selector_value):
for kse in self.keyset_expressions {
if case .Ok(let kse_key) = kse.key.evaluate(execution: execution),
kse_key.eq(rhs: selector_value)
{
return (kse.next_state!, execution)
}
}
case .Error(let e): return (reject, execution.setError(error: e))
}
return (reject, execution)
}
}
+7
View File
@@ -31,3 +31,10 @@ extension ExpressionStatement: EvaluatableParserStatement {
return execution
}
}
// Variables are evaluatable because they can be looked up by identifiers.
extension Identifier: EvaluatableExpression {
public func evaluate(execution: Common.ProgramExecution) -> Result<P4Value> {
return execution.scopes.evaluate(identifier: self)
}
}
+1 -1
View File
@@ -18,7 +18,7 @@
import Common
import Lang
protocol EvaluatableParserState {
protocol EvaluatableParserTransition {
func evaluate(execution: ProgramExecution) -> (ParserState, ProgramExecution)
}
+7 -6
View File
@@ -20,14 +20,15 @@ import Lang
/// The runtime for a parser
public class ParserRuntime: CustomStringConvertible {
var execution: ParserInstance
var parser: ParserInstance
init(execution: ParserInstance) {
self.execution = execution
self.parser = execution
}
/// Create a parser runtime from a P4 program
public static func create(program: Lang.Program) -> Result<ParserRuntime> {
return switch program.starting_parser() {
case .Ok(let parser):
switch ParserInstance.create(parser) {
@@ -40,12 +41,12 @@ public class ParserRuntime: CustomStringConvertible {
/// Run the P4 parser on a given packet
public func run(input: Packet) -> Result<(ParserState, ProgramExecution)> {
execution.scopes.enter()
return .Ok(execution.execute())
parser.scopes.enter()
return .Ok(parser.execute())
}
public var description: String {
return "Runtime:\nExecution: \(execution)"
return "Runtime:\nExecution: \(parser)"
}
}
@@ -57,7 +58,7 @@ extension ParserInstance: Execution {
// Evaluate until the state is either accept or reject.
while state != accept && state != reject {
(state, execution) = self.state.evaluate(execution: execution)
(state, execution) = state.evaluate(execution: execution)
}
return (state, execution)
}
+27 -23
View File
@@ -15,15 +15,14 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import Common
import Foundation
import Macros
import Runtime
import SwiftTreeSitter
import Testing
import TreeSitter
import SwiftTreeSitter
import TreeSitterP4
import Foundation
import Runtime
import Common
import Macros
@testable import Parser
@@ -31,7 +30,7 @@ import Macros
let simple_parser_declaration = """
parser main_parser() {
state start {
transition drop;
transition start;
}
};
"""
@@ -39,20 +38,28 @@ import Macros
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)
#expect(parser.states[0].state_name == "start")
#expect(parser.states[0].statements.count == 0)
#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() {
state
transition drop;
transition start;
}
};
"""
#expect(#RequireErrorResult(Error(withMessage: "Could not compile the P4 program"), Parser.Program(simple_parser_declaration)))
#expect(
#RequireErrorResult(
Error(withMessage: "Could not compile the P4 program"),
Parser.Program(simple_parser_declaration)))
}
@Test func test_simple_parser_with_statement() async throws {
@@ -60,7 +67,7 @@ import Macros
parser main_parser() {
state start {
true;
transition drop;
transition start;
}
};
"""
@@ -68,9 +75,11 @@ import Macros
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)
#expect(parser.states[0].state_name == "start")
#expect(parser.states[0].statements.count == 1)
#expect(parser.states.count() == 1)
let state = try! #require(parser.states.find(withName: "start"))
#expect(state.state_name == "start")
#expect(state.statements.count == 1)
}
@Test func test_simple_parser_with_instantiation() async throws {
@@ -78,17 +87,12 @@ import Macros
parser main_parser() {
state start {
true;
transition drop;
transition start;
}
};
bool() main;
"""
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)
#expect(parser.states[0].state_name == "start")
#expect(parser.states[0].statements.count == 1)
#expect(#RequireOkResult(program.find_parser(withName: Identifier(name: "main_parser"))))
}
+90 -1
View File
@@ -28,6 +28,30 @@ import TreeSitterP4
@testable import Parser
@Test func test_simple_runtime() async throws {
let simple_parser_declaration = """
parser main_parser() {
state start {
true;
transition accept;
}
};
"""
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)
}
// We should be in the accept state.
#expect(state_result == Lang.accept)
}
@Test func test_simple_runtime_to_accept() async throws {
let simple_parser_declaration = """
parser main_parser() {
state start {
@@ -39,8 +63,19 @@ 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)
}
// We should be in the accept state.
#expect(state_result == Lang.reject)
}
@Test func test_simple_runtime_no_start_state() async throws {
let simple_parser_declaration = """
parser main_parser() {
@@ -89,7 +124,7 @@ import TreeSitterP4
}
// We should be in the accept state.
#expect(state_result == Lang.accept)
#expect(state_result == Lang.reject)
// There are two variables declared.
#expect(scope.count == 2)
@@ -100,3 +135,57 @@ import TreeSitterP4
#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() {
state start {
transition select (true) {
false: reject;
true: accept;
};
}
};
"""
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")))
#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)
}
@Test func test_simple_parser_with_transition_select_expression_to_reject() async throws {
let simple_parser_declaration = """
parser main_parser() {
state start {
transition select (false) {
false: reject;
true: accept;
};
}
};
"""
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")))
#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)
}