Begin Implementation of Binary Operator Support
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
@@ -199,6 +199,7 @@ public class P4IntValue: P4Value {
|
|||||||
self.value = value
|
self.value = value
|
||||||
}
|
}
|
||||||
public func eq(rhs: P4Value) -> Bool {
|
public func eq(rhs: P4Value) -> Bool {
|
||||||
|
print("Int value equal.")
|
||||||
guard let int_rhs = rhs as? P4IntValue else {
|
guard let int_rhs = rhs as? P4IntValue else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import Common
|
|||||||
import P4Lang
|
import P4Lang
|
||||||
import SwiftTreeSitter
|
import SwiftTreeSitter
|
||||||
import TreeSitterP4
|
import TreeSitterP4
|
||||||
|
import P4Runtime
|
||||||
|
|
||||||
protocol CompilableExpression {
|
protocol CompilableExpression {
|
||||||
static func compile(
|
static func compile(
|
||||||
@@ -108,7 +109,7 @@ struct Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let localElementsParsers: [CompilableExpression.Type] = [
|
let localElementsParsers: [CompilableExpression.Type] = [
|
||||||
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self,
|
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self, BinaryOperatorExpression.self
|
||||||
]
|
]
|
||||||
|
|
||||||
for le_parser in localElementsParsers {
|
for le_parser in localElementsParsers {
|
||||||
@@ -242,3 +243,66 @@ extension KeysetExpression: CompilableExpression {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension BinaryOperatorExpression: CompilableExpression {
|
||||||
|
static func compile(
|
||||||
|
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||||
|
) -> Result<(EvaluatableExpression)?> {
|
||||||
|
let expression = node.child(at: 0)!
|
||||||
|
|
||||||
|
#SkipUnlessNodeType<Node, EvaluatableExpression?>(node: expression, type: "binaryOperatorExpression")
|
||||||
|
|
||||||
|
var currentChildIdx = 0
|
||||||
|
var currentChildIdxSafe = 1
|
||||||
|
var currentChild: Node? = .none
|
||||||
|
|
||||||
|
if expression.childCount < currentChildIdxSafe {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorOnNode(node: node, withError: "Malformed binary operator expression"))
|
||||||
|
}
|
||||||
|
currentChild = expression.child(at: currentChildIdx)
|
||||||
|
|
||||||
|
let binary_operator_expression_node = currentChild!
|
||||||
|
#RequireNodesType<Node, EvaluatableExpression?>(nodes: binary_operator_expression_node, type: ["binaryEqualOperatorExpression"], nice_type_names: ["binary equal operator"])
|
||||||
|
|
||||||
|
if binary_operator_expression_node.childCount < currentChildIdxSafe {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorOnNode(node: node, withError: "Missing LHS for binary operator expression"))
|
||||||
|
}
|
||||||
|
currentChild = binary_operator_expression_node.child(at: currentChildIdx)
|
||||||
|
let left_hand_side_raw = currentChild!
|
||||||
|
|
||||||
|
currentChildIdx = currentChildIdx + 1
|
||||||
|
currentChildIdxSafe = currentChildIdxSafe + 1
|
||||||
|
if binary_operator_expression_node.childCount < currentChildIdxSafe {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorOnNode(node: node, withError: "Missing binary operator for binary operator expression"))
|
||||||
|
}
|
||||||
|
currentChild = binary_operator_expression_node.child(at: currentChildIdx)
|
||||||
|
|
||||||
|
|
||||||
|
currentChildIdx = currentChildIdx + 1
|
||||||
|
currentChildIdxSafe = currentChildIdxSafe + 1
|
||||||
|
if binary_operator_expression_node.childCount < currentChildIdxSafe {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorOnNode(node: node, withError: "Missing binary operator for binary operator expression"))
|
||||||
|
}
|
||||||
|
currentChild = binary_operator_expression_node.child(at: currentChildIdx)
|
||||||
|
let right_hand_side_raw = currentChild!
|
||||||
|
|
||||||
|
let maybe_left_hand_side = Expression.Compile(node: left_hand_side_raw, withContext: context)
|
||||||
|
guard case Result.Ok(let left_hand_side) = maybe_left_hand_side else {
|
||||||
|
return Result.Error(maybe_left_hand_side.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_right_hand_side = Expression.Compile(node: right_hand_side_raw, withContext: context)
|
||||||
|
guard case Result.Ok(let right_hand_side) = maybe_right_hand_side else {
|
||||||
|
return Result.Error(maybe_right_hand_side.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(
|
||||||
|
BinaryOperatorExpression(
|
||||||
|
withEvaluator: ("Binary Equal", P4Boolean.create(), binary_equal_operator_evaluator),
|
||||||
|
withLhs: left_hand_side, withRhs: right_hand_side))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,3 +56,19 @@ public struct SelectExpression {
|
|||||||
withSelector: self.selector, withKeysetExpressions: new_kse)
|
withSelector: self.selector, withKeysetExpressions: new_kse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public typealias NamedBinaryOperatorEvaluator = (String, P4Type, (P4Value, P4Value) -> P4Value)
|
||||||
|
public struct BinaryOperatorExpression {
|
||||||
|
public let evaluator: NamedBinaryOperatorEvaluator
|
||||||
|
public let left: EvaluatableExpression
|
||||||
|
public let right: EvaluatableExpression
|
||||||
|
|
||||||
|
public init(
|
||||||
|
withEvaluator evaluator: NamedBinaryOperatorEvaluator, withLhs lhs: EvaluatableExpression,
|
||||||
|
withRhs rhs: EvaluatableExpression
|
||||||
|
) {
|
||||||
|
self.evaluator = evaluator
|
||||||
|
self.left = lhs
|
||||||
|
self.right = rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -86,3 +86,30 @@ extension TypedIdentifier: EvaluatableExpression {
|
|||||||
return execution.scopes.lookup(identifier: self)
|
return execution.scopes.lookup(identifier: self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func binary_equal_operator_evaluator(left: P4Value, right: P4Value) -> P4Value {
|
||||||
|
if left.eq(rhs: right) {
|
||||||
|
return P4BooleanValue(withValue: true)
|
||||||
|
}
|
||||||
|
return P4BooleanValue(withValue: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BinaryOperatorExpression: EvaluatableExpression {
|
||||||
|
public func evaluate(execution: Common.ProgramExecution) -> Common.Result<any Common.P4Value> {
|
||||||
|
let maybe_evaluated_left = self.left.evaluate(execution: execution)
|
||||||
|
guard case Result.Ok(let evaluated_left) = maybe_evaluated_left else {
|
||||||
|
return maybe_evaluated_left
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_evaluated_right = self.right.evaluate(execution: execution)
|
||||||
|
guard case Result.Ok(let evaluated_right) = maybe_evaluated_right else {
|
||||||
|
return maybe_evaluated_right
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Ok(self.evaluator.2(evaluated_left, evaluated_right))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func type() -> any Common.P4Type {
|
||||||
|
return self.evaluator.1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -197,3 +197,101 @@ import TreeSitterP4
|
|||||||
Error(withMessage: "No key matched the selector"),
|
Error(withMessage: "No key matched the selector"),
|
||||||
runtime.run()))
|
runtime.run()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test func test_simple_parser_binary_operator_equal() async throws {
|
||||||
|
let simple = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
transition select (true == true) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
|
||||||
|
let program = try #UseOkResult(Program.Compile(simple))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
|
||||||
|
#expect(state_result == P4Lang.accept)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_simple_parser_binary_operator_equal_not_equal() async throws {
|
||||||
|
let simple = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
transition select (true == false) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
|
||||||
|
let program = try #UseOkResult(Program.Compile(simple))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
|
||||||
|
#expect(state_result == P4Lang.reject)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_simple_parser_binary_operator_equal_integer() async throws {
|
||||||
|
let simple = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
transition select (5 == 5) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
|
||||||
|
let program = try #UseOkResult(Program.Compile(simple))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
|
||||||
|
#expect(state_result == P4Lang.accept)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_simple_parser_binary_operator_equal_not_equal_integer() async throws {
|
||||||
|
let simple = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
transition select (5 == 6) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
|
||||||
|
let program = try #UseOkResult(Program.Compile(simple))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
|
||||||
|
#expect(state_result == P4Lang.reject)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_simple_parser_binary_operator_equal_invalid_types() async throws {
|
||||||
|
let simple = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
transition select (5 == true) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
|
||||||
|
let program = try #UseOkResult(Program.Compile(simple))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: This test should throw an error.
|
||||||
|
|
||||||
|
#expect(state_result == P4Lang.reject)
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import Common
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Macros
|
import Macros
|
||||||
import P4Runtime
|
import P4Runtime
|
||||||
|
import P4Lang
|
||||||
import SwiftTreeSitter
|
import SwiftTreeSitter
|
||||||
import Testing
|
import Testing
|
||||||
import TreeSitter
|
import TreeSitter
|
||||||
@@ -116,3 +117,115 @@ import TreeSitterP4
|
|||||||
),
|
),
|
||||||
Program.Compile(simple_parser_declaration)))
|
Program.Compile(simple_parser_declaration)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test func test_expression_in_declaration_initializer() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
bool where_to = 5 == 5 == true;
|
||||||
|
transition select (where_to) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
|
||||||
|
// 5 == 5 == true
|
||||||
|
// true == true
|
||||||
|
// true
|
||||||
|
#expect(state_result == P4Lang.accept)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_expression_in_declaration_initializer2() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
bool where_to = 5 == 5 == false;
|
||||||
|
transition select (where_to) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
|
||||||
|
// 5 == 5 == true
|
||||||
|
// true == false
|
||||||
|
// false
|
||||||
|
#expect(state_result == P4Lang.reject)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_expression_in_declaration_initializer_false() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
bool where_to = 6 == 5 == true;
|
||||||
|
transition select (where_to) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
|
||||||
|
// 6 == 5 == true
|
||||||
|
// false == true
|
||||||
|
// false
|
||||||
|
#expect(state_result == P4Lang.reject)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_expression_in_declaration_initializer_false2() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
bool where_to = 6 == 5 == false;
|
||||||
|
transition select (where_to) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
|
||||||
|
// 6 == 5 == false
|
||||||
|
// false == false
|
||||||
|
// true
|
||||||
|
#expect(state_result == P4Lang.accept)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_expression_in_declaration_initializer_invalid_types() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
bool where_to = false == 5 == true;
|
||||||
|
transition select (where_to) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
let program = try #UseOkResult(Program.Compile(simple_parser_declaration))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
|
||||||
|
// TODO: This test should throw an error.
|
||||||
|
|
||||||
|
// false == 5 == true
|
||||||
|
// false == true
|
||||||
|
// false
|
||||||
|
#expect(state_result == P4Lang.reject)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user