diff --git a/Sources/Common/Execution.swift b/Sources/Common/Execution.swift index 0ff24a6..a7f27cc 100644 --- a/Sources/Common/Execution.swift +++ b/Sources/Common/Execution.swift @@ -73,6 +73,13 @@ open class ProgramExecution: CustomStringConvertible { return new_pe } + public func declare(identifier: Identifier, withValue value: P4Value) -> ProgramExecution { + let new_pe = self + let new_scopes = self.scopes.declare(identifier: identifier, withValue: value) + + new_pe.scopes = new_scopes + return new_pe + } } public typealias ValueScope = Scope diff --git a/Sources/Common/ProgramTypes.swift b/Sources/Common/ProgramTypes.swift index f8e41e9..0548738 100644 --- a/Sources/Common/ProgramTypes.swift +++ b/Sources/Common/ProgramTypes.swift @@ -72,29 +72,6 @@ public class Variable: TypedIdentifier { } } -/// A base for all instances of P4 types -open class P4ValueBase: 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 { - public func evaluate(execution: ProgramExecution) -> Result { - return .Ok(self) - } -} /// The type for a P4 struct public struct P4Struct: P4Type { @@ -136,7 +113,19 @@ public struct P4StructField { } /// An instance of a P4 struct -public class P4StructValue: P4ValueBase { +public class P4StructValue: P4Value { + public func type() -> any P4Type { + return P4Struct() + } + + public func eq(rhs: any P4Value) -> Bool { + return true + } + + public var description: String { + return "Struct" + } + public let fields: [P4StructField] public init(withFields fields: [P4StructField]) { self.fields = fields @@ -161,20 +150,24 @@ public struct P4Boolean: P4Type { } /// An instance of a P4 boolean -public class P4BooleanValue: P4ValueBase { +public class P4BooleanValue: P4Value { + public func type() -> any P4Type { + return P4Boolean() + } + let value: Bool public init(withValue value: Bool) { self.value = value } - public override func eq(rhs: P4Value) -> Bool { + public func eq(rhs: P4Value) -> Bool { guard let bool_rhs = rhs as? P4BooleanValue else { return false } return self.value == bool_rhs.value } - public override var description: String { + public var description: String { "\(self.value ? "true" : "false") of \(self.type()) type" } } @@ -196,18 +189,22 @@ public struct P4Int: P4Type { } /// An instance of a P4 integer -public class P4IntValue: P4ValueBase { +public class P4IntValue: P4Value { + public func type() -> any P4Type { + return P4Int() + } + let value: Int public init(withValue value: Int) { self.value = value } - public override func eq(rhs: P4Value) -> Bool { + public func eq(rhs: P4Value) -> Bool { guard let int_rhs = rhs as? P4IntValue else { return false } return self.value == int_rhs.value } - public override var description: String { + public var description: String { "\(self.value) of \(self.type()) type" } } @@ -229,23 +226,28 @@ public struct P4String: P4Type { } } /// An instance of a P4 string -public class P4StringValue: P4ValueBase { +public class P4StringValue: P4Value { + public func type() -> any P4Type { + return P4String() + } + let value: String public init(withValue value: String) { self.value = value } - public override func eq(rhs: P4Value) -> Bool { + public func eq(rhs: P4Value) -> Bool { guard let string_rhs = rhs as? P4StringValue else { return false } return self.value == string_rhs.value } - public override var description: String { + public var description: String { "\(self.value) of \(self.type()) type" } } + public class Packet { public init() {} } diff --git a/Sources/Common/Protocols.swift b/Sources/Common/Protocols.swift index a6edc9a..9d93d4d 100644 --- a/Sources/Common/Protocols.swift +++ b/Sources/Common/Protocols.swift @@ -33,7 +33,6 @@ public protocol EvaluatableStatement { } public protocol P4Type: CustomStringConvertible { - static func create() -> any P4Type func eq(rhs: any P4Type) -> Bool } diff --git a/Sources/Common/Support.swift b/Sources/Common/Support.swift index 6c70117..40a0d7d 100644 --- a/Sources/Common/Support.swift +++ b/Sources/Common/Support.swift @@ -141,10 +141,10 @@ extension Result: CustomStringConvertible { #externalMacro(module: "Macros", type: "UseOkResult") @freestanding(expression) public macro UseErrorResult(_: Result) -> Error = #externalMacro(module: "Macros", type: "UseErrorResult") -@freestanding(codeItem) public macro RequireNodeType(node: N, type: String, msg: String) = +@freestanding(codeItem) public macro RequireNodeType(node: N, type: String, nice_type_name: String) = #externalMacro(module: "Macros", type: "RequireNodeType") @freestanding(codeItem) public macro RequireNodesType( - nodes: N, type: [String], msg: [String] + nodes: N, type: [String], nice_type_names: [String] ) = #externalMacro(module: "Macros", type: "RequireNodesType") @freestanding(codeItem) public macro SkipUnlessNodeType(node: N, type: String) = diff --git a/Sources/P4Compiler/Expression.swift b/Sources/P4Compiler/Expression.swift index f03191e..9047e31 100644 --- a/Sources/P4Compiler/Expression.swift +++ b/Sources/P4Compiler/Expression.swift @@ -101,7 +101,7 @@ struct Expression { #RequireNodesType( nodes: node, type: ["expression", "keysetExpression"], - msg: ["expression", "keyset expression"]) + nice_type_names: ["expression", "keyset expression"]) // If the node is a keyset expression, then dig out the expression: let node = @@ -152,3 +152,97 @@ struct Identifier { } } } + +extension SelectExpression: CompilableExpression { + static func compile( + node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes + ) -> Result { + #RequireNodeType( + node: node, type: "selectExpression", nice_type_name: "parser select expression") + + guard let selector_node = node.child(at: 2), + selector_node.nodeType == "expression" + else { + return .Error(ErrorOnNode(node: node, withError: "Could not find selector expression")) + } + + guard let select_body_node = node.child(at: 5), + select_body_node.nodeType == "selectBody" + else { + return .Error(ErrorOnNode(node: node, withError: "Could not find select expression body")) + } + + let maybe_selector = Expression.Compile(node: selector_node, inTree: tree, withScopes: scopes) + guard case .Ok(let selector) = maybe_selector else { + return .Error( + Error( + withMessage: + "Could not parse transition select expression selector expression: \(maybe_selector.error()!)" + )) + } + + var kses: [KeysetExpression] = Array() + var kses_errors: [Error] = Array() + + select_body_node.enumerateNamedChildren() { current_node in + let maybe_parsed_kse = KeysetExpression.compile(node: current_node, inTree: tree, withScopes: scopes) + if case .Ok(let parsed_kse) = maybe_parsed_kse { + kses.append(parsed_kse as! KeysetExpression) + } else { + kses_errors.append(Error(withMessage: "\(maybe_parsed_kse.error()!)")) + } + } + + if !kses_errors.isEmpty { + return .Error( + Error( + withMessage: "Error(s) parsing select cases: " + + (kses_errors.map { error in + return "\(error.msg)" + }.joined(separator: ";\n")))) + } + return .Ok( + SelectExpression(withSelector: selector, withKeysetExpressions: kses), + ) + } +} + +extension KeysetExpression: CompilableExpression { + static func compile( + node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes + ) -> Result { + if node.nodeType != "selectCase" { + return Result.Error(Error(withMessage: "Expected select case not found")) + } + + guard let keysetexpression_node = node.child(at: 0), + keysetexpression_node.nodeType == "keysetExpression" + else { + return Result.Error(Error(withMessage: "Missing keyset expression in select case")) + } + + guard let targetstate_node = node.child(at: 2), + targetstate_node.nodeType == "identifier" + else { + return Result.Error(Error(withMessage: "Missing target state in select case")) + } + + let maybe_parsed_keysetexpression = Expression.Compile( + node: keysetexpression_node, inTree: tree, withScopes: scopes) + guard case Result.Ok(let keysetexpression) = maybe_parsed_keysetexpression else { + return Result.Error(maybe_parsed_keysetexpression.error()!) + } + + let maybe_parsed_targetstate = Identifier.Compile( + node: targetstate_node, inTree: tree, withScopes: scopes) + guard case .Ok(let targetstate) = maybe_parsed_targetstate else { + return Result.Error(maybe_parsed_targetstate.error()!) + } + + return .Ok( + KeysetExpression( + withKey: keysetexpression, withNextState: targetstate) + ) + } + } + diff --git a/Sources/P4Compiler/Parser.swift b/Sources/P4Compiler/Parser.swift index 34d5e7e..c1177ff 100644 --- a/Sources/P4Compiler/Parser.swift +++ b/Sources/P4Compiler/Parser.swift @@ -28,7 +28,7 @@ extension ParserAssignmentStatement: CompilableStatement { ) -> Result<(EvaluatableStatement, LexicalScopes)> { #RequireNodeType( - node: node, type: "assignmentStatement", msg: "assignment statement") + node: node, type: "assignmentStatement", nice_type_name: "assignment statement") guard let lvalue_node = node.child(at: 0), lvalue_node.nodeType == "expression" @@ -142,137 +142,26 @@ public struct Parser { } } - public struct TransitionSelectExpressionCaseStatement { - static func Compile( - node: Node, inTree tree: MutableTree, withLexicalScopes scopes: LexicalScopes - ) -> Result<(KeysetExpression, LexicalScopes)> { - if node.nodeType != "selectCase" { - return Result.Error(Error(withMessage: "Expected select case not found")) - } - - guard let keysetexpression_node = node.child(at: 0), - keysetexpression_node.nodeType == "keysetExpression" - else { - return Result.Error(Error(withMessage: "Missing keyset expression in select case")) - } - - guard let targetstate_node = node.child(at: 2), - targetstate_node.nodeType == "identifier" - else { - return Result.Error(Error(withMessage: "Missing target state in select case")) - } - - let maybe_parsed_keysetexpression = Expression.Compile( - node: keysetexpression_node, inTree: tree, withScopes: scopes) - guard case Result.Ok(let keysetexpression) = maybe_parsed_keysetexpression else { - return Result.Error(maybe_parsed_keysetexpression.error()!) - } - - let maybe_parsed_targetstate = Identifier.Compile( - node: targetstate_node, inTree: tree, withScopes: scopes) - guard case .Ok(let targetstate) = maybe_parsed_targetstate else { - return Result.Error(maybe_parsed_targetstate.error()!) - } - - return .Ok( - ( - KeysetExpression( - withKey: keysetexpression, withNextState: targetstate), scopes - )) - } - } - - public struct TransitionSelectExpression { - static func Compile( - node: Node, inTree tree: MutableTree, withScope scopes: LexicalScopes - ) -> Result<(ParserTransitionSelectExpression, LexicalScopes)> { - 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") - - if selector.isEmpty { - return .Error( - Error(withMessage: "Could not find transition select expression selector expression")) - } - let selector_node = selector[0].node - let maybe_selector = Expression.Compile(node: selector_node, inTree: tree, withScopes: scopes) - guard case .Ok(let selector) = maybe_selector else { - return .Error( - Error( - withMessage: - "Could not parse transition select expression selector expression: \(maybe_selector.error()!)" - )) - } - - if body.isEmpty { - return .Error(Error(withMessage: "Could not find transition select expression body")) - } - let body_node = body[0].node - var kses: [KeysetExpression] = Array() - var kses_errors: [Error] = Array() - - body_node.enumerateNamedChildren { current_node in - let maybe_parsed_kse = TransitionSelectExpressionCaseStatement.Compile( - node: current_node, inTree: tree, withLexicalScopes: scopes) - if case .Ok((let parsed_kse, _)) = maybe_parsed_kse { - kses.append(parsed_kse) - } else { - kses_errors.append(Error(withMessage: "\(maybe_parsed_kse.error()!)")) - } - } - - if !kses_errors.isEmpty { - return .Error( - Error( - withMessage: "Error(s) parsing select cases: " - + (kses_errors.map { error in - return "\(error.msg)" - }.joined(separator: ";\n")))) - } - - return .Ok( - ( - ParserTransitionSelectExpression(withSelector: selector, withKeysetExpressions: kses), - scopes - )) - } - } - public struct TransitionStatement { static func Compile( node: Node, inTree tree: MutableTree, withScope scopes: LexicalScopes ) -> Result<(ParserTransitionStatement, LexicalScopes)> { - 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")) + + #RequireNodeType(node: node, type: "parserTransitionStatement", nice_type_name: "parser transition statement") + + guard let tse_node = node.child(at: 1), + tse_node.nodeType! == "transitionSelectionExpression" else { + return .Error(ErrorOnNode(node: node, withError: "Could not find transition select expression")) } - let qr = next_state_query.execute(node: node, in: tree) + guard let next_node = tse_node.child(at: 0) else { + return .Error(ErrorOnNode(node: node, withError: "Could not find the next token in a transition selection expression")) + } - if let next_state_result = qr.next() { - let transition_capture = next_state_result.captures(named: "next-state") + // If the next node is an identifier, we have the simple form ... + if next_node.nodeType == "identifier" { let maybe_parsed_next_state = Identifier.Compile( - node: transition_capture[0].node, inTree: tree, withScopes: scopes) + node: next_node, inTree: tree, withScopes: scopes) if case .Ok(let next_state) = maybe_parsed_next_state { return .Ok( (ParserTransitionStatement(withNextState: next_state), scopes)) @@ -285,11 +174,12 @@ public struct Parser { } } + // We know that the next node is a select expression. return - switch TransitionSelectExpression.Compile(node: node, inTree: tree, withScope: scopes) + switch SelectExpression.compile(node: next_node, inTree: tree, withScopes: scopes) { - case .Ok((let tse, _)): - .Ok((ParserTransitionStatement(withTransitionExpression: tse), scopes)) + case .Ok(let tse): + .Ok((ParserTransitionStatement(withTransitionExpression: tse! as! SelectExpression), scopes)) case .Error(let e): .Error(e) } } diff --git a/Sources/P4Compiler/Statement.swift b/Sources/P4Compiler/Statement.swift index ffc2914..eae6c63 100644 --- a/Sources/P4Compiler/Statement.swift +++ b/Sources/P4Compiler/Statement.swift @@ -26,10 +26,7 @@ extension BlockStatement: CompilableStatement { public static func Compile( node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes ) -> Result<(EvaluatableStatement, LexicalScopes)> { - if node.nodeType != "blockStatement" { - return Result.Error( - ErrorOnNode(node: node, withError: "Did not find expected block statement")) - } + #RequireNodeType(node: node, type: "blockStatement", nice_type_name: "block statement") var currentChildIdx = 0 var currentChildIdxSafe = 1 @@ -94,12 +91,7 @@ extension ConditionalStatement: CompilableStatement { node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes ) -> Result<(EvaluatableStatement, LexicalScopes)> { - guard let node_type = node.nodeType, - node_type == "conditionalStatement" - else { - return Result.Error( - ErrorOnNode(node: node, withError: "Did not find expected conditional statement")) - } + #RequireNodeType(node: node, type: "conditionalStatement", nice_type_name: "conditional statement") let maybe_condition_expression = node.child(at: 2) guard let condition_expression = maybe_condition_expression, @@ -166,12 +158,7 @@ extension VariableDeclarationStatement: CompilableStatement { node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes ) -> Result<(EvaluatableStatement, LexicalScopes)> { - guard let node_type = node.nodeType, - node_type == "variableDeclaration" - else { - return Result.Error( - ErrorOnNode(node: node, withError: "Did not find expected variable declaration statement")) - } + #RequireNodeType(node: node, type: "variableDeclaration", nice_type_name: "variable declaration statement") let maybe_typeref = node.child(at: 0) guard let typeref = maybe_typeref, @@ -248,10 +235,7 @@ extension ExpressionStatement: CompilableStatement { public static func Compile( node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes ) -> Result<(EvaluatableStatement, LexicalScopes)> { - if node.nodeType != "expressionStatement" { - return Result.Error( - ErrorOnNode(node: node, withError: "Did not find expected expression statement")) - } + #RequireNodeType(node: node, type: "expressionStatement", nice_type_name: "expression statement") let _ = node.child(at: 0) diff --git a/Sources/P4Lang/Parser.swift b/Sources/P4Lang/Parser.swift index bab02df..f1f0b3d 100644 --- a/Sources/P4Lang/Parser.swift +++ b/Sources/P4Lang/Parser.swift @@ -56,7 +56,7 @@ public struct KeysetExpression { } -public struct ParserTransitionSelectExpression { +public struct SelectExpression { public let selector: EvaluatableExpression public let keyset_expressions: [KeysetExpression] @@ -67,24 +67,24 @@ public struct ParserTransitionSelectExpression { self.keyset_expressions = kses } - public func append_checked_kse(kse: KeysetExpression) -> ParserTransitionSelectExpression { + public func append_checked_kse(kse: KeysetExpression) -> SelectExpression { var new_kse = self.keyset_expressions new_kse.append(kse) - return ParserTransitionSelectExpression( + return SelectExpression( withSelector: self.selector, withKeysetExpressions: new_kse) } } public struct ParserTransitionStatement { public let next_state: Identifier? - public let transition_expression: ParserTransitionSelectExpression? + public let transition_expression: SelectExpression? public init() { self.next_state = .none self.transition_expression = .none } - public init(withTransitionExpression transition_expression: ParserTransitionSelectExpression) { + public init(withTransitionExpression transition_expression: SelectExpression) { self.next_state = .none self.transition_expression = transition_expression } @@ -163,6 +163,22 @@ public class ParserState: Equatable, CustomStringConvertible, Comparable { } } +/// A P4 parser state type +public struct P4ParserState: P4Type { + public static func create() -> any P4Type { + return P4ParserState() + } + public var description: String { + return "Parser State" + } + public func eq(rhs: any P4Type) -> Bool { + return switch rhs { + case is P4ParserState: true + default: false + } + } +} + nonisolated(unsafe) public let accept: ParserState = ParserState(name: Identifier(name: "accept")) nonisolated(unsafe) public let reject: ParserState = ParserState(name: Identifier(name: "reject")) diff --git a/Sources/P4Runtime/Expressions.swift b/Sources/P4Runtime/Expressions.swift new file mode 100644 index 0000000..060d5e6 --- /dev/null +++ b/Sources/P4Runtime/Expressions.swift @@ -0,0 +1,86 @@ +// p4rse, Copyright 2026, Will Hawkins +// +// This file is part of p4rse. +// +// This file is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import Common +import P4Lang + +extension KeysetExpression: EvaluatableExpression { + public func evaluate(execution: Common.ProgramExecution) -> Common.Result { + return execution.scopes.lookup(identifier: next_state_identifier) + } + + public func type() -> any Common.P4Type { + return P4ParserState.create() + } +} + +extension SelectExpression: EvaluatableExpression { + public func evaluate(execution: Common.ProgramExecution) -> Common.Result { + 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) + { + let result = kse.evaluate(execution: execution) + return result + } + } + return .Error(Error(withMessage: "No key matched the selector")) + case .Error(let e): return .Error(e) + } + } + + public func type() -> any Common.P4Type { + return P4ParserState.create() + } +} + +extension P4StringValue: EvaluatableExpression { + public func evaluate(execution: Common.ProgramExecution) -> Common.Result { + return .Ok(self) + } +} + +extension P4BooleanValue: EvaluatableExpression { + public func evaluate(execution: Common.ProgramExecution) -> Common.Result { + return .Ok(self) + } +} + +extension P4StructValue: EvaluatableExpression { + public func evaluate(execution: Common.ProgramExecution) -> Common.Result { + return .Ok(self) + } +} + +extension P4IntValue: EvaluatableExpression { + public func evaluate(execution: Common.ProgramExecution) -> Common.Result { + return .Ok(self) + } +} + +// Variables are evaluatable because they can be looked up by identifiers. +extension TypedIdentifier: EvaluatableExpression { + public func type() -> any Common.P4Type { + return self.parsed_type + } + + public func evaluate(execution: Common.ProgramExecution) -> Result { + return execution.scopes.lookup(identifier: self) + } +} \ No newline at end of file diff --git a/Sources/P4Runtime/Parser.swift b/Sources/P4Runtime/Parser.swift index a6329a9..13e70fc 100644 --- a/Sources/P4Runtime/Parser.swift +++ b/Sources/P4Runtime/Parser.swift @@ -20,8 +20,9 @@ import P4Lang extension ParserAssignmentStatement: EvaluatableStatement { public func evaluate(execution: ProgramExecution) -> ProgramExecution { - guard case Result.Ok(let value) = self.value.evaluate(execution: execution) else { - return execution.setError(error: Error(withMessage: "AHHHHH")) + let result = self.value.evaluate(execution: execution) + guard case Result.Ok(let value) = result else { + return execution.setError(error: result.error()!) } let updated_scopes = execution.scopes.set(identifier: self.lvalue, withValue: value) @@ -32,38 +33,82 @@ extension ParserAssignmentStatement: EvaluatableStatement { } public struct ParserStateDirectTransition: ParserStateInstance { + + public func type() -> any Common.P4Type { + return P4ParserState.create() + } + + public func eq(rhs: any Common.P4Value) -> Bool { + switch rhs { + case let state as ParserStateInstance: return currrent_state == state.state() + default: return false + } + } + + public var description: String { + get { + return "Instance of \(currrent_state)" + } + } + public let currrent_state: ParserState - public let next_state: ParserStateInstance + public let next_state_identifier: Identifier public func execute( program: Common.ProgramExecution ) -> (any ParserStateInstance, Common.ProgramExecution) { - var program = program.exit_scope() - program = program.enter_scope() + var program = program.enter_scope() for statement in currrent_state.statements { program = statement.evaluate(execution: program) } + let res = program.scopes.lookup(identifier: next_state_identifier) - return (self.next_state, program) + if case .Ok(let value) = res { + if value.type().eq(rhs: P4ParserState.create()) { + return (value as! ParserStateInstance, program.exit_scope()) + } + } + + program = program.setError(error: res.error()!).exit_scope() + + return (self, program.exit_scope()) } public func done() -> Bool { return false } - public func current() -> P4Lang.ParserState { + public func state() -> P4Lang.ParserState { return currrent_state } } public struct ParserStateNoTransition: ParserStateInstance { + + public func type() -> any Common.P4Type { + return P4ParserState.create() + } + + public func eq(rhs: any Common.P4Value) -> Bool { + switch rhs { + case let state as ParserStateInstance: return currrent_state == state.state() + default: return false + } + } + + public var description: String { + get { + return "Instance of \(currrent_state)" + } + } + public let currrent_state: ParserState + public func execute( program: Common.ProgramExecution ) -> (any ParserStateInstance, Common.ProgramExecution) { - let program = program.exit_scope() return (self, program) } @@ -71,90 +116,83 @@ public struct ParserStateNoTransition: ParserStateInstance { return true } - public func current() -> P4Lang.ParserState { + public func state() -> P4Lang.ParserState { return currrent_state } } public struct ParserStateSelectTransition: ParserStateInstance { + public func type() -> any Common.P4Type { + return P4ParserState.create() + } + + public func eq(rhs: any Common.P4Value) -> Bool { + switch rhs { + case let state as ParserStateInstance: return currrent_state == state.state() + default: return false + } + } + + public var description: String { + get { + return "Instance of \(currrent_state)" + } + } + 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() + var program = program.enter_scope() // First, evaluate the statements. for statement in currrent_state.statements { program = statement.evaluate(execution: program) } - switch self.selector.evaluate(execution: program) { - case .Ok(let selector_value): - 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 (target, program) - } + let res = self.selectExpression.evaluate(execution: program) + + if case .Ok(let value) = res { + if value.type().eq(rhs: P4ParserState.create()) { + return (value as! ParserStateInstance, program.exit_scope()) } - return ( - self, program.setError(error: Error(withMessage: "No selector key matched")).setDone() - ) - case .Error(let e): return (self, program.setError(error: e)) } + + program = program.setError(error: res.error()!).exit_scope() + return (self, program.exit_scope()) } public func done() -> Bool { return false } - public func current() -> P4Lang.ParserState { + public func state() -> P4Lang.ParserState { return currrent_state } - public let keys: [any EvaluatableExpression] - public let states: [ParserStateInstance] - public let selector: any EvaluatableExpression + public let selectExpression: SelectExpression public let currrent_state: ParserState } extension ParserState: Compilable { - public typealias ToCompile = (ParserState, [Identifier: ParserStateInstance]) + public typealias ToCompile = ParserState public typealias Compiled = ParserStateInstance - public static func compile(_ parser: ToCompile) -> Result { - let (state, current) = parser - if state == accept || state == reject { - return .Ok(ParserStateNoTransition(currrent_state: state)) - } + public static func compile(_ state: ToCompile) -> Result { if state.direct_transition(), let transition_statement = state.transition { return .Ok( ParserStateDirectTransition( - currrent_state: state, next_state: current[transition_statement.next_state!]!)) + currrent_state: state, next_state_identifier: transition_statement.next_state!)) } if let transition_select_statement = state.transition, let transition_select_expression = transition_select_statement.transition_expression { - var keys: [any EvaluatableExpression] = Array() - var states: [any ParserStateInstance] = Array() - - for kse in transition_select_expression.keyset_expressions { - guard let next_state = current[kse.next_state_identifier] else { - return .Error( - Error(withMessage: "Cannot find \(kse.next_state_identifier) 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)) + selectExpression: transition_select_expression, currrent_state: state)) } return .Error(Error(withMessage: "Invalid parser state: No meaningful transition")) @@ -163,24 +201,30 @@ extension ParserState: Compilable { extension ParserStates: Compilable { public typealias ToCompile = ParserStates - public typealias Compiled = ParserStateInstance + public typealias Compiled = (ParserStateInstance, [ParserStateInstance]) public static func compile(_ parser: ToCompile) -> Result { - var compiled_states = [Identifier: ParserStateInstance]() + var compiled_states:[ParserStateInstance] = Array() - compiled_states[Identifier(name: "accept")] = ParserStateNoTransition(currrent_state: accept) - compiled_states[Identifier(name: "reject")] = ParserStateNoTransition(currrent_state: reject) + compiled_states.append(ParserStateNoTransition(currrent_state: accept)) + compiled_states.append(ParserStateNoTransition(currrent_state: reject)) + + var start_state: ParserStateInstance? = .none // 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] = compiled + switch ParserState.compile(state) { + case .Ok(let compiled): + if compiled.state().state == Identifier(name: "start") { + start_state = compiled + } + compiled_states.append(compiled) case .Error(let e): return .Error(e) } } // Now, find the start state: - if let start_state = compiled_states[Identifier(name: "start")] { - return .Ok(start_state) + if let start_state = start_state { + return .Ok((start_state, compiled_states)) } else { return .Error(Error(withMessage: "No start state defined")) } @@ -189,14 +233,16 @@ extension ParserStates: Compilable { public class ParserInstance: ProgramExecution { + let states: [ParserStateInstance] let start_state: ParserStateInstance - public init(_ _start_state: ParserStateInstance) { - self.start_state = _start_state + public init(start: ParserStateInstance, states: [ParserStateInstance]) { + start_state = start + self.states = states } public override var description: String { - return "Execution: \(super.description)\nStart State: \(start_state)" + return "Execution: \(super.description)\nStates: \(states)" } } @@ -206,7 +252,7 @@ extension ParserInstance: Compilable { public static func compile(_ parser: ToCompile) -> Result { return switch ParserStates.compile(parser.states) { - case .Ok(let start_state): Result.Ok(ParserInstance(start_state)) + case .Ok(let (start_state, states)): Result.Ok(ParserInstance(start: start_state, states: states)) case .Error(let e): Result.Error(e) } } diff --git a/Sources/P4Runtime/Program.swift b/Sources/P4Runtime/Program.swift index aa7e78d..af0d28a 100644 --- a/Sources/P4Runtime/Program.swift +++ b/Sources/P4Runtime/Program.swift @@ -66,15 +66,4 @@ extension ExpressionStatement: EvaluatableStatement { public func evaluate(execution: ProgramExecution) -> ProgramExecution { return execution } -} - -// Variables are evaluatable because they can be looked up by identifiers. -extension TypedIdentifier: EvaluatableExpression { - public func type() -> any Common.P4Type { - return self.parsed_type - } - - public func evaluate(execution: Common.ProgramExecution) -> Result { - return execution.scopes.lookup(identifier: self) - } -} +} \ No newline at end of file diff --git a/Sources/P4Runtime/Protocols.swift b/Sources/P4Runtime/Protocols.swift index 742d4e9..5a0866d 100644 --- a/Sources/P4Runtime/Protocols.swift +++ b/Sources/P4Runtime/Protocols.swift @@ -18,14 +18,6 @@ import Common import P4Lang -protocol EvaluatableParserTransition { - func program(execution: ProgramExecution) -> (ParserState, ProgramExecution) -} - -protocol EvaluatableParserTransitionStatement { - func transition(execution: ProgramExecution) -> (ParserState, ProgramExecution) -} - public protocol Execution { func execute() -> (ParserState, ProgramExecution) } @@ -36,10 +28,10 @@ public protocol Compilable { static func compile(_: ToCompile) -> Result } -public protocol ParserStateInstance { +public protocol ParserStateInstance: P4Value { func execute(program: ProgramExecution) -> (ParserStateInstance, ProgramExecution) func done() -> Bool - func current() -> ParserState + func state() -> ParserState } public protocol ParserExecution { diff --git a/Sources/P4Runtime/Runtime.swift b/Sources/P4Runtime/Runtime.swift index d4837dc..0429507 100644 --- a/Sources/P4Runtime/Runtime.swift +++ b/Sources/P4Runtime/Runtime.swift @@ -41,7 +41,11 @@ public class ParserRuntime: CustomStringConvertible { /// Run the P4 parser on a given packet public func run() -> Result<(ParserState, ProgramExecution)> { - return .Ok(parser.execute()) + let (end_state, execution) = parser.execute() + if let error = execution.getError() { + return .Error(error) + } + return .Ok((end_state, execution)) } public var description: String { @@ -53,12 +57,19 @@ public class ParserRuntime: CustomStringConvertible { extension ParserInstance: ParserExecution { public func execute() -> (ParserState, ProgramExecution) { var execution = self as ProgramExecution + + execution = execution.enter_scope() + // First, add every state to the scope! + for state in self.states { + execution = execution.declare(identifier: state.state().state, withValue: state) + } + var c = self.start_state // Evaluate until the state is either accept or reject. while !c.done() && !execution.hasError() { (c, execution) = c.execute(program: execution) } - return (c.current(), execution) + return (c.state(), execution) } } diff --git a/Tests/p4rseTests/RuntimeTests.swift b/Tests/p4rseTests/RuntimeTests.swift index 4e65cd7..3c5ff68 100644 --- a/Tests/p4rseTests/RuntimeTests.swift +++ b/Tests/p4rseTests/RuntimeTests.swift @@ -142,17 +142,10 @@ import TreeSitterP4 """ let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) - let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser"))) let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) - let (state_result, exec_result) = try! #UseOkResult(runtime.run()) + let (state_result, _) = try! #UseOkResult(runtime.run()) - #expect(parser.states.count() == 1) #expect(state_result == P4Lang.reject) - - let x = try #UseOkResult(exec_result.scopes.lookup(identifier: Identifier(name: "x"))) - #expect(x.eq(rhs: P4BooleanValue(withValue: false))) - let check = try #UseOkResult(exec_result.scopes.lookup(identifier: Identifier(name: "check"))) - #expect(check.eq(rhs: P4StringValue(withValue: "\"valid\""))) } @Test func test_simple_parser_with_conditional_statement_and_else() async throws { @@ -177,15 +170,29 @@ import TreeSitterP4 """ let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) - let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser"))) let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) - let (state_result, exec_result) = try! #UseOkResult(runtime.run()) + let (state_result, _) = try! #UseOkResult(runtime.run()) - #expect(parser.states.count() == 1) #expect(state_result == P4Lang.accept) - let x = try #UseOkResult(exec_result.scopes.lookup(identifier: Identifier(name: "x"))) - #expect(x.eq(rhs: P4BooleanValue(withValue: true))) - let check = try #UseOkResult(exec_result.scopes.lookup(identifier: Identifier(name: "check"))) - #expect(check.eq(rhs: P4StringValue(withValue: "\"b\""))) +} + +@Test func test_no_matching_key_transition() async throws { + let simple_parser_declaration = """ + parser main_parser() { + state start { + transition select (false) { + true: accept; + }; + } + }; + """ + + let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) + let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) + + #expect( + #RequireErrorResult<(ParserState, ProgramExecution)>( + Error(withMessage: "No key matched the selector"), + runtime.run())) } diff --git a/Tests/p4rseTests/ScopeRuntimeTests.swift b/Tests/p4rseTests/ScopeRuntimeTests.swift index 9d48143..61c2a95 100644 --- a/Tests/p4rseTests/ScopeRuntimeTests.swift +++ b/Tests/p4rseTests/ScopeRuntimeTests.swift @@ -44,25 +44,9 @@ import TreeSitterP4 let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) - let (state_result, execution_result) = try! #UseOkResult(runtime.run()) + let (state_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 == P4Lang.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.eq(rhs: P4BooleanValue(withValue: false))) - #expect(s.eq(rhs: P4StringValue(withValue: "\"testing\""))) } @Test func test_simple_scope() async throws { @@ -86,17 +70,10 @@ import TreeSitterP4 let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) - let (state_result, execution_result) = try! #UseOkResult(runtime.run()) + let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(state_result == P4Lang.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.eq(rhs: P4BooleanValue(withValue: false))) - #expect(va.eq(rhs: P4IntValue(withValue: 5))) } @Test func test_simple_scope2() async throws { @@ -120,20 +97,10 @@ import TreeSitterP4 let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) - let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser"))) let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) - let (state_result, execution_result) = try! #UseOkResult(runtime.run()) - - #expect(parser.states.count() == 2) + let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(state_result == P4Lang.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.eq(rhs: P4BooleanValue(withValue: false))) } @Test func test_simple_assignment() async throws { @@ -144,7 +111,7 @@ import TreeSitterP4 string where_from = "here"; where_to = false; where_from = "there"; - transition select (true) { + transition select (where_to) { false: reject; true: accept; }; @@ -154,22 +121,11 @@ import TreeSitterP4 let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) - let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser"))) let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) - let (state_result, execution_result) = try! #UseOkResult(runtime.run()) + let (state_result, _) = try! #UseOkResult(runtime.run()) - #expect(parser.states.count() == 1) + #expect(state_result == P4Lang.reject) - #expect(state_result == P4Lang.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.eq(rhs: P4BooleanValue(withValue: false))) - let where_from = try #require(scope.lookup(identifier: Identifier(name: "where_from"))) - #expect(where_from.eq(rhs: P4StringValue(withValue: "\"there\""))) } @Test func test_nested_declaration_assignment() async throws { @@ -196,20 +152,8 @@ import TreeSitterP4 let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) - let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser"))) let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) - let (state_result, execution_result) = try! #UseOkResult(runtime.run()) - - #expect(parser.states.count() == 1) + let (state_result, _) = try! #UseOkResult(runtime.run()) #expect(state_result == P4Lang.reject) - - #expect(execution_result.scopes.count == 1) - let scope = try! #require(execution_result.scopes.current) - - #expect(scope.count == 3) - let where_to = try #require(scope.lookup(identifier: Identifier(name: "where_to"))) - #expect(where_to.eq(rhs: P4BooleanValue(withValue: false))) - let where_from = try #require(scope.lookup(identifier: Identifier(name: "where_from"))) - #expect(where_from.eq(rhs: P4StringValue(withValue: "\"there\""))) } \ No newline at end of file