// 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 .
import Common
import P4Lang
import P4Runtime
import SwiftTreeSitter
import TreeSitterExtensions
import TreeSitterP4
func parameter_list_compiler(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result {
var walker = Walker(node: node)
var current_node: Node? = .none
if node.text == ")" {
// There are no parameters!
return Result.Ok(ParameterList([]))
}
#RequireNodeType(
node: node, type: "parameter_list", nice_type_name: "Parameter List")
var parameters: ParameterList = ParameterList([])
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing parameter list component")))
if current_node?.nodeType == "parameter_list" {
switch parameter_list_compiler(node: current_node!, withContext: context) {
case .Ok(let ps):
parameters = ps
case .Error(let e): return Result.Error(e)
}
walker.next()
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing parameter list component")))
// If this is a ')', we are done.
if current_node?.text == ")" {
return Result.Ok(parameters)
}
// If this is a comma, we skip it!
if current_node?.text == "," {
walker.next()
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing parameter list component")))
// Otherwise, there should be one parameter left!
switch Parameter.Compile(node: current_node!, withContext: context) {
case .Ok(let parsed_parameter):
return Result.Ok(parameters.addParameter(parsed_parameter))
case .Error(let e): return Result.Error(e)
}
}
extension ParameterList: Compilable {
public typealias C = ParameterList
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result {
let parameter_node = node
#RequireNodeType(
node: parameter_node, type: "parameters", nice_type_name: "Parameters")
var walker = Walker(node: parameter_node)
var current_node: Node? = .none
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing '(' in parameter list component")))
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing parameter list component")))
return parameter_list_compiler(node: current_node!, withContext: context)
}
}
extension Direction: Compilable {
public typealias C = Direction
public static func Compile(
node: Node, withContext context: CompilerContext
) -> Result {
let direction_node = node
#RequireNodeType(
node: direction_node, type: "direction", nice_type_name: "direction")
let directions = [
"in": Direction.In,
"out": Direction.Out,
"inout": Direction.InOut,
]
guard let parsed_direction = directions[direction_node.text!] else {
return .Error(
ErrorWithLocation(
sourceLocation: direction_node.toSourceLocation(),
withError: "\(direction_node.text!) is not a valid direction"))
}
return .Ok(parsed_direction)
}
}
extension Parameter: Compilable {
public typealias C = Parameter
public static func Compile(
node: Node, withContext context: CompilerContext
) -> Result {
#RequireNodeType(
node: node, type: "parameter", nice_type_name: "parameter")
var walker = Walker(node: node)
var current_node: Node? = .none
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing parameter declaration component")))
// Annotation?
if current_node!.nodeType == "annotations" {
return .Error(
ErrorWithLocation(
sourceLocation: current_node!.toSourceLocation(),
withError: "Annotations in parameter declarations are not yet handled"))
// Will increment indexes here.
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing parameter declaration component")))
var direction: Direction? = .none
// Direction?
if current_node!.nodeType == "direction" {
let maybe_parsed_direction = Direction.Compile(node: current_node!, withContext: context)
guard case .Ok(let parsed_direction) = maybe_parsed_direction else {
return .Error(maybe_parsed_direction.error()!)
}
direction = parsed_direction
walker.next()
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing parameter declaration component")))
if current_node!.nodeType != "typeRef" {
return Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Did not find type name for parameter declaration"))
}
guard
case .Ok(let parameter_type) = Types.CompileType(type: current_node!, withContext: context)
else {
return Result.Error(
Error(withMessage: "Could not parse a P4 type from \(current_node!.text!)"))
}
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing parameter declaration component")))
if current_node!.nodeType != "identifier" {
return Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Did not find identifier for parameter statement"))
}
guard
case .Ok(let parameter_name) = Identifier.Compile(node: current_node!, withContext: context)
else {
return Result.Error(
Error(withMessage: "Could not parse a parameter name from \(current_node!.text!)"))
}
return Result.Ok(
Parameter(
identifier: parameter_name,
withType: direction != nil
? parameter_type.update(addAttribute: P4TypeQualifier.Direction(direction!))
: parameter_type),
)
}
}
func argument_list_compiler(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result {
var walker = Walker(node: node)
var current_node: Node? = .none
if node.text == ")" {
// There are no arguments!
return Result.Ok(ArgumentList([]))
}
#RequireNodeType(
node: node, type: "argument_list", nice_type_name: "argument List")
var arguments: ArgumentList = ArgumentList([])
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing argument list component")))
if current_node?.nodeType == "argument_list" {
switch argument_list_compiler(node: current_node!, withContext: context) {
case .Ok(let ps):
arguments = ps
case .Error(let e): return Result.Error(e)
}
walker.next()
}
// We may have moved nodes, check/reset current_node.
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing argument list component")))
// If this is a ')', we are done.
if current_node?.text == ")" {
return Result.Ok(arguments)
}
// If this is a comma, we skip it!
if current_node?.text == "," {
walker.next()
}
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing argument list component")))
// Otherwise, there should be one argument left!
switch Argument.Compile(node: current_node!, withContext: context) {
case .Ok(let ce):
return Result.Ok(
arguments.addArgument(Argument(ce, atIndex: arguments.count() + 1)))
case .Error(let e): return Result.Error(e)
}
}
extension ArgumentList: Compilable {
public typealias C = ArgumentList
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result {
let argument_node = node
#RequireNodeType(
node: argument_node, type: "arguments", nice_type_name: "arguments")
var walker = Walker(node: argument_node)
var current_node: Node? = .none
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(),
withError: "Missing '(' in argument list component")))
walker.next()
#MustOr(
result: current_node, thing: walker.getNext(),
or: Result.Error(
ErrorWithLocation(
sourceLocation: node.toSourceLocation(), withError: "Missing argument list component")))
return argument_list_compiler(node: current_node!, withContext: context)
}
}
extension Argument: Compilable {
public typealias C = P4Expression
public static func Compile(
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Common.Result {
let argument_node = node
#RequireNodeType(
node: argument_node, type: "argument", nice_type_name: "argument")
let expression_node = node.child(at: 0)!
return switch Expression.Compile(node: expression_node, withContext: context) {
case .Ok(let compiled_expression): .Ok(compiled_expression)
case .Error(let e): .Error(e)
}
}
}
func ContainsInvalidStatements(
statement: P4Statement, invalids: [P4Statement.Type]
) -> Bool {
for es in invalids {
if type(of: statement) == es {
return true
}
}
return false
}
func ContainsInvalidStatements(block: BlockStatement, invalids: [P4Statement.Type]) -> Bool {
return block.statements.contains { statement in
for es in invalids {
if type(of: statement) == es {
return true
}
}
return false
}
}
extension Node {
public func toSourceLocation() -> SourceLocation {
return SourceLocation(self.range.location, self.range.length)
}
}