From 6dba17c97a240287bfc283458d71d8ae875046c0 Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Mon, 2 Mar 2026 08:25:29 -0500 Subject: [PATCH] Refactor Parsing -> Compiling Signed-off-by: Will Hawkins --- Package.swift | 10 +- Sources/Common/Scope.swift | 6 +- .../{P4Parser => P4Compiler}/Expression.swift | 30 +- .../LexicalScopes.swift | 0 Sources/{P4Parser => P4Compiler}/Parser.swift | 303 ++++++++++-------- .../{P4Parser => P4Compiler}/Program.swift | 4 +- .../{P4Parser => P4Compiler}/Protocols.swift | 14 +- Sources/P4Compiler/Statement.swift | 253 +++++++++++++++ Sources/{P4Parser => P4Compiler}/Types.swift | 18 +- Sources/P4Lang/Parser.swift | 6 +- Sources/P4Parser/Statement.swift | 253 --------------- Sources/P4Runtime/Parser.swift | 11 +- Sources/P4Runtime/Program.swift | 6 +- Tests/p4rseTests/ParserTests.swift | 10 +- Tests/p4rseTests/RuntimeTests.swift | 16 +- Tests/p4rseTests/ScopeRuntimeTests.swift | 53 ++- Tests/p4rseTests/ScopeTests.swift | 2 +- Tests/p4rseTests/SupportTests.swift | 2 +- Tests/p4rseTests/ValueTypeParserTests.swift | 20 +- tree-sitter-p4/grammar.js | 6 +- tree-sitter-p4/test.txt | 16 +- 21 files changed, 550 insertions(+), 489 deletions(-) rename Sources/{P4Parser => P4Compiler}/Expression.swift (91%) rename Sources/{P4Parser => P4Compiler}/LexicalScopes.swift (100%) rename Sources/{P4Parser => P4Compiler}/Parser.swift (60%) rename Sources/{P4Parser => P4Compiler}/Program.swift (95%) rename Sources/{P4Parser => P4Compiler}/Protocols.swift (74%) create mode 100644 Sources/P4Compiler/Statement.swift rename Sources/{P4Parser => P4Compiler}/Types.swift (69%) delete mode 100644 Sources/P4Parser/Statement.swift diff --git a/Package.swift b/Package.swift index 25dca9b..b957fd9 100644 --- a/Package.swift +++ b/Package.swift @@ -10,8 +10,8 @@ let package = Package( products: [ // Products define the executables and libraries a package produces, making them visible to other packages. .library( - name: "P4Parser", - targets: ["P4Parser"] + name: "P4Compiler", + targets: ["P4Compiler"] ), .library( name: "Common", @@ -40,7 +40,7 @@ let package = Package( .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), ]), .target( - name: "P4Parser", + name: "P4Compiler", dependencies: [ .product(name: "SwiftTreeSitter", package: "swift-tree-sitter"), .product(name: "SwiftTreeSitterLayer", package: "swift-tree-sitter"), @@ -71,8 +71,8 @@ let package = Package( dependencies: ["P4Lang", "Common"] ), .testTarget( - name: "ParserTests", - dependencies: ["P4Parser", "P4Runtime", "P4Lang", "Macros", "TreeSitterExtensions", "Common"] + name: "Tests", + dependencies: ["P4Compiler", "P4Runtime", "P4Lang", "Macros", "TreeSitterExtensions", "Common"] ), ] ) diff --git a/Sources/Common/Scope.swift b/Sources/Common/Scope.swift index fd2ec4e..ce2e999 100644 --- a/Sources/Common/Scope.swift +++ b/Sources/Common/Scope.swift @@ -69,8 +69,10 @@ public struct Scopes: CustomStringConvertible { public var description: String { var result = String() + var counter = 0 for s in scopes { - result += "LexicalScope:\n\(s)\n" + result += "Scope #\(counter):\n\(s)\n" + counter += 1; } return result @@ -107,7 +109,7 @@ public struct Scopes: CustomStringConvertible { } public func lookup(identifier: Identifier) -> Result { - for scope in scopes { + for scope in scopes.reversed() { if let vari = scope.lookup(identifier: identifier) { return .Ok(vari) } diff --git a/Sources/P4Parser/Expression.swift b/Sources/P4Compiler/Expression.swift similarity index 91% rename from Sources/P4Parser/Expression.swift rename to Sources/P4Compiler/Expression.swift index f22d1dc..fc1512e 100644 --- a/Sources/P4Parser/Expression.swift +++ b/Sources/P4Compiler/Expression.swift @@ -20,14 +20,14 @@ import P4Lang import SwiftTreeSitter import TreeSitterP4 -protocol ParseableEvaluatableExpression { - static func parse( +protocol CompilableExpression { + static func compile( node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes ) -> Result } -extension TypedIdentifier: ParseableEvaluatableExpression { - static func parse( +extension TypedIdentifier: CompilableExpression { + static func compile( node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree, withScopes scopes: LexicalScopes ) -> Result { @@ -60,8 +60,8 @@ extension TypedIdentifier: ParseableEvaluatableExpression { } } -extension P4BooleanValue: ParseableEvaluatableExpression { - static func parse( +extension P4BooleanValue: CompilableExpression { + static func compile( node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree, withScopes scopes: LexicalScopes ) -> Result { @@ -101,8 +101,8 @@ extension P4BooleanValue: ParseableEvaluatableExpression { } } -extension P4IntValue: ParseableEvaluatableExpression { - static func parse( +extension P4IntValue: CompilableExpression { + static func compile( node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree, withScopes scopes: LexicalScopes ) -> Result { @@ -133,8 +133,8 @@ extension P4IntValue: ParseableEvaluatableExpression { } } -extension P4StringValue: ParseableEvaluatableExpression { - static func parse( +extension P4StringValue: CompilableExpression { + static func compile( node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree, withScopes scopes: LexicalScopes ) -> Result { @@ -160,15 +160,15 @@ extension P4StringValue: ParseableEvaluatableExpression { } struct Expression { - public static func Parse( + public static func Compile( node: Node, inTree: MutableTree, withScopes scopes: LexicalScopes ) -> Result { - let localElementsParsers: [ParseableEvaluatableExpression.Type] = [ + let localElementsParsers: [CompilableExpression.Type] = [ P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self, ] for le_parser in localElementsParsers { - switch le_parser.parse( + switch le_parser.compile( node: node, inTree: inTree, withScopes: scopes) { case .Ok(.some(let parsed)): return .Ok(parsed) @@ -182,7 +182,7 @@ struct Expression { } struct LValue { - public static func Parse( + public static func Compile( node: Node, inTree: MutableTree, withScopes scopes: LexicalScopes ) -> Result { return if let node_text_value = node.text { @@ -194,7 +194,7 @@ struct LValue { } struct Identifier { - public static func Parse( + public static func Compile( node: Node, inTree: MutableTree, withScopes scopes: LexicalScopes ) -> Result { return if let node_text_value = node.text { diff --git a/Sources/P4Parser/LexicalScopes.swift b/Sources/P4Compiler/LexicalScopes.swift similarity index 100% rename from Sources/P4Parser/LexicalScopes.swift rename to Sources/P4Compiler/LexicalScopes.swift diff --git a/Sources/P4Parser/Parser.swift b/Sources/P4Compiler/Parser.swift similarity index 60% rename from Sources/P4Parser/Parser.swift rename to Sources/P4Compiler/Parser.swift index 111fd08..7bf0a2c 100644 --- a/Sources/P4Parser/Parser.swift +++ b/Sources/P4Compiler/Parser.swift @@ -24,39 +24,48 @@ import TreeSitterP4 let p4lang = Language(tree_sitter_p4()) -extension ParserAssignmentStatement: ParseableStatement { - public static func Parse( +public func ErrorOnNode(node: Node, withError error: String) -> Error { + return Error(withMessage: "\(node.range): \(error)") +} + +extension ParserAssignmentStatement: CompilableStatement { + public static func Compile( node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes - ) -> Result<(EvaluatableStatement?, LexicalScopes)> { + ) -> Result<(EvaluatableStatement, LexicalScopes)> { if node.nodeType != "assignmentStatement" { - return Result.Ok((.none, scopes)) + return Result.Error( + ErrorOnNode(node: node, withError: "Did not find expected assignment statement")) } guard let lvalue_node = node.child(at: 0), lvalue_node.nodeType == "expression" else { - return Result.Error(Error(withMessage: "Missing lvalue in assignment statement")) + return Result.Error( + ErrorOnNode(node: node, withError: "Missing lvalue in assignment statement")) } guard let rvalue_node = node.child(at: 2), rvalue_node.nodeType == "expression" else { - return Result.Error(Error(withMessage: "Missing lvalue in assignment statement")) + return Result.Error( + ErrorOnNode(node: node, withError: "Missing rvalue in assignment statement")) } - let maybe_parsed_rvalue = Expression.Parse(node: rvalue_node, inTree: tree, withScopes: scopes) + let maybe_parsed_rvalue = Expression.Compile(node: rvalue_node, inTree: tree, withScopes: scopes) guard case Result.Ok(let rvalue) = maybe_parsed_rvalue else { return Result.Error(maybe_parsed_rvalue.error()!) } - let maybe_parsed_lvalue = LValue.Parse(node: lvalue_node, inTree: tree, withScopes: scopes) + let maybe_parsed_lvalue = LValue.Compile(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")) + ErrorOnNode( + node: lvalue_node, + withError: "Cannot assign to variable \(lvalue_identifier) not in scope")) } if rvalue.type().eq(rhs: lvalue_type) { @@ -70,68 +79,75 @@ extension ParserAssignmentStatement: ParseableStatement { } else { return Result.Error( - Error( - withMessage: + ErrorOnNode( + node: node, + withError: "Cannot assign value of type \(rvalue.type()) to \(lvalue_identifier) (with type \(lvalue_type))" )) - } } } public struct Parser { public struct LocalElements { - static func Parse( + static func Compile( node: Node, inTree tree: MutableTree, withScope scopes: LexicalScopes ) -> Result<(EvaluatableStatement, LexicalScopes)> { - let localElementsParsers: [ParseableStatement.Type] = [ - VariableDeclarationStatement.self + let localElementsParsers: [String: CompilableStatement.Type] = [ + "variableDeclaration": VariableDeclarationStatement.self ] - for local_element_parser in localElementsParsers { - switch local_element_parser.Parse(node: node, inTree: tree, withScopes: scopes) { - case Result.Ok((.some(let parsed), let parsed_updated_scopes)): - return Result.Ok((parsed, parsed_updated_scopes)) - case Result.Error(let e): - return Result.Error(Error(withMessage: "Failed to parse local element: \(e)")) - default: continue - } + guard let parser = localElementsParsers[node.nodeType ?? ""] else { + return Result.Error( + ErrorOnNode( + node: node, withError: "Unparseable statement type (\(node.nodeType))")) } - return Result.Error( - Error( - withMessage: - "Failed to parse any local elements from specified local elements: \(node.text!)") - ) + switch parser.Compile(node: node, inTree: tree, withScopes: scopes) { + case Result.Ok(let (parsed, parsed_updated_scopes)): + return Result.Ok((parsed, parsed_updated_scopes)) + case Result.Error(let e): + return Result.Error(Error(withMessage: "Failed to parse local element: \(e)")) + } } } - public struct Statements { - static func Parse( + public struct Statement { + static func Compile( node: Node, inTree tree: MutableTree, withScope scopes: LexicalScopes ) -> Result<(EvaluatableStatement, LexicalScopes)> { - let statementParsers: [ParseableStatement.Type] = [ - ParserAssignmentStatement.self, ExpressionStatement.self, - VariableDeclarationStatement.self, ConditionalStatement.self, BlockStatement.self, + if node.nodeType != "parserStatement" && node.nodeType != "statement" { + return Result.Error(ErrorOnNode(node: node, withError: "Missing expected parser statement")) + } + + let statement = node.child(at: 0)! + + let statementParsers: [String: CompilableStatement.Type] = [ + "assignmentStatement": ParserAssignmentStatement.self, + "expressionStatement": ExpressionStatement.self, + "variableDeclaration": VariableDeclarationStatement.self, + "conditionalStatement": ConditionalStatement.self, "blockStatement": BlockStatement.self, ] // Iterate through statement parsers and give each one a chance. - for parser in statementParsers { - switch parser.Parse(node: node, inTree: tree, withScopes: scopes) { - case Result.Ok((.some(let parsed), let updatedLexicalScopes)): - return .Ok((parsed, updatedLexicalScopes)) - case Result.Error(let e): - return .Error(Error(withMessage: "Failed to parse a statement element: \(e)")) - default: continue - } + + guard let parser = statementParsers[statement.nodeType ?? ""] else { + return Result.Error( + ErrorOnNode( + node: statement, withError: "Unparseable statement type (\(statement.nodeType))")) + } + switch parser.Compile(node: statement, inTree: tree, withScopes: scopes) { + case Result.Ok(let (parsed, updatedLexicalScopes)): + return .Ok((parsed, updatedLexicalScopes)) + case Result.Error(let e): + return .Error( + ErrorOnNode(node: node, withError: "Failed to parse a statement element: \(e)")) } - return Result.Error( - Error(withMessage: "Failed to parse a statement element: \(node.text!)")) } } public struct TransitionSelectExpressionCaseStatement { - static func Parse( + static func Compile( node: Node, inTree tree: MutableTree, withLexicalScopes scopes: LexicalScopes ) -> Result<(KeysetExpression, LexicalScopes)> { if node.nodeType != "selectCase" { @@ -150,13 +166,13 @@ public struct Parser { return Result.Error(Error(withMessage: "Missing target state in select case")) } - let maybe_parsed_keysetexpression = Expression.Parse( + 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.Parse( + 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()!) @@ -171,7 +187,7 @@ public struct Parser { } public struct TransitionSelectExpression { - static func Parse( + static func Compile( node: Node, inTree tree: MutableTree, withScope scopes: LexicalScopes ) -> Result<(ParserTransitionSelectExpression, LexicalScopes)> { guard @@ -198,7 +214,7 @@ public struct Parser { 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) + let maybe_selector = Expression.Compile(node: selector_node, inTree: tree, withScopes: scopes) guard case .Ok(let selector) = maybe_selector else { return .Error( Error( @@ -215,7 +231,7 @@ public struct Parser { var kses_errors: [Error] = Array() body_node.enumerateNamedChildren { current_node in - let maybe_parsed_kse = TransitionSelectExpressionCaseStatement.Parse( + 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) @@ -242,9 +258,9 @@ public struct Parser { } public struct TransitionStatement { - static func Parse( + static func Compile( node: Node, inTree tree: MutableTree, withScope scopes: LexicalScopes - ) -> Result<(ParserTransitionStatement?, LexicalScopes)> { + ) -> Result<(ParserTransitionStatement, LexicalScopes)> { guard let next_state_query = try? SwiftTreeSitter.Query( language: p4lang, @@ -259,7 +275,7 @@ public struct Parser { if let next_state_result = qr.next() { let transition_capture = next_state_result.captures(named: "next-state") - let maybe_parsed_next_state = Identifier.Parse( + let maybe_parsed_next_state = Identifier.Compile( node: transition_capture[0].node, inTree: tree, withScopes: scopes) if case .Ok(let next_state) = maybe_parsed_next_state { return .Ok( @@ -273,7 +289,7 @@ public struct Parser { } } - return switch TransitionSelectExpression.Parse(node: node, inTree: tree, withScope: scopes) { + return switch TransitionSelectExpression.Compile(node: node, inTree: tree, withScope: scopes) { case .Ok((let tse, _)): .Ok((ParserTransitionStatement(withTransitionExpression: tse), scopes)) case .Error(let e): .Error(e) @@ -281,105 +297,116 @@ public struct Parser { } } - public struct State { - static func Parse( + public struct Statements { + static func Compile( node: Node, inTree tree: MutableTree, withLexicalScopes scopes: LexicalScopes - ) -> Result<(ParserState, LexicalScopes)> { - guard - let parser_state_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(parserState (state) (identifier) @state-name (parserLocalElements ((parserLocalElement) @state-local-element)*)? (parserStatements ((parserStatement) @state-statement)*)? (parserTransitionStatement) @transition)" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Error(Error(withMessage: "Could not compile the tree sitter query")) + ) -> Result<([EvaluatableStatement], LexicalScopes)> { + if node.nodeType != "statements" && node.nodeType != "parserStatements" { + return Result.Error(ErrorOnNode(node: node, withError: "Did not find expected statements")) } - let qr = parser_state_query.execute(node: node, in: tree) - - let parser_declaration = qr.next()! - - let transition_capture = parser_declaration.captures(named: "transition") - let state_name_capture = parser_declaration.captures(named: "state-name") - let state_le_capture = parser_declaration.captures(named: "state-local-element") - let statements_capture = parser_declaration.captures(named: "state-statement") - - var parsed_les: [EvaluatableStatement] = Array() var parse_err: Error? = .none - var current_scopes = scopes.enter() - - defer { - current_scopes = current_scopes.exit() - } - - for state_le in state_le_capture { - state_le.node.enumerateNamedChildren { node in - switch LocalElements.Parse( - node: node, inTree: tree, withScope: current_scopes) - { - case .Ok((let le, let le_parsed_scopes)): - current_scopes = le_parsed_scopes - parsed_les.append(le) - case .Error(let e): - parse_err = e - } - } - } - - if let parse_err = parse_err { - return Result.Error(parse_err) - } - + var current_scopes = scopes var parsed_s: [EvaluatableStatement] = Array() - if !statements_capture.isEmpty { - for statement in statements_capture { - statement.node.enumerateNamedChildren { node in - switch Statements.Parse( - node: node, inTree: tree, withScope: current_scopes) - { - case .Ok((let le, let le_parsed_scopes)): - current_scopes = le_parsed_scopes - parsed_s.append(le) - case .Error(let e): - parse_err = e - } - } + node.enumerateNamedChildren { node in + switch Statement.Compile( + node: node, inTree: tree, withScope: current_scopes) + { + case .Ok((let parsed_statement, let updated_scopes)): + current_scopes = updated_scopes + parsed_s.append(parsed_statement) + case .Error(let e): + parse_err = e } } if let parse_err = parse_err { return Result.Error(parse_err) } - - // TODO: Now that scopes are involved, doing this out of order will not work! - guard !state_name_capture.isEmpty, - !transition_capture.isEmpty, - case .Ok(let parsed_state_name) = Identifier.Parse( - node: state_name_capture[0].node, inTree: tree, withScopes: scopes) - else { - return Result.Error(Error(withMessage: "Could not parse a parser declaration")) - } - - switch TransitionStatement.Parse( - node: transition_capture[0].node, inTree: tree, withScope: current_scopes) - { - case .Ok((.some(let transition_statement), let current_scopes)): - return Result.Ok( - ( - ParserState( - name: parsed_state_name, withLocalElements: parsed_les, - withStatements: parsed_s, - withTransition: transition_statement), current_scopes - )) - case .Error(let e): return .Error(e) - case .Ok((.none, _)): return .Error(Error(withMessage: "Missing transition statement")) - } - + return Result.Ok((parsed_s, current_scopes)) } } - static func Parse( + public struct State { + static func Compile( + node: Node, inTree tree: MutableTree, withLexicalScopes scopes: LexicalScopes + ) -> Result<(ParserState, LexicalScopes)> { + + var currentChildIdx = 1 + var currentChildIdxSafe = 2 + + var currentChild: Node? = .none + + guard let node_type = node.nodeType, + node_type == "parserState" + else { + return Result.Error( + ErrorOnNode(node: node, withError: "Did not find a parser state declaration")) + } + + if node.childCount < currentChildIdxSafe { + return Result.Error( + ErrorOnNode(node: node, withError: "Missing state name in state declaration")) + } + currentChild = node.child(at: 1) + let maybe_state_identifier = Identifier.Compile( + node: currentChild!, inTree: tree, withScopes: scopes) + guard case Result.Ok(let state_identifier) = maybe_state_identifier else { + return Result.Error(maybe_state_identifier.error()!) + } + + // Skip the '{' + currentChildIdx += 2 + currentChildIdxSafe += 2 + + var parse_err: Error? = .none + var current_scopes: LexicalScopes = LexicalScopes() + var parsed_s: [EvaluatableStatement] = Array() + + if node.childCount < currentChildIdxSafe { + return Result.Error(ErrorOnNode(node: node, withError: "Missing body of state declaration")) + } + currentChild = node.child(at: currentChildIdx) + if currentChild!.nodeType == "parserStatements" { + switch Statements.Compile( + node: currentChild!, inTree: tree, withLexicalScopes: scopes.enter()) + { + case .Ok(let (state_statements, updated_scopes)): + parsed_s = state_statements + current_scopes = updated_scopes + case .Error(let error): + parse_err = error + } + currentChildIdx += 1 + currentChildIdxSafe += 1 + } + + if let parse_err = parse_err { + return Result.Error(parse_err) + } + + if node.childCount < currentChildIdxSafe { + return Result.Error( + ErrorOnNode(node: node, withError: "Missing transition statement of state declaration")) + } + currentChild = node.child(at: currentChildIdx) + switch TransitionStatement.Compile( + node: currentChild!, inTree: tree, withScope: current_scopes) + { + case .Ok(let (transition_statement, new_scopes)): + return Result.Ok( + ( + ParserState( + name: state_identifier, withStatements: parsed_s, + withTransition: transition_statement), new_scopes + )) + case .Error(let e): return .Error(e) + } + } + } + + static func Compile( withName name: Common.Identifier, node: Node, inTree tree: MutableTree, withLexicalScopes scopes: LexicalScopes ) -> Result<(P4Lang.Parser, LexicalScopes)> { @@ -407,7 +434,7 @@ public struct Parser { // TODO: Assert that there is only one. captures[0].node.enumerateChildren { parser_state in - switch Parser.State.Parse(node: parser_state, inTree: tree, withLexicalScopes: scopes) { + switch Parser.State.Compile(node: parser_state, inTree: tree, withLexicalScopes: scopes) { case Result.Ok(let (state, new_parser_scopes)): parser.states = parser.states.append(state: state) parser_scopes = new_parser_scopes diff --git a/Sources/P4Parser/Program.swift b/Sources/P4Compiler/Program.swift similarity index 95% rename from Sources/P4Parser/Program.swift rename to Sources/P4Compiler/Program.swift index ac1e083..edf91b7 100644 --- a/Sources/P4Parser/Program.swift +++ b/Sources/P4Compiler/Program.swift @@ -23,7 +23,7 @@ import TreeSitterExtensions import TreeSitterP4 public struct Program { - public static func Parse(_ source: String) -> Result { + public static func Compile(_ source: String) -> Result { let p = SwiftTreeSitter.Parser.init() do { @@ -59,7 +59,7 @@ public struct Program { let parser_qc = parser_declaration_query.execute(in: tree) for parser_declaration in parser_qc { - switch Parser.Parse( + switch Parser.Compile( withName: Common.Identifier(name: parser_declaration.nodes[0].text!), node: parser_declaration.nodes[1], inTree: tree, withLexicalScopes: program_scope) { diff --git a/Sources/P4Parser/Protocols.swift b/Sources/P4Compiler/Protocols.swift similarity index 74% rename from Sources/P4Parser/Protocols.swift rename to Sources/P4Compiler/Protocols.swift index 271498d..84d1b4a 100644 --- a/Sources/P4Parser/Protocols.swift +++ b/Sources/P4Compiler/Protocols.swift @@ -22,16 +22,16 @@ import SwiftTreeSitter import TreeSitterExtensions import TreeSitterP4 -public protocol ParseableStatement { - static func Parse( +public protocol CompilableStatement { + static func Compile( node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes - ) -> Result<(EvaluatableStatement?, LexicalScopes)> + ) -> Result<(EvaluatableStatement, LexicalScopes)> } -public protocol ParseableValue { - static func ParseValue(withValue value: String) -> Result +public protocol CompilableValue { + static func CompileValue(withValue value: String) -> Result } -public protocol ParseableType { - static func ParseType(type: String) -> Result +public protocol CompilableType { + static func CompileType(type: String) -> Result } diff --git a/Sources/P4Compiler/Statement.swift b/Sources/P4Compiler/Statement.swift new file mode 100644 index 0000000..862a28c --- /dev/null +++ b/Sources/P4Compiler/Statement.swift @@ -0,0 +1,253 @@ +// 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 +import P4Runtime +import SwiftTreeSitter +import TreeSitterExtensions +import TreeSitterP4 + +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")) + } + + var currentChildIdx = 0 + var currentChildIdxSafe = 1 + var currentChild: Node? = .none + + if node.childCount < currentChildIdxSafe { + return Result.Error( + ErrorOnNode(node: node, withError: "Malformed block statement")) + } + currentChild = node.child(at: currentChildIdx) + if currentChild!.nodeType != "{" { + return Result.Error( + ErrorOnNode(node: currentChild!, withError: "Missing { on block statement")) + } + currentChildIdx += 1 + currentChildIdxSafe += 1 + + var statements: [EvaluatableStatement] = Array() + var parse_err: Error? = .none + var new_scopes: LexicalScopes = LexicalScopes() + + if node.childCount < currentChildIdxSafe { + return Result.Error( + ErrorOnNode(node: node, withError: "Malformed block statement")) + } + currentChild = node.child(at: currentChildIdx) + if currentChild!.nodeType == "statements" { + switch Parser.Statements.Compile(node: currentChild!, inTree: tree, withLexicalScopes: scopes.enter()) { + case .Ok(let (parsed_statements, parsed_scopes)): + new_scopes = parsed_scopes + statements = parsed_statements + case .Error(let error): + parse_err = error + } + + currentChildIdx += 1 + currentChildIdxSafe += 1 + } + + if let err = parse_err { + return .Error(err) + } + + if node.childCount < currentChildIdxSafe { + return Result.Error( + ErrorOnNode(node: node, withError: "Malformed block statement")) + } + currentChild = node.child(at: currentChildIdx) + if currentChild!.nodeType != "}" { + return Result.Error( + ErrorOnNode(node: currentChild!, withError: "Missing } on block statement")) + } + + return .Ok((BlockStatement(statements), new_scopes)) + } +} + +extension ConditionalStatement: CompilableStatement { + public static func Compile( + 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")) + } + + let maybe_condition_expression = node.child(at: 2) + guard let condition_expression = maybe_condition_expression, + condition_expression.nodeType == "expression" + else { + return Result.Error( + ErrorOnNode(node: node, withError: "Did not find condition for conditional statement")) + } + + let maybe_thens = node.child(at: 4) + guard let thens = maybe_thens, + thens.nodeType == "statement" + else { + return Result.Error( + ErrorOnNode(node: node, withError: "Did not find then statement block for conditional statement")) + } + + guard + case .Ok(let condition) = Expression.Compile( + node: condition_expression, inTree: tree, withScopes: scopes) + else { + return Result.Error( + Error(withMessage: "Could not parse a conditional expression in a conditional statement")) + } + + guard + case .Ok((let thenns, _)) = Parser.Statement.Compile( + node: thens, inTree: tree, withScope: scopes) + else { + return Result.Error( + Error( + withMessage: + "Could not parse the then block in a conditional statement")) + } + + let optional_elss: Result<(any EvaluatableStatement, LexicalScopes)>? = + if let elss = node.child(at: 6) { + .some( + Parser.Statement.Compile( + node: elss, inTree: tree, withScope: scopes)) + } else { + .none + } + + if let parsed_elss = optional_elss { + guard + case .Ok((let elss, _)) = parsed_elss + else { + return Result.Error( + Error( + withMessage: + "Could not parse the else block in a conditional statement")) + } + return .Ok( + (ConditionalStatement(condition: condition, withThen: thenns, andElse: elss), scopes)) + } + return .Ok((ConditionalStatement(condition: condition, withThen: thenns), scopes)) + } +} + +extension VariableDeclarationStatement: CompilableStatement { + public static func Compile( + 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")) + } + + let maybe_typeref = node.child(at: 0) + guard let typeref = maybe_typeref, + typeref.nodeType == "typeRef" + else { + return Result.Error( + ErrorOnNode(node: node, withError: "Did not find type name for variable declaration statement")) + } + + let maybe_variablename = node.child(at: 1) + guard let variablename = maybe_variablename, + variablename.nodeType == "identifier" + else { + return Result.Error( + ErrorOnNode(node: node, withError: "Did not find identifier name for variable declaration statement")) + } + + let maybe_rvalue = node.childCount > 3 ? node.child(at: 3) : .none + guard let rvalue = maybe_rvalue, + rvalue.nodeType == "expression" + else { + return Result.Error( + ErrorOnNode(node: node, withError: "Did not find initial value expression for variable declaration statement")) + } + + guard + case .Ok(let parsed_variablename) = Identifier.Compile( + node: variablename, inTree: tree, withScopes: scopes.enter()) + else { + return Result.Error( + Error(withMessage: "Could not parse variable name")) + } + + guard + case .Ok(let parsed_rvalue) = Expression.Compile( + node: rvalue, inTree: tree, withScopes: scopes.enter()) + else { + return Result.Error( + Error( + withMessage: + "Could not parse initial value expression in a variable declaration statement")) + } + + guard case .Ok(let declaration_p4_type) = Types.CompileBasicType(type: typeref.text!) else { + return Result.Error( + Error(withMessage: "Could not parse a P4 type from \(typeref.text!)")) + } + + if parsed_rvalue.type().eq(rhs: declaration_p4_type) { + return Result.Ok( + ( + VariableDeclarationStatement( + identifier: parsed_variablename, withInitializer: parsed_rvalue), + scopes.declare( + identifier: parsed_variablename, withValue: declaration_p4_type) + )) + + } else { + return Result.Error( + Error( + withMessage: + "Cannot initialize \(parsed_variablename) (with type \(declaration_p4_type)) from rvalue with type \(parsed_rvalue.type())" + )) + + } + } +} + +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")) + } + + let _ = node.child(at: 0) + + return Result.Ok((ExpressionStatement(), scopes)) + } +} diff --git a/Sources/P4Parser/Types.swift b/Sources/P4Compiler/Types.swift similarity index 69% rename from Sources/P4Parser/Types.swift rename to Sources/P4Compiler/Types.swift index fe0d67c..4a05e5e 100644 --- a/Sources/P4Parser/Types.swift +++ b/Sources/P4Compiler/Types.swift @@ -22,28 +22,28 @@ import SwiftTreeSitter import TreeSitterExtensions import TreeSitterP4 -extension P4Boolean: ParseableType { - public static func ParseType(type: String) -> Common.Result<(any Common.P4Type)?> { +extension P4Boolean: CompilableType { + public static func CompileType(type: String) -> Common.Result<(any Common.P4Type)?> { return type == "bool" ? .Ok(P4Boolean.create()) : .Ok(.none) } } -extension P4Int: ParseableType { - public static func ParseType(type: String) -> Common.Result<(any Common.P4Type)?> { +extension P4Int: CompilableType { + public static func CompileType(type: String) -> Common.Result<(any Common.P4Type)?> { return type == "int" ? .Ok(P4Int.create()) : .Ok(.none) } } -extension P4String: ParseableType { - public static func ParseType(type: String) -> Common.Result<(any Common.P4Type)?> { +extension P4String: CompilableType { + public static func CompileType(type: String) -> Common.Result<(any Common.P4Type)?> { return type == "string" ? .Ok(P4String.create()) : .Ok(.none) } } public struct Types { - static func ParseBasicType(type: String) -> Result { - let type_parsers: [ParseableType.Type] = [P4Boolean.self, P4Int.self, P4String.self] + static func CompileBasicType(type: String) -> Result { + let type_parsers: [CompilableType.Type] = [P4Boolean.self, P4Int.self, P4String.self] for type_parser in type_parsers { - switch type_parser.ParseType(type: type) { + switch type_parser.CompileType(type: type) { case .Ok(.some(let type)): return .Ok(type) case .Ok(.none): continue case .Error(let e): return .Error(e) diff --git a/Sources/P4Lang/Parser.swift b/Sources/P4Lang/Parser.swift index d9abed0..61418d6 100644 --- a/Sources/P4Lang/Parser.swift +++ b/Sources/P4Lang/Parser.swift @@ -98,7 +98,6 @@ public struct ParserTransitionStatement { public class ParserState: Equatable, CustomStringConvertible, Comparable { 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? public private(set) var next_state: ParserState? @@ -120,13 +119,11 @@ public class ParserState: Equatable, CustomStringConvertible, Comparable { /// Construct a ParserState public init( - name: Identifier, withLocalElements localElements: [EvaluatableStatement]?, - withStatements stmts: [EvaluatableStatement]?, + name: Identifier, withStatements stmts: [EvaluatableStatement]?, withTransition transitionStatement: ParserTransitionStatement ) { state = name transition = transitionStatement - local_elements = localElements ?? Array() statements = stmts ?? Array() } @@ -151,7 +148,6 @@ public class ParserState: Equatable, CustomStringConvertible, Comparable { init(name: Identifier) { state = name transition = .none - local_elements = Array() statements = Array() } diff --git a/Sources/P4Parser/Statement.swift b/Sources/P4Parser/Statement.swift deleted file mode 100644 index e4008a6..0000000 --- a/Sources/P4Parser/Statement.swift +++ /dev/null @@ -1,253 +0,0 @@ -// 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 -import P4Runtime -import SwiftTreeSitter -import TreeSitterExtensions -import TreeSitterP4 - -extension BlockStatement: ParseableStatement { - public static func Parse( - node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes - ) -> Result<(EvaluatableStatement?, LexicalScopes)> { - // TODO: Make sure that this works. - // (And apply in other places!) - guard - let block_statement_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(statement . (blockStatement . (statements . ((statement) @astatement)*) @statements) @block-statement) @statement" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Ok((.none, scopes)) - } - - let qr = block_statement_query.execute(node: node, in: tree) - guard let variable_declaration = qr.next() else { - return .Ok((.none, scopes)) - } - - let statement_capture = variable_declaration.captures(named: "statement") - let blockstatement_capture = variable_declaration.captures(named: "block-statement") - let astatement_capture = variable_declaration.captures(named: "astatement") - let statements_capture = variable_declaration.captures(named: "statements") - - if statement_capture.isEmpty || blockstatement_capture.isEmpty { - return .Error(Error(withMessage: "Could not parse a block statement")) - } - - let statement_node = statement_capture[0].node - let blockstatement_capture_node = blockstatement_capture[0].node - let statements_capture_node = statements_capture[0].node - - /* - if statement_node.parent != node.parent - { - return .Ok((.none, scopes)) - } - */ - var statements: [EvaluatableStatement] = Array() - var parse_err: Error? = .none - - for statement in astatement_capture { - if let statement_node = statement.node.child( - at: 0) /* - let statement_node_parent = statement_node.parent, - statement_node_parent.parent == statements_capture_node - */ - { - switch P4Parser.Parser.Statements.Parse( - node: statement_node, inTree: tree, withScope: scopes.enter()) - { - case .Ok((let parsed_statement, _)): statements.append(parsed_statement) - case .Error(let e): - parse_err = e - break - } - } - } - - if let err = parse_err { - return .Error(err) - } - - return .Ok((BlockStatement(statements), scopes)) - } -} - -extension ConditionalStatement: ParseableStatement { - public static func Parse( - node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes - ) -> Result<(EvaluatableStatement?, LexicalScopes)> { - - guard let node_type = node.nodeType, - node_type == "conditionalStatement" - else { - return Result.Ok((.none, scopes)) - } - - let maybe_condition_expression = node.child(at: 2) - guard let condition_expression = maybe_condition_expression, - condition_expression.nodeType == "expression" - else { - return Result.Ok((.none, scopes)) - } - - let maybe_thens = node.child(at: 4) - guard let thens = maybe_thens, - thens.nodeType == "statement" - else { - return Result.Ok((.none, scopes)) - } - - guard - case .Ok(let condition) = Expression.Parse( - node: condition_expression, inTree: tree, withScopes: scopes.enter()) - else { - return Result.Error( - Error(withMessage: "Could not parse a conditional expression in a conditional statement")) - } - - guard - case .Ok((let thenns, _)) = Parser.Statements.Parse( - node: thens, inTree: tree, withScope: scopes.enter()) - else { - return Result.Error( - Error( - withMessage: - "Could not parse the then block in a conditional statement")) - } - - let optional_elss: Result<(any EvaluatableStatement, LexicalScopes)>? = - if let elss = node.child(at: 6) { - .some( - Parser.Statements.Parse( - node: elss, inTree: tree, withScope: scopes.enter())) - } else { - .none - } - - if let parsed_elss = optional_elss { - guard - case .Ok((let elss, _)) = parsed_elss - else { - return Result.Error( - Error( - withMessage: - "Could not parse the else block in a conditional statement")) - } - return .Ok( - (ConditionalStatement(condition: condition, withThen: thenns, andElse: elss), scopes)) - } - return .Ok((ConditionalStatement(condition: condition, withThen: thenns), scopes)) - } -} - -extension VariableDeclarationStatement: ParseableStatement { - public static func Parse( - node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes - ) -> Result<(EvaluatableStatement?, LexicalScopes)> { - guard - let variable_declaration_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(variableDeclaration (annotations)? (typeRef) @type-name variable_name: (identifier) @identifier ((assignment) (expression) @value)?)" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Ok((.none, scopes)) - } - - let qr = variable_declaration_query.execute(node: node, in: tree) - guard let variable_declaration = qr.next() else { - return .Ok((.none, scopes)) - } - - let type_name_capture = variable_declaration.captures(named: "type-name") - let variable_name_capture = variable_declaration.captures(named: "identifier") - let value_capture = variable_declaration.captures(named: "value") - - // There must be a type name and a variable name - guard !type_name_capture.isEmpty, - !variable_name_capture.isEmpty, - !value_capture.isEmpty, - let variable_name = variable_name_capture[0].node.text, - let type_name = type_name_capture[0].node.text - else { - return Result.Error( - Error(withMessage: "Could not parse a parser variable declaration statement")) - } - - guard case .Ok(let declaration_p4_type) = Types.ParseBasicType(type: type_name) else { - return Result.Error( - Error(withMessage: "Could not parse a P4 type from \(type_name)")) - } - - let rvalue_raw = value_capture[0].node - let maybe_parsed_rvalue = Expression.Parse(node: rvalue_raw, inTree: tree, withScopes: scopes) - guard case Result.Ok(let rvalue) = maybe_parsed_rvalue else { - return Result.Error(maybe_parsed_rvalue.error()!) - } - - if rvalue.type().eq(rhs: declaration_p4_type) { - return Result.Ok( - ( - VariableDeclarationStatement( - identifier: Common.Identifier(name: variable_name), withInitializer: rvalue), - scopes.declare( - identifier: Common.Identifier(name: variable_name), withValue: declaration_p4_type) - )) - - } else { - return Result.Error( - Error( - withMessage: - "Cannot initialize \(variable_name) (with type \(declaration_p4_type)) from rvalue with type \(rvalue.type())" - )) - - } - } -} - -extension ExpressionStatement: ParseableStatement { - public static func Parse( - node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes - ) -> Result<(EvaluatableStatement?, LexicalScopes)> { - guard - let expression_statement_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(expressionStatement (expression) @expression)" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Ok((.none, scopes)) - } - - let qr = expression_statement_query.execute(node: node, in: tree) - guard let query_result = qr.next() else { - return Result.Ok((.none, scopes)) - } - - let expression_capture = query_result.captures(named: "expression") - if !expression_capture.isEmpty { - // TODO: Actually create an ExpressionStatement - return Result.Ok((ExpressionStatement(), scopes)) - } - return Result.Ok((.none, scopes)) - } -} diff --git a/Sources/P4Runtime/Parser.swift b/Sources/P4Runtime/Parser.swift index 4d79daf..a6329a9 100644 --- a/Sources/P4Runtime/Parser.swift +++ b/Sources/P4Runtime/Parser.swift @@ -41,10 +41,6 @@ public struct ParserStateDirectTransition: ParserStateInstance { var program = program.exit_scope() program = program.enter_scope() - for local_element in currrent_state.local_elements { - program = local_element.evaluate(execution: program) - } - for statement in currrent_state.statements { program = statement.evaluate(execution: program) } @@ -88,12 +84,7 @@ public struct ParserStateSelectTransition: ParserStateInstance { var program = program.exit_scope() program = program.enter_scope() - // First, evaluate the local elements. - for local_element in currrent_state.local_elements { - program = local_element.evaluate(execution: program) - } - - // Then, evaluate the statements. + // First, evaluate the statements. for statement in currrent_state.statements { program = statement.evaluate(execution: program) } diff --git a/Sources/P4Runtime/Program.swift b/Sources/P4Runtime/Program.swift index cc11b14..aa7e78d 100644 --- a/Sources/P4Runtime/Program.swift +++ b/Sources/P4Runtime/Program.swift @@ -41,13 +41,13 @@ extension VariableDeclarationStatement: EvaluatableStatement { extension ConditionalStatement: EvaluatableStatement { public func evaluate(execution: ProgramExecution) -> ProgramExecution { - guard case .Ok(let initial_value) = self.condition.evaluate(execution: execution) else { + guard case .Ok(let evaluated_condition) = self.condition.evaluate(execution: execution) else { return execution.setError(error: Error(withMessage: "Could not evaluate \(self.condition)")) } - if !initial_value.type().eq(rhs: P4Boolean.create()) { + if !evaluated_condition.type().eq(rhs: P4Boolean.create()) { return execution.setError(error: Error(withMessage: "Condition expression is not a Boolean")) } - if initial_value.eq(rhs: P4BooleanValue.init(withValue: true)) { + if evaluated_condition.eq(rhs: P4BooleanValue.init(withValue: true)) { let execution = execution.enter_scope() var result = self.thenn.evaluate(execution: execution) result = result.exit_scope() diff --git a/Tests/p4rseTests/ParserTests.swift b/Tests/p4rseTests/ParserTests.swift index c999447..453578c 100644 --- a/Tests/p4rseTests/ParserTests.swift +++ b/Tests/p4rseTests/ParserTests.swift @@ -24,7 +24,7 @@ import Testing import TreeSitter import TreeSitterP4 -@testable import P4Parser +@testable import P4Compiler @Test func test_simple_parser_syntax_error() async throws { let simple_parser_declaration = """ @@ -37,7 +37,7 @@ import TreeSitterP4 #expect( #RequireErrorResult( Error(withMessage: "Could not compile the P4 program"), - Program.Parse(simple_parser_declaration))) + Program.Compile(simple_parser_declaration))) } @Test func test_simple_parser_with_statement() async throws { @@ -50,7 +50,7 @@ import TreeSitterP4 }; """ - let program = try #UseOkResult(Program.Parse(simple_parser_declaration)) + let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) let parser = try #UseOkResult(program.find_parser(withName: Identifier(name: "main_parser"))) #expect(parser.states.count() == 1) @@ -72,7 +72,7 @@ import TreeSitterP4 bool() main; """ - let program = try #UseOkResult(Program.Parse(simple_parser_declaration)) + let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) #expect(#RequireOkResult(program.find_parser(withName: Identifier(name: "main_parser")))) } @@ -88,7 +88,7 @@ import TreeSitterP4 }; """ - let compilation_error = try #UseErrorResult(Program.Parse(simple_parser_declaration)) + let compilation_error = try #UseErrorResult(Program.Compile(simple_parser_declaration)) #expect(compilation_error.msg.contains("asde")) #expect(compilation_error.msg.contains("asdf")) diff --git a/Tests/p4rseTests/RuntimeTests.swift b/Tests/p4rseTests/RuntimeTests.swift index 7a18fd0..4e65cd7 100644 --- a/Tests/p4rseTests/RuntimeTests.swift +++ b/Tests/p4rseTests/RuntimeTests.swift @@ -25,7 +25,7 @@ import Testing import TreeSitter import TreeSitterP4 -@testable import P4Parser +@testable import P4Compiler @Test func test_simple_runtime() async throws { let simple_parser_declaration = """ @@ -37,7 +37,7 @@ import TreeSitterP4 }; """ - let program = try #UseOkResult(Program.Parse(simple_parser_declaration)) + let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) let (state_result, _) = try! #UseOkResult(runtime.run()) @@ -55,7 +55,7 @@ import TreeSitterP4 }; """ - let program = try #UseOkResult(Program.Parse(simple_parser_declaration)) + let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program)) let (state_result, _) = try! #UseOkResult(runtime.run()) // We should be in the accept state. @@ -72,7 +72,7 @@ import TreeSitterP4 }; """ - let program = try #UseOkResult(Program.Parse(simple_parser_declaration)) + let program = try #UseOkResult(Program.Compile(simple_parser_declaration)) #expect( #RequireErrorResult( @@ -92,7 +92,7 @@ import TreeSitterP4 }; """ - let program = try #UseOkResult(Program.Parse(simple_parser_declaration)) + 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, _) = try! #UseOkResult(runtime.run()) @@ -114,7 +114,7 @@ import TreeSitterP4 }; """ - let program = try #UseOkResult(Program.Parse(simple_parser_declaration)) + 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, _) = try! #UseOkResult(runtime.run()) @@ -141,7 +141,7 @@ import TreeSitterP4 }; """ - let program = try #UseOkResult(Program.Parse(simple_parser_declaration)) + 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()) @@ -176,7 +176,7 @@ import TreeSitterP4 }; """ - let program = try #UseOkResult(Program.Parse(simple_parser_declaration)) + 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()) diff --git a/Tests/p4rseTests/ScopeRuntimeTests.swift b/Tests/p4rseTests/ScopeRuntimeTests.swift index fc62033..9d48143 100644 --- a/Tests/p4rseTests/ScopeRuntimeTests.swift +++ b/Tests/p4rseTests/ScopeRuntimeTests.swift @@ -25,7 +25,7 @@ import Testing import TreeSitter import TreeSitterP4 -@testable import P4Parser +@testable import P4Compiler @Test func test_simple_local_element_variable_declaration() async throws { let simple_parser_declaration = """ @@ -42,7 +42,7 @@ import TreeSitterP4 """ - let program = try #UseOkResult(Program.Parse(simple_parser_declaration)) + 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()) @@ -84,7 +84,7 @@ import TreeSitterP4 """ - let program = try #UseOkResult(Program.Parse(simple_parser_declaration)) + 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()) @@ -119,7 +119,7 @@ import TreeSitterP4 """ - let program = try #UseOkResult(Program.Parse(simple_parser_declaration)) + 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()) @@ -135,6 +135,7 @@ import TreeSitterP4 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 { let simple_parser_declaration = """ parser main_parser() { @@ -152,7 +153,7 @@ import TreeSitterP4 """ - let program = try #UseOkResult(Program.Parse(simple_parser_declaration)) + 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()) @@ -169,4 +170,46 @@ import TreeSitterP4 #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 { + let simple_parser_declaration = """ + parser main_parser() { + state start { + bool where_to = true; + string where_from = "here"; + string where_where = "here"; + if (where_to) { + bool where_from = true; + if (where_from) { + where_to = false; + } + } + where_from = "there"; + transition select (where_to) { + false: reject; + true: accept; + }; + } + }; + """ + + + 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) + + #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 diff --git a/Tests/p4rseTests/ScopeTests.swift b/Tests/p4rseTests/ScopeTests.swift index 190c745..b2a3074 100644 --- a/Tests/p4rseTests/ScopeTests.swift +++ b/Tests/p4rseTests/ScopeTests.swift @@ -24,7 +24,7 @@ import Testing import TreeSitter import TreeSitterP4 -@testable import P4Parser +@testable import P4Compiler @Test func test_scope() async throws { let s = LexicalScope() diff --git a/Tests/p4rseTests/SupportTests.swift b/Tests/p4rseTests/SupportTests.swift index 8c9fa95..38b24d2 100644 --- a/Tests/p4rseTests/SupportTests.swift +++ b/Tests/p4rseTests/SupportTests.swift @@ -23,7 +23,7 @@ import Testing import TreeSitter import TreeSitterP4 -@testable import P4Parser +@testable import P4Compiler struct NotStringConvertible {} diff --git a/Tests/p4rseTests/ValueTypeParserTests.swift b/Tests/p4rseTests/ValueTypeParserTests.swift index faac429..6000587 100644 --- a/Tests/p4rseTests/ValueTypeParserTests.swift +++ b/Tests/p4rseTests/ValueTypeParserTests.swift @@ -24,14 +24,14 @@ import Testing import TreeSitter import TreeSitterP4 -@testable import P4Parser +@testable import P4Compiler @Test func test_invalid_types() async throws { for invalid_type_name in ["boo", "str", "in"] { #expect( #RequireErrorResult( Error(withMessage: "Type name not recognized"), - Types.ParseBasicType(type: invalid_type_name))) + Types.CompileBasicType(type: invalid_type_name))) } } @@ -51,9 +51,9 @@ import TreeSitterP4 #RequireErrorResult( Error( withMessage: - "Failed to parse a statement element: Cannot assign value of type Boolean to where_to (with type String)" + "{112, 16}: Failed to parse a statement element: {112, 16}: Cannot assign value of type Boolean to where_to (with type String)" ), - Program.Parse(simple_parser_declaration))) + Program.Compile(simple_parser_declaration))) } @Test func test_invalid_type_in_assignment2() async throws { @@ -72,9 +72,9 @@ import TreeSitterP4 #RequireErrorResult( Error( withMessage: - "Failed to parse a statement element: Cannot assign value of type String to where_to (with type Boolean)" + "{114, 22}: Failed to parse a statement element: {114, 22}: Cannot assign value of type String to where_to (with type Boolean)" ), - Program.Parse(simple_parser_declaration))) + Program.Compile(simple_parser_declaration))) } @Test func test_invalid_type_in_declaration() async throws { @@ -92,9 +92,9 @@ import TreeSitterP4 #RequireErrorResult( Error( withMessage: - "Failed to parse local element: Cannot initialize where_to (with type Boolean) from rvalue with type String" + "{86, 27}: Failed to parse a statement element: Cannot initialize where_to (with type Boolean) from rvalue with type String" ), - Program.Parse(simple_parser_declaration))) + Program.Compile(simple_parser_declaration))) } @Test func test_invalid_type_in_declaration2() async throws { @@ -112,7 +112,7 @@ import TreeSitterP4 #RequireErrorResult( Error( withMessage: - "Failed to parse local element: Cannot initialize where_from (with type String) from rvalue with type Boolean" + "{77, 29}: Failed to parse a statement element: Cannot initialize where_from (with type String) from rvalue with type Boolean" ), - Program.Parse(simple_parser_declaration))) + Program.Compile(simple_parser_declaration))) } diff --git a/tree-sitter-p4/grammar.js b/tree-sitter-p4/grammar.js index 047eec4..be52c52 100644 --- a/tree-sitter-p4/grammar.js +++ b/tree-sitter-p4/grammar.js @@ -46,7 +46,7 @@ export default grammar({ parserLocalElements: $ => prec(2, repeat1($.parserLocalElement)), parserStates: $ => repeat1($.parserState), - parserState: $ => seq(optional($.annotations), $.state, $.identifier, '{', optional($.parserLocalElements), optional($.parserStatements), $.parserTransitionStatement, '}'), + parserState: $ => seq(optional($.annotations), $.state, $.identifier, '{', optional($.parserStatements), $.parserTransitionStatement, '}'), parserLocalElement: $ => choice($.variableDeclaration, $.todo), @@ -75,7 +75,7 @@ export default grammar({ // General statements statements: $ => repeat1($.statement), - statement: $ => choice($.conditionalStatement, $.blockStatement, $.expressionStatement, $.assignmentStatement),// Limited, so far. + statement: $ => choice($.conditionalStatement, $.blockStatement, $.expressionStatement, $.assignmentStatement, $.variableDeclaration),// Limited, so far. blockStatement: $ => seq(optional($.annotations), '{', optional($.statements), '}'), conditionalStatement: $ => choice(prec(1, seq($.if, '(', $.expression, ')', $.statement)), prec(2, seq($.if, '(', $.expression, ')', $.statement, $.else, $.statement))), expressionStatement: $=> seq($.expression, $._semicolon), @@ -83,7 +83,7 @@ export default grammar({ // Parser statements parserStatements: $ => repeat1($.parserStatement), - parserStatement: $ => choice($.conditionalStatement, $.parserBlockStatement, $.expressionStatement, $.assignmentStatement), // Limited, so far. + parserStatement: $ => choice($.conditionalStatement, $.parserBlockStatement, $.expressionStatement, $.assignmentStatement, $.variableDeclaration), // Limited, so far. parserBlockStatement: $ => seq(optional($.annotations), '{', $.parserStatements, '}'), parserTransitionStatement: $ => seq($.transition, $.transitionSelectionExpression, $._semicolon), diff --git a/tree-sitter-p4/test.txt b/tree-sitter-p4/test.txt index 4236484..b70e319 100644 --- a/tree-sitter-p4/test.txt +++ b/tree-sitter-p4/test.txt @@ -1,7 +1,9 @@ - parser main_parser() { - state start { - bool where_to = true; - string where_from = where_to; - transition reject; - } - }; +parser simple() { + state start { + bool x = true; + int y = 0; + y = 1; + transition accept; + } +}; +