Start Rewrite
Continuous Integration / Grammar Tests (push) Successful in 3m54s
Continuous Integration / Library Format Tests (push) Failing after 4m49s
Continuous Integration / Library Tests (push) Successful in 7m40s

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
Will Hawkins
2026-06-12 06:24:53 -04:00
parent 6908d9a91d
commit b9ff228362
73 changed files with 1779 additions and 11477 deletions
-134
View File
@@ -1,134 +0,0 @@
// 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/>.
public struct Parameter: CustomStringConvertible, Equatable {
public static func == (lhs: Parameter, rhs: Parameter) -> Bool {
return lhs.name == rhs.name && lhs.type.eq(rhs.type)
}
public var name: Identifier
public var type: P4QualifiedType
public init(
identifier: Identifier, withType type: P4QualifiedType
) {
self.name = identifier
self.type = type
}
public var description: String {
return "Parameter: \(self.name) with type \(self.type)"
}
/// Calculate whether the `argument` is compatible with this parameter.
public func compatible(_ argument: Argument) -> Bool {
let arg_type = argument.argument.type()
// If the parameter is (in)out, then the argument must be an lvalue.
if let param_direction = self.type.direction(),
param_direction == Direction.In || param_direction == Direction.InOut
{
if !(argument.argument is P4LValueExpression) {
return false
}
}
return arg_type.baseType().eq(rhs: self.type.baseType())
}
}
public struct ParameterList: CustomStringConvertible, Equatable {
public static func == (lhs: ParameterList, rhs: ParameterList) -> Bool {
if lhs.parameters.count != rhs.parameters.count {
return false
}
return 0
== zip(lhs.parameters, rhs.parameters).count { (lparam, rparam) in
return lparam != rparam
}
}
public var parameters: [Parameter]
public init() {
self.parameters = Array()
}
public init(_ parameters: [Parameter]) {
self.parameters = parameters
}
public func addParameter(_ parameter: Parameter) -> ParameterList {
return ParameterList(self.parameters + [parameter])
}
public var description: String {
let parameters = self.parameters.map { parameter in
parameter.description
}.joined(separator: ";")
return "Parameter list: \(parameters)"
}
}
public struct ArgumentList {
public let arguments: [Argument]
public init(_ arguments: [Argument] = []) {
self.arguments = arguments
}
public func compatible(_ parameters: ParameterList) -> Result<()> {
if self.arguments.count != parameters.parameters.count {
return .Error(
Error(
withMessage:
"\(self.arguments.count) arguments found but \(parameters.parameters.count) required"))
}
for (arg, param) in zip(self.arguments, parameters.parameters) {
let arg_index = arg.index
let arg_type = arg.argument.type()
if !param.compatible(arg) {
return .Error(
Error(
withMessage:
"Argument \(arg_index)'s type (\(arg_type)) is incompatible with the parameter type (\(param.type))"
))
}
}
return .Ok(())
}
public func addArgument(_ argument: Argument) -> ArgumentList {
return ArgumentList(self.arguments + [argument])
}
public func count() -> Int {
return self.arguments.count
}
}
public struct Argument {
public let index: Int
public let argument: P4Expression
public init(_ argument: P4Expression, atIndex index: Int) {
self.argument = argument
self.index = index
}
}
-203
View File
@@ -1,203 +0,0 @@
// 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/>.
/// A scope that resolves variable identifiers to their types.
public typealias VarTypeScope = Scope<P4QualifiedType>
/// Scopes that resolve variable identifiers to their types.
public typealias VarTypeScopes = Scopes<P4QualifiedType>
/// A scope that resolves type identifiers to their types.
public typealias TypeTypeScope = Scope<P4Type>
/// Scopes that resolve type identifiers to their types.
public typealias TypeTypeScopes = Scopes<P4Type>
/// Context for compilation
///
/// It contains (at least) three important pieces of information:
/// 1. Instances: A ``VarTypeScopes`` that contains information about instantiated objects
/// (and their types) in scope
/// 1. Types: A ``TypeTypeScopes`` that contains information about declared types in scope.
/// 1. Expected Type: In certain situations, to typecheck an element of a P4 program requires
/// 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 {
public let instances: StaticVarValueScopes
public let types: TypeTypeScopes
public let externs: TypeTypeScopes
public let ffis: [P4FFI]
public let expected_type: P4QualifiedType?
public let extern_context: Bool
public let lexical_context_name: Identifier?
public let lexical_context_statements: [P4Statement]?
public init() {
instances = StaticVarValueScopes().enter()
types = TypeTypeScopes().enter()
externs = TypeTypeScopes().enter()
expected_type = .none
extern_context = false
ffis = Array()
lexical_context_name = .none
lexical_context_statements = .none
}
public init(withInstances _instances: StaticVarValueScopes, withTypes _types: TypeTypeScopes) {
instances = _instances
types = _types
externs = TypeTypeScopes().enter()
expected_type = .none
extern_context = false
ffis = Array()
lexical_context_name = .none
lexical_context_statements = .none
}
public init(
withInstances _instances: StaticVarValueScopes, withTypes _types: TypeTypeScopes,
withExpectation expectation: P4QualifiedType?, withExtern extern: Bool,
withExterns externs: TypeTypeScopes, withFFIs foreigns: [P4FFI],
withLexicalContextName lexical_context_name: Identifier?,
withLexicalContextStatements lexical_context_statements: [P4Statement]?
) {
instances = _instances
types = _types
expected_type = expectation
extern_context = extern
self.externs = externs
ffis = foreigns
self.lexical_context_name = lexical_context_name
self.lexical_context_statements = lexical_context_statements
}
/// Update a compiler context
///
/// Create a new compiler context based on the current but new instances.
///
/// - 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: StaticVarValueScopes) -> CompilerContext {
return CompilerContext(
withInstances: instances, withTypes: self.types, withExpectation: self.expected_type,
withExtern: self.extern_context, withExterns: self.externs, withFFIs: self.ffis,
withLexicalContextName: self.lexical_context_name,
withLexicalContextStatements: self.lexical_context_statements)
}
/// Update a compiler context
///
/// Create a new compiler context based on the current but new types.
///
/// - Parameter types: a ``TypeTypeScopes`` with the updated types for the newly created compiler context.
/// - Returns: A new compiler context based on the current but new types.
public func update(newTypes types: TypeTypeScopes) -> CompilerContext {
return CompilerContext(
withInstances: self.instances, withTypes: types, withExpectation: self.expected_type,
withExtern: self.extern_context, withExterns: self.externs, withFFIs: self.ffis,
withLexicalContextName: self.lexical_context_name,
withLexicalContextStatements: self.lexical_context_statements)
}
/// Update a compiler context
///
/// Create a new compiler context based on the current but new expected type.
///
/// - Parameter expectation: a ``P4Type?`` to (re)set the type the compiler is expecting.
/// - Returns: A new compiler context based on the current but new expected type.
public func update(newExpectation expectation: P4QualifiedType?) -> CompilerContext {
return CompilerContext(
withInstances: self.instances, withTypes: self.types, withExpectation: expectation,
withExtern: self.extern_context, withExterns: self.externs, withFFIs: self.ffis,
withLexicalContextName: self.lexical_context_name,
withLexicalContextStatements: self.lexical_context_statements)
}
/// Update a compiler context
///
/// Create a new compiler context based on the current but new extern context value.
///
/// - Parameter extern: a ``Bool`` to (re)set whether the compiler is compiling in an extern context.
/// - Returns: A new compiler context based on the current but new extern context value.
public func update(newExtern extern: Bool) -> CompilerContext {
return CompilerContext(
withInstances: self.instances, withTypes: self.types, withExpectation: self.expected_type,
withExtern: extern, withExterns: self.externs, withFFIs: self.ffis,
withLexicalContextName: self.lexical_context_name,
withLexicalContextStatements: self.lexical_context_statements)
}
/// Update a compiler context
///
/// Create a new compiler context based on the current but new externs.
///
/// - Parameter externs: a ``TypeTypeScopes`` to (re)set the list of extern-al declarations.
/// - Returns: A new compiler context based on the current but new list of external-al declarations.
public func update(newExterns externs: TypeTypeScopes) -> 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: self.lexical_context_statements)
}
/// Update a compiler context
///
/// Create a new compiler context based on the current but new FFIs.
///
/// - Parameter foreigns: an array of ``P4FFI`` to (re)set the list of foreign functions.
/// - Returns: A new compiler context based on the current but with a new list of foreign functions.
public func update(newFFIs foreigns: [P4FFI]) -> CompilerContext {
return CompilerContext(
withInstances: self.instances, withTypes: self.types, withExpectation: self.expected_type,
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)
}
}
-285
View File
@@ -1,285 +0,0 @@
// 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/>.
public typealias ExecuteStatementResultHandlerT = (ControlFlow, ProgramExecution) -> (
ControlFlow, ProgramExecution
)
public typealias ExecuteStatementT = (EvaluatableStatement, ProgramExecution) -> (
ControlFlow, ProgramExecution
)
func CanonicalExecuteStatements(
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
_ executor: ExecuteStatementT
) -> (ControlFlow, ProgramExecution) {
var execution = execution
for s in statements {
// Execute the statement with the user-provided statement executor.
switch executor(s, execution) {
// And decide what to do next!
case (ControlFlow.Next, let handled_next_execution): execution = handled_next_execution
case (ControlFlow.Return(let value), let handled_next_execution):
return (ControlFlow.Return(value), handled_next_execution)
case (let handled_control_flow, let handled_next_execution):
return (handled_control_flow, handled_next_execution)
}
}
return (ControlFlow.Next, execution)
}
public struct ClassicEvaluator: ProgramExecutionEvaluator {
public func ExecuteStatements(
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
_ handler: ExecuteStatementResultHandlerT?
) -> (ControlFlow, ProgramExecution) {
return CanonicalExecuteStatements(statements, inExecution: execution) { statement, execution in
let (cf, value) = statement.evaluate(execution: execution)
// Apply the user-specified handler before continuing.
guard let handler = handler else {
return (cf, value)
}
return handler(cf, value)
}
}
public func EvaluateExpression(
_ expression: EvaluatableExpression, inExecution execution: ProgramExecution,
) -> (Result<P4Value>, ProgramExecution) {
return expression.evaluate(execution: execution)
}
}
public struct InterloperEvaluator: ProgramExecutionEvaluator {
var statement_interloper: StatementInterloper?
var expression_interloper: ExpressionInterloper?
public init() {}
public func getStatementInterloper() -> StatementInterloper? {
return self.statement_interloper
}
public func setStatementInterloper(
_ interloper: @escaping StatementInterloper
) -> InterloperEvaluator {
var pe = self
pe.statement_interloper = interloper
return pe
}
public func getExpressionInterloper() -> ExpressionInterloper? {
return self.expression_interloper
}
public func setExpressionInterloper(
_ interloper: @escaping ExpressionInterloper
) -> InterloperEvaluator {
var pe = self
pe.expression_interloper = interloper
return pe
}
public func ExecuteStatements(
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
_ handler: ExecuteStatementResultHandlerT?
) -> (ControlFlow, ProgramExecution) {
var debugger: StatementInterloper? = .none
var hasDebugInterloper = false
if let found_deb = self.getStatementInterloper() {
debugger = found_deb
hasDebugInterloper = true
}
return CanonicalExecuteStatements(statements, inExecution: execution) { statement, execution in
let (cf, value) = statement.evaluate(execution: execution)
let (handled_cf, handled_value) =
if let handler = handler {
handler(cf, value)
} else {
(cf, value)
}
if hasDebugInterloper {
debugger!(statement, handled_cf, handled_value)
}
return (handled_cf, handled_value)
/*
let (handled_cf, handled_value) =
if let handler = handler {
handler(cf, value)
} else {
(cf, value)
}
if hasDebugInterloper {
debugger!(statement, handled_cf, handled_value)
}
*/
}
}
public func EvaluateExpression(
_ expression: EvaluatableExpression, inExecution execution: ProgramExecution,
) -> (Result<P4Value>, ProgramExecution) {
var debugger: ExpressionInterloper? = .none
var hasDebugInterloper = false
if let found_deb = self.getExpressionInterloper() {
debugger = found_deb
hasDebugInterloper = true
}
let (result, execution) = expression.evaluate(execution: execution)
if hasDebugInterloper {
debugger!(expression, result, execution)
}
return (result, execution)
}
}
public typealias StatementInterloper = (EvaluatableStatement, ControlFlow, ProgramExecution) -> Void
public typealias ExpressionInterloper = (EvaluatableExpression, Result<P4Value>, ProgramExecution)
-> Void
open class ProgramExecution: CustomStringConvertible {
public var scopes: VarValueScopes = VarValueScopes()
var globalValues: VarValueScopes?
var error: (any Errorable)?
var debug: DebugLevel = DebugLevel.Error
public let evaluator: ProgramExecutionEvaluator
init(copy: ProgramExecution) {
self.scopes = copy.scopes
self.globalValues = copy.globalValues
self.error = copy.error
self.debug = copy.debug
self.evaluator = copy.evaluator
}
public init() {
globalValues = .none
evaluator = ClassicEvaluator()
}
public init(_ evaluator: ProgramExecutionEvaluator) {
globalValues = .none
self.evaluator = evaluator
}
open var description: String {
return "Runtime:\nScopes: \(scopes)"
}
public func hasError() -> Bool {
return self.error != nil
}
public func getError() -> (any Errorable)? {
return self.error
}
public func setError(error: any Errorable) -> ProgramExecution {
let npe = ProgramExecution(copy: self)
npe.error = error
return npe
}
public func getDebugLevel() -> DebugLevel {
return self.debug
}
public func setDebugLevel(_ dl: DebugLevel) -> ProgramExecution {
let pe = ProgramExecution(copy: self)
pe.debug = dl
return pe
}
open func isDone() -> Bool {
return false
}
open func setDone() -> ProgramExecution {
// For a bare ProgramExecution, setDone is a noop.
return self
}
public func enter_scope() -> ProgramExecution {
let new_pe = ProgramExecution(copy: self)
new_pe.scopes = new_pe.scopes.enter()
return new_pe
}
public func exit_scope() -> ProgramExecution {
let new_pe = ProgramExecution(copy: self)
new_pe.scopes = new_pe.scopes.exit()
return new_pe
}
public func replaceScopes(_ new_scopes: VarValueScopes) -> ProgramExecution {
let new_pe = ProgramExecution(copy: self)
new_pe.scopes = new_scopes
return new_pe
}
public func declare(identifier: Identifier, withValue value: P4Value) -> ProgramExecution {
let new_pe = ProgramExecution(copy: self)
let new_scopes = new_pe.scopes.declare(identifier: identifier, withValue: value)
new_pe.scopes = new_scopes
return new_pe
}
public func getGlobalValues() -> VarValueScopes {
return self.globalValues ?? VarValueScopes()
}
public func setGlobalValues(_ global_values: VarValueScopes) -> ProgramExecution {
let new_pe = ProgramExecution(copy: self)
new_pe.globalValues = global_values
return new_pe
}
}
/// A scope that resolves variable identifiers to their values.
public typealias VarValueScope = Scope<P4Value>
/// Scopes that resolves variable identifiers to their values.
public typealias VarValueScopes = Scopes<P4Value>
/// A scope that resolves variable identifiers to their values.
public typealias StaticVarValueScope = Scope<(P4QualifiedType, P4Value?)>
/// Scopes that resolves variable identifiers to their values.
public typealias StaticVarValueScopes = Scopes<(P4QualifiedType, P4Value?)>
/// Indicate the control flow result of a particular statement.
public enum ControlFlow {
case Next
case Continue
case Break
case Return(P4Value?)
case Error
}
-22
View File
@@ -1,22 +0,0 @@
// 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/>.
public protocol P4FFI {
func execute(execution: ProgramExecution) -> (ControlFlow, ProgramExecution)
func type() -> P4QualifiedType
func parameters() -> ParameterList
}
+1 -55
View File
@@ -58,67 +58,13 @@ public protocol Formattable {
}
public protocol P4Statement {
/// Evaluate a statement for a given execution
/// - Parameters
/// - execution: The execution context in which to evaluate the parser statement
/// - Returns: A tuple of
/// 1. Whether this statement affects control flow.
/// 2. An updated execution after evaluating the parser statement
func evaluate(execution: ProgramExecution) -> (ControlFlow, ProgramExecution)
func effect(context: CompilerContext) -> CompilerContext
}
public protocol P4Expression {
func effect(context: CompilerContext) -> CompilerContext
func type() -> P4QualifiedType
/// Evaluate an expression for a given execution
/// - Parameters
/// - execution: The execution context in which to evaluate the expression
/// - Returns: The value of expression
func evaluate(execution: ProgramExecution) -> (Result<P4Value>, ProgramExecution)
}
extension P4Expression {
func effect(context: CompilerContext) -> CompilerContext {
return context
}
}
public protocol P4LValueExpression {
func effect(context: CompilerContext) -> CompilerContext
func type() -> P4QualifiedType
func check(to: P4Expression, inScopes scopes: StaticVarValueScopes) -> Result<()>
func set(
to: P4Value, inScopes scopes: VarValueScopes, duringExecution execution: ProgramExecution
) -> Result<(VarValueScopes, P4Value)>
}
/// TODO: Only generate these after going through the codegen!
public typealias EvaluatableStatement = P4Statement
public typealias EvaluatableLValueExpression = P4LValueExpression
public typealias EvaluatableExpression = P4Expression
public protocol ProgramExecutionEvaluator {
func ExecuteStatements(
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution,
_ handler: ExecuteStatementResultHandlerT?
) -> (ControlFlow, ProgramExecution)
func ExecuteStatements(
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution
) -> (ControlFlow, ProgramExecution)
func EvaluateExpression(
_ expression: EvaluatableExpression, inExecution execution: ProgramExecution,
) -> (Result<P4Value>, ProgramExecution)
}
extension ProgramExecutionEvaluator {
public func ExecuteStatements(
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution
) -> (ControlFlow, ProgramExecution) {
return ExecuteStatements(statements, inExecution: execution, .none)
}
extension P4Expression {
}