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
+12 -12
View File
@@ -41,14 +41,14 @@ import TreeSitterP4
}
};
"""
let x = { (tipe: P4QualifiedType) -> Bool in
switch tipe.baseType() {
let x = { (tipe: P4Type) -> Bool in
switch tipe {
case let c as Control: c.name == "simple"
default: false
}
}
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
#expect(program.InstancesWithTypes(x).count == 1)
#expect(program.TypesWithTypes(x).count == 1)
}
@Test func test_simple_control_declaration2() async throws {
@@ -79,14 +79,14 @@ import TreeSitterP4
};
"""
let filter = { (tipe: P4QualifiedType) -> Bool in
switch tipe.baseType() {
let filter = { (tipe: P4Type) -> Bool in
switch tipe {
case let c as Control: c.name == "simple" || c.name == "complex"
default: false
}
}
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
#expect(program.InstancesWithTypes(filter).count == 2)
#expect(program.TypesWithTypes(filter).count == 2)
}
@Test func test_simple_control_declaration_with_actions() async throws {
@@ -106,14 +106,14 @@ import TreeSitterP4
}
};
"""
let x = { (tipe: P4QualifiedType) -> Bool in
switch tipe.baseType() {
let x = { (tipe: P4Type) -> Bool in
switch tipe {
case let c as Control: c.name == "simple"
default: false
}
}
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
#expect(program.InstancesWithTypes(x).count == 1)
#expect(program.TypesWithTypes(x).count == 1)
}
@Test func test_simple_control_declaration_with_misnamed_actions() async throws {
@@ -341,14 +341,14 @@ import TreeSitterP4
};
"""
let x = { (tipe: P4QualifiedType) -> Bool in
switch tipe.baseType() {
let x = { (tipe: P4Type) -> Bool in
switch tipe {
case let c as Control: c.name == "simple"
default: false
}
}
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
#expect(program.InstancesWithTypes(x).count == 1)
#expect(program.TypesWithTypes(x).count == 1)
}
@Test func test_simple_control_declaration_with_action_with_params_wrong_order() async throws {
+145 -108
View File
@@ -53,13 +53,13 @@ import TreeSitterP4
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
// Pull the control out of the compiled program.
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
switch tipe.baseType() {
let controls = program.TypesWithTypes { (tipe: P4Type) -> Bool in
return switch tipe {
case let c as Control: c.name == "simple"
default: false
}
}
var control = ((controls[0].baseType() as P4Type) as! Control)
var control = ((controls[0]) as! Control)
// Add entries to the table.
control = control.updateTable(
@@ -78,23 +78,27 @@ import TreeSitterP4
global_values = global_values.declare(
identifier: Identifier(name: "result_arg"),
withValue: P4Value(
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
let runtime = try #UseOkResult(
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(
control: control, withGlobalValues: global_values))
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
withArguments: ArgumentList([
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(P4BooleanValue(withValue: true)), atIndex: 1),
])))
let (hit_miss, updated_execution) = try #UseOkResult(
runtime.run(
withArguments: ArgumentList([
Argument(
TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(P4BooleanValue(withValue: true)), atIndex: 1),
])))
// We expect there to be a hit.
#expect(hit_miss == P4TableHitMissValue.Hit)
// And that the proper action was invoked.
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
let result_arg = try #UseOkResult(
updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 5))))
}
@@ -124,13 +128,13 @@ import TreeSitterP4
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
// Pull the control out of the compiled program.
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
switch tipe.baseType() {
let controls = program.TypesWithTypes { (tipe: P4Type) -> Bool in
switch tipe {
case let c as Control: c.name == "simple"
default: false
}
}
var control = ((controls[0].baseType() as P4Type) as! Control)
var control = ((controls[0]) as! Control)
// Add entries to the table.
control = control.updateTable(
@@ -149,23 +153,27 @@ import TreeSitterP4
global_values = global_values.declare(
identifier: Identifier(name: "result_arg"),
withValue: P4Value(
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
let runtime = try #UseOkResult(
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(
control: control, withGlobalValues: global_values))
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
withArguments: ArgumentList([
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(P4BooleanValue(withValue: false)), atIndex: 1),
])))
let (hit_miss, updated_execution) = try #UseOkResult(
runtime.run(
withArguments: ArgumentList([
Argument(
TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(P4BooleanValue(withValue: false)), atIndex: 1),
])))
// We expect there to be a hit.
#expect(hit_miss == P4TableHitMissValue.Hit)
// And that the proper action was invoked.
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
let result_arg = try #UseOkResult(
updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 7))))
}
@@ -195,13 +203,13 @@ import TreeSitterP4
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
// Pull the control out of the compiled program.
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
switch tipe.baseType() {
let controls = program.TypesWithTypes { (tipe: P4Type) -> Bool in
switch tipe {
case let c as Control: c.name == "simple"
default: false
}
}
var control = ((controls[0].baseType() as P4Type) as! Control)
var control = ((controls[0]) as! Control)
// Add entries to the table.
control = control.updateTable(
@@ -220,22 +228,26 @@ import TreeSitterP4
global_values = global_values.declare(
identifier: Identifier(name: "result_arg"),
withValue: P4Value(
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
let runtime = try #UseOkResult(
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(
control: control, withGlobalValues: global_values))
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
withArguments: ArgumentList([
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(P4IntValue(withValue: 5)), atIndex: 1),
])))
let (hit_miss, updated_execution) = try #UseOkResult(
runtime.run(
withArguments: ArgumentList([
Argument(
TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(P4IntValue(withValue: 5)), atIndex: 1),
])))
// We expect there to be a hit.
#expect(hit_miss == P4TableHitMissValue.Hit)
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
let result_arg = try #UseOkResult(
updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 5))))
}
@@ -265,13 +277,13 @@ import TreeSitterP4
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
// Pull the control out of the compiled program.
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
switch tipe.baseType() {
let controls = program.TypesWithTypes { (tipe: P4Type) -> Bool in
return switch tipe {
case let c as Control: c.name == "simple"
default: false
}
}
var control = ((controls[0].baseType() as P4Type) as! Control)
var control = ((controls[0]) as! Control)
// Add entries to the table.
control = control.updateTable(
@@ -290,22 +302,26 @@ import TreeSitterP4
global_values = global_values.declare(
identifier: Identifier(name: "result_arg"),
withValue: P4Value(
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
let runtime = try #UseOkResult(
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(
control: control, withGlobalValues: global_values))
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
withArguments: ArgumentList([
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(P4IntValue(withValue: 3)), atIndex: 1),
])))
let (hit_miss, updated_execution) = try #UseOkResult(
runtime.run(
withArguments: ArgumentList([
Argument(
TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(P4IntValue(withValue: 3)), atIndex: 1),
])))
// We expect there to be a hit.
#expect(hit_miss == P4TableHitMissValue.Miss)
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
let result_arg = try #UseOkResult(
updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 0))))
}
@@ -336,13 +352,13 @@ import TreeSitterP4
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
// Pull the control out of the compiled program.
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
switch tipe.baseType() {
let controls = program.TypesWithTypes { (tipe: P4Type) -> Bool in
return switch tipe {
case let c as Control: c.name == "simple"
default: false
}
}
var control = ((controls[0].baseType() as P4Type) as! Control)
var control = ((controls[0]) as! Control)
// Add entries to the table.
control = control.updateTable(
@@ -361,24 +377,28 @@ import TreeSitterP4
global_values = global_values.declare(
identifier: Identifier(name: "result_arg"),
withValue: P4Value(
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
let runtime = try #UseOkResult(
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(
control: control, withGlobalValues: global_values))
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
withArguments: ArgumentList([
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(P4BooleanValue(withValue: false)), atIndex: 1), // false will make the x key miss.
Argument(P4Value(P4IntValue(withValue: 5)), atIndex: 2),
])))
let (hit_miss, updated_execution) = try #UseOkResult(
runtime.run(
withArguments: ArgumentList([
Argument(
TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(P4BooleanValue(withValue: false)), atIndex: 1), // false will make the x key miss.
Argument(P4Value(P4IntValue(withValue: 5)), atIndex: 2),
])))
// We expect there to be a hit -- but from the second key!
#expect(hit_miss == P4TableHitMissValue.Hit)
// And that the proper action was invoked.
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
let result_arg = try #UseOkResult(
updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 7))))
}
@@ -412,13 +432,13 @@ import TreeSitterP4
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
// Pull the control out of the compiled program.
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
switch tipe.baseType() {
let controls = program.TypesWithTypes { (tipe: P4Type) -> Bool in
return switch tipe {
case let c as Control: c.name == "simple"
default: false
}
}
var control = ((controls[0].baseType() as P4Type) as! Control)
var control = ((controls[0]) as! Control)
let k_fields = P4StructFields([
P4StructFieldIdentifier(name: "i", withType: P4QualifiedType(P4Int())),
@@ -426,10 +446,12 @@ import TreeSitterP4
])
let k_type = P4Struct(withName: Identifier(name: "K"), andFields: k_fields)
let k_instance = P4StructValue(withType: k_type, andInitializers: [
P4Value(P4IntValue(withValue: 5)),
P4Value(P4IntValue(withValue: 1)),
])
let k_instance = P4StructValue(
withType: k_type,
andInitializers: [
P4Value(P4IntValue(withValue: 5)),
P4Value(P4IntValue(withValue: 1)),
])
// Add entries to the table.
control = control.updateTable(
@@ -448,54 +470,63 @@ import TreeSitterP4
global_values = global_values.declare(
identifier: Identifier(name: "result_arg"),
withValue: P4Value(
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
let runtime = try #UseOkResult(
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(
control: control, withGlobalValues: global_values))
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
withArguments: ArgumentList([
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(k_instance), atIndex: 1),
])))
let (hit_miss, updated_execution) = try #UseOkResult(
runtime.run(
withArguments: ArgumentList([
Argument(
TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(k_instance), atIndex: 1),
])))
// We expect there to be a hit.
#expect(hit_miss == P4TableHitMissValue.Hit)
// And that the proper action was invoked.
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
let result_arg = try #UseOkResult(
updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 5))))
// Now, check whether the b action can be invoked.
let k_instance2 = P4StructValue(withType: k_type, andInitializers: [
P4Value(P4IntValue(withValue: 4)),
P4Value(P4IntValue(withValue: 1)),
])
let k_instance2 = P4StructValue(
withType: k_type,
andInitializers: [
P4Value(P4IntValue(withValue: 4)),
P4Value(P4IntValue(withValue: 1)),
])
// Set a variable in the global scope for the inout first parameter.
var next_global_values = VarValueScopes().enter()
next_global_values = global_values.declare(
identifier: Identifier(name: "result_arg"),
withValue: P4Value(
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
let runtime2 = try #UseOkResult(
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: next_global_values))
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(
control: control, withGlobalValues: next_global_values))
let (hit_miss2, updated_execution2) = try #UseOkResult(runtime2.run(
withArguments: ArgumentList([
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(k_instance2), atIndex: 1),
])))
let (hit_miss2, updated_execution2) = try #UseOkResult(
runtime2.run(
withArguments: ArgumentList([
Argument(
TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(k_instance2), atIndex: 1),
])))
// We expect there to be a hit.
#expect(hit_miss2 == P4TableHitMissValue.Hit)
// And that the proper action was invoked.
let result_arg2 = try #UseOkResult(updated_execution2.scopes.lookup(identifier: Identifier(name: "result_arg")))
let result_arg2 = try #UseOkResult(
updated_execution2.scopes.lookup(identifier: Identifier(name: "result_arg")))
#expect(result_arg2.eq(P4Value(P4IntValue(withValue: 7))))
}
@@ -529,13 +560,13 @@ import TreeSitterP4
let program = try! #UseOkResult(Program.Compile(simple_parser_declaration))
// Pull the control out of the compiled program.
let controls = program.InstancesWithTypes() { (tipe: P4QualifiedType) -> Bool in
switch tipe.baseType() {
let controls = program.TypesWithTypes { (tipe: P4Type) -> Bool in
return switch tipe {
case let c as Control: c.name == "simple"
default: false
}
}
var control = ((controls[0].baseType() as P4Type) as! Control)
var control = ((controls[0]) as! Control)
let k_fields = P4StructFields([
P4StructFieldIdentifier(name: "i", withType: P4QualifiedType(P4Int())),
@@ -543,10 +574,12 @@ import TreeSitterP4
])
let k_type = P4Struct(withName: Identifier(name: "K"), andFields: k_fields)
let k_instance = P4StructValue(withType: k_type, andInitializers: [
P4Value(P4IntValue(withValue: 8)),
P4Value(P4IntValue(withValue: 1)),
])
let k_instance = P4StructValue(
withType: k_type,
andInitializers: [
P4Value(P4IntValue(withValue: 8)),
P4Value(P4IntValue(withValue: 1)),
])
// Add entries to the table.
control = control.updateTable(
@@ -565,22 +598,26 @@ import TreeSitterP4
global_values = global_values.declare(
identifier: Identifier(name: "result_arg"),
withValue: P4Value(
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
P4IntValue(withValue: 0),
P4QualifiedType(P4Int())))
let runtime = try #UseOkResult(
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(control: control, withGlobalValues: global_values))
P4Runtime.Runtime<P4TableHitMissValue, Control>.create(
control: control, withGlobalValues: global_values))
let (hit_miss, updated_execution) = try #UseOkResult(runtime.run(
withArguments: ArgumentList([
Argument(TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(k_instance), atIndex: 1),
])))
let (hit_miss, updated_execution) = try #UseOkResult(
runtime.run(
withArguments: ArgumentList([
Argument(
TypedIdentifier(name: "result_arg", withType: P4QualifiedType(P4Int())), atIndex: 0),
Argument(P4Value(k_instance), atIndex: 1),
])))
// We expect there to be a hit.
#expect(hit_miss == P4TableHitMissValue.Miss)
// And that the proper action was invoked.
let result_arg = try #UseOkResult(updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
let result_arg = try #UseOkResult(
updated_execution.scopes.lookup(identifier: Identifier(name: "result_arg")))
#expect(result_arg.eq(P4Value(P4IntValue(withValue: 0))))
}
}