Support Binary Math Operators

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
Will Hawkins
2026-03-26 23:48:39 -04:00
parent 12ec6a77ed
commit fafc80553e
5 changed files with 430 additions and 20 deletions
+20 -6
View File
@@ -246,8 +246,9 @@ extension SelectExpression: CompilableExpression {
let maybe_selector = Expression.Compile(node: selector_node, withContext: context)
guard case .Ok(let selector) = maybe_selector else {
return .Error(
Error(
withMessage:
ErrorOnNode(
node: selector_node,
withError:
"Could not parse transition select expression selector expression: \(maybe_selector.error()!)"
))
}
@@ -343,8 +344,8 @@ extension BinaryOperatorExpression: CompilableExpression {
// swift-format-ignore
#RequireNodesType<Node, EvaluatableExpression?>(
nodes: binary_operator_expression_node,
type: ["binaryEqualOperatorExpression", "binaryLessThanOperatorExpression", "binaryLessThanEqualOperatorExpression", "binaryGreaterThanOperatorExpression", "binaryGreaterThanEqualOperatorExpression", "binaryAndOperatorExpression", "binaryOrOperatorExpression"],
nice_type_names: [ "binary equal operator", "binary less than operator", "binary less than or equal to operator", "binary greater than operator", "binary greater than or equal to operator", "binary and operator", "binary or operator"])
type: ["binaryEqualOperatorExpression", "binaryLessThanOperatorExpression", "binaryLessThanEqualOperatorExpression", "binaryGreaterThanOperatorExpression", "binaryGreaterThanEqualOperatorExpression", "binaryAndOperatorExpression", "binaryOrOperatorExpression", "binaryAddOperatorExpression", "binarySubtractOperatorExpression", "binaryMultiplyOperatorExpression", "binaryDivideOperatorExpression"],
nice_type_names: [ "binary equal operator", "binary less than operator", "binary less than or equal to operator", "binary greater than operator", "binary greater than or equal to operator", "binary and operator", "binary or operator", "binary add operator", "binary subtract operator", "binary multiply operator", "binary divide operator"])
if binary_operator_expression_node.childCount < currentChildIdxSafe {
return Result.Error(
@@ -382,7 +383,7 @@ extension BinaryOperatorExpression: CompilableExpression {
return Result.Error(maybe_right_hand_side.error()!)
}
let evaluators = [
let evaluators: [String: (String, P4Type, BinaryOperatorChecker?, BinaryOperatorEvaluator)] = [
"binaryEqualOperatorExpression": (
"Binary Equal", P4Boolean(), Optional<BinaryOperatorChecker>.none,
binary_equal_operator_evaluator
@@ -409,6 +410,20 @@ extension BinaryOperatorExpression: CompilableExpression {
"binaryOrOperatorExpression": (
"Binary And", P4Boolean(), binary_and_or_operator_checker, binary_or_operator_evaluator
),
"binaryAddOperatorExpression": (
"Binary Add", P4Int(), binary_int_math_operator_checker, binary_add_operator_evaluator
),
"binarySubtractOperatorExpression": (
"Binary Subtract", P4Int(), binary_int_math_operator_checker,
binary_subtract_operator_evaluator
),
"binaryMultiplyOperatorExpression": (
"Binary Multiply", P4Int(), binary_int_math_operator_checker,
binary_multiply_operator_evaluator
),
"binaryDivideOperatorExpression": (
"Binary Divide", P4Int(), binary_int_math_operator_checker, binary_divide_operator_evaluator
),
]
guard let selected_evaluator = evaluators[binary_operator_expression_node.nodeType!] else {
@@ -600,7 +615,6 @@ extension FieldAccessExpression: CompilableLValueExpression {
node: SwiftTreeSitter.Node, withContext context: CompilerContext
) -> Result<EvaluatableLValueExpression?> {
let expression = node.child(at: 0)!
print("expression: \(expression)")
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
node: expression, type: "fieldAccessExpression")
+1
View File
@@ -58,6 +58,7 @@ public struct SelectExpression {
}
public typealias NamedBinaryOperatorEvaluator = (String, P4Type, (P4Value, P4Value) -> P4Value)
public typealias BinaryOperatorEvaluator = (P4Value, P4Value) -> P4Value
public struct BinaryOperatorExpression {
public let evaluator: NamedBinaryOperatorEvaluator
public let left: EvaluatableExpression
+56 -14
View File
@@ -123,20 +123,6 @@ public func binary_gte_operator_evaluator(left: P4Value, right: P4Value) -> P4Va
}
}
public typealias BinaryOperatorChecker = (EvaluatableExpression, EvaluatableExpression) -> Result<
()
>
public func binary_and_or_operator_checker(
left: EvaluatableExpression, right: EvaluatableExpression
) -> Result<()> {
// Check that both are Boolean-typed things!
if !(left.type().eq(rhs: P4Boolean()) && right.type().eq(rhs: P4Boolean())) {
return .Error(Error(withMessage: "And/Or on operands with non-bool type is not allowed"))
}
return .Ok(())
}
public func binary_and_operator_evaluator(left: P4Value, right: P4Value) -> P4Value {
let bleft = left as! P4BooleanValue
let bright = right as! P4BooleanValue
@@ -153,6 +139,62 @@ public func binary_or_operator_evaluator(left: P4Value, right: P4Value) -> P4Val
}
}
public func binary_add_operator_evaluator(left: P4Value, right: P4Value) -> P4Value {
let ileft = left as! P4IntValue
let iright = right as! P4IntValue
return Map(input: ileft.access() + iright.access()) { input in
P4IntValue(withValue: input)
}
}
public func binary_subtract_operator_evaluator(left: P4Value, right: P4Value) -> P4Value {
let ileft = left as! P4IntValue
let iright = right as! P4IntValue
return Map(input: ileft.access() - iright.access()) { input in
P4IntValue(withValue: input)
}
}
public func binary_multiply_operator_evaluator(left: P4Value, right: P4Value) -> P4Value {
let ileft = left as! P4IntValue
let iright = right as! P4IntValue
return Map(input: ileft.access() * iright.access()) { input in
P4IntValue(withValue: input)
}
}
public func binary_divide_operator_evaluator(left: P4Value, right: P4Value) -> P4Value {
let ileft = left as! P4IntValue
let iright = right as! P4IntValue
return Map(input: ileft.access() / iright.access()) { input in
P4IntValue(withValue: input)
}
}
// swift-format-ignore
public typealias BinaryOperatorChecker = (EvaluatableExpression, EvaluatableExpression) -> Result<()>
public func binary_and_or_operator_checker(
left: EvaluatableExpression, right: EvaluatableExpression
) -> Result<()> {
// Check that both are Boolean-typed things!
if !(left.type().eq(rhs: P4Boolean()) && right.type().eq(rhs: P4Boolean())) {
return .Error(Error(withMessage: "And/Or on operands with non-bool type is not allowed"))
}
return .Ok(())
}
public func binary_int_math_operator_checker(
left: EvaluatableExpression, right: EvaluatableExpression
) -> Result<()> {
// Check that both are int-typed things!
if !(left.type().eq(rhs: P4Int()) && right.type().eq(rhs: P4Int())) {
return .Error(
Error(withMessage: "Mathematical operation on operands with non-int type is not allowed"))
}
return .Ok(())
}
extension BinaryOperatorExpression: EvaluatableExpression {
public func evaluate(execution: Common.ProgramExecution) -> Common.Result<any Common.P4Value> {
let maybe_evaluated_left = self.left.evaluate(execution: execution)
@@ -294,3 +294,342 @@ import TreeSitterP4
#expect(state_result == P4Lang.reject)
}
// Add Integers
@Test func test_simple_parser_binary_operator_add_integer() async throws {
let simple = """
parser main_parser() {
state start {
transition select (10 == (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_add_non_integer() async throws {
let simple = """
parser main_parser() {
state start {
transition select (10 == (true + 5)) {
true: accept;
false: reject;
};
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"{72, 16}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
),
Program.Compile(simple)))
}
@Test func test_simple_parser_binary_operator_add_non_integer2() async throws {
let simple = """
parser main_parser() {
state start {
transition select (10 == (5 + false)) {
true: accept;
false: reject;
};
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"{72, 17}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
),
Program.Compile(simple)))
}
@Test func test_simple_parser_binary_operator_add_non_integer3() async throws {
let simple = """
parser main_parser() {
state start {
transition select (10 == (false + false)) {
true: accept;
false: reject;
};
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"{72, 21}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
),
Program.Compile(simple)))
}
// Subtract Integers
@Test func test_simple_parser_binary_operator_subtract_integer() async throws {
let simple = """
parser main_parser() {
state start {
transition select (0 == (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_subtract_non_integer() async throws {
let simple = """
parser main_parser() {
state start {
transition select (10 == (true - 5)) {
true: accept;
false: reject;
};
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"{72, 16}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
),
Program.Compile(simple)))
}
@Test func test_simple_parser_binary_operator_subtract_non_integer2() async throws {
let simple = """
parser main_parser() {
state start {
transition select (10 == (5 - false)) {
true: accept;
false: reject;
};
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"{72, 17}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
),
Program.Compile(simple)))
}
@Test func test_simple_parser_binary_operator_subtract_non_integer3() async throws {
let simple = """
parser main_parser() {
state start {
transition select (10 == (false - false)) {
true: accept;
false: reject;
};
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"{72, 21}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
),
Program.Compile(simple)))
}
// Multiply Integers
@Test func test_simple_parser_binary_operator_multiply_integer() async throws {
let simple = """
parser main_parser() {
state start {
transition select (25 == (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_multiply_non_integer() async throws {
let simple = """
parser main_parser() {
state start {
transition select (10 == (true * 5)) {
true: accept;
false: reject;
};
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"{72, 16}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
),
Program.Compile(simple)))
}
@Test func test_simple_parser_binary_operator_multiply_non_integer2() async throws {
let simple = """
parser main_parser() {
state start {
transition select (10 == (5 * false)) {
true: accept;
false: reject;
};
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"{72, 17}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
),
Program.Compile(simple)))
}
@Test func test_simple_parser_binary_operator_multiply_non_integer3() async throws {
let simple = """
parser main_parser() {
state start {
transition select (10 == (false * false)) {
true: accept;
false: reject;
};
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"{72, 21}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
),
Program.Compile(simple)))
}
// Divide Integers
@Test func test_simple_parser_binary_operator_divide_integer() async throws {
let simple = """
parser main_parser() {
state start {
transition select (1 == (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_divide_non_integer() async throws {
let simple = """
parser main_parser() {
state start {
transition select (10 == (true / 5)) {
true: accept;
false: reject;
};
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"{72, 16}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
),
Program.Compile(simple)))
}
@Test func test_simple_parser_binary_operator_divide_non_integer2() async throws {
let simple = """
parser main_parser() {
state start {
transition select (10 == (5 / false)) {
true: accept;
false: reject;
};
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"{72, 17}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
),
Program.Compile(simple)))
}
@Test func test_simple_parser_binary_operator_divide_non_integer3() async throws {
let simple = """
parser main_parser() {
state start {
transition select (10 == (false / false)) {
true: accept;
false: reject;
};
}
};
"""
#expect(
#RequireErrorResult(
Error(
withMessage:
"{72, 21}: Could not parse transition select expression selector expression: Mathematical operation on operands with non-int type is not allowed"
),
Program.Compile(simple)))
}
+14
View File
@@ -102,6 +102,10 @@ export default grammar({
$.binaryGreaterThanEqualOperatorExpression,
$.binaryAndOperatorExpression,
$.binaryOrOperatorExpression,
$.binaryAddOperatorExpression,
$.binarySubtractOperatorExpression,
$.binaryMultiplyOperatorExpression,
$.binaryDivideOperatorExpression,
),
arrayAccessExpression: $ => seq($.expression, $.open_bracket, $.expression, $.close_bracket),
fieldAccessExpression: $=> prec.left(2, seq($.expression, $.field_access, $.identifier)),
@@ -115,6 +119,11 @@ export default grammar({
binaryAndOperatorExpression: $ => prec.left(2, seq($.expression, $.and, $.expression)),
binaryOrOperatorExpression: $ => prec.left(2, seq($.expression, $.or, $.expression)),
binaryAddOperatorExpression: $ => prec.left(2, seq($.expression, $.add, $.expression)),
binarySubtractOperatorExpression: $ => prec.left(2, seq($.expression, $.subtract, $.expression)),
binaryMultiplyOperatorExpression: $ => prec.left(2, seq($.expression, $.multiply, $.expression)),
binaryDivideOperatorExpression: $ => prec.left(2, seq($.expression, $.divide, $.expression)),
// Tokens
_semicolon: $ => ";",
colon: $ => ":",
@@ -178,6 +187,11 @@ export default grammar({
and: $=> "&&",
or: $=> "||",
add: $=> '+',
subtract: $=> '-',
multiply: $=> '*',
divide: $=> '/',
open_bracket: $=> '[',
close_bracket: $=> ']',
field_access: $=> '.',