Files
gp4/Sources/P4Compiler/Declarations.swift
T
Will Hawkins 294f76acd4
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
compiler: Refactor Compiler To Remove Ambiguities
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>
2026-05-27 12:59:29 -04:00

1011 lines
35 KiB
Swift

// 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
extension Declaration: CompilableDeclaration {
public static func Compile(
node: Node, withContext context: CompilerContext
) -> Result<(Declaration, CompilerContext)?> {
let declaration_compilers: [String: CompilableDeclaration.Type] = [
"function_declaration": FunctionDeclaration.self,
"control_declaration": Control.self,
"type_declaration": P4Struct.self,
/// ASSUME: Type declarations are struct declarations.
"extern_declaration": ExternDeclaration.self,
]
guard let declaration_compiler = declaration_compilers[node.nodeType!] else {
return .Ok(.none)
}
return declaration_compiler.Compile(node: node, withContext: context)
}
}
extension FunctionDeclaration: CompilableDeclaration {
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(Declaration, CompilerContext)?> {
let function_declaration_node = node
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
node: function_declaration_node, type: "function_declaration",
nice_type_name: "Function Declaration")
var walker = Walker(node: function_declaration_node)
var context = context
var current_node: Node? = .none
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: function_declaration_node.toSourceLocation(),
withError: "Missing function declaration component")))
let maybe_function_type = Types.CompileType(type: current_node!, withContext: context)
guard case .Ok(let function_type) = maybe_function_type else {
return .Error(maybe_function_type.error()!)
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: function_declaration_node.toSourceLocation(),
withError: "Missing function declaration component")))
let maybe_function_name = Identifier.Compile(node: current_node!, withContext: context)
guard case .Ok(let function_name) = maybe_function_name else {
return .Error(maybe_function_name.error()!)
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: function_declaration_node.toSourceLocation(),
withError: "Missing function declaration component")))
let maybe_function_parameters = ParameterList.Compile(node: current_node!, withContext: context)
guard case .Ok((let function_parameters, let updated_context)) = maybe_function_parameters
else {
return .Error(maybe_function_parameters.error()!)
}
context = updated_context
var function_body: BlockStatement? = .none
walker.next()
if let body = walker.getNext() {
// Add the parameters into scope.
var function_scope = context.instances.enter()
for parameter in function_parameters.parameters {
function_scope = function_scope.declare(
identifier: parameter.name, withValue: (parameter.type, .none))
}
let maybe_function_body = BlockStatement.Compile(
node: body,
withContext: context.update(newInstances: function_scope).update(
newExpectation: function_type))
guard case .Ok((let parsed_function_body, _)) = maybe_function_body else {
return .Error(maybe_function_body.error()!)
}
function_body = (parsed_function_body as! BlockStatement)
} else {
// If we are in an extern context, no body is okay!
if !context.extern_context {
return Result.Error(
ErrorWithLocation(
sourceLocation: function_declaration_node.toSourceLocation(),
withError: "Missing function declaration component"))
}
}
let function_declaration = Declaration(
TypedIdentifier(
id: function_name,
withType: P4QualifiedType(
FunctionDeclaration(
named: function_name, ofType: function_type, withParameters: function_parameters,
withBody: function_body))))
// Do not use the updated context returned by parsing the body
// and do not use the function_scope, either.
// And, do not update the context if we are compiling in an
// extern context -- the wrapping extern declaration will take care of that.
return .Ok(
(
function_declaration,
context.extern_context
? context
: context.update(
newTypes: context.types.declare(
identifier: function_name, withValue: function_declaration.identifier.type.baseType())
)
))
}
}
extension P4Struct: CompilableDeclaration {
static public func Compile(
node: Node, withContext context: CompilerContext
) -> Result<(Declaration, CompilerContext)?> {
let struct_declaration_node = node.child(at: 0)!
var walker = Walker(node: struct_declaration_node)
var currentNode: Node? = .none
#SkipUnlessNodeType(node: struct_declaration_node, type: "struct_declaration")
// Skip the keyword struct
walker.next()
#MustOr(
result: currentNode, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: struct_declaration_node.toSourceLocation(),
withError: "Missing function declaration component")))
// The name of the struct type.
let maybe_struct_identifier = Identifier.Compile(
node: currentNode!, withContext: context)
guard case Result.Ok(let struct_identifier) = maybe_struct_identifier else {
return Result.Error(maybe_struct_identifier.error()!)
}
walker.next()
// Skip the '{'
walker.next()
#MustOr(
result: currentNode, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: struct_declaration_node.toSourceLocation(),
withError: "Missing function declaration component")))
// If there are no fields, it will be a "}"
if currentNode!.nodeType == "}" {
let struc = Declaration(
TypedIdentifier(
id: struct_identifier,
withType: P4QualifiedType(
P4Struct(withName: struct_identifier, andFields: P4StructFields([])))))
return Result.Ok(
(
struc,
context.extern_context
? context
: context.update(
newTypes: context.types.declare(
identifier: struct_identifier, withValue: struc.identifier.type.baseType()))
))
}
var parse_errs: (any Errorable)? = .none
var current_context = context
var parsed_fields: [P4StructFieldIdentifier] = Array()
if currentNode!.nodeType == "struct_declaration_fields" {
currentNode!.enumerateNamedChildren { declaration_field in
switch VariableDeclarationStatement.Compile(
node: declaration_field, withContext: current_context)
{
case .Ok((let declaration, let updated_context)):
let variable_declaration = declaration as! VariableDeclarationStatement
parsed_fields.append(
P4StructFieldIdentifier(
id: variable_declaration.identifier, withType: variable_declaration.initializer.type()
))
current_context = updated_context
case .Error(let e):
parse_errs =
if let e = parse_errs {
parse_errs?.append(error: e)
} else {
e
}
}
}
}
if let parse_errs = parse_errs {
return .Error(ErrorWithLabel("Error(s) parsing select cases", parse_errs))
}
let declared_struct = Declaration(
TypedIdentifier(
id: struct_identifier,
withType: P4QualifiedType(
P4Struct(
withName: struct_identifier, andFields: P4StructFields(parsed_fields)))))
return .Ok(
(
declared_struct,
current_context.extern_context
? current_context
: current_context.update(
newTypes: current_context.types.declare(
identifier: struct_identifier, withValue: declared_struct.identifier.type.baseType()))
))
}
}
extension P4Lang.Parser: CompilableDeclaration {
public static func Compile(
node: Node, withContext context: CompilerContext
) -> Result<(Declaration, CompilerContext)?> {
let parser_node = node
#SkipUnlessNodeType<Node>(node: parser_node, type: "parserDeclaration")
var current_context = context
var walker = Walker(node: parser_node)
var current_node: Node? = .none
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: parser_node.toSourceLocation(),
withError: "Missing elements of parser declaration")))
if current_node!.nodeType != "parserType" {
return .Error(
ErrorWithLocation(
sourceLocation: current_node!.toSourceLocation(),
withError: "Missing type for parser declaration"))
}
let type_node = current_node
var parser_name: Common.Identifier? = .none
/// TODO: Handle parser parameter lists.
var parameter_list = ParameterList()
do {
var type_node_walker = Walker(node: type_node!)
var type_node_child: Node? = .none
#MustOr(
result: type_node_child, thing: type_node_walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: parser_node.toSourceLocation(),
withError: "Missing elements of parser type in parser declaration")))
if type_node_child!.nodeType == "annotations" {
return .Error(
ErrorWithLocation(
sourceLocation: type_node_child!.toSourceLocation(),
withError: "Annotations in parser type are not yet handled."))
// Will increment indexes here.
}
type_node_walker.next()
#MustOr(
result: type_node_child, thing: type_node_walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: type_node_child!.toSourceLocation(),
withError: "Missing name in parser type declaration")))
switch Identifier.Compile(node: type_node_child!, withContext: current_context) {
case .Ok(let id): parser_name = id
case .Error(let e):
return .Error(e)
}
type_node_walker.next()
#MustOr(
result: type_node_child, thing: type_node_walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: type_node_child!.toSourceLocation(),
withError: "Missing parser parameters")))
switch ParameterList.Compile(node: type_node_child!, withContext: current_context) {
case .Ok(let (parsed_parameter_list, updated_context)):
parameter_list = parsed_parameter_list
current_context = updated_context
case .Error(let e):
return .Error(e)
}
}
// Now, let's put the parameters into scope.
for parameter in parameter_list.parameters {
current_context = current_context.update(
newInstances: current_context.instances.declare(
identifier: parameter.name, withValue: (parameter.type, .none)))
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: parser_node.toSourceLocation(),
withError: "Missing parser declaration component")))
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: parser_node.toSourceLocation(),
withError: "Missing elements of parser declaration")))
if current_node!.nodeType == "parserLocalElements" {
return .Error(
ErrorWithLocation(
sourceLocation: current_node!.toSourceLocation(),
withError: "Parser Local Elements are not yet handled."))
// Will increment indexes here.
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: parser_node.toSourceLocation(),
withError: "Missing body of parser declaration")))
if current_node!.nodeType != "parserStates" {
return .Error(Error(withMessage: "Missing parser states in parser declaration"))
}
/// TODO: Handle extern parsers.
switch SpecialCompilers.Compile(
withName: parser_name!, withParameters: parameter_list, node: current_node!,
withContext: current_context)
{
case Result.Ok((let parser, let updated_context)):
let parser_declaration = Declaration(
TypedIdentifier(id: parser.name, withType: P4QualifiedType(parser)))
// Create a new context with the name of the parser that was just compiled in scope.
return .Ok(
(
parser_declaration,
context.extern_context
? context
: updated_context.update(
newTypes: updated_context.types.declare(identifier: parser.name, withValue: parser))
))
case Result.Error(let error): return .Error(error)
}
}
}
extension Control: CompilableDeclaration {
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(Declaration, CompilerContext)?> {
#SkipUnlessNodeType<Node>(node: node, type: "control_declaration")
var walker = Walker(node: node)
var current_node: Node? = .none
var local_context = context
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing control declaration component")))
guard
case .Ok(let control_name) = Identifier.Compile(
node: current_node!, withContext: local_context)
else {
return Result.Error(
Error(withMessage: "Could not parse a parameter name from \(current_node!.text!)"))
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing control declaration component")))
let maybe_control_parameters = ParameterList.Compile(
node: current_node!, withContext: local_context)
guard case .Ok((let control_parameters, let updated_context)) = maybe_control_parameters
else {
return .Error(maybe_control_parameters.error()!)
}
local_context = updated_context
// Before continuing, make sure to put the parameters into context.
var control_scope = local_context.instances.enter()
for parameter in control_parameters.parameters {
control_scope = control_scope.declare(
identifier: parameter.name, withValue: (parameter.type, .none))
}
local_context = local_context.update(newInstances: control_scope)
walker.next()
// Skip the '{'
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Declaration, CompilerContext)?>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing control declaration component")))
var actions: [Action] = Array()
var tables: [Table] = Array()
var apply: ApplyStatement? = .none
// Because the final child
// is the '}'.
// \/\/
let body_parse_results = walker.overUntil(n: node.childCount - 1) { current_node in
if current_node.nodeType == "action_declaration" {
let maybe_action_declaration = Action.Compile(
node: current_node, withContext: local_context)
guard
case .Ok((let action_declaration, let updated_context)) = maybe_action_declaration
else {
return .Error(maybe_action_declaration.error()!)
}
actions.append(action_declaration)
local_context = updated_context
// Now, add the declaration into the context.
local_context = local_context.update(
newTypes: local_context.types.declare(
identifier: action_declaration.name, withValue: action_declaration))
} else if current_node.nodeType == "table_declaration" {
let maybe_table_declaration = Table.Compile(
node: current_node, withContext: local_context)
guard
case .Ok((let table_declaration, let updated_context)) = maybe_table_declaration
else {
return .Error(maybe_table_declaration.error()!)
}
tables.append(table_declaration)
local_context = updated_context
} else if current_node.nodeType == "apply_statement" {
// When we see an apply, that is it for the actions and the tables.
let maybe_apply_statement = ApplyStatement.Compile(
node: current_node, withContext: local_context)
guard
case .Ok((let apply_statement, let updated_context)) = maybe_apply_statement
else {
return .Error(maybe_apply_statement.error()!)
}
local_context = updated_context
apply = (apply_statement as! ApplyStatement)
// The apply is the last thing in a control declaration.
// But, that is handled by the compiler.
} else {
return .Error(
ErrorWithLocation(
sourceLocation: current_node.toSourceLocation(),
withError: "Uknown node type in control declaration"))
}
return .Ok(())
}
if case .Error(let e) = body_parse_results {
return .Error(e)
}
// There should only be a single table!
/// TODO: Check the semantics here.
if tables.count > 1 {
/// TODO: Make this error message better.
/// IDEA: Add a "compilation context" for the error message into the `CompilationContext`
// that can be retrieved to make the error messages nicer.
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "More than one table in control declaration"))
}
// Check to make sure that there is an apply.
guard let apply = apply else {
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing apply in control declaration"
))
}
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)))
// 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).
return .Ok(
(
declared_control,
context.extern_context
? context
: context.update(
newTypes: context.types.declare(
identifier: control_name, withValue: control))
))
}
}
extension Action: Compilable {
public typealias T = Action
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(Action, CompilerContext)> {
#RequireNodeType<Node, (P4Type, CompilerContext)>(
node: node, type: "action_declaration", nice_type_name: "Action Declaration")
var walker = Walker(node: node)
var current_node: Node? = .none
var current_context = context
// Skip action keyword
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Action, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing action declaration component"
))
)
guard
case .Ok(let action_name) = Identifier.Compile(
node: current_node!, withContext: current_context)
else {
return Result.Error(
Error(withMessage: "Could not parse an action name from \(current_node!.text!)"))
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Action, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing action declaration component"
))
)
let maybe_action_parameters = ParameterList.Compile(
node: current_node!, withContext: current_context)
guard case .Ok((let action_parameters, let updated_context)) = maybe_action_parameters
else {
return .Error(maybe_action_parameters.error()!)
}
current_context = updated_context
// Check whether the parameters are in the proper order.
let remaining_parameters = action_parameters.parameters.drop(while: {
$0.type.direction() != .none
})
if remaining_parameters.contains(where: { $0.type.direction() != .none }) {
return .Error(
ErrorWithLocation(
sourceLocation: current_node!.toSourceLocation(),
withError: "All parameters with direction must precede directionless parameters"))
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Action, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing action declaration component"
))
)
// Add the parameters into scope.
var function_scope = context.instances.enter()
for parameter in action_parameters.parameters {
function_scope = function_scope.declare(
identifier: parameter.name, withValue: (parameter.type, .none))
}
let maybe_action_body = BlockStatement.Compile(
node: current_node!, withContext: context.update(newInstances: function_scope))
guard case .Ok((let action_body, _)) = maybe_action_body else {
return .Error(maybe_action_body.error()!)
}
/// TODO: Actions cannot contain switches!
return .Ok(
(
Action(
named: action_name, withParameters: action_parameters,
withBody: (action_body as! BlockStatement)),
current_context
))
}
}
extension TableKeyEntry: Compilable {
public typealias T = TableKeyEntry
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(TableKeyEntry, CompilerContext)> {
#RequireNodeType<Node, (P4Type, CompilerContext)>(
node: node, type: "table_key_entry", nice_type_name: "Table Key Entry")
var walker = Walker(node: node)
var current_node: Node? = .none
let current_context = context
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(TableKeyEntry, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing table key entry declaration component")))
let maybe_keyset_expression = KeysetExpression.compile(
node: current_node!, withContext: current_context)
guard case .Ok(let keyset_expression) = maybe_keyset_expression else {
return Result.Error(maybe_keyset_expression.error()!)
}
walker.next()
// Skip the ':'
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(TableKeyEntry, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing table key entry declaration component")))
let maybe_match_type = TableKeyMatchType.Compile(
node: current_node!, withContext: current_context)
guard case .Ok((let match_type, _)) = maybe_match_type else {
return .Error(maybe_match_type.error()!)
}
return .Ok((TableKeyEntry(keyset_expression as! KeysetExpression, match_type), current_context))
}
}
extension TableKeyMatchType: Compilable {
public typealias T = TableKeyMatchType
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(TableKeyMatchType, CompilerContext)> {
#RequireNodeType<Node, (TableKeyMatchType, CompilerContext)>(
node: node, type: "table_key_match_type", nice_type_name: "Table Key Match Type")
if node.text! == "exact" {
return .Ok((TableKeyMatchType.Exact, context))
}
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "\(node.text!) is not a valid match type)"))
}
}
extension TableKeys: Compilable {
public typealias T = TableKeys
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(TableKeys, CompilerContext)> {
#RequireNodeType<Node, (TableKeyMatchType, CompilerContext)>(
node: node, type: "table_keys", nice_type_name: "Table Keys")
var walker = Walker(node: node)
// Skip the
// keys = {
// 1 2 3
walker.next() // 1
walker.next() // 2
walker.next() // 3
var current_node: Node? = .none
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(TableKeys, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing table keys declaration component in control declaration"))
)
let (keys, errors) = walker.try_map(n: node.childCount - 1, onlyNamed: true) { current_node in
return switch TableKeyEntry.Compile(node: current_node, withContext: context) {
case .Ok((let keyset_expression, _)): .Ok(keyset_expression)
case .Error(let e): .Error(e)
}
}
if !errors.isEmpty {
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Error(s) parsing table key: "
+ (errors.map { error in
return "\(error.msg())"
}.joined(separator: ";"))))
}
return .Ok((TableKeys(withEntries: keys), context))
}
}
extension TableActionsProperty: Compilable {
public typealias T = TableActionsProperty
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(TableActionsProperty, CompilerContext)> {
#RequireNodeType<Node, (TableActionsProperty, CompilerContext)>(
node: node, type: "table_actions", nice_type_name: "Table Actions")
var walker = Walker(node: node)
// Skip the
// actions = {
// 1 2 3
walker.next() // 1
walker.next() // 2
walker.next() // 3
var current_node: Node? = .none
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(TableActionsProperty, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing table actions declaration component in control declaration"))
)
let (actions, errors) = walker.try_map(n: node.childCount - 1, onlyNamed: true) {
current_node in
switch Identifier.Compile(node: current_node, withContext: context) {
case .Ok(let listed_action):
switch context.types.lookup(identifier: listed_action) {
case .Ok(let maybe_action):
if maybe_action.eq(rhs: Action()) {
return .Ok(TypedIdentifier(id: listed_action, withType: P4QualifiedType(maybe_action)))
}
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "\(listed_action) does not name an action"))
case .Error(let e): return .Error(e)
}
case .Error(let e): return .Error(e)
}
}
if !errors.isEmpty {
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Error(s) parsing table actions: "
+ (errors.map { error in
return "\(error.msg())"
}.joined(separator: ";"))))
}
return .Ok((TableActionsProperty(actions), context))
}
}
extension TablePropertyList: Compilable {
public typealias T = TablePropertyList
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(TablePropertyList, CompilerContext)> {
#RequireNodeType<Node, (P4Type, CompilerContext)>(
node: node, type: "table_property_list", nice_type_name: "Table Property List")
var current_context = context
var keys: [TableKeys] = Array()
var actions: [TableActionsProperty] = Array()
var errors: [any Errorable] = Array()
node.enumerateNamedChildren { child in
if child.nodeType == "table_keys" {
switch TableKeys.Compile(node: child, withContext: current_context) {
case .Ok((let table_key, let updated_context)):
current_context = updated_context
keys.append(table_key)
case .Error(let e): errors.append(e)
}
} else if child.nodeType == "table_actions" {
switch TableActionsProperty.Compile(node: child, withContext: current_context) {
case .Ok((let table_action_property, let updated_context)):
current_context = updated_context
actions.append(table_action_property)
case .Error(let e): errors.append(e)
}
} else {
errors.append(
ErrorWithLocation(
sourceLocation: child.toSourceLocation(),
withError: "Uknown node type in control declaration"))
}
}
if !errors.isEmpty {
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Error(s) parsing property list: "
+ (errors.map { error in
return "\(error.msg())"
}.joined(separator: ";"))))
}
// There should be only one table keys!
if keys.count > 1 {
// Todo: Make this error message better.
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "More than one key set in table property list"))
}
// There should be only one table actions!
if actions.count > 1 {
// Todo: Make this error message better.
return .Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "More than one actions in table property list"))
}
if actions.isEmpty {
actions.append(TableActionsProperty())
}
return .Ok((TablePropertyList(withActions: actions[0], withKeys: keys[0]), current_context))
}
}
extension Table: Compilable {
public typealias T = Table
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(Table, CompilerContext)> {
let table_declaration_node = node
#RequireNodeType<Node, (P4Type, CompilerContext)>(
node: table_declaration_node, type: "table_declaration", nice_type_name: "Table Declaration")
var walker = Walker(node: table_declaration_node)
var current_node: Node? = .none
let current_context = context
walker.next() // Skip the XXX?
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Table, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing table declaration component")
))
guard
case .Ok(let table_name) = Identifier.Compile(
node: current_node!, withContext: current_context)
else {
return Result.Error(
Error(withMessage: "Could not parse a table name from \(current_node!.text!)"))
}
walker.next()
// Skip the '{'
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result<(Table, CompilerContext)>.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing table declaration component")
))
let maybe_table_property_list = TablePropertyList.Compile(
node: current_node!, withContext: current_context)
guard case .Ok((let table_property_list, _)) = maybe_table_property_list else {
return Result.Error(maybe_table_property_list.error()!)
}
return .Ok(
(Table(withName: table_name, withPropertyList: table_property_list), current_context))
}
}
extension ExternDeclaration: CompilableDeclaration {
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result<(Declaration, CompilerContext)?> {
let extern_declaration_node = node
#RequireNodeType<Node, (Declaration, CompilerContext)>(
node: extern_declaration_node, type: "extern_declaration",
nice_type_name: "Extern Declaration")
let declaration_node = extern_declaration_node.child(at: 1)!
#RequireNodeType<Node, (Declaration, CompilerContext)>(
node: declaration_node, type: "declaration", nice_type_name: "Declaration")
let declarationed_node = declaration_node.child(at: 0)!
let maybe_declared = Declaration.Compile(
node: declarationed_node, withContext: context.update(newExtern: true))
guard case .Ok(let maybe_declared) = maybe_declared else {
return .Error(maybe_declared.error()!)
}
guard case .some((let declared, _)) = maybe_declared else {
return .Ok(.none)
}
// Before we are okay with this declaration, it must already be registered as an extern
// with the matching "stuff".
let found_ffi = context.ffis.first { ffi in
ffi.type().baseType().eq(rhs: declared.identifier.type.baseType())
}
guard let found_ffi = found_ffi else {
return .Error(
ErrorWithLocation(
sourceLocation: declarationed_node.toSourceLocation(),
withError:
"Could not find a foreign function that matches the extern declaration (\(declared))"))
}
let extern_declaration = Declaration(extern: declared, ffi: found_ffi)
return .Ok(
(
extern_declaration,
context.update(
newExterns: context.externs.declare(
identifier: declared.identifier, withValue: extern_declaration))
))
}
}