parser, compiler: Support Table Actions
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
@@ -459,6 +459,10 @@ extension Control: CompilableDeclaration {
|
|||||||
}
|
}
|
||||||
actions.append(action_declaration)
|
actions.append(action_declaration)
|
||||||
local_context = updated_context
|
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" {
|
} else if current_node.nodeType == "table_declaration" {
|
||||||
let maybe_table_declaration = Table.Compile(
|
let maybe_table_declaration = Table.Compile(
|
||||||
node: current_node, withContext: local_context)
|
node: current_node, withContext: local_context)
|
||||||
@@ -721,6 +725,58 @@ extension TableKeys: Compilable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
ErrorOnNode(
|
||||||
|
node: node, 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: P4Type(maybe_action)))
|
||||||
|
}
|
||||||
|
return .Error(
|
||||||
|
ErrorOnNode(node: node, 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(
|
||||||
|
ErrorOnNode(node: node, withError: "Error(s) parsing table actions: "
|
||||||
|
+ (errors.map { error in
|
||||||
|
return "\(error.msg)"
|
||||||
|
}.joined(separator: ";"))))
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok((TableActionsProperty(actions), context))
|
||||||
|
}
|
||||||
|
}
|
||||||
extension TablePropertyList: Compilable {
|
extension TablePropertyList: Compilable {
|
||||||
public typealias T = TablePropertyList
|
public typealias T = TablePropertyList
|
||||||
public static func Compile(
|
public static func Compile(
|
||||||
@@ -733,7 +789,7 @@ extension TablePropertyList: Compilable {
|
|||||||
var current_context = context
|
var current_context = context
|
||||||
|
|
||||||
var keys: [TableKeys] = Array()
|
var keys: [TableKeys] = Array()
|
||||||
var _: [Action] = Array() // Actions are not yet supported
|
var actions: [TableActionsProperty] = Array()
|
||||||
var errors: [Error] = Array()
|
var errors: [Error] = Array()
|
||||||
|
|
||||||
node.enumerateNamedChildren { child in
|
node.enumerateNamedChildren { child in
|
||||||
@@ -745,9 +801,12 @@ extension TablePropertyList: Compilable {
|
|||||||
case .Error(let e): errors.append(e)
|
case .Error(let e): errors.append(e)
|
||||||
}
|
}
|
||||||
} else if child.nodeType == "table_actions" {
|
} else if child.nodeType == "table_actions" {
|
||||||
errors.append(
|
switch TableActionsProperty.Compile(node: child, withContext: current_context) {
|
||||||
ErrorOnNode(
|
case .Ok((let table_action_property, let updated_context)):
|
||||||
node: child, withError: "Actions in table property lists are not yet supported"))
|
current_context = updated_context
|
||||||
|
actions.append(table_action_property)
|
||||||
|
case .Error(let e): errors.append(e)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
errors.append(
|
errors.append(
|
||||||
ErrorOnNode(node: child, withError: "Uknown node type in control declaration"))
|
ErrorOnNode(node: child, withError: "Uknown node type in control declaration"))
|
||||||
@@ -756,8 +815,7 @@ extension TablePropertyList: Compilable {
|
|||||||
|
|
||||||
if !errors.isEmpty {
|
if !errors.isEmpty {
|
||||||
return .Error(
|
return .Error(
|
||||||
Error(
|
ErrorOnNode(node: node, withError: "Error(s) parsing property list: "
|
||||||
withMessage: "Error(s) parsing property list: "
|
|
||||||
+ (errors.map { error in
|
+ (errors.map { error in
|
||||||
return "\(error.msg)"
|
return "\(error.msg)"
|
||||||
}.joined(separator: ";"))))
|
}.joined(separator: ";"))))
|
||||||
@@ -770,7 +828,17 @@ extension TablePropertyList: Compilable {
|
|||||||
ErrorOnNode(node: node, withError: "More than one key set in table property list"))
|
ErrorOnNode(node: node, withError: "More than one key set in table property list"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return .Ok((TablePropertyList(withActions: TableActions(), withKeys: keys[0]), current_context))
|
// There should be only one table keys!
|
||||||
|
if actions.count > 1 {
|
||||||
|
// Todo: Make this error message better.
|
||||||
|
return .Error(
|
||||||
|
ErrorOnNode(node: node, 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))
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,4 +51,21 @@ public struct Walker {
|
|||||||
}
|
}
|
||||||
return Result.Ok(())
|
return Result.Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func try_map<T>(n: Int, onlyNamed: Bool = false, todo: (Node) -> Result<T>) -> ([T], [Error]) {
|
||||||
|
var errors: [Error] = Array()
|
||||||
|
var results: [T] = Array()
|
||||||
|
|
||||||
|
for currentChildIdx in currentChildIdx..<n {
|
||||||
|
let currentChild = node.child(at: currentChildIdx)!
|
||||||
|
if onlyNamed && !currentChild.isNamed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch todo(currentChild) {
|
||||||
|
case .Ok(let r): results.append(r)
|
||||||
|
case .Error(let e): errors.append(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (results, errors)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,57 @@
|
|||||||
|
|
||||||
import Common
|
import Common
|
||||||
|
|
||||||
public struct Action: CustomStringConvertible {
|
public struct Action: CustomStringConvertible, P4DataType, P4DataValue {
|
||||||
|
public func type() -> any Common.P4DataType {
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
public func eq(rhs: any Common.P4DataValue) -> Bool {
|
||||||
|
return switch rhs {
|
||||||
|
case let arhs as Action: self.name == arhs.name
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func eq(rhs: any Common.P4DataType) -> Bool {
|
||||||
|
return switch rhs {
|
||||||
|
case is Action: true
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public func lt(rhs: any Common.P4DataValue) -> Bool {
|
||||||
|
switch rhs {
|
||||||
|
case let arhs as Action: return self.name < arhs.name
|
||||||
|
default: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func lte(rhs: any Common.P4DataValue) -> Bool {
|
||||||
|
switch rhs {
|
||||||
|
case let arhs as Action: return self.name <= arhs.name
|
||||||
|
default: return false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public func gt(rhs: any Common.P4DataValue) -> Bool {
|
||||||
|
switch rhs {
|
||||||
|
case let arhs as Action: return self.name > arhs.name
|
||||||
|
default: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func gte(rhs: any Common.P4DataValue) -> Bool {
|
||||||
|
switch rhs {
|
||||||
|
case let arhs as Action: return self.name >= arhs.name
|
||||||
|
default: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func def() -> any Common.P4DataValue {
|
||||||
|
return Action()
|
||||||
|
}
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return "Action: "
|
return "Action: "
|
||||||
+ "\(self.name) with parameters \(self.params) and body \(String(describing: self.body))"
|
+ "\(self.name) with parameters \(self.params) and body \(String(describing: self.body))"
|
||||||
@@ -28,8 +78,8 @@ public struct Action: CustomStringConvertible {
|
|||||||
public var name: Identifier
|
public var name: Identifier
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
named name: Identifier, withParameters parameters: ParameterList,
|
named name: Identifier = Identifier(name: ""), withParameters parameters: ParameterList = ParameterList([]),
|
||||||
withBody body: BlockStatement?
|
withBody body: BlockStatement? = .none
|
||||||
) {
|
) {
|
||||||
self.name = name
|
self.name = name
|
||||||
self.params = parameters
|
self.params = parameters
|
||||||
@@ -88,15 +138,24 @@ public struct TableKeys: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO
|
public struct TableActionsProperty: CustomStringConvertible {
|
||||||
public struct TableActions {
|
public let actions: [TypedIdentifier]
|
||||||
public init() {}
|
public init(_ actions: [TypedIdentifier] = []) {
|
||||||
|
self.actions = actions
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
return "Table Actions: "
|
||||||
|
+ self.actions.map { action in
|
||||||
|
return action.description
|
||||||
|
}.joined(separator: ";")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct TablePropertyList: CustomStringConvertible {
|
public struct TablePropertyList: CustomStringConvertible {
|
||||||
let actions: TableActions
|
let actions: TableActionsProperty
|
||||||
let keys: TableKeys
|
let keys: TableKeys
|
||||||
public init(withActions actions: TableActions, withKeys keys: TableKeys) {
|
public init(withActions actions: TableActionsProperty, withKeys keys: TableKeys) {
|
||||||
self.actions = actions
|
self.actions = actions
|
||||||
self.keys = keys
|
self.keys = keys
|
||||||
}
|
}
|
||||||
@@ -208,7 +267,8 @@ public struct Control: P4DataType, P4DataValue, Equatable, CustomStringConvertib
|
|||||||
withParameters: ParameterList(),
|
withParameters: ParameterList(),
|
||||||
withTable: Table(
|
withTable: Table(
|
||||||
withName: Identifier(name: "empty"),
|
withName: Identifier(name: "empty"),
|
||||||
withPropertyList: TablePropertyList(withActions: TableActions(), withKeys: TableKeys())),
|
withPropertyList: TablePropertyList(
|
||||||
|
withActions: TableActionsProperty(), withKeys: TableKeys())),
|
||||||
withActions: Actions(withActions: []), withApply: ApplyStatement())
|
withActions: Actions(withActions: []), withApply: ApplyStatement())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,113 @@ import P4Lang
|
|||||||
#expect(program.InstancesWithTypes(filter).count == 2)
|
#expect(program.InstancesWithTypes(filter).count == 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test func test_simple_control_declaration_with_actions() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
control simple() {
|
||||||
|
action a() {
|
||||||
|
}
|
||||||
|
table t {
|
||||||
|
key = {
|
||||||
|
true: exact;
|
||||||
|
}
|
||||||
|
actions = {
|
||||||
|
a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apply {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
let x = { (tipe: P4Type) -> Bool in
|
||||||
|
switch tipe.dataType() {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_simple_control_declaration_with_misnamed_actions() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
control simple() {
|
||||||
|
action a() {
|
||||||
|
}
|
||||||
|
table t {
|
||||||
|
key = {
|
||||||
|
true: exact;
|
||||||
|
}
|
||||||
|
actions = {
|
||||||
|
b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apply {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
#expect(
|
||||||
|
#RequireErrorResult(
|
||||||
|
Error(
|
||||||
|
withMessage: "{54, 63}: Error(s) parsing property list: {91, 26}: Error(s) parsing table actions: Cannot find b in lexical scope."
|
||||||
|
),
|
||||||
|
Program.Compile(simple_parser_declaration))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_simple_control_declaration_with_misnamed_actions2() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
control simple() {
|
||||||
|
action a() {
|
||||||
|
}
|
||||||
|
table t {
|
||||||
|
key = {
|
||||||
|
true: exact;
|
||||||
|
}
|
||||||
|
actions = {
|
||||||
|
a;
|
||||||
|
b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apply {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
#expect(
|
||||||
|
#RequireErrorResult(
|
||||||
|
Error(
|
||||||
|
withMessage: "{54, 72}: Error(s) parsing property list: {91, 35}: Error(s) parsing table actions: Cannot find b in lexical scope."
|
||||||
|
),
|
||||||
|
Program.Compile(simple_parser_declaration))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_simple_control_declaration_with_mistyped_actions() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
bool a() {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
control simple() {
|
||||||
|
table t {
|
||||||
|
key = {
|
||||||
|
true: exact;
|
||||||
|
}
|
||||||
|
actions = {
|
||||||
|
a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apply {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
#expect(
|
||||||
|
#RequireErrorResult(
|
||||||
|
Error(
|
||||||
|
withMessage: "{64, 63}: Error(s) parsing property list: {101, 26}: Error(s) parsing table actions: {101, 26}: a does not name an action"
|
||||||
|
),
|
||||||
|
Program.Compile(simple_parser_declaration))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test func test_simple_control_declaration_with_parameters() async throws {
|
@Test func test_simple_control_declaration_with_parameters() async throws {
|
||||||
let simple_parser_declaration = """
|
let simple_parser_declaration = """
|
||||||
control simple(bool x, bool y) {
|
control simple(bool x, bool y) {
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export default grammar({
|
|||||||
table_property_list: $ => repeat1(choice($.table_keys, $.table_actions)),
|
table_property_list: $ => repeat1(choice($.table_keys, $.table_actions)),
|
||||||
table_keys: $=> seq($.key, '=', '{', repeat($.table_key_entry), '}'),
|
table_keys: $=> seq($.key, '=', '{', repeat($.table_key_entry), '}'),
|
||||||
table_key_entry: $=> seq($.keysetExpression, ':', $.table_key_match_type, $._semicolon),
|
table_key_entry: $=> seq($.keysetExpression, ':', $.table_key_match_type, $._semicolon),
|
||||||
table_actions: $=> seq($.actions, '=', '{', optional(repeat1($.identifier)), '}'),
|
table_actions: $=> seq($.actions, '=', '{', optional(repeat1(seq($.identifier, $._semicolon))), '}'),
|
||||||
|
|
||||||
// match types
|
// match types
|
||||||
table_key_match_type: $ => choice($.exact), // support exact only for now.
|
table_key_match_type: $ => choice($.exact), // support exact only for now.
|
||||||
|
|||||||
@@ -54,4 +54,77 @@ control simple() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
=========================
|
||||||
|
Simple Control Declaration With Actions
|
||||||
|
=========================
|
||||||
|
control simple() {
|
||||||
|
action a() {
|
||||||
|
}
|
||||||
|
action b() {
|
||||||
|
}
|
||||||
|
table t {
|
||||||
|
key = {
|
||||||
|
x: exact;
|
||||||
|
}
|
||||||
|
actions = {
|
||||||
|
a;
|
||||||
|
b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apply {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
---
|
||||||
|
(p4program
|
||||||
|
(declaration
|
||||||
|
(control_declaration
|
||||||
|
(control)
|
||||||
|
(identifier)
|
||||||
|
(parameters)
|
||||||
|
(action_declaration
|
||||||
|
(action)
|
||||||
|
(identifier)
|
||||||
|
(parameters)
|
||||||
|
(blockStatement)
|
||||||
|
)
|
||||||
|
(action_declaration
|
||||||
|
(action)
|
||||||
|
(identifier)
|
||||||
|
(parameters)
|
||||||
|
(blockStatement)
|
||||||
|
)
|
||||||
|
(table_declaration
|
||||||
|
(table)
|
||||||
|
(identifier)
|
||||||
|
(table_property_list
|
||||||
|
(table_keys
|
||||||
|
(key)
|
||||||
|
(table_key_entry
|
||||||
|
(keysetExpression
|
||||||
|
(expression
|
||||||
|
(simple_expression
|
||||||
|
(identifier)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(table_key_match_type
|
||||||
|
(exact)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(table_actions
|
||||||
|
(actions)
|
||||||
|
(identifier)
|
||||||
|
(identifier)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(apply_statement
|
||||||
|
(apply)
|
||||||
|
(blockStatement)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user