Begin Implementation of Binary Operator Support

Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
Will Hawkins
2026-03-13 08:26:35 -04:00
parent 4a3a6bdf56
commit d323434787
6 changed files with 320 additions and 1 deletions
+1
View File
@@ -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
} }
+65 -1
View File
@@ -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))
}
}
+16
View File
@@ -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
}
}
+27
View File
@@ -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
}
}
+98
View File
@@ -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)
}
+113
View File
@@ -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)
}