From 2c5bfc3e1cd1580d1fa187da8a8c3b242d2ac891 Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Fri, 27 Feb 2026 06:16:14 -0500 Subject: [PATCH] Start Moving Away From Queries Signed-off-by: Will Hawkins --- Sources/P4Lang/Parser.swift | 50 ++++---- Sources/P4Parser/Expression.swift | 26 +++- Sources/P4Parser/Parser.swift | 184 +++++++++++++++++------------ Sources/P4Parser/Program.swift | 2 +- Sources/P4Parser/Statement.swift | 4 +- Sources/P4Runtime/Parser.swift | 18 +-- Tests/p4rseTests/ParserTests.swift | 4 +- 7 files changed, 171 insertions(+), 117 deletions(-) diff --git a/Sources/P4Lang/Parser.swift b/Sources/P4Lang/Parser.swift index 2717c8a..d9abed0 100644 --- a/Sources/P4Lang/Parser.swift +++ b/Sources/P4Lang/Parser.swift @@ -37,20 +37,20 @@ public struct ParserAssignmentStatement { public struct KeysetExpression { public let key: EvaluatableExpression - public let next_state_name: String + public let next_state_identifier: Identifier public let next_state: ParserState? - public init(withKey key: EvaluatableExpression, withNextStateName next_state_name: String) { + public init(withKey key: EvaluatableExpression, withNextState next_state_id: Identifier) { self.key = key - self.next_state_name = next_state_name + self.next_state_identifier = next_state_id self.next_state = .none } public init( - withKey key: EvaluatableExpression, withNextStateName next_state_name: String, + withKey key: EvaluatableExpression, withNextState next_state_id: Identifier, withNextState next_state: ParserState ) { self.key = key - self.next_state_name = next_state_name + self.next_state_identifier = next_state_id self.next_state = next_state } @@ -76,28 +76,28 @@ public struct ParserTransitionSelectExpression { } public struct ParserTransitionStatement { - public let next_state_name: String? + public let next_state: Identifier? public let transition_expression: ParserTransitionSelectExpression? public init() { - self.next_state_name = .none + self.next_state = .none self.transition_expression = .none } public init(withTransitionExpression transition_expression: ParserTransitionSelectExpression) { - self.next_state_name = .none + self.next_state = .none self.transition_expression = transition_expression } - public init(withNextState next_state_name: String) { - self.next_state_name = next_state_name + public init(withNextState next_state: Identifier) { + self.next_state = next_state self.transition_expression = .none } } public class ParserState: Equatable, CustomStringConvertible, Comparable { - public private(set) var state_name: String + public private(set) var state: Identifier public private(set) var local_elements: [EvaluatableStatement] public private(set) var statements: [EvaluatableStatement] public private(set) var transition: ParserTransitionStatement? @@ -111,20 +111,20 @@ public class ParserState: Equatable, CustomStringConvertible, Comparable { } public var description: String { - return "Name: \(state_name)" + return "Name: \(state)" } public static func == (lhs: ParserState, rhs: ParserState) -> Bool { - return lhs.state_name == rhs.state_name + return lhs.state == rhs.state } /// Construct a ParserState public init( - name: String, withLocalElements localElements: [EvaluatableStatement]?, + name: Identifier, withLocalElements localElements: [EvaluatableStatement]?, withStatements stmts: [EvaluatableStatement]?, withTransition transitionStatement: ParserTransitionStatement ) { - state_name = name + state = name transition = transitionStatement local_elements = localElements ?? Array() statements = stmts ?? Array() @@ -135,8 +135,8 @@ public class ParserState: Equatable, CustomStringConvertible, Comparable { return self == accept || self == reject } - if let next_state_name = transition.next_state_name, - let next_state = states.find(withName: next_state_name) + if let next_state = transition.next_state, + let next_state = states.find(withIdentifier: next_state) { self.next_state = next_state return true @@ -148,8 +148,8 @@ public class ParserState: Equatable, CustomStringConvertible, Comparable { /// (private) constructor (no transition) /// /// accept and reject are the only final states and they are constructed internally. - init(name: String) { - state_name = name + init(name: Identifier) { + state = name transition = .none local_elements = Array() statements = Array() @@ -158,7 +158,7 @@ public class ParserState: Equatable, CustomStringConvertible, Comparable { public func direct_transition() -> Bool { return if let transition = self.transition, - transition.next_state_name != nil + transition.next_state != nil { true } else { @@ -167,8 +167,8 @@ public class ParserState: Equatable, CustomStringConvertible, Comparable { } } -nonisolated(unsafe) public let accept: ParserState = ParserState(name: "accept") -nonisolated(unsafe) public let reject: ParserState = ParserState(name: "reject") +nonisolated(unsafe) public let accept: ParserState = ParserState(name: Identifier(name: "accept")) +nonisolated(unsafe) public let reject: ParserState = ParserState(name: Identifier(name: "reject")) public struct ParserStates { public var states: [ParserState] = Array() @@ -177,9 +177,9 @@ public struct ParserStates { return states.count } - public func find(withName name: String) -> ParserState? { + public func find(withIdentifier id: Identifier) -> ParserState? { for state in states { - if state.state_name == name { + if state.state == id { return .some(state) } } @@ -233,7 +233,7 @@ public struct Parser: P4Type { public func findStartState() -> ParserState? { for state in states.states { - if state.state_name == "start" { + if state.state == Identifier(name:"start") { return state } } diff --git a/Sources/P4Parser/Expression.swift b/Sources/P4Parser/Expression.swift index ca5808d..d5f3ae2 100644 --- a/Sources/P4Parser/Expression.swift +++ b/Sources/P4Parser/Expression.swift @@ -51,7 +51,7 @@ extension TypedIdentifier: ParseableEvaluatableExpression { let value_capture = result.captures(named: "identifier") guard case Result.Ok(let type) = scopes.lookup( - identifier: Identifier(name: value_capture[0].node.text!)) + identifier: Common.Identifier(name: value_capture[0].node.text!)) else { return .Error(Error(withMessage: "Cannot find \(result.captures[0].node.text!) in scope")) } @@ -178,3 +178,27 @@ struct Expression { return Result.Error(Error(withMessage: "Could not parse into expression.")) } } + +struct LValue { + public static func Parse( + node: Node, inTree: MutableTree, withScopes scopes: LexicalScopes + ) -> Result { + return if let node_text_value = node.text { + .Ok(Common.Identifier(name: node_text_value)) + } else { + .Error(Error(withMessage: "Could not parse an identifier for an LValue")) + } + } +} + +struct Identifier { + public static func Parse( + node: Node, inTree: MutableTree, withScopes scopes: LexicalScopes + ) -> Result { + return if let node_text_value = node.text { + .Ok(Common.Identifier(name: node_text_value)) + } else { + .Error(Error(withMessage: "Could not parse an identifier from \(node)")) + } + } +} diff --git a/Sources/P4Parser/Parser.swift b/Sources/P4Parser/Parser.swift index 6802658..ce727dd 100644 --- a/Sources/P4Parser/Parser.swift +++ b/Sources/P4Parser/Parser.swift @@ -28,40 +28,21 @@ extension ParserAssignmentStatement: ParseableStatement { public static func Parse( node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes ) -> Result<(EvaluatableStatement?, LexicalScopes)> { - guard - let parser_assignment_statement_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(assignmentStatement (expression) @lvalue (assignment) (expression) @value) @assignment" - ).data(using: String.Encoding.utf8)!) + + if node.nodeType != "assignmentStatement" { + return Result.Ok((.none, scopes)) + } + + guard let lvalue_node = node.child(at: 0), + lvalue_node.nodeType == "expression" else { - return Result.Ok((.none, scopes)) + return Result.Error(Error(withMessage: "Missing lvalue in assignment statement")) } - let qr = parser_assignment_statement_query.execute(node: node, in: tree) - guard let parser_assignment_statement = qr.next() else { - return Result.Ok((.none, scopes)) - } - - let assignment_capture = parser_assignment_statement.captures(named: "assignment") - let lvalue_capture = parser_assignment_statement.captures(named: "lvalue") - let rvalue_capture = parser_assignment_statement.captures(named: "value") - - // There must be a type name and a variable name - guard !lvalue_capture.isEmpty, - !rvalue_capture.isEmpty, - let lvalue_expression_raw = lvalue_capture[0].node.text + guard let rvalue_node = node.child(at: 2), + rvalue_node.nodeType == "expression" else { - return Result.Error( - Error(withMessage: "Could not parse a parser assignment statement")) - } - - let rvalue_node = rvalue_capture[0].node - let lvalue_node = lvalue_capture[0].node - let assignment_node = assignment_capture[0].node - - if assignment_node.parent != node.parent { - return Result.Ok((.none, scopes)) + return Result.Error(Error(withMessage: "Missing lvalue in assignment statement")) } let maybe_parsed_rvalue = Expression.Parse(node: rvalue_node, inTree: tree, withScopes: scopes) @@ -69,7 +50,10 @@ extension ParserAssignmentStatement: ParseableStatement { return Result.Error(maybe_parsed_rvalue.error()!) } - let lvalue_identifier = Identifier(name: lvalue_expression_raw) + let maybe_parsed_lvalue = LValue.Parse(node: lvalue_node, inTree: tree, withScopes: scopes) + guard case .Ok(let lvalue_identifier) = maybe_parsed_lvalue else { + return Result.Error(maybe_parsed_lvalue.error()!) + } guard case Result.Ok(let lvalue_type) = scopes.lookup(identifier: lvalue_identifier) else { return Result.Error( Error(withMessage: "Cannot assign to variable \(lvalue_identifier) not in scope")) @@ -149,44 +133,47 @@ public struct Parser { public struct TransitionSelectExpressionCaseStatement { static func Parse( node: Node, inTree tree: MutableTree, withLexicalScopes scopes: LexicalScopes - ) -> Result<([KeysetExpression], LexicalScopes)> { - 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)!) + ) -> 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: "Could not compile the tree sitter query")) + return Result.Error(Error(withMessage: "Missing keyset expression in select case")) } - 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, withScopes: scopes - ) - .map(block: { expression in - kses.append( - KeysetExpression( - withKey: expression, withNextStateName: next_state_name)) - return .Ok(expression) - }) { - return .Error(e) - } + guard let targetstate_node = node.child(at: 2), + targetstate_node.nodeType == "identifier" + else { + return Result.Error(Error(withMessage: "Missing target state in select case")) } - return .Ok((kses, scopes)) + let maybe_parsed_keysetexpression = Expression.Parse( + 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.Parse( + 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 Parse( node: Node, inTree tree: MutableTree, withScope scopes: LexicalScopes - ) -> Result<(ParserTransitionStatement?, LexicalScopes)> { + ) -> Result<(ParserTransitionSelectExpression, LexicalScopes)> { guard let transition_selection_expression_query = try? SwiftTreeSitter.Query( language: p4lang, @@ -206,22 +193,50 @@ public struct Parser { let selector = query_result.captures(named: "selector") let body = query_result.captures(named: "body") - return Expression.Parse(node: selector[0].node, inTree: tree, withScopes: scopes).map { - expression in - return - switch TransitionSelectExpressionCaseStatement.Parse( - node: body[0].node, inTree: tree, withLexicalScopes: scopes) - { - case .Ok((let kse, let newLexicalScopes)): - Result<(ParserTransitionStatement?, LexicalScopes)>.Ok( - ( - ParserTransitionStatement( - withTransitionExpression: ParserTransitionSelectExpression( - withSelector: expression, withKeysetExpressions: kse)), newLexicalScopes - )) - case .Error(let e): Result.Error(e) + 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.Parse(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() + for childidx in 0.. Result<(P4Lang.Parser, LexicalScopes)> { guard diff --git a/Sources/P4Parser/Program.swift b/Sources/P4Parser/Program.swift index 15efbaa..ac1e083 100644 --- a/Sources/P4Parser/Program.swift +++ b/Sources/P4Parser/Program.swift @@ -60,7 +60,7 @@ public struct Program { for parser_declaration in parser_qc { switch Parser.Parse( - withName: Identifier(name: parser_declaration.nodes[0].text!), + withName: Common.Identifier(name: parser_declaration.nodes[0].text!), node: parser_declaration.nodes[1], inTree: tree, withLexicalScopes: program_scope) { case Result.Ok((let parser, let new_program_scope)): diff --git a/Sources/P4Parser/Statement.swift b/Sources/P4Parser/Statement.swift index 5902dfc..eed5f7c 100644 --- a/Sources/P4Parser/Statement.swift +++ b/Sources/P4Parser/Statement.swift @@ -207,9 +207,9 @@ extension VariableDeclarationStatement: ParseableStatement { return Result.Ok( ( VariableDeclarationStatement( - identifier: Identifier(name: variable_name), withInitializer: rvalue), + identifier: Common.Identifier(name: variable_name), withInitializer: rvalue), scopes.declare( - identifier: Identifier(name: variable_name), withValue: declaration_p4_type) + identifier: Common.Identifier(name: variable_name), withValue: declaration_p4_type) )) } else { diff --git a/Sources/P4Runtime/Parser.swift b/Sources/P4Runtime/Parser.swift index 12bc148..4d79daf 100644 --- a/Sources/P4Runtime/Parser.swift +++ b/Sources/P4Runtime/Parser.swift @@ -129,7 +129,7 @@ public struct ParserStateSelectTransition: ParserStateInstance { } extension ParserState: Compilable { - public typealias ToCompile = (ParserState, [String: ParserStateInstance]) + public typealias ToCompile = (ParserState, [Identifier: ParserStateInstance]) public typealias Compiled = ParserStateInstance public static func compile(_ parser: ToCompile) -> Result { let (state, current) = parser @@ -142,7 +142,7 @@ extension ParserState: Compilable { { return .Ok( ParserStateDirectTransition( - currrent_state: state, next_state: current[transition_statement.next_state_name!]!)) + currrent_state: state, next_state: current[transition_statement.next_state!]!)) } if let transition_select_statement = state.transition, @@ -153,9 +153,9 @@ extension ParserState: Compilable { var states: [any ParserStateInstance] = Array() for kse in transition_select_expression.keyset_expressions { - guard let next_state = current[kse.next_state_name] else { + guard let next_state = current[kse.next_state_identifier] else { return .Error( - Error(withMessage: "Cannot find \(kse.next_state_name) as transition target")) + Error(withMessage: "Cannot find \(kse.next_state_identifier) as transition target")) } keys.append(kse.key) states.append(next_state) @@ -174,21 +174,21 @@ extension ParserStates: Compilable { public typealias ToCompile = ParserStates public typealias Compiled = ParserStateInstance public static func compile(_ parser: ToCompile) -> Result { - var compiled_states = [String: ParserStateInstance]() + var compiled_states = [Identifier: ParserStateInstance]() - compiled_states["accept"] = ParserStateNoTransition(currrent_state: accept) - compiled_states["reject"] = ParserStateNoTransition(currrent_state: reject) + compiled_states[Identifier(name: "accept")] = ParserStateNoTransition(currrent_state: accept) + compiled_states[Identifier(name: "reject")] = ParserStateNoTransition(currrent_state: reject) // TODO: We assume that states are in transition-order! for state in parser.states { switch ParserState.compile((state, compiled_states)) { - case .Ok(let compiled): compiled_states[state.state_name] = compiled + case .Ok(let compiled): compiled_states[state.state] = compiled case .Error(let e): return .Error(e) } } // Now, find the start state: - if let start_state = compiled_states["start"] { + if let start_state = compiled_states[Identifier(name: "start")] { return .Ok(start_state) } else { return .Error(Error(withMessage: "No start state defined")) diff --git a/Tests/p4rseTests/ParserTests.swift b/Tests/p4rseTests/ParserTests.swift index 52e04c6..828ba26 100644 --- a/Tests/p4rseTests/ParserTests.swift +++ b/Tests/p4rseTests/ParserTests.swift @@ -55,8 +55,8 @@ import TreeSitterP4 #expect(parser.states.count() == 1) - let state = try! #require(parser.states.find(withName: "start")) - #expect(state.state_name == "start") + let state = try! #require(parser.states.find(withIdentifier: Identifier(name: "start"))) + #expect(state.state == Identifier(name: "start")) #expect(state.statements.count == 1) }