compiler: Refactor Compiler To Remove Ambiguities
Continuous Integration / Grammar Tests (push) Successful in 39s
Continuous Integration / Library Format Tests (push) Successful in 1m51s
Continuous Integration / Library Tests (push) Failing after 4m44s

There were significant overlaps in the names of data structures
between the compiler and the language that made it necessary
to litter the code with P4Lang.xxxx. This refactor removes that
requirement in most places (Parser is ambiguous wherever TreeSitter
is used -- cannot avoid that!)

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
Will Hawkins
2026-05-27 12:59:29 -04:00
parent 61d8f601e8
commit 294f76acd4
39 changed files with 735 additions and 699 deletions
+155 -312
View File
@@ -22,322 +22,165 @@ import SwiftTreeSitter
import TreeSitterExtensions
import TreeSitterP4
public struct Parser {
public struct LocalElements {
static func Compile(
node: Node, withContext context: CompilerContext
) -> Result<(EvaluatableStatement, CompilerContext)> {
let localElementsParsers: [String: CompilableStatement.Type] = [
"variableDeclaration": VariableDeclarationStatement.self
]
extension Statement: Compilable {
public typealias T = EvaluatableStatement
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(EvaluatableStatement, CompilerContext)> {
guard let parser = localElementsParsers[node.nodeType ?? ""] else {
return Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Unparseable statement type (\(node.nodeType ?? "Unknown Statement Type"))"))
}
switch parser.Compile(node: node, withContext: context) {
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 Statement {
static func Compile(
node: Node, withContext context: CompilerContext
) -> Result<(EvaluatableStatement, CompilerContext)> {
if node.nodeType != "parserStatement" && node.nodeType != "statement" {
return Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), 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,
"return_statement": ReturnStatement.self,
]
guard let parser = statementParsers[statement.nodeType ?? ""] else {
return Result.Error(
ErrorWithLocation(
sourceLocation: statement.toSourceLocation(),
withError:
"Unparseable statement type (\(statement.nodeType ?? "Unknown Statement Type"))"))
}
switch parser.Compile(node: statement, withContext: context) {
case Result.Ok(let (parsed, updated_context)):
return .Ok((parsed, updated_context))
case Result.Error(let e):
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Failed to parse a statement element: \(e)"))
}
}
}
public struct TransitionStatement {
static func Compile(
node: Node, forState state_identifier: Common.Identifier,
withStatements stmts: [EvaluatableStatement], withContext context: CompilerContext
) -> Result<(ParserState, CompilerContext)> {
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
node: node, type: "parserTransitionStatement", nice_type_name: "parser transition statement"
if node.nodeType != "parserStatement" && node.nodeType != "statement" {
return Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing expected parser statement")
)
guard let tse_node = node.child(at: 1),
tse_node.nodeType! == "transitionSelectionExpression"
else {
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Could not find transition select expression"))
}
guard let next_node = tse_node.child(at: 0) else {
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Could not find the next token in a transition selection expression"))
}
// If the next node is an identifier, we have the simple form ...
if next_node.nodeType == "identifier" {
let maybe_parsed_next_state_id = Identifier.Compile(
node: next_node, withContext: context)
if case .Ok(let next_state_id) = maybe_parsed_next_state_id {
if case .Ok(let next_state) = context.instances.lookup(identifier: next_state_id) {
switch next_state {
case (_, .some(let instance)):
return .Ok(
(
ParserStateDirectTransition(
name: state_identifier,
withNextState: instance.dataValue() as! InstantiatedParserState,
withStatements: stmts), context
))
case (_, .none):
return .Ok(
(
ParserStateDirectTransition(
name: state_identifier,
withNextStateIdentifier: next_state_id, withStatements: stmts), context
))
}
} else {
return .Error(
Error(
withMessage:
"\(next_state_id) does not name a parser state in scope"
))
}
} else {
return .Error(
Error(
withMessage:
"Could not parse the next state in a transition statement: \(maybe_parsed_next_state_id.error()!)"
))
}
}
// We know that the next node is a select expression.
return
switch SelectExpression.compile(node: next_node, withContext: context)
{
case .Ok(let tse):
.Ok(
(
ParserStateSelectTransition(
name: state_identifier, withTransitionExpression: tse as! SelectExpression,
withStatements: stmts,
), context
))
case .Error(let e): .Error(e)
}
}
}
public struct Statements {
static func Compile(
node: Node, withContext context: CompilerContext
) -> Result<([EvaluatableStatement], CompilerContext)> {
if node.nodeType != "statements" && node.nodeType != "parserStatements" {
return Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Did not find expected statements"))
}
var errors: (any Errorable)? = .none
var current_context = context
var parsed_s: [EvaluatableStatement] = Array()
node.enumerateNamedChildren { node in
switch Statement.Compile(
node: node, withContext: current_context)
{
case .Ok((let parsed_statement, let updated_context)):
current_context = updated_context
parsed_s.append(parsed_statement)
case .Error(let e):
errors =
if let errors = errors {
errors.append(error: e)
} else {
e
}
}
}
if let errors = errors {
return .Error(errors)
}
return Result.Ok((parsed_s, current_context))
}
}
public struct State {
static func Compile(
node: Node, withContext context: CompilerContext
) -> Result<(ParserState, CompilerContext)> {
var walker = Walker(node: node)
var current_node: Node? = .none
guard let node_type = node.nodeType,
node_type == "parserState"
else {
return Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Did not find a parser state declaration"))
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParserState, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing elements in parser state declaration")))
if current_node!.nodeType == "annotations" {
return Result.Error(
ErrorWithLocation(
sourceLocation: current_node!.toSourceLocation(),
withError: "Annotations in parser state are not yet handled."))
// Would increment here.
}
// Skip the keyword state
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParserState, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing elements in parser state declaration")))
let maybe_state_identifier = Identifier.Compile(
node: current_node!, withContext: context)
guard case Result.Ok(let state_identifier) = maybe_state_identifier else {
return Result.Error(maybe_state_identifier.error()!)
}
walker.next()
// Skip the '{'
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParserState, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing body of state declaration")
))
var errors: (any Errorable)? = .none
var current_context = context
var parsed_s: [EvaluatableStatement] = Array()
if current_node!.nodeType == "parserStatements" {
switch Statements.Compile(
node: current_node!, withContext: current_context)
{
case .Ok(let (state_statements, updated_context)):
parsed_s = state_statements
current_context = updated_context
case .Error(let error):
errors =
if let errors = errors {
errors.append(error: error)
} else {
error
}
}
walker.next()
}
if let errors = errors {
return .Error(errors)
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParserState, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing transition statement of state declaration")))
return TransitionStatement.Compile(
node: current_node!, forState: state_identifier, withStatements: parsed_s,
withContext: current_context)
}
}
static func Compile(
withName name: Common.Identifier, withParameters parameters: ParameterList, node: Node,
withContext context: CompilerContext
) -> Result<(P4Lang.Parser, CompilerContext)> {
var parser = P4Lang.Parser(withName: name, withParameters: parameters)
// Build a state from each one listed.
var error: (any Errorable)? = .none
var current_context = context
/// TODO: Assert that there is only one.
node.enumerateNamedChildren { parser_state in
if parser_state.nodeType != "parserState" {
return
}
// Parse a state in a nested scope.
switch Parser.State.Compile(
node: parser_state,
withContext: context.update(newInstances: current_context.instances.enter()))
{
case Result.Ok(let (state, updated_context)):
parser.states = parser.states.append(state: state)
current_context = updated_context
case Result.Error(let e): error = e
}
}
if let error = error {
return .Error(error)
}
let statement = node.child(at: 0)!
return Result.Ok((parser, current_context))
let statementParsers: [String: CompilableStatement.Type] = [
"assignmentStatement": ParserAssignmentStatement.self,
"expressionStatement": ExpressionStatement.self,
"variableDeclaration": VariableDeclarationStatement.self,
"conditionalStatement": ConditionalStatement.self, "blockStatement": BlockStatement.self,
"return_statement": ReturnStatement.self,
]
guard let parser = statementParsers[statement.nodeType ?? ""] else {
return Result.Error(
ErrorWithLocation(
sourceLocation: statement.toSourceLocation(),
withError:
"Unparseable statement type (\(statement.nodeType ?? "Unknown Statement Type"))"))
}
switch parser.Compile(node: statement, withContext: context) {
case Result.Ok(let (parsed, updated_context)):
return .Ok((parsed, updated_context))
case Result.Error(let e):
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Failed to parse a statement element: \(e)"))
}
}
}
extension LocalElements: Compilable {
public typealias T = EvaluatableStatement
public static func Compile(
node: Node, withContext context: CompilerContext
) -> Result<(EvaluatableStatement, CompilerContext)> {
let localElementsParsers: [String: CompilableStatement.Type] = [
"variableDeclaration": VariableDeclarationStatement.self
]
guard let parser = localElementsParsers[node.nodeType ?? ""] else {
return Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Unparseable statement type (\(node.nodeType ?? "Unknown Statement Type"))"))
}
switch parser.Compile(node: node, withContext: context) {
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)"))
}
}
}
extension ParserState: Compilable {
public typealias T = ParserState
public static func Compile(
node: Node, withContext context: CompilerContext
) -> Result<(ParserState, CompilerContext)> {
var walker = Walker(node: node)
var current_node: Node? = .none
guard let node_type = node.nodeType,
node_type == "parserState"
else {
return Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Did not find a parser state declaration"))
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParserState, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing elements in parser state declaration")))
if current_node!.nodeType == "annotations" {
return Result.Error(
ErrorWithLocation(
sourceLocation: current_node!.toSourceLocation(),
withError: "Annotations in parser state are not yet handled."))
// Would increment here.
}
// Skip the keyword state
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParserState, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing elements in parser state declaration")))
let maybe_state_identifier = Identifier.Compile(
node: current_node!, withContext: context)
guard case Result.Ok(let state_identifier) = maybe_state_identifier else {
return Result.Error(maybe_state_identifier.error()!)
}
walker.next()
// Skip the '{'
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParserState, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing body of state declaration")
))
var errors: (any Errorable)? = .none
var current_context = context
var parsed_s: [EvaluatableStatement] = Array()
if current_node!.nodeType == "parserStatements" {
switch SpecialCompilers.Statements.Compile(
node: current_node!, withContext: current_context)
{
case .Ok(let (state_statements, updated_context)):
parsed_s = state_statements
current_context = updated_context
case .Error(let error):
errors =
if let errors = errors {
errors.append(error: error)
} else {
error
}
}
walker.next()
}
if let errors = errors {
return .Error(errors)
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(ParserState, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing transition statement of state declaration")))
return SpecialCompilers.TransitionStatement.Compile(
node: current_node!, forState: state_identifier, withStatements: parsed_s,
withContext: current_context)
}
}