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)
|
||||
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" {
|
||||
let maybe_table_declaration = Table.Compile(
|
||||
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 {
|
||||
public typealias T = TablePropertyList
|
||||
public static func Compile(
|
||||
@@ -733,7 +789,7 @@ extension TablePropertyList: Compilable {
|
||||
var current_context = context
|
||||
|
||||
var keys: [TableKeys] = Array()
|
||||
var _: [Action] = Array() // Actions are not yet supported
|
||||
var actions: [TableActionsProperty] = Array()
|
||||
var errors: [Error] = Array()
|
||||
|
||||
node.enumerateNamedChildren { child in
|
||||
@@ -745,9 +801,12 @@ extension TablePropertyList: Compilable {
|
||||
case .Error(let e): errors.append(e)
|
||||
}
|
||||
} else if child.nodeType == "table_actions" {
|
||||
errors.append(
|
||||
ErrorOnNode(
|
||||
node: child, withError: "Actions in table property lists are not yet supported"))
|
||||
switch TableActionsProperty.Compile(node: child, withContext: current_context) {
|
||||
case .Ok((let table_action_property, let updated_context)):
|
||||
current_context = updated_context
|
||||
actions.append(table_action_property)
|
||||
case .Error(let e): errors.append(e)
|
||||
}
|
||||
} else {
|
||||
errors.append(
|
||||
ErrorOnNode(node: child, withError: "Uknown node type in control declaration"))
|
||||
@@ -756,8 +815,7 @@ extension TablePropertyList: Compilable {
|
||||
|
||||
if !errors.isEmpty {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage: "Error(s) parsing property list: "
|
||||
ErrorOnNode(node: node, withError: "Error(s) parsing property list: "
|
||||
+ (errors.map { error in
|
||||
return "\(error.msg)"
|
||||
}.joined(separator: ";"))))
|
||||
@@ -770,7 +828,17 @@ extension TablePropertyList: Compilable {
|
||||
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(())
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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 {
|
||||
return "Action: "
|
||||
+ "\(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 init(
|
||||
named name: Identifier, withParameters parameters: ParameterList,
|
||||
withBody body: BlockStatement?
|
||||
named name: Identifier = Identifier(name: ""), withParameters parameters: ParameterList = ParameterList([]),
|
||||
withBody body: BlockStatement? = .none
|
||||
) {
|
||||
self.name = name
|
||||
self.params = parameters
|
||||
@@ -88,15 +138,24 @@ public struct TableKeys: CustomStringConvertible {
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO
|
||||
public struct TableActions {
|
||||
public init() {}
|
||||
public struct TableActionsProperty: CustomStringConvertible {
|
||||
public let actions: [TypedIdentifier]
|
||||
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 {
|
||||
let actions: TableActions
|
||||
let actions: TableActionsProperty
|
||||
let keys: TableKeys
|
||||
public init(withActions actions: TableActions, withKeys keys: TableKeys) {
|
||||
public init(withActions actions: TableActionsProperty, withKeys keys: TableKeys) {
|
||||
self.actions = actions
|
||||
self.keys = keys
|
||||
}
|
||||
@@ -208,7 +267,8 @@ public struct Control: P4DataType, P4DataValue, Equatable, CustomStringConvertib
|
||||
withParameters: ParameterList(),
|
||||
withTable: Table(
|
||||
withName: Identifier(name: "empty"),
|
||||
withPropertyList: TablePropertyList(withActions: TableActions(), withKeys: TableKeys())),
|
||||
withPropertyList: TablePropertyList(
|
||||
withActions: TableActionsProperty(), withKeys: TableKeys())),
|
||||
withActions: Actions(withActions: []), withApply: ApplyStatement())
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,113 @@ import P4Lang
|
||||
#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 {
|
||||
let simple_parser_declaration = """
|
||||
control simple(bool x, bool y) {
|
||||
|
||||
@@ -94,7 +94,7 @@ export default grammar({
|
||||
table_property_list: $ => repeat1(choice($.table_keys, $.table_actions)),
|
||||
table_keys: $=> seq($.key, '=', '{', repeat($.table_key_entry), '}'),
|
||||
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
|
||||
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