From c3b3be77eb11cc7638812ea4ab784f36f4a5c6ee Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Fri, 6 Mar 2026 08:02:35 -0500 Subject: [PATCH] Update Compiler To Use Macros Signed-off-by: Will Hawkins --- Sources/Common/Support.swift | 6 ++ Sources/P4Compiler/Compiler.swift | 41 ++++++++++ Sources/P4Compiler/Expression.swift | 116 +++++++--------------------- Sources/P4Compiler/Parser.swift | 15 +--- Sources/P4Compiler/Program.swift | 8 +- Tests/p4rseTests/ParserTests.swift | 23 ++++++ 6 files changed, 105 insertions(+), 104 deletions(-) create mode 100644 Sources/P4Compiler/Compiler.swift diff --git a/Sources/Common/Support.swift b/Sources/Common/Support.swift index 58b9625..9557db6 100644 --- a/Sources/Common/Support.swift +++ b/Sources/Common/Support.swift @@ -141,3 +141,9 @@ 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) = + #externalMacro(module: "Macros", type: "RequireNodeType") +@freestanding(codeItem) public macro RequireNodesType(nodes: N, type: [String], msg: [String]) = + #externalMacro(module: "Macros", type: "RequireNodesType") +@freestanding(codeItem) public macro SkipUnlessNodeType(node: N, type: String) = + #externalMacro(module: "Macros", type: "SkipUnlessNodeType") diff --git a/Sources/P4Compiler/Compiler.swift b/Sources/P4Compiler/Compiler.swift new file mode 100644 index 0000000..6419e71 --- /dev/null +++ b/Sources/P4Compiler/Compiler.swift @@ -0,0 +1,41 @@ +// 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 + +let p4lang = Language(tree_sitter_p4()) + +public func ConfigureP4Parser() -> Result { + let p = SwiftTreeSitter.Parser.init() + + do { + try p.setLanguage(p4lang) + } catch { + return Result.Error(Error(withMessage: "Could not configure the P4 parser")) + } + + return .Ok(p) +} + +public func ErrorOnNode(node: Node, withError error: String) -> Error { + return Error(withMessage: "\(node.range): \(error)") +} diff --git a/Sources/P4Compiler/Expression.swift b/Sources/P4Compiler/Expression.swift index fc1512e..8186d0f 100644 --- a/Sources/P4Compiler/Expression.swift +++ b/Sources/P4Compiler/Expression.swift @@ -32,31 +32,17 @@ extension TypedIdentifier: CompilableExpression { withScopes scopes: LexicalScopes ) -> Result { - guard - let parser_statement_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(expression (identifier) @identifier)" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Error(Error(withMessage: "Could not compile the tree sitter query")) - } + let node = node.child(at: 0)! + #SkipUnlessNodeType(node: node, type: "identifier") - let qr = parser_statement_query.execute(node: node, in: tree) - - guard let result = qr.next() else { - return .Ok(.none) - } - - let value_capture = result.captures(named: "identifier") guard case Result.Ok(let type) = scopes.lookup( - identifier: Common.Identifier(name: value_capture[0].node.text!)) + identifier: Common.Identifier(name: node.text!)) else { - return .Error(Error(withMessage: "Cannot find \(result.captures[0].node.text!) in scope")) + return .Error(ErrorOnNode(node: node, withError: "Cannot find \(node.text!) in scope")) } - return .Ok(TypedIdentifier(name: result.captures[0].node.text!, withType: type)) + return .Ok(TypedIdentifier(name: node.text!, withType: type)) } } @@ -65,39 +51,16 @@ extension P4BooleanValue: CompilableExpression { node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree, withScopes scopes: LexicalScopes ) -> Result { + let node = node.child(at: 0)! + #SkipUnlessNodeType(node: node, type: "booleanLiteralExpression") - guard - let true_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(expression (true))" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Error(Error(withMessage: "Could not compile the tree sitter query")) - } - - let true_qr = true_query.execute(node: node, in: tree) - - if true_qr.next() != nil { + if node.text == "false" { + return .Ok(P4BooleanValue(withValue: false)) + } else if node.text == "true" { return .Ok(P4BooleanValue(withValue: true)) } - guard - let false_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(expression (false))" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Error(Error(withMessage: "Could not compile the tree sitter query")) - } - - let false_qr = false_query.execute(node: node, in: tree) - - if false_qr.next() != nil { - return .Ok(P4BooleanValue(withValue: false)) - } - return .Ok(.none) + return .Error(ErrorOnNode(node: node, withError: "Failed to parse boolean literal: \(node.text!)")) } } @@ -106,29 +69,12 @@ extension P4IntValue: CompilableExpression { node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree, withScopes scopes: LexicalScopes ) -> Result { - - guard - let integer_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(expression (integer) @value)" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Error(Error(withMessage: "Could not compile the tree sitter query")) - } - - let integer_qr = integer_query.execute(node: node, in: tree) - - guard let result = integer_qr.next() else { - return .Ok(.none) - } - - let value_capture = result.captures(named: "value") - if let parsed_int = Int(value_capture[0].node.text!) { + let node = node.child(at: 0)! + #SkipUnlessNodeType(node: node, type: "integer") + if let parsed_int = Int(node.text!) { return .Ok(P4IntValue(withValue: parsed_int)) } else { - print("HERE!!") - return .Error(Error(withMessage: "Failed to parse integer: \(result.captures[0].node.text!)")) + return .Error(ErrorOnNode(node: node, withError: "Failed to parse integer: \(node.text!)")) } } } @@ -138,24 +84,9 @@ extension P4StringValue: CompilableExpression { node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree, withScopes scopes: LexicalScopes ) -> Result { - - guard - let string_query = try? SwiftTreeSitter.Query( - language: p4lang, - data: String( - "(expression (string_literal) @value)" - ).data(using: String.Encoding.utf8)!) - else { - return Result.Error(Error(withMessage: "Could not compile the tree sitter query")) - } - - let string_qr = string_query.execute(node: node, in: tree) - - guard let result = string_qr.next() else { - return .Ok(.none) - } - - return .Ok(P4StringValue(withValue: result.captures[0].node.text!)) + let node = node.child(at: 0)! + #SkipUnlessNodeType(node: node, type: "string_literal") + return .Ok(P4StringValue(withValue: node.text!)) } } @@ -163,6 +94,17 @@ struct Expression { public static func Compile( node: Node, inTree: MutableTree, withScopes scopes: LexicalScopes ) -> Result { + + #RequireNodesType(nodes: node, type: ["expression", "keysetExpression"], msg: ["expression", "keyset expression"]) + + // If the node is a keyset expression, then dig out the expression: + let node = + if node.nodeType == "keysetExpression" { + node.child(at: 0)! + } else { + node + } + let localElementsParsers: [CompilableExpression.Type] = [ P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self, ] diff --git a/Sources/P4Compiler/Parser.swift b/Sources/P4Compiler/Parser.swift index f376559..3a5d5b4 100644 --- a/Sources/P4Compiler/Parser.swift +++ b/Sources/P4Compiler/Parser.swift @@ -22,21 +22,12 @@ import SwiftTreeSitter import TreeSitterExtensions import TreeSitterP4 -let p4lang = Language(tree_sitter_p4()) - -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)> { - if node.nodeType != "assignmentStatement" { - return Result.Error( - ErrorOnNode(node: node, withError: "Did not find expected assignment statement")) - } + #RequireNodeType(node: node, type: "assignmentStatement", msg: "assignment statement") guard let lvalue_node = node.child(at: 0), lvalue_node.nodeType == "expression" @@ -101,7 +92,7 @@ public struct Parser { guard let parser = localElementsParsers[node.nodeType ?? ""] else { return Result.Error( ErrorOnNode( - node: node, withError: "Unparseable statement type (\(node.nodeType))")) + node: node, withError: "Unparseable statement type (\(node.nodeType ?? "Unknown Statement Type"))")) } switch parser.Compile(node: node, inTree: tree, withScopes: scopes) { @@ -135,7 +126,7 @@ public struct Parser { guard let parser = statementParsers[statement.nodeType ?? ""] else { return Result.Error( ErrorOnNode( - node: statement, withError: "Unparseable statement type (\(statement.nodeType))")) + node: statement, withError: "Unparseable statement type (\(statement.nodeType ?? "Unknown Statement Type"))")) } switch parser.Compile(node: statement, inTree: tree, withScopes: scopes) { case Result.Ok(let (parsed, updatedLexicalScopes)): diff --git a/Sources/P4Compiler/Program.swift b/Sources/P4Compiler/Program.swift index edf91b7..daee625 100644 --- a/Sources/P4Compiler/Program.swift +++ b/Sources/P4Compiler/Program.swift @@ -24,12 +24,10 @@ import TreeSitterP4 public struct Program { public static func Compile(_ source: String) -> Result { - let p = SwiftTreeSitter.Parser.init() - do { - try p.setLanguage(p4lang) - } catch { - return Result.Error(Error(withMessage: "Could not configure the P4 parser")) + let maybe_parser = ConfigureP4Parser() + guard case .Ok(let p) = maybe_parser else { + return .Error(maybe_parser.error()!) } let result = p.parse(source) diff --git a/Tests/p4rseTests/ParserTests.swift b/Tests/p4rseTests/ParserTests.swift index 453578c..f42ebef 100644 --- a/Tests/p4rseTests/ParserTests.swift +++ b/Tests/p4rseTests/ParserTests.swift @@ -23,6 +23,7 @@ import SwiftTreeSitter import Testing import TreeSitter import TreeSitterP4 +import P4Lang @testable import P4Compiler @@ -93,3 +94,25 @@ import TreeSitterP4 #expect(compilation_error.msg.contains("asde")) #expect(compilation_error.msg.contains("asdf")) } + +@Test func test_simple_parser_macro_nodetype_test() async throws { + let simple = """ + parser main_parser() { + state start { + transition select (false) { + asdf: reject; + asde: reject; + }; + } + }; + """ + + let p = try! #UseOkResult(ConfigureP4Parser()) + let result = try! #require(p.parse(simple)) + + #expect( + #RequireErrorResult<(EvaluatableStatement, LexicalScopes)>( + Error(withMessage: "{2, 154}: Did not find assignment statement"), + ParserAssignmentStatement.Compile( + node: result.rootNode!, inTree: result, withScopes: LexicalScopes()))) +}