Update Compiler To Use Macros
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
@@ -141,3 +141,9 @@ extension Result: CustomStringConvertible {
|
|||||||
#externalMacro(module: "Macros", type: "UseOkResult")
|
#externalMacro(module: "Macros", type: "UseOkResult")
|
||||||
@freestanding(expression) public macro UseErrorResult<T>(_: Result<T>) -> Error =
|
@freestanding(expression) public macro UseErrorResult<T>(_: Result<T>) -> Error =
|
||||||
#externalMacro(module: "Macros", type: "UseErrorResult")
|
#externalMacro(module: "Macros", type: "UseErrorResult")
|
||||||
|
@freestanding(codeItem) public macro RequireNodeType<N, T>(node: N, type: String, msg: String) =
|
||||||
|
#externalMacro(module: "Macros", type: "RequireNodeType")
|
||||||
|
@freestanding(codeItem) public macro RequireNodesType<N, T>(nodes: N, type: [String], msg: [String]) =
|
||||||
|
#externalMacro(module: "Macros", type: "RequireNodesType")
|
||||||
|
@freestanding(codeItem) public macro SkipUnlessNodeType<N, T>(node: N, type: String) =
|
||||||
|
#externalMacro(module: "Macros", type: "SkipUnlessNodeType")
|
||||||
|
|||||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import Common
|
||||||
|
import P4Lang
|
||||||
|
import P4Runtime
|
||||||
|
import SwiftTreeSitter
|
||||||
|
import TreeSitterExtensions
|
||||||
|
import TreeSitterP4
|
||||||
|
|
||||||
|
let p4lang = Language(tree_sitter_p4())
|
||||||
|
|
||||||
|
public func ConfigureP4Parser() -> Result<SwiftTreeSitter.Parser> {
|
||||||
|
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)")
|
||||||
|
}
|
||||||
@@ -32,31 +32,17 @@ extension TypedIdentifier: CompilableExpression {
|
|||||||
withScopes scopes: LexicalScopes
|
withScopes scopes: LexicalScopes
|
||||||
) -> Result<EvaluatableExpression?> {
|
) -> Result<EvaluatableExpression?> {
|
||||||
|
|
||||||
guard
|
let node = node.child(at: 0)!
|
||||||
let parser_statement_query = try? SwiftTreeSitter.Query(
|
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(node: node, type: "identifier")
|
||||||
language: p4lang,
|
|
||||||
data: String(
|
|
||||||
"(expression (identifier) @identifier)"
|
|
||||||
).data(using: String.Encoding.utf8)!)
|
|
||||||
else {
|
|
||||||
return Result.Error(Error(withMessage: "Could not compile the tree sitter query"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let qr = parser_statement_query.execute(node: node, in: tree)
|
|
||||||
|
|
||||||
guard let result = qr.next() else {
|
|
||||||
return .Ok(.none)
|
|
||||||
}
|
|
||||||
|
|
||||||
let value_capture = result.captures(named: "identifier")
|
|
||||||
guard
|
guard
|
||||||
case Result.Ok(let type) = scopes.lookup(
|
case Result.Ok(let type) = scopes.lookup(
|
||||||
identifier: Common.Identifier(name: value_capture[0].node.text!))
|
identifier: Common.Identifier(name: node.text!))
|
||||||
else {
|
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,
|
node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree,
|
||||||
withScopes scopes: LexicalScopes
|
withScopes scopes: LexicalScopes
|
||||||
) -> Result<EvaluatableExpression?> {
|
) -> Result<EvaluatableExpression?> {
|
||||||
|
let node = node.child(at: 0)!
|
||||||
|
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(node: node, type: "booleanLiteralExpression")
|
||||||
|
|
||||||
guard
|
if node.text == "false" {
|
||||||
let true_query = try? SwiftTreeSitter.Query(
|
return .Ok(P4BooleanValue(withValue: false))
|
||||||
language: p4lang,
|
} else if node.text == "true" {
|
||||||
data: String(
|
|
||||||
"(expression (true))"
|
|
||||||
).data(using: String.Encoding.utf8)!)
|
|
||||||
else {
|
|
||||||
return Result.Error(Error(withMessage: "Could not compile the tree sitter query"))
|
|
||||||
}
|
|
||||||
|
|
||||||
let true_qr = true_query.execute(node: node, in: tree)
|
|
||||||
|
|
||||||
if true_qr.next() != nil {
|
|
||||||
return .Ok(P4BooleanValue(withValue: true))
|
return .Ok(P4BooleanValue(withValue: true))
|
||||||
}
|
}
|
||||||
|
|
||||||
guard
|
return .Error(ErrorOnNode(node: node, withError: "Failed to parse boolean literal: \(node.text!)"))
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,29 +69,12 @@ extension P4IntValue: CompilableExpression {
|
|||||||
node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree,
|
node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree,
|
||||||
withScopes scopes: LexicalScopes
|
withScopes scopes: LexicalScopes
|
||||||
) -> Result<EvaluatableExpression?> {
|
) -> Result<EvaluatableExpression?> {
|
||||||
|
let node = node.child(at: 0)!
|
||||||
guard
|
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(node: node, type: "integer")
|
||||||
let integer_query = try? SwiftTreeSitter.Query(
|
if let parsed_int = Int(node.text!) {
|
||||||
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!) {
|
|
||||||
return .Ok(P4IntValue(withValue: parsed_int))
|
return .Ok(P4IntValue(withValue: parsed_int))
|
||||||
} else {
|
} else {
|
||||||
print("HERE!!")
|
return .Error(ErrorOnNode(node: node, withError: "Failed to parse integer: \(node.text!)"))
|
||||||
return .Error(Error(withMessage: "Failed to parse integer: \(result.captures[0].node.text!)"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,24 +84,9 @@ extension P4StringValue: CompilableExpression {
|
|||||||
node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree,
|
node: SwiftTreeSitter.Node, inTree tree: SwiftTreeSitter.MutableTree,
|
||||||
withScopes scopes: LexicalScopes
|
withScopes scopes: LexicalScopes
|
||||||
) -> Result<EvaluatableExpression?> {
|
) -> Result<EvaluatableExpression?> {
|
||||||
|
let node = node.child(at: 0)!
|
||||||
guard
|
#SkipUnlessNodeType<SwiftTreeSitter.Node, EvaluatableExpression?>(node: node, type: "string_literal")
|
||||||
let string_query = try? SwiftTreeSitter.Query(
|
return .Ok(P4StringValue(withValue: node.text!))
|
||||||
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!))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +94,17 @@ struct Expression {
|
|||||||
public static func Compile(
|
public static func Compile(
|
||||||
node: Node, inTree: MutableTree, withScopes scopes: LexicalScopes
|
node: Node, inTree: MutableTree, withScopes scopes: LexicalScopes
|
||||||
) -> Result<EvaluatableExpression> {
|
) -> Result<EvaluatableExpression> {
|
||||||
|
|
||||||
|
#RequireNodesType<Node, EvaluatableExpression>(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] = [
|
let localElementsParsers: [CompilableExpression.Type] = [
|
||||||
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self,
|
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -22,21 +22,12 @@ import SwiftTreeSitter
|
|||||||
import TreeSitterExtensions
|
import TreeSitterExtensions
|
||||||
import TreeSitterP4
|
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 {
|
extension ParserAssignmentStatement: CompilableStatement {
|
||||||
public static func Compile(
|
public static func Compile(
|
||||||
node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes
|
node: Node, inTree tree: MutableTree, withScopes scopes: LexicalScopes
|
||||||
) -> Result<(EvaluatableStatement, LexicalScopes)> {
|
) -> Result<(EvaluatableStatement, LexicalScopes)> {
|
||||||
|
|
||||||
if node.nodeType != "assignmentStatement" {
|
#RequireNodeType<Node, (EvaluatableStatement, LexicalScopes)>(node: node, type: "assignmentStatement", msg: "assignment statement")
|
||||||
return Result.Error(
|
|
||||||
ErrorOnNode(node: node, withError: "Did not find expected assignment statement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let lvalue_node = node.child(at: 0),
|
guard let lvalue_node = node.child(at: 0),
|
||||||
lvalue_node.nodeType == "expression"
|
lvalue_node.nodeType == "expression"
|
||||||
@@ -101,7 +92,7 @@ public struct Parser {
|
|||||||
guard let parser = localElementsParsers[node.nodeType ?? ""] else {
|
guard let parser = localElementsParsers[node.nodeType ?? ""] else {
|
||||||
return Result.Error(
|
return Result.Error(
|
||||||
ErrorOnNode(
|
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) {
|
switch parser.Compile(node: node, inTree: tree, withScopes: scopes) {
|
||||||
@@ -135,7 +126,7 @@ public struct Parser {
|
|||||||
guard let parser = statementParsers[statement.nodeType ?? ""] else {
|
guard let parser = statementParsers[statement.nodeType ?? ""] else {
|
||||||
return Result.Error(
|
return Result.Error(
|
||||||
ErrorOnNode(
|
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) {
|
switch parser.Compile(node: statement, inTree: tree, withScopes: scopes) {
|
||||||
case Result.Ok(let (parsed, updatedLexicalScopes)):
|
case Result.Ok(let (parsed, updatedLexicalScopes)):
|
||||||
|
|||||||
@@ -24,12 +24,10 @@ import TreeSitterP4
|
|||||||
|
|
||||||
public struct Program {
|
public struct Program {
|
||||||
public static func Compile(_ source: String) -> Result<P4Lang.Program> {
|
public static func Compile(_ source: String) -> Result<P4Lang.Program> {
|
||||||
let p = SwiftTreeSitter.Parser.init()
|
|
||||||
|
|
||||||
do {
|
let maybe_parser = ConfigureP4Parser()
|
||||||
try p.setLanguage(p4lang)
|
guard case .Ok(let p) = maybe_parser else {
|
||||||
} catch {
|
return .Error(maybe_parser.error()!)
|
||||||
return Result.Error(Error(withMessage: "Could not configure the P4 parser"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = p.parse(source)
|
let result = p.parse(source)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import SwiftTreeSitter
|
|||||||
import Testing
|
import Testing
|
||||||
import TreeSitter
|
import TreeSitter
|
||||||
import TreeSitterP4
|
import TreeSitterP4
|
||||||
|
import P4Lang
|
||||||
|
|
||||||
@testable import P4Compiler
|
@testable import P4Compiler
|
||||||
|
|
||||||
@@ -93,3 +94,25 @@ import TreeSitterP4
|
|||||||
#expect(compilation_error.msg.contains("asde"))
|
#expect(compilation_error.msg.contains("asde"))
|
||||||
#expect(compilation_error.msg.contains("asdf"))
|
#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())))
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user