diff --git a/Sources/P4Compiler/Expression.swift b/Sources/P4Compiler/Expression.swift index a178e22..8f3a3fa 100644 --- a/Sources/P4Compiler/Expression.swift +++ b/Sources/P4Compiler/Expression.swift @@ -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( 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.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 { let expression = node.child(at: 0)! - print("expression: \(expression)") #SkipUnlessNodeType( node: expression, type: "fieldAccessExpression") diff --git a/Sources/P4Lang/Expressions.swift b/Sources/P4Lang/Expressions.swift index db8a84f..f130ea1 100644 --- a/Sources/P4Lang/Expressions.swift +++ b/Sources/P4Lang/Expressions.swift @@ -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 diff --git a/Sources/P4Runtime/Expressions.swift b/Sources/P4Runtime/Expressions.swift index 7718d1a..cdde769 100644 --- a/Sources/P4Runtime/Expressions.swift +++ b/Sources/P4Runtime/Expressions.swift @@ -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 { let maybe_evaluated_left = self.left.evaluate(execution: execution) diff --git a/Tests/p4rseTests/BinaryOperatorTests/Integer.swift b/Tests/p4rseTests/BinaryOperatorTests/Integer.swift index 2973ac7..1324fc9 100644 --- a/Tests/p4rseTests/BinaryOperatorTests/Integer.swift +++ b/Tests/p4rseTests/BinaryOperatorTests/Integer.swift @@ -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))) +} + diff --git a/tree-sitter-p4/grammar.js b/tree-sitter-p4/grammar.js index 077d14b..d459694 100644 --- a/tree-sitter-p4/grammar.js +++ b/tree-sitter-p4/grammar.js @@ -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: $=> '.',