compiler: Refactor Transition Statement Compilation

Make it follow the standard protocol(s). Unfortunately that means
that some additional information will have to be carried in
the compilation context. It seems like a decent tradeoff -- but
it may be revisited in the future.

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
Will Hawkins
2026-05-29 17:33:19 -04:00
parent 297288e2b0
commit 4f6de341cc
3 changed files with 146 additions and 77 deletions
+61 -7
View File
@@ -43,6 +43,8 @@ public struct CompilerContext {
public let ffis: [P4FFI] public let ffis: [P4FFI]
public let expected_type: P4QualifiedType? public let expected_type: P4QualifiedType?
public let extern_context: Bool public let extern_context: Bool
public let lexical_context_name: Identifier?
public let lexical_context_statements: [P4Statement]?
public init() { public init() {
instances = StaticVarValueScopes().enter() instances = StaticVarValueScopes().enter()
@@ -51,6 +53,8 @@ public struct CompilerContext {
expected_type = .none expected_type = .none
extern_context = false extern_context = false
ffis = Array() ffis = Array()
lexical_context_name = .none
lexical_context_statements = .none
} }
public init(withInstances _instances: StaticVarValueScopes, withTypes _types: TypeTypeScopes) { public init(withInstances _instances: StaticVarValueScopes, withTypes _types: TypeTypeScopes) {
@@ -60,12 +64,16 @@ public struct CompilerContext {
expected_type = .none expected_type = .none
extern_context = false extern_context = false
ffis = Array() ffis = Array()
lexical_context_name = .none
lexical_context_statements = .none
} }
public init( public init(
withInstances _instances: StaticVarValueScopes, withTypes _types: TypeTypeScopes, withInstances _instances: StaticVarValueScopes, withTypes _types: TypeTypeScopes,
withExpectation expectation: P4QualifiedType?, withExtern extern: Bool, withExpectation expectation: P4QualifiedType?, withExtern extern: Bool,
withExterns externs: TypeTypeScopes, withFFIs foreigns: [P4FFI] withExterns externs: TypeTypeScopes, withFFIs foreigns: [P4FFI],
withLexicalContextName lexical_context_name: Identifier?,
withLexicalContextStatements lexical_context_statements: [P4Statement]?
) { ) {
instances = _instances instances = _instances
types = _types types = _types
@@ -73,6 +81,8 @@ public struct CompilerContext {
extern_context = extern extern_context = extern
self.externs = externs self.externs = externs
ffis = foreigns ffis = foreigns
self.lexical_context_name = lexical_context_name
self.lexical_context_statements = lexical_context_statements
} }
/// Update a compiler context /// Update a compiler context
@@ -84,7 +94,9 @@ public struct CompilerContext {
public func update(newInstances instances: StaticVarValueScopes) -> CompilerContext { public func update(newInstances instances: StaticVarValueScopes) -> CompilerContext {
return CompilerContext( return CompilerContext(
withInstances: instances, withTypes: self.types, withExpectation: self.expected_type, withInstances: instances, withTypes: self.types, withExpectation: self.expected_type,
withExtern: self.extern_context, withExterns: self.externs, withFFIs: self.ffis) withExtern: self.extern_context, withExterns: self.externs, withFFIs: self.ffis,
withLexicalContextName: self.lexical_context_name,
withLexicalContextStatements: self.lexical_context_statements)
} }
/// Update a compiler context /// Update a compiler context
@@ -96,7 +108,9 @@ public struct CompilerContext {
public func update(newTypes types: TypeTypeScopes) -> CompilerContext { public func update(newTypes types: TypeTypeScopes) -> CompilerContext {
return CompilerContext( return CompilerContext(
withInstances: self.instances, withTypes: types, withExpectation: self.expected_type, withInstances: self.instances, withTypes: types, withExpectation: self.expected_type,
withExtern: self.extern_context, withExterns: self.externs, withFFIs: self.ffis) withExtern: self.extern_context, withExterns: self.externs, withFFIs: self.ffis,
withLexicalContextName: self.lexical_context_name,
withLexicalContextStatements: self.lexical_context_statements)
} }
/// Update a compiler context /// Update a compiler context
@@ -108,7 +122,9 @@ public struct CompilerContext {
public func update(newExpectation expectation: P4QualifiedType?) -> CompilerContext { public func update(newExpectation expectation: P4QualifiedType?) -> CompilerContext {
return CompilerContext( return CompilerContext(
withInstances: self.instances, withTypes: self.types, withExpectation: expectation, withInstances: self.instances, withTypes: self.types, withExpectation: expectation,
withExtern: self.extern_context, withExterns: self.externs, withFFIs: self.ffis) withExtern: self.extern_context, withExterns: self.externs, withFFIs: self.ffis,
withLexicalContextName: self.lexical_context_name,
withLexicalContextStatements: self.lexical_context_statements)
} }
/// Update a compiler context /// Update a compiler context
@@ -120,7 +136,9 @@ public struct CompilerContext {
public func update(newExtern extern: Bool) -> CompilerContext { public func update(newExtern extern: Bool) -> CompilerContext {
return CompilerContext( return CompilerContext(
withInstances: self.instances, withTypes: self.types, withExpectation: self.expected_type, withInstances: self.instances, withTypes: self.types, withExpectation: self.expected_type,
withExtern: extern, withExterns: self.externs, withFFIs: self.ffis) withExtern: extern, withExterns: self.externs, withFFIs: self.ffis,
withLexicalContextName: self.lexical_context_name,
withLexicalContextStatements: self.lexical_context_statements)
} }
/// Update a compiler context /// Update a compiler context
@@ -132,7 +150,9 @@ public struct CompilerContext {
public func update(newExterns externs: TypeTypeScopes) -> CompilerContext { public func update(newExterns externs: TypeTypeScopes) -> CompilerContext {
return CompilerContext( return CompilerContext(
withInstances: self.instances, withTypes: self.types, withExpectation: self.expected_type, withInstances: self.instances, withTypes: self.types, withExpectation: self.expected_type,
withExtern: self.extern_context, withExterns: externs, withFFIs: self.ffis) withExtern: self.extern_context, withExterns: externs, withFFIs: self.ffis,
withLexicalContextName: self.lexical_context_name,
withLexicalContextStatements: self.lexical_context_statements)
} }
/// Update a compiler context /// Update a compiler context
@@ -144,6 +164,40 @@ public struct CompilerContext {
public func update(newFFIs foreigns: [P4FFI]) -> CompilerContext { public func update(newFFIs foreigns: [P4FFI]) -> CompilerContext {
return CompilerContext( return CompilerContext(
withInstances: self.instances, withTypes: self.types, withExpectation: self.expected_type, withInstances: self.instances, withTypes: self.types, withExpectation: self.expected_type,
withExtern: self.extern_context, withExterns: externs, withFFIs: foreigns) withExtern: self.extern_context, withExterns: externs, withFFIs: foreigns,
withLexicalContextName: self.lexical_context_name,
withLexicalContextStatements: self.lexical_context_statements)
} }
/// Update a compiler context
///
/// Create a new compiler context based on the current but with a new lexical context name.
///
/// - Parameter new_lexical_context_name: an optional new lexical context name; passing `.none` resets.
/// - Returns: A new compiler context based on the current but with a new lexical context name.
public func update(newLexicalContextName new_lexical_context_name: Identifier?) -> CompilerContext
{
return CompilerContext(
withInstances: self.instances, withTypes: self.types, withExpectation: self.expected_type,
withExtern: self.extern_context, withExterns: externs, withFFIs: self.ffis,
withLexicalContextName: new_lexical_context_name,
withLexicalContextStatements: self.lexical_context_statements)
}
/// Update a compiler context
///
/// Create a new compiler context based on the current but with a new set of lexical context statements.
///
/// - Parameter new_lexical_context_statements: an optional new set of lexical context statements; passing `.none` resets.
/// - Returns: A new compiler context based on the current but with a new set of lexical context statements.
public func update(
newLexicalContextStatements new_lexical_context_statements: [P4Statement]?
) -> CompilerContext {
return CompilerContext(
withInstances: self.instances, withTypes: self.types, withExpectation: self.expected_type,
withExtern: self.extern_context, withExterns: externs, withFFIs: self.ffis,
withLexicalContextName: self.lexical_context_name,
withLexicalContextStatements: new_lexical_context_statements)
}
} }
+4 -3
View File
@@ -178,8 +178,9 @@ extension ParserState: Compilable {
sourceLocation: node.toSourceLocation(), sourceLocation: node.toSourceLocation(),
withError: "Missing transition statement of state declaration"))) withError: "Missing transition statement of state declaration")))
return SpecialCompilers.TransitionStatement.Compile( let updated_context = SpecialCompilers.Statements.effect(statements: parsed_s, context: context)
node: current_node!, forState: state_identifier, withStatements: parsed_s, .update(newLexicalContextName: state_identifier).update(newLexicalContextStatements: parsed_s)
withContext: SpecialCompilers.Statements.effect(statements: parsed_s, context: context))
return TransitionStatement.Compile(node: current_node!, withContext: updated_context)
} }
} }
+81 -67
View File
@@ -28,88 +28,102 @@ protocol AnyCompilable {
) -> Common.Result<(Any, CompilerContext)> ) -> Common.Result<(Any, CompilerContext)>
} }
public struct SpecialCompilers { extension TransitionStatement: Compilable {
public struct TransitionStatement { public static func Compile(
static func Compile( node: Node, withContext context: CompilerContext
node: Node, forState state_identifier: Common.Identifier, ) -> Result<ParserState> {
withStatements stmts: [P4Statement], withContext context: CompilerContext
) -> Result<ParserState> {
#RequireNodeType<Node, P4Statement>( guard let state_identifier = context.lexical_context_name else {
node: node, type: "parserTransitionStatement", nice_type_name: "parser transition statement" return .Error(
) ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Cannot parse a transition statement without the name of the containing state."))
}
guard let tse_node = node.child(at: 1), guard let stmts = context.lexical_context_statements else {
tse_node.nodeType! == "transitionSelectionExpression" return .Error(
else { ErrorWithLocation(
return .Error( sourceLocation: node.toSourceLocation(),
ErrorWithLocation( withError: "Cannot parse a transition statement without statements of the containing state."))
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 ... #RequireNodeType<Node, P4Statement>(
if next_node.nodeType == "identifier" { node: node, type: "parserTransitionStatement", nice_type_name: "parser transition statement"
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 { guard let tse_node = node.child(at: 1),
if case .Ok(let next_state) = context.instances.lookup(identifier: next_state_id) { tse_node.nodeType! == "transitionSelectionExpression"
switch next_state { else {
case (_, .some(let instance)): return .Error(
return .Ok( ErrorWithLocation(
ParserStateDirectTransition( sourceLocation: node.toSourceLocation(),
name: state_identifier, withError: "Could not find transition select expression"))
withNextState: instance.dataValue() as! InstantiatedParserState, }
withStatements: stmts,
) 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,
) )
case (_, .none): )
return .Ok( case (_, .none):
ParserStateDirectTransition( return .Ok(
name: state_identifier, ParserStateDirectTransition(
withNextStateIdentifier: next_state_id, withStatements: stmts) name: state_identifier,
) withNextStateIdentifier: next_state_id, withStatements: stmts)
} )
} else {
return .Error(
Error(
withMessage:
"\(next_state_id) does not name a parser state in scope"
))
} }
} else { } else {
return .Error( return .Error(
Error( Error(
withMessage: withMessage:
"Could not parse the next state in a transition statement: \(maybe_parsed_next_state_id.error()!)" "\(next_state_id) does not name a parser state in scope"
)) ))
} }
} } else {
return .Error(
// We know that the next node is a select expression. Error(
return withMessage:
switch SelectExpression.compile(node: next_node, withContext: context) "Could not parse the next state in a transition statement: \(maybe_parsed_next_state_id.error()!)"
{ ))
case .Ok(let tse):
.Ok(
ParserStateSelectTransition(
name: state_identifier, withTransitionExpression: tse as! SelectExpression,
withStatements: stmts,
)
)
case .Error(let e): .Error(e)
} }
} }
}
// 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,
)
)
case .Error(let e): .Error(e)
}
}
}
public struct SpecialCompilers {
public struct Statements { public struct Statements {
static func Compile( static func Compile(
node: Node, withContext context: CompilerContext node: Node, withContext context: CompilerContext