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 { open class ProgramExecution: CustomStringConvertible {
public var scopes: Scopes = Scopes() public var scopes: Scopes = Scopes()
var error: Error?
public init() {} public init() {}
open var description: String { open var description: String {
return "Runtime:\nScopes: \(scopes)" 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 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 { public var count: Int {
get { get {
scopes.count 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 /// The type for a P4 struct
public struct P4Struct: P4Type { public struct P4Struct: P4Type {
public let name: String public let name: String
@@ -122,6 +128,7 @@ public class P4BooleanValue: P4ValueBase<P4Boolean> {
} }
} }
/// A P4 int type /// A P4 int type
public struct P4Int: P4Type { public struct P4Int: P4Type {
public static func create() -> any P4Type { public static func create() -> any P4Type {
+1 -1
View File
@@ -20,7 +20,7 @@ public protocol EvaluatableExpression {
/// - Parameters /// - Parameters
/// - execution: The execution context in which to evaluate the expression /// - execution: The execution context in which to evaluate the expression
/// - Returns: The value of expression /// - Returns: The value of expression
func evaluate(execution: ProgramExecution) -> P4Value func evaluate(execution: ProgramExecution) -> Result<P4Value>
} }
public protocol EvaluatableParserStatement { public protocol EvaluatableParserStatement {
+7
View File
@@ -53,6 +53,13 @@ public enum Result<OKT>: Equatable {
} }
return nil 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 { 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 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 { public struct VariableDeclarationStatement {
@@ -36,16 +93,13 @@ public struct VariableDeclarationStatement {
} }
} }
public struct ExpressionStatement { public class ParserState: Equatable, CustomStringConvertible {
public init() {}
}
public struct ParserState: Equatable, CustomStringConvertible {
public private(set) var state_name: String public private(set) var state_name: String
public private(set) var local_elements: [EvaluatableParserStatement] public private(set) var local_elements: [EvaluatableParserStatement]
public private(set) var statements: [EvaluatableParserStatement] public private(set) var statements: [EvaluatableParserStatement]
public private(set) var transition: ParserTransitionStatement? public private(set) var transition: ParserTransitionStatement?
public private(set) var next_state: ParserState?
public var description: String { public var description: String {
return "Name: \(state_name)" return "Name: \(state_name)"
@@ -67,6 +121,37 @@ public struct ParserState: Equatable, CustomStringConvertible {
self.statements = statements ?? Array() 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) /// (private) constructor (no transition)
/// ///
/// accept and reject are the only final states and they are constructed internally. /// 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() local_elements = Array()
statements = 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 struct ParserStates {
public var states: [ParserState] = Array() 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 accept: ParserState = ParserState(name: "accept")
nonisolated(unsafe) public let reject: ParserState = ParserState(name: "reject") nonisolated(unsafe) public let reject: ParserState = ParserState(name: "reject")
public struct Parser: P4Type { public struct Parser: P4Type {
public var states: [ParserState] = Array() public var states: ParserStates
public var count: Int {
states.count
}
public var name: Identifier public var name: Identifier
public init(withName name: Identifier) { public init(withName name: Identifier) {
self.states = ParserStates()
self.name = name self.name = name
} }
@@ -102,24 +239,38 @@ public struct Parser: P4Type {
} }
public func findStartState() -> ParserState? { public func findStartState() -> ParserState? {
for state in states { for state in states.states {
if state.state_name == "start" { if state.state_name == "start" {
return state return state
} }
} }
return .none return .none
} }
public func semantic_check() -> Result<()> {
return self.states.semantic_check()
}
} }
public class ParserInstance: ProgramExecution { public class ParserInstance: ProgramExecution {
public var state: ParserState
private init(state: ParserState) { private init(state: ParserState) {
self.state = state self.state = state
super.init() super.init()
} }
public static func create(_ parser: Parser) -> Result<ParserInstance> { 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")) return Result.Error(Error(withMessage: "Could not find the start state"))
} }
let new = ParserInstance(state: start_state) let new = ParserInstance(state: start_state)
@@ -127,8 +278,6 @@ public class ParserInstance: ProgramExecution {
return Result.Ok(new) return Result.Ok(new)
} }
public var state: ParserState
public func transition(toNextState state: ParserState) -> ParserInstance { public func transition(toNextState state: ParserState) -> ParserInstance {
let next = self let next = self
next.state = state next.state = state
+4 -1
View File
@@ -17,6 +17,10 @@
import Common import Common
public struct ExpressionStatement {
public init() {}
}
public struct Program { public struct Program {
public var types: [P4Type] = Array() public var types: [P4Type] = Array()
@@ -29,7 +33,6 @@ public struct Program {
public func find_parser(withName name: Identifier) -> Result<Parser> { public func find_parser(withName name: Identifier) -> Result<Parser> {
for type in self.types { for type in self.types {
print("type: \(type)")
guard let parser = type as? Parser else { guard let parser = type as? Parser else {
continue 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) 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( static func TransitionStatement(
node: Node, inTree tree: MutableTree node: Node, inTree tree: MutableTree
) -> ParserTransitionStatement? { ) -> Result<ParserTransitionStatement> {
return 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> { static func State(node: Node, inTree tree: MutableTree) -> Result<ParserState> {
@@ -259,7 +344,7 @@ public struct Parser {
guard !state_name_capture.isEmpty, guard !state_name_capture.isEmpty,
!transition_capture.isEmpty, !transition_capture.isEmpty,
let parsed_state_name = state_name_capture[0].node.text, 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) node: transition_capture[0].node, inTree: tree)
else { else {
return Result.Error(Error(withMessage: "Could not parse a parser declaration")) return Result.Error(Error(withMessage: "Could not parse a parser declaration"))
@@ -297,7 +382,9 @@ public struct Parser {
withTransition: transition_statement)) 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 guard
let parser_state_query = try? SwiftTreeSitter.Query( let parser_state_query = try? SwiftTreeSitter.Query(
language: p4lang, language: p4lang,
@@ -312,12 +399,23 @@ public struct Parser {
var parser = Lang.Parser(withName: name) var parser = Lang.Parser(withName: name)
// Build a state from each one listed. // Build a state from each one listed.
for parser_states in parser_state_query.execute(node: node, in: tree) { let qr = parser_state_query.execute(node: node, in: tree)
switch P4Parser.State(node: parser_states.nodes[0], inTree: tree) { let qr_value = qr.next()!
case Result.Ok(let state): parser.states.append(state) let captures = qr_value.captures(named: "parser-states")
case Result.Error(let error): return Result.Error(error)
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) return Result.Ok(parser)
} }
@@ -354,7 +452,10 @@ public struct Parser {
let parser_qc = parser_declaration_query.execute(in: tree) let parser_qc = parser_declaration_query.execute(in: tree)
for parser_declaration in parser_qc { 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.Ok(let parser): program.types.append(parser)
case Result.Error(let error): return Result.Error(error) case Result.Error(let error): return Result.Error(error)
} }
+29 -14
View File
@@ -18,16 +18,7 @@
import Common import Common
import Lang import Lang
extension ParserState: EvaluatableParserTransition {
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) { public func evaluate(execution: ProgramExecution) -> (ParserState, ProgramExecution) {
var currentExecution = execution var currentExecution = execution
@@ -41,10 +32,34 @@ extension ParserState: EvaluatableParserState {
currentExecution = statement.evaluate(execution: currentExecution) currentExecution = statement.evaluate(execution: currentExecution)
} }
return if let transition = transition { if direct_transition() {
transition.transition(execution: currentExecution) return (next_state!, currentExecution)
} else {
(reject, 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 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 Common
import Lang import Lang
protocol EvaluatableParserState { protocol EvaluatableParserTransition {
func evaluate(execution: ProgramExecution) -> (ParserState, ProgramExecution) func evaluate(execution: ProgramExecution) -> (ParserState, ProgramExecution)
} }
+7 -6
View File
@@ -20,14 +20,15 @@ import Lang
/// The runtime for a parser /// The runtime for a parser
public class ParserRuntime: CustomStringConvertible { public class ParserRuntime: CustomStringConvertible {
var execution: ParserInstance var parser: ParserInstance
init(execution: ParserInstance) { init(execution: ParserInstance) {
self.execution = execution self.parser = execution
} }
/// Create a parser runtime from a P4 program /// Create a parser runtime from a P4 program
public static func create(program: Lang.Program) -> Result<ParserRuntime> { public static func create(program: Lang.Program) -> Result<ParserRuntime> {
return switch program.starting_parser() { return switch program.starting_parser() {
case .Ok(let parser): case .Ok(let parser):
switch ParserInstance.create(parser) { switch ParserInstance.create(parser) {
@@ -40,12 +41,12 @@ public class ParserRuntime: CustomStringConvertible {
/// Run the P4 parser on a given packet /// Run the P4 parser on a given packet
public func run(input: Packet) -> Result<(ParserState, ProgramExecution)> { public func run(input: Packet) -> Result<(ParserState, ProgramExecution)> {
execution.scopes.enter() parser.scopes.enter()
return .Ok(execution.execute()) return .Ok(parser.execute())
} }
public var description: String { 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. // Evaluate until the state is either accept or reject.
while state != accept && state != reject { while state != accept && state != reject {
(state, execution) = self.state.evaluate(execution: execution) (state, execution) = state.evaluate(execution: execution)
} }
return (state, execution) return (state, execution)
} }
+27 -23
View File
@@ -15,15 +15,14 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // 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 Testing
import TreeSitter import TreeSitter
import SwiftTreeSitter
import TreeSitterP4 import TreeSitterP4
import Foundation
import Runtime
import Common
import Macros
@testable import Parser @testable import Parser
@@ -31,7 +30,7 @@ import Macros
let simple_parser_declaration = """ let simple_parser_declaration = """
parser main_parser() { parser main_parser() {
state start { state start {
transition drop; transition start;
} }
}; };
""" """
@@ -39,20 +38,28 @@ import Macros
let program = try #UseOkResult(Parser.Program(simple_parser_declaration)) let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
let parser = try #UseOkResult(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) #expect(parser.states.count() == 1)
#expect(parser.states[0].state_name == "start") let state = try! #require(parser.states.find(withName: "start"))
#expect(parser.states[0].statements.count == 0) #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 { @Test func test_simple_parser_syntax_error() async throws {
let simple_parser_declaration = """ let simple_parser_declaration = """
parser main_parser() { parser main_parser() {
state 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 { @Test func test_simple_parser_with_statement() async throws {
@@ -60,7 +67,7 @@ import Macros
parser main_parser() { parser main_parser() {
state start { state start {
true; true;
transition drop; transition start;
} }
}; };
""" """
@@ -68,9 +75,11 @@ import Macros
let program = try #UseOkResult(Parser.Program(simple_parser_declaration)) let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
let parser = try #UseOkResult(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) #expect(parser.states.count() == 1)
#expect(parser.states[0].state_name == "start")
#expect(parser.states[0].statements.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 { @Test func test_simple_parser_with_instantiation() async throws {
@@ -78,17 +87,12 @@ import Macros
parser main_parser() { parser main_parser() {
state start { state start {
true; true;
transition drop; transition start;
} }
}; };
bool() main; bool() main;
""" """
let program = try #UseOkResult(Parser.Program(simple_parser_declaration)) 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)
#expect(parser.states[0].state_name == "start")
#expect(parser.states[0].statements.count == 1)
} }
+90 -1
View File
@@ -28,6 +28,30 @@ import TreeSitterP4
@testable import Parser @testable import Parser
@Test func test_simple_runtime() async throws { @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 = """ let simple_parser_declaration = """
parser main_parser() { parser main_parser() {
state start { state start {
@@ -39,8 +63,19 @@ import TreeSitterP4
let program = try #UseOkResult(Parser.Program(simple_parser_declaration)) let program = try #UseOkResult(Parser.Program(simple_parser_declaration))
#expect(#RequireOkResult(Runtime.ParserRuntime.create(program: program))) #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 { @Test func test_simple_runtime_no_start_state() async throws {
let simple_parser_declaration = """ let simple_parser_declaration = """
parser main_parser() { parser main_parser() {
@@ -89,7 +124,7 @@ import TreeSitterP4
} }
// We should be in the accept state. // We should be in the accept state.
#expect(state_result == Lang.accept) #expect(state_result == Lang.reject)
// There are two variables declared. // There are two variables declared.
#expect(scope.count == 2) #expect(scope.count == 2)
@@ -100,3 +135,57 @@ import TreeSitterP4
#expect(b.value_type.eq(rhs: P4BooleanValue(withValue: false))) #expect(b.value_type.eq(rhs: P4BooleanValue(withValue: false)))
#expect(s.value_type.eq(rhs: P4StringValue(withValue: "\"testing\""))) #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)
}