compiler, language, runtime: Separate Parser Type From Instances
Continuous Integration / Grammar Tests (push) Successful in 4m2s
Continuous Integration / Library Format Tests (push) Successful in 5m0s
Continuous Integration / Library Tests (push) Successful in 8m1s

In P4, parsers are considered types. Those parsers are instantiated.
The instantiated parsers are values. Previously, gp4 treated a parser
type and a parser value as identical. This PR makes that difference
clear _and_ sets the stage for the future.

TODO: Make the same distinction between control and action types and
values.

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
Will Hawkins
2026-05-27 05:41:23 -04:00
parent 925f20a13b
commit 61d8f601e8
36 changed files with 1058 additions and 796 deletions
+5 -5
View File
@@ -46,7 +46,7 @@ public func ConfigureP4Parser() -> Result<SwiftTreeSitter.Parser> {
/// knowledge of an expected type. For instance, when compiling a return statement, the
/// compiler must know the return type of the function to type check.
public struct CompilerContext {
let instances: VarTypeScopes
let instances: StaticVarValueScopes
let types: TypeTypeScopes
let externs: TypeTypeScopes
let ffis: [P4FFI]
@@ -54,7 +54,7 @@ public struct CompilerContext {
let extern_context: Bool
public init() {
instances = VarTypeScopes().enter()
instances = StaticVarValueScopes().enter()
types = TypeTypeScopes().enter()
externs = TypeTypeScopes().enter()
expected_type = .none
@@ -62,7 +62,7 @@ public struct CompilerContext {
ffis = Array()
}
public init(withInstances _instances: VarTypeScopes, withTypes _types: TypeTypeScopes) {
public init(withInstances _instances: StaticVarValueScopes, withTypes _types: TypeTypeScopes) {
instances = _instances
types = _types
externs = TypeTypeScopes().enter()
@@ -72,7 +72,7 @@ public struct CompilerContext {
}
public init(
withInstances _instances: VarTypeScopes, withTypes _types: TypeTypeScopes,
withInstances _instances: StaticVarValueScopes, withTypes _types: TypeTypeScopes,
withExpectation expectation: P4QualifiedType?, withExtern extern: Bool,
withExterns externs: TypeTypeScopes, withFFIs foreigns: [P4FFI]
) {
@@ -90,7 +90,7 @@ public struct CompilerContext {
///
/// - Parameter instances: a ``VarTypeScopes`` with the updated instances for the newly created compiler context.
/// - Returns: A new compiler context based on the current but new instances.
public func update(newInstances instances: VarTypeScopes) -> CompilerContext {
public func update(newInstances instances: StaticVarValueScopes) -> CompilerContext {
return CompilerContext(
withInstances: instances, withTypes: self.types, withExpectation: self.expected_type,
withExtern: self.extern_context, withExterns: self.externs, withFFIs: self.ffis)
+20 -15
View File
@@ -105,7 +105,7 @@ extension FunctionDeclaration: CompilableDeclaration {
var function_scope = context.instances.enter()
for parameter in function_parameters.parameters {
function_scope = function_scope.declare(
identifier: parameter.name, withValue: parameter.type)
identifier: parameter.name, withValue: (parameter.type, .none))
}
let maybe_function_body = BlockStatement.Compile(
@@ -342,7 +342,7 @@ extension P4Lang.Parser: CompilableDeclaration {
for parameter in parameter_list.parameters {
current_context = current_context.update(
newInstances: current_context.instances.declare(
identifier: parameter.name, withValue: parameter.type))
identifier: parameter.name, withValue: (parameter.type, .none)))
}
walker.next()
@@ -380,6 +380,7 @@ extension P4Lang.Parser: CompilableDeclaration {
return .Error(Error(withMessage: "Missing parser states in parser declaration"))
}
/// TODO: Handle extern parsers.
switch Parser.Compile(
withName: parser_name!, withParameters: parameter_list, node: current_node!,
withContext: current_context)
@@ -392,10 +393,11 @@ extension P4Lang.Parser: CompilableDeclaration {
(
parser_declaration,
context.extern_context
? context
: context.update(
newInstances: updated_context.instances.declare(
identifier: parser.name, withValue: parser_declaration.identifier.type))
? updated_context.update(
newExterns: updated_context.externs.declare(
identifier: parser.name, withValue: parser_declaration))
: updated_context.update(
newTypes: updated_context.types.declare(identifier: parser.name, withValue: parser))
))
case Result.Error(let error): return .Error(error)
}
@@ -450,7 +452,7 @@ extension Control: CompilableDeclaration {
var control_scope = local_context.instances.enter()
for parameter in control_parameters.parameters {
control_scope = control_scope.declare(
identifier: parameter.name, withValue: parameter.type)
identifier: parameter.name, withValue: (parameter.type, .none))
}
local_context = local_context.update(newInstances: control_scope)
@@ -543,14 +545,15 @@ extension Control: CompilableDeclaration {
))
}
let control = Control(
named: control_name, withParameters: control_parameters, withTable: tables[0],
withActions: Actions(withActions: actions), withApply: apply)
let declared_control =
Declaration(
TypedIdentifier(
id: control_name,
withType: P4QualifiedType(
Control(
named: control_name, withParameters: control_parameters, withTable: tables[0],
withActions: Actions(withActions: actions), withApply: apply))))
withType: P4QualifiedType(control)))
// Don't forget to add the newly declared Control to the instance that we were given
// (and not the one that we entered to do the parsing of this Control).
@@ -558,10 +561,12 @@ extension Control: CompilableDeclaration {
(
declared_control,
context.extern_context
? context
? context.update(
newTypes: context.externs.declare(
identifier: control_name, withValue: declared_control))
: context.update(
newInstances: context.instances.declare(
identifier: control_name, withValue: declared_control.identifier.type))
newTypes: context.types.declare(
identifier: control_name, withValue: control))
))
}
}
@@ -638,7 +643,7 @@ extension Action: Compilable {
var function_scope = context.instances.enter()
for parameter in action_parameters.parameters {
function_scope = function_scope.declare(
identifier: parameter.name, withValue: parameter.type)
identifier: parameter.name, withValue: (parameter.type, .none))
}
let maybe_action_body = BlockStatement.Compile(
+2 -1
View File
@@ -51,7 +51,8 @@ extension TypedIdentifier: CompilableExpression {
sourceLocation: node.toSourceLocation(), withError: "Cannot find \(node.text!) in scope"))
}
return .Ok(TypedIdentifier(name: node.text!, withType: type))
/// TODO: If there is a value here, then we can make this a compile-time constant!
return .Ok(TypedIdentifier(name: node.text!, withType: type.0))
}
}
+38 -16
View File
@@ -90,7 +90,7 @@ public struct Parser {
static func Compile(
node: Node, forState state_identifier: Common.Identifier,
withStatements stmts: [EvaluatableStatement], withContext context: CompilerContext
) -> Result<(InstantiatedParserState, CompilerContext)> {
) -> Result<(ParserState, CompilerContext)> {
#RequireNodeType<Node, (EvaluatableStatement, CompilerContext)>(
node: node, type: "parserTransitionStatement", nice_type_name: "parser transition statement"
@@ -114,19 +114,40 @@ public struct Parser {
// If the next node is an identifier, we have the simple form ...
if next_node.nodeType == "identifier" {
let maybe_parsed_next_state = Identifier.Compile(
let maybe_parsed_next_state_id = Identifier.Compile(
node: next_node, withContext: context)
if case .Ok(let next_state) = maybe_parsed_next_state {
return .Ok(
(
ParserStateDirectTransition(
name: state_identifier, withStatements: stmts, withNextState: next_state), 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.error()!)"
"Could not parse the next state in a transition statement: \(maybe_parsed_next_state_id.error()!)"
))
}
}
@@ -139,8 +160,9 @@ public struct Parser {
.Ok(
(
ParserStateSelectTransition(
name: state_identifier, withStatements: stmts,
withTransitioniExpression: tse as! SelectExpression), context
name: state_identifier, withTransitionExpression: tse as! SelectExpression,
withStatements: stmts,
), context
))
case .Error(let e): .Error(e)
}
@@ -189,7 +211,7 @@ public struct Parser {
public struct State {
static func Compile(
node: Node, withContext context: CompilerContext
) -> Result<(InstantiatedParserState, CompilerContext)> {
) -> Result<(ParserState, CompilerContext)> {
var walker = Walker(node: node)
var current_node: Node? = .none
@@ -205,7 +227,7 @@ public struct Parser {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
or: Result<(ParserState, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing elements in parser state declaration")))
@@ -223,7 +245,7 @@ public struct Parser {
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
or: Result<(ParserState, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing elements in parser state declaration")))
@@ -239,7 +261,7 @@ public struct Parser {
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
or: Result<(ParserState, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing body of state declaration")
))
@@ -272,7 +294,7 @@ public struct Parser {
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(InstantiatedParserState, CompilerContext)>.Error(
or: Result<(ParserState, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing transition statement of state declaration")))
+19 -5
View File
@@ -24,18 +24,30 @@ import TreeSitterP4
public struct Program {
public static func Compile(_ source: String) -> Result<P4Lang.Program> {
return Program.Compile(source, withGlobalInstances: .none, withGlobalTypes: .none, withFFIs: [])
// Certain names are always in scope during compilation.
var globals = StaticVarValueScopes().enter()
globals = globals.declare(
identifier: accept.state().getName(),
withValue: (P4QualifiedType(accept.type()), P4Value(accept))
)
.declare(
identifier: reject.state.getName(),
withValue: (P4QualifiedType(reject.type()), P4Value(reject)))
return Program.Compile(
source, withGlobalInstances: globals, withGlobalTypes: .none, withFFIs: [])
}
public static func Compile(
_ source: String, withGlobalInstances globalInstances: VarTypeScopes
_ source: String, withGlobalInstances globalInstances: StaticVarValueScopes
) -> Result<P4Lang.Program> {
return Program.Compile(
source, withGlobalInstances: globalInstances, withGlobalTypes: .none, withFFIs: [])
}
public static func Compile(
_ source: String, withGlobalInstances globalInstances: VarTypeScopes?,
_ source: String, withGlobalInstances globalInstances: StaticVarValueScopes?,
withGlobalTypes globalTypes: TypeTypeScopes?, withFFIs ffis: [P4FFI] = Array()
) -> Result<P4Lang.Program> {
@@ -122,8 +134,10 @@ public struct Program {
// Any of the instances that are in the top-level scope should go into the program!
program.instances = Array(
compilation_context.instances.map { (_, v) in
v
compilation_context.instances.filter { (_, v) in
v.1 != nil
}.map { (_, v) in
v.1!
})
// Any of the types that are in the top-level scope should go into the program!
+1 -1
View File
@@ -255,7 +255,7 @@ extension VariableDeclarationStatement: CompilableStatement {
// Context with updated names to include the newly declared name.
context.update(
newInstances: context.instances.declare(
identifier: parsed_variablename, withValue: declaration_p4_type))
identifier: parsed_variablename, withValue: (declaration_p4_type, .none)))
)
)
}