Refactor Type System

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
Will Hawkins
2026-02-06 07:46:18 -05:00
parent bc700509c1
commit c3fdfb62e8
11 changed files with 395 additions and 199 deletions
+146 -60
View File
@@ -15,85 +15,171 @@
// 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 P4 identifier
public class Identifier: CustomStringConvertible, Equatable {
var name: String
var name: String
public init(name: String) {
self.name = name
}
public init(name: String) {
self.name = name
}
public var description: String {
return "\(name)"
}
public var description: String {
return "\(name)"
}
public static func ==(lhs: Identifier, rhs: Identifier) -> Bool {
return lhs.name == rhs.name
}
public static func == (lhs: Identifier, rhs: Identifier) -> Bool {
return lhs.name == rhs.name
}
}
/// A P4 variable
public class Variable: Identifier {
var constant: Bool
var value: ValueType
var constant: Bool
var value: P4Value
public init(name: String, withValue value: ValueType, isConstant constant: Bool) {
self.constant = constant
self.value = value
super.init(name: name)
}
public init(name: String, withValue value: P4Value, isConstant constant: Bool) {
self.constant = constant
self.value = value
super.init(name: name)
}
public override var description: String {
return "\(super.description) = \(value) \(constant ? "(constant)" : "")"
}
public override var description: String {
return "\(super.description) = \(value) \(constant ? "(constant)" : "")"
}
public var value_type: ValueType {
get {
value
}
}
public var value_type: P4Value {
value
}
}
public enum ValueType: CustomStringConvertible, Equatable {
case Boolean(Bool)
case Int(Int)
case String(String)
/// A base for all instances of P4 types
open class P4ValueBase<T: P4Type>: P4Value {
public var description: String {
switch self {
case ValueType.Boolean(let b):
return "\(b) of Boolean"
case ValueType.Int(let i):
return "\(i) of Int"
case ValueType.String(let s):
return "\(s) of String"
}
}
public static func==(lhs: ValueType, rhs: ValueType) -> Bool {
switch (lhs,rhs) {
case (ValueType.Boolean(let lhsb), ValueType.Boolean(let rhsb)):
return lhsb == rhsb
case (ValueType.String(let lhsb), ValueType.String(let rhsb)):
return lhsb == rhsb
case (ValueType.Int(let lhsb), ValueType.Int(let rhsb)):
return lhsb == rhsb
default: return false
}
}
public init() {}
public func type() -> P4Type {
return T.create()
}
public func eq(rhs: P4Value) -> Bool {
return false
}
}
public struct Value: CustomStringConvertible {
public var value_type: ValueType
/// The type for a P4 struct
public struct P4Struct: P4Type {
public let name: String
// The type of the struct created is always anonymous.
public static func create() -> any P4Type {
return P4Struct()
}
public init(withValue value: ValueType) {
self.value_type = value
public init(withName name: String) {
self.name = name
}
public init() {
self.name = ""
}
}
/// The field of a P4 struct
public struct P4StructField {
public let name: Identifier
public let type: P4Type
public init(withName name: Identifier, withType type: P4Type) {
self.name = name
self.type = type
}
}
/// An instance of a P4 struct
public class P4StructValue: P4ValueBase<P4Struct> {
public let fields: [P4StructField]
public init(withFields fields: [P4StructField]) {
self.fields = fields
}
}
/// A P4 boolean type
public struct P4Boolean: P4Type {
public static func create() -> any P4Type {
return P4Boolean()
}
}
/// An instance of a P4 boolean
public class P4BooleanValue: P4ValueBase<P4Boolean> {
let value: Bool
public init(withValue value: Bool) {
self.value = value
}
public override func eq(rhs: P4Value) -> Bool {
guard let bool_rhs = rhs as? P4BooleanValue else {
return false
}
public var description: String {
return "\(value_type)"
return self.value == bool_rhs.value
}
}
/// A P4 int type
public struct P4Int: P4Type {
public static func create() -> any P4Type {
return P4Int()
}
}
/// An instance of a P4 integer
public class P4IntValue: P4ValueBase<P4Int> {
let value: Int
public init(withValue value: Int) {
self.value = value
}
public override func eq(rhs: P4Value) -> Bool {
guard let int_rhs = rhs as? P4IntValue else {
return false
}
return self.value == int_rhs.value
}
}
/// A P4 string type
public struct P4String: P4Type {
public static func create() -> any P4Type {
return P4String()
}
}
/// An instance of a P4 string
public class P4StringValue: P4ValueBase<P4String> {
let value: String
public init(withValue value: String) {
self.value = value
}
public override func eq(rhs: P4Value) -> Bool {
guard let string_rhs = rhs as? P4StringValue else {
return false
}
return self.value == string_rhs.value
}
}
/// A P4 value (with a type)
public struct Value: CustomStringConvertible, Equatable {
public var type: P4Type
public var value: P4Value
public init(withValue value: P4Value, andType type: P4Type) {
self.value = value
self.type = type
}
public var description: String {
return "\(self.value) of \(self.type)"
}
public static func == (lhs: Value, rhs: Value) -> Bool {
return lhs.value.eq(rhs: rhs.value)
}
}
public class Packet {
public init() {}
}
public init() {}
}
+9 -1
View File
@@ -20,7 +20,7 @@ public protocol EvaluatableExpression {
/// - Parameters
/// - execution: The execution context in which to evaluate the expression
/// - Returns: The value of expression
func evaluate(execution: ProgramExecution) -> ValueType
func evaluate(execution: ProgramExecution) -> P4Value
}
public protocol EvaluatableParserStatement {
@@ -31,3 +31,11 @@ public protocol EvaluatableParserStatement {
func evaluate(execution: ProgramExecution) -> ProgramExecution
}
public protocol P4Type {
static func create() -> P4Type
}
public protocol P4Value {
func type() -> P4Type
func eq(rhs: P4Value) -> Bool
}
+22
View File
@@ -0,0 +1,22 @@
// 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
public struct Instantiation {
}
+14 -6
View File
@@ -85,13 +85,21 @@ public struct ParserStates {
nonisolated(unsafe) public let accept: ParserState = ParserState(name: "accept")
nonisolated(unsafe) public let reject: ParserState = ParserState(name: "reject")
public struct Parser {
public struct Parser: P4Type {
public var states: [ParserState] = Array()
public var count: Int {
states.count
}
public init() {}
public var name: Identifier
public init(withName name: Identifier) {
self.name = name
}
public static func create() -> any P4Type {
return Parser(withName: Identifier(name: ""))
}
public func findStartState() -> ParserState? {
for state in states {
@@ -103,25 +111,25 @@ public struct Parser {
}
}
public class ParserExecution: ProgramExecution {
public class ParserInstance: ProgramExecution {
private init(state: ParserState) {
self.state = state
super.init()
}
public static func create(_ parser: Parser) -> Result<ParserExecution> {
public static func create(_ parser: Parser) -> Result<ParserInstance> {
guard let start_state = parser.findStartState() else {
return Result.Error(Error(withMessage: "Could not find the start state"))
}
let new = ParserExecution(state: start_state)
let new = ParserInstance(state: start_state)
return Result.Ok(new)
}
public var state: ParserState
public func transition(toNextState state: ParserState) -> ParserExecution {
public func transition(toNextState state: ParserState) -> ParserInstance {
let next = self
next.state = state
return next
+24 -1
View File
@@ -15,7 +15,30 @@
// 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
public struct Program {
public var parsers: [Parser] = Array()
public var types: [P4Type] = Array()
/// Find the program's main parser
///
/// Note: For now, the main parser is expected to be named main_parser.
public func starting_parser() -> Result<Parser> {
return self.find_parser(withName: Identifier(name: "main_parser"))
}
public func find_parser(withName name: Identifier) -> Result<Parser> {
for type in self.types {
print("type: \(type)")
guard let parser = type as? Parser else {
continue
}
if parser.name == name {
return .Ok(parser)
}
}
return .Error(Error(withMessage: "Could not find parser named \(name)"))
}
public init() {}
}
+39 -46
View File
@@ -24,45 +24,6 @@ import TreeSitterP4
let p4lang = Language(tree_sitter_p4())
public protocol ParseableValueType {
static func Parse(type: String, withValue value: String) -> Result<ValueType>
}
// This seems unnecessary because all the value types are in a single enum?
extension ValueType: ParseableValueType {
public static func Parse(type: String, withValue value: String) -> Result<ValueType> {
if type == "bool" {
// Default
if value == "" {
return .Ok(ValueType.Boolean(false))
}
if value == "true" {
return .Ok(ValueType.Boolean(true))
} else if value == "false" {
return .Ok(ValueType.Boolean(false))
}
return .Error(Error(withMessage: "Cannot convert \(value) into boolean value"))
} else if type == "string" {
return .Ok(ValueType.String(value))
} else if type == "int" {
// Default
if value == "" {
return .Ok(ValueType.Int(0))
}
guard let parsed_value = Swift.Int(value) else {
return .Error(Error(withMessage: "Cannot convert \(value) into integer value"))
}
return .Ok(ValueType.Int(parsed_value))
}
return .Error(Error(withMessage: "Invalid type"))
}
}
public protocol ParseableParserStatement {
static func Parse(node: Node, inTree tree: MutableTree) -> Result<EvaluatableParserStatement?>
}
@@ -129,7 +90,7 @@ extension VariableDeclarationStatement: ParseableParserStatement {
""
}
return switch ValueType.Parse(type: type_name, withValue: value) {
return switch Parser.ParseValueType(type: type_name, withValue: value) {
case Result.Ok(let value_type):
Result.Ok(
VariableDeclarationStatement(
@@ -141,6 +102,37 @@ extension VariableDeclarationStatement: ParseableParserStatement {
}
public struct Parser {
static func ParseValueType(type: String, withValue value: String) -> Result<P4Value> {
if type == "bool" {
// Default
if value == "" {
return .Ok(P4BooleanValue(withValue: false))
}
if value == "true" {
return .Ok(P4BooleanValue(withValue: true))
} else if value == "false" {
return .Ok(P4BooleanValue(withValue: false))
}
return .Error(Error(withMessage: "Cannot convert \(value) into boolean value"))
} else if type == "string" {
return .Ok(P4StringValue.init(withValue: value))
} else if type == "int" {
// Default
if value == "" {
return .Ok(P4IntValue.init(withValue: 0))
}
guard let parsed_value = Swift.Int(value) else {
return .Error(Error(withMessage: "Cannot convert \(value) into integer value"))
}
return .Ok(P4IntValue.init(withValue: parsed_value))
}
return .Error(Error(withMessage: "Invalid type"))
}
public struct P4Parser {
@@ -305,7 +297,7 @@ public struct Parser {
withTransition: transition_statement))
}
}
static func Parser(node: Node, inTree tree: MutableTree) -> Result<Lang.Parser> {
static func Parser(withName name: Identifier, node: Node, inTree tree: MutableTree) -> Result<Lang.Parser> {
guard
let parser_state_query = try? SwiftTreeSitter.Query(
language: p4lang,
@@ -317,7 +309,7 @@ public struct Parser {
Error(withMessage: "Could not compile the parser state tree sitter query"))
}
var parser = Lang.Parser()
var parser = Lang.Parser(withName: name)
// Build a state from each one listed.
for parser_states in parser_state_query.execute(node: node, in: tree) {
@@ -340,7 +332,8 @@ public struct Parser {
let result = p.parse(source)
guard let tree = result,
!tree.isError(lang: p4lang)
!tree.isError(lang: p4lang),
!tree.containsMissing(lang: p4lang)
else {
return Result.Error(Error(withMessage: "Could not compile the P4 program"))
}
@@ -349,7 +342,7 @@ public struct Parser {
let parser_declaration_query = try? SwiftTreeSitter.Query(
language: p4lang,
data: String(
"(parserDeclaration (parserType) (parserStates) @parser-states)"
"(parserDeclaration (parserType parser_name: (identifier) @parser-name) (parserStates) @parser-states)"
).data(using: String.Encoding.utf8)!)
else {
return Result.Error(
@@ -361,8 +354,8 @@ public struct Parser {
let parser_qc = parser_declaration_query.execute(in: tree)
for parser_declaration in parser_qc {
switch Parser(node: parser_declaration.nodes[0], inTree: tree) {
case Result.Ok(let parser): program.parsers.append(parser)
switch Parser(withName: Identifier(name: parser_declaration.nodes[0].text!), node: parser_declaration.nodes[1], inTree: tree) {
case Result.Ok(let parser): program.types.append(parser)
case Result.Error(let error): return Result.Error(error)
}
}
+15 -9
View File
@@ -18,33 +18,39 @@
import Common
import Lang
/// The runtime for a parser
public class ParserRuntime: CustomStringConvertible {
var execution: ParserExecution
var execution: ParserInstance
init(execution: ParserExecution) {
init(execution: ParserInstance) {
self.execution = execution
}
public static func create(program: Lang.Parser) -> Result<ParserRuntime> {
switch ParserExecution.create(program) {
case .Ok(let execution): return .Ok(Runtime.ParserRuntime(execution: execution))
case .Error(let error): return .Error(error)
/// Create a parser runtime from a P4 program
public static func create(program: Lang.Program) -> Result<ParserRuntime> {
return switch program.starting_parser() {
case .Ok(let parser):
switch ParserInstance.create(parser) {
case .Ok(let execution): .Ok(Runtime.ParserRuntime(execution: execution))
case .Error(let error): .Error(error)
}
case .Error(let error): .Error(error)
}
}
/// Run the P4 parser on a given packet
public func run(input: Packet) -> Result<(ParserState, ProgramExecution)> {
execution.scopes.enter()
return .Ok(execution.execute())
}
public var description: String {
//return "\(super.description)\nState: \(execution?.description ?? "N/A")\nError: \(error?.description ?? "None")"
return "Runtime:\nExecution: \(execution)"
}
}
extension ParserExecution: Execution {
/// Instances of parsers are executable
extension ParserInstance: Execution {
public func execute() -> (ParserState, ProgramExecution) {
var execution = self as ProgramExecution
var state = self.state
+17 -1
View File
@@ -35,5 +35,21 @@ extension MutableTree {
}
return false
}
}
public func containsMissing(lang: Language) -> Bool {
guard
let parser_error_query = try? SwiftTreeSitter.Query(
language: lang,
data: String(
"(MISSING)"
).data(using: String.Encoding.utf8)!)
else {
return false
}
let error_qr = parser_error_query.execute(in: self)
for _ in error_qr {
return true
}
return false
}
}