|
|
@@ -0,0 +1,948 @@
|
|
|
+using Invercargill;
|
|
|
+using Invercargill.DataStructures;
|
|
|
+using Invercargill.Expressions;
|
|
|
+
|
|
|
+class ExprTestPerson : Object {
|
|
|
+ public string name { get; set; }
|
|
|
+ public int age { get; set; }
|
|
|
+ public int rank { get; set; }
|
|
|
+ public bool is_important { get; set; }
|
|
|
+
|
|
|
+ public ExprTestPerson(string name, int age, int rank = 0, bool is_important = false) {
|
|
|
+ this.name = name;
|
|
|
+ this.age = age;
|
|
|
+ this.rank = rank;
|
|
|
+ this.is_important = is_important;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class ExprTestContainer : Object {
|
|
|
+ public int min_rank { get; set; }
|
|
|
+ public Enumerable<ExprTestPerson> values { get; set; }
|
|
|
+
|
|
|
+ public ExprTestContainer(int min_rank, Enumerable<ExprTestPerson> values) {
|
|
|
+ this.min_rank = min_rank;
|
|
|
+ this.values = values;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void expression_tests() {
|
|
|
+
|
|
|
+ // ==================== Literal Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/literal_int", () => {
|
|
|
+ var expr = new LiteralExpression(new NativeElement<int?>(42));
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int? value;
|
|
|
+ assert(result.try_get_as<int?>(out value));
|
|
|
+ assert(value == 42);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/literal_string", () => {
|
|
|
+ var expr = new LiteralExpression(new NativeElement<string?>("hello"));
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ string? value;
|
|
|
+ assert(result.try_get_as<string?>(out value));
|
|
|
+ assert(value == "hello");
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/literal_bool", () => {
|
|
|
+ var expr = new LiteralExpression(new NativeElement<bool?>(true));
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/literal_null", () => {
|
|
|
+ var expr = new LiteralExpression(new NullElement());
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+ assert(result.is_null());
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Variable Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/variable", () => {
|
|
|
+ var expr = new VariableExpression("x");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ props.set("x", new NativeElement<int?>(10));
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int? value;
|
|
|
+ assert(result.try_get_as<int?>(out value));
|
|
|
+ assert(value == 10);
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Binary Comparison Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/binary_equals", () => {
|
|
|
+ var expr = new BinaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<int?>(5)),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(5)),
|
|
|
+ BinaryOperator.EQUAL
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/binary_not_equals", () => {
|
|
|
+ var expr = new BinaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<int?>(5)),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(3)),
|
|
|
+ BinaryOperator.NOT_EQUAL
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/binary_greater_than", () => {
|
|
|
+ var expr = new BinaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<int?>(5)),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(3)),
|
|
|
+ BinaryOperator.GREATER_THAN
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/binary_less_than", () => {
|
|
|
+ var expr = new BinaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<int?>(3)),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(5)),
|
|
|
+ BinaryOperator.LESS_THAN
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Binary Logical Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/binary_and", () => {
|
|
|
+ var expr = new BinaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<bool?>(true)),
|
|
|
+ new LiteralExpression(new NativeElement<bool?>(true)),
|
|
|
+ BinaryOperator.AND
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/binary_or", () => {
|
|
|
+ var expr = new BinaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<bool?>(false)),
|
|
|
+ new LiteralExpression(new NativeElement<bool?>(true)),
|
|
|
+ BinaryOperator.OR
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Binary Arithmetic Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/binary_arithmetic_add", () => {
|
|
|
+ var expr = new BinaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<int?>(5)),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(3)),
|
|
|
+ BinaryOperator.ADD
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int64? value;
|
|
|
+ assert(result.try_get_as<int64?>(out value));
|
|
|
+ assert(value == 8);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/binary_arithmetic_subtract", () => {
|
|
|
+ var expr = new BinaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<int?>(5)),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(3)),
|
|
|
+ BinaryOperator.SUBTRACT
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int64? value;
|
|
|
+ assert(result.try_get_as<int64?>(out value));
|
|
|
+ assert(value == 2);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/binary_arithmetic_multiply", () => {
|
|
|
+ var expr = new BinaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<int?>(5)),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(3)),
|
|
|
+ BinaryOperator.MULTIPLY
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int64? value;
|
|
|
+ assert(result.try_get_as<int64?>(out value));
|
|
|
+ assert(value == 15);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/binary_arithmetic_divide", () => {
|
|
|
+ var expr = new BinaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<int?>(15)),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(3)),
|
|
|
+ BinaryOperator.DIVIDE
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int64? value;
|
|
|
+ assert(result.try_get_as<int64?>(out value));
|
|
|
+ assert(value == 5);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/binary_arithmetic_modulo", () => {
|
|
|
+ var expr = new BinaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<int?>(17)),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(5)),
|
|
|
+ BinaryOperator.MODULO
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int64? value;
|
|
|
+ assert(result.try_get_as<int64?>(out value));
|
|
|
+ assert(value == 2);
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Unary Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/unary_negate", () => {
|
|
|
+ var expr = new UnaryExpression(
|
|
|
+ UnaryOperator.NEGATE,
|
|
|
+ new LiteralExpression(new NativeElement<int?>(5))
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int64? value;
|
|
|
+ assert(result.try_get_as<int64?>(out value));
|
|
|
+ assert(value == -5);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/unary_not", () => {
|
|
|
+ var expr = new UnaryExpression(
|
|
|
+ UnaryOperator.NOT,
|
|
|
+ new LiteralExpression(new NativeElement<bool?>(false))
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Ternary Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/ternary_true", () => {
|
|
|
+ var expr = new TernaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<bool?>(true)),
|
|
|
+ new LiteralExpression(new NativeElement<string?>("yes")),
|
|
|
+ new LiteralExpression(new NativeElement<string?>("no"))
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ string? value;
|
|
|
+ assert(result.try_get_as<string?>(out value));
|
|
|
+ assert(value == "yes");
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/ternary_false", () => {
|
|
|
+ var expr = new TernaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<bool?>(false)),
|
|
|
+ new LiteralExpression(new NativeElement<string?>("yes")),
|
|
|
+ new LiteralExpression(new NativeElement<string?>("no"))
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ string? value;
|
|
|
+ assert(result.try_get_as<string?>(out value));
|
|
|
+ assert(value == "no");
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Property Access Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/property_access", () => {
|
|
|
+ var person = new ExprTestPerson("Alice", 30);
|
|
|
+ var expr = new PropertyExpression(
|
|
|
+ new VariableExpression("p"),
|
|
|
+ "name"
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ props.set("p", new NativeElement<ExprTestPerson?>(person));
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ string? value;
|
|
|
+ assert(result.try_get_as<string?>(out value));
|
|
|
+ assert(value == "Alice");
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/nested_property_access", () => {
|
|
|
+ var person = new ExprTestPerson("Bob", 25);
|
|
|
+ var expr = new PropertyExpression(
|
|
|
+ new VariableExpression("p"),
|
|
|
+ "age"
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ props.set("p", new NativeElement<ExprTestPerson?>(person));
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int? value;
|
|
|
+ assert(result.try_get_as<int?>(out value));
|
|
|
+ assert(value == 25);
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Bracketed Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/bracketed_expression", () => {
|
|
|
+ // (5 + 3) * 2 = 16
|
|
|
+ var inner = new BracketedExpression(
|
|
|
+ new BinaryExpression(
|
|
|
+ new LiteralExpression(new NativeElement<int?>(5)),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(3)),
|
|
|
+ BinaryOperator.ADD
|
|
|
+ )
|
|
|
+ );
|
|
|
+ var expr = new BinaryExpression(
|
|
|
+ inner,
|
|
|
+ new LiteralExpression(new NativeElement<int?>(2)),
|
|
|
+ BinaryOperator.MULTIPLY
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int64? value;
|
|
|
+ assert(result.try_get_as<int64?>(out value));
|
|
|
+ assert(value == 16);
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Complex Expression Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/complex_expression", () => {
|
|
|
+ // a > 5 && b < 10
|
|
|
+ var expr = new BinaryExpression(
|
|
|
+ new BinaryExpression(
|
|
|
+ new VariableExpression("a"),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(5)),
|
|
|
+ BinaryOperator.GREATER_THAN
|
|
|
+ ),
|
|
|
+ new BinaryExpression(
|
|
|
+ new VariableExpression("b"),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(10)),
|
|
|
+ BinaryOperator.LESS_THAN
|
|
|
+ ),
|
|
|
+ BinaryOperator.AND
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ props.set("a", new NativeElement<int?>(7));
|
|
|
+ props.set("b", new NativeElement<int?>(8));
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Lambda Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/lambda_basic", () => {
|
|
|
+ // x => x > 5
|
|
|
+ var body = new BinaryExpression(
|
|
|
+ new VariableExpression("x"),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(5)),
|
|
|
+ BinaryOperator.GREATER_THAN
|
|
|
+ );
|
|
|
+ var expr = new LambdaExpression("x", body);
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ var lambda = result as LambdaElement;
|
|
|
+ assert(lambda != null);
|
|
|
+ assert(lambda.get_lambda().parameter_name == "x");
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Function Call Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/function_call_where", () => {
|
|
|
+ // Create an enumerable of test persons
|
|
|
+ var persons = new Series<ExprTestPerson>();
|
|
|
+ persons.add(new ExprTestPerson("Alice", 30, 5));
|
|
|
+ persons.add(new ExprTestPerson("Bob", 25, 3));
|
|
|
+ persons.add(new ExprTestPerson("Charlie", 35, 7));
|
|
|
+
|
|
|
+ // persons.where(p => p.rank > 4)
|
|
|
+ var lambda_body = new BinaryExpression(
|
|
|
+ new PropertyExpression(new VariableExpression("p"), "rank"),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(4)),
|
|
|
+ BinaryOperator.GREATER_THAN
|
|
|
+ );
|
|
|
+ var lambda = new LambdaExpression("p", lambda_body);
|
|
|
+
|
|
|
+ var args = new Series<Expression>();
|
|
|
+ args.add(lambda);
|
|
|
+
|
|
|
+ var expr = new FunctionCallExpression(
|
|
|
+ new LiteralExpression(new NativeElement<Enumerable<ExprTestPerson>?>(persons)),
|
|
|
+ "where",
|
|
|
+ args
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ Elements elements;
|
|
|
+ assert(result.try_get_as<Elements>(out elements));
|
|
|
+
|
|
|
+ // Should have 2 elements (Alice with rank 5, Charlie with rank 7)
|
|
|
+ var count = 0;
|
|
|
+ elements.iterate(e => count++);
|
|
|
+ assert(count == 2);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/function_call_select", () => {
|
|
|
+ // Create an enumerable of test persons
|
|
|
+ var persons = new Series<ExprTestPerson>();
|
|
|
+ persons.add(new ExprTestPerson("Alice", 30));
|
|
|
+ persons.add(new ExprTestPerson("Bob", 25));
|
|
|
+
|
|
|
+ // persons.select(p => p.name)
|
|
|
+ var lambda_body = new PropertyExpression(new VariableExpression("p"), "name");
|
|
|
+ var lambda = new LambdaExpression("p", lambda_body);
|
|
|
+
|
|
|
+ var args = new Series<Expression>();
|
|
|
+ args.add(lambda);
|
|
|
+
|
|
|
+ var expr = new FunctionCallExpression(
|
|
|
+ new LiteralExpression(new NativeElement<Enumerable<ExprTestPerson>?>(persons)),
|
|
|
+ "select",
|
|
|
+ args
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ Elements elements;
|
|
|
+ assert(result.try_get_as<Elements>(out elements));
|
|
|
+
|
|
|
+ // Should have 2 string elements
|
|
|
+ var names = new Series<string>();
|
|
|
+ elements.iterate(e => {
|
|
|
+ string? name;
|
|
|
+ if(e.try_get_as<string?>(out name)) {
|
|
|
+ names.add(name);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ assert(names.length == 2);
|
|
|
+
|
|
|
+ // Check names using iterate
|
|
|
+ var idx = 0;
|
|
|
+ names.iterate(n => {
|
|
|
+ if (idx == 0) assert(n == "Alice");
|
|
|
+ if (idx == 1) assert(n == "Bob");
|
|
|
+ idx++;
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/function_call_first", () => {
|
|
|
+ // Create an enumerable of test persons
|
|
|
+ var persons = new Series<ExprTestPerson>();
|
|
|
+ persons.add(new ExprTestPerson("Alice", 30));
|
|
|
+ persons.add(new ExprTestPerson("Bob", 25));
|
|
|
+
|
|
|
+ // persons.first()
|
|
|
+ var expr = new FunctionCallExpression(
|
|
|
+ new LiteralExpression(new NativeElement<Enumerable<ExprTestPerson>?>(persons)),
|
|
|
+ "first"
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ ExprTestPerson? person;
|
|
|
+ assert(result.try_get_as<ExprTestPerson?>(out person));
|
|
|
+ assert(person.name == "Alice");
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/function_call_count", () => {
|
|
|
+ // Create an enumerable of test persons
|
|
|
+ var persons = new Series<ExprTestPerson>();
|
|
|
+ persons.add(new ExprTestPerson("Alice", 30));
|
|
|
+ persons.add(new ExprTestPerson("Bob", 25));
|
|
|
+ persons.add(new ExprTestPerson("Charlie", 35));
|
|
|
+
|
|
|
+ // persons.count()
|
|
|
+ var expr = new FunctionCallExpression(
|
|
|
+ new LiteralExpression(new NativeElement<Enumerable<ExprTestPerson>?>(persons)),
|
|
|
+ "count"
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int? count;
|
|
|
+ assert(result.try_get_as<int?>(out count));
|
|
|
+ assert(count == 3);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/function_call_any", () => {
|
|
|
+ // Create an enumerable of test persons
|
|
|
+ var persons = new Series<ExprTestPerson>();
|
|
|
+ persons.add(new ExprTestPerson("Alice", 30, 5));
|
|
|
+ persons.add(new ExprTestPerson("Bob", 25, 3));
|
|
|
+
|
|
|
+ // persons.any(p => p.rank > 4)
|
|
|
+ var lambda_body = new BinaryExpression(
|
|
|
+ new PropertyExpression(new VariableExpression("p"), "rank"),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(4)),
|
|
|
+ BinaryOperator.GREATER_THAN
|
|
|
+ );
|
|
|
+ var lambda = new LambdaExpression("p", lambda_body);
|
|
|
+
|
|
|
+ var args = new Series<Expression>();
|
|
|
+ args.add(lambda);
|
|
|
+
|
|
|
+ var expr = new FunctionCallExpression(
|
|
|
+ new LiteralExpression(new NativeElement<Enumerable<ExprTestPerson>?>(persons)),
|
|
|
+ "any",
|
|
|
+ args
|
|
|
+ );
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? any;
|
|
|
+ assert(result.try_get_as<bool?>(out any));
|
|
|
+ assert(any == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Chained Function Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/chained_where_first", () => {
|
|
|
+ // Create an enumerable of test persons
|
|
|
+ var persons = new Series<ExprTestPerson>();
|
|
|
+ persons.add(new ExprTestPerson("Alice", 30, 5));
|
|
|
+ persons.add(new ExprTestPerson("Bob", 25, 3));
|
|
|
+ persons.add(new ExprTestPerson("Charlie", 35, 7));
|
|
|
+
|
|
|
+ // persons.where(p => p.rank > 4).first()
|
|
|
+ var lambda_body = new BinaryExpression(
|
|
|
+ new PropertyExpression(new VariableExpression("p"), "rank"),
|
|
|
+ new LiteralExpression(new NativeElement<int?>(4)),
|
|
|
+ BinaryOperator.GREATER_THAN
|
|
|
+ );
|
|
|
+ var lambda = new LambdaExpression("p", lambda_body);
|
|
|
+
|
|
|
+ var args = new Series<Expression>();
|
|
|
+ args.add(lambda);
|
|
|
+
|
|
|
+ var where_call = new FunctionCallExpression(
|
|
|
+ new LiteralExpression(new NativeElement<Enumerable<ExprTestPerson>?>(persons)),
|
|
|
+ "where",
|
|
|
+ args
|
|
|
+ );
|
|
|
+
|
|
|
+ var first_call = new FunctionCallExpression(
|
|
|
+ where_call,
|
|
|
+ "first"
|
|
|
+ );
|
|
|
+
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = first_call.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ ExprTestPerson? person;
|
|
|
+ assert(result.try_get_as<ExprTestPerson?>(out person));
|
|
|
+ assert(person.name == "Alice"); // First with rank > 4
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Closure Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/closure_capture", () => {
|
|
|
+ // Test that lambda can capture variables from outer scope
|
|
|
+ // threshold = 4
|
|
|
+ // persons.where(p => p.rank > threshold)
|
|
|
+ var persons = new Series<ExprTestPerson>();
|
|
|
+ persons.add(new ExprTestPerson("Alice", 30, 5));
|
|
|
+ persons.add(new ExprTestPerson("Bob", 25, 3));
|
|
|
+ persons.add(new ExprTestPerson("Charlie", 35, 7));
|
|
|
+
|
|
|
+ var threshold = 4;
|
|
|
+
|
|
|
+ // Create context with threshold
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ props.set("threshold", new NativeElement<int?>(threshold));
|
|
|
+ var parent_context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ // Lambda body: p.rank > threshold
|
|
|
+ var lambda_body = new BinaryExpression(
|
|
|
+ new PropertyExpression(new VariableExpression("p"), "rank"),
|
|
|
+ new VariableExpression("threshold"),
|
|
|
+ BinaryOperator.GREATER_THAN
|
|
|
+ );
|
|
|
+ var lambda = new LambdaExpression("p", lambda_body);
|
|
|
+
|
|
|
+ var args = new Series<Expression>();
|
|
|
+ args.add(lambda);
|
|
|
+
|
|
|
+ var expr = new FunctionCallExpression(
|
|
|
+ new LiteralExpression(new NativeElement<Enumerable<ExprTestPerson>?>(persons)),
|
|
|
+ "where",
|
|
|
+ args
|
|
|
+ );
|
|
|
+
|
|
|
+ var result = expr.evaluate(parent_context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ Elements elements;
|
|
|
+ assert(result.try_get_as<Elements>(out elements));
|
|
|
+
|
|
|
+ // Should have 2 elements (Alice with rank 5, Charlie with rank 7)
|
|
|
+ var count = 0;
|
|
|
+ elements.iterate(e => count++);
|
|
|
+ assert(count == 2);
|
|
|
+ });
|
|
|
+
|
|
|
+ // ==================== Parser Tests ====================
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_literal_int", () => {
|
|
|
+ var expr = ExpressionParser.parse("42");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int64? value;
|
|
|
+ assert(result.try_get_as<int64?>(out value));
|
|
|
+ assert(value == 42);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_literal_string", () => {
|
|
|
+ var expr = ExpressionParser.parse("\"hello\"");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ string? value;
|
|
|
+ assert(result.try_get_as<string?>(out value));
|
|
|
+ assert(value == "hello");
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_literal_bool", () => {
|
|
|
+ var expr = ExpressionParser.parse("true");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_variable", () => {
|
|
|
+ var expr = ExpressionParser.parse("x");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ props.set("x", new NativeElement<int?>(10));
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int? value;
|
|
|
+ assert(result.try_get_as<int?>(out value));
|
|
|
+ assert(value == 10);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_binary_equals", () => {
|
|
|
+ var expr = ExpressionParser.parse("5 == 5");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_binary_arithmetic", () => {
|
|
|
+ var expr = ExpressionParser.parse("5 + 3");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int64? value;
|
|
|
+ assert(result.try_get_as<int64?>(out value));
|
|
|
+ assert(value == 8);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_binary_logical", () => {
|
|
|
+ var expr = ExpressionParser.parse("true && false");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == false);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_ternary", () => {
|
|
|
+ var expr = ExpressionParser.parse("true ? \"yes\" : \"no\"");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ string? value;
|
|
|
+ assert(result.try_get_as<string?>(out value));
|
|
|
+ assert(value == "yes");
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_property_access", () => {
|
|
|
+ var person = new ExprTestPerson("Alice", 30);
|
|
|
+ var expr = ExpressionParser.parse("p.name");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ props.set("p", new NativeElement<ExprTestPerson?>(person));
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ string? value;
|
|
|
+ assert(result.try_get_as<string?>(out value));
|
|
|
+ assert(value == "Alice");
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_bracketed", () => {
|
|
|
+ // (5 + 3) * 2 = 16
|
|
|
+ var expr = ExpressionParser.parse("(5 + 3) * 2");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int64? value;
|
|
|
+ assert(result.try_get_as<int64?>(out value));
|
|
|
+ assert(value == 16);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_unary_negate", () => {
|
|
|
+ var expr = ExpressionParser.parse("-5");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int64? value;
|
|
|
+ assert(result.try_get_as<int64?>(out value));
|
|
|
+ assert(value == -5);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_unary_not", () => {
|
|
|
+ var expr = ExpressionParser.parse("!false");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_lambda", () => {
|
|
|
+ var expr = ExpressionParser.parse("x => x > 5");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ var lambda = result as LambdaElement;
|
|
|
+ assert(lambda != null);
|
|
|
+ assert(lambda.get_lambda().parameter_name == "x");
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_complex", () => {
|
|
|
+ // a > 5 && b < 10
|
|
|
+ var expr = ExpressionParser.parse("a > 5 && b < 10");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ props.set("a", new NativeElement<int?>(7));
|
|
|
+ props.set("b", new NativeElement<int?>(8));
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_operator_precedence", () => {
|
|
|
+ // 2 + 3 * 4 = 14 (not 20)
|
|
|
+ var expr = ExpressionParser.parse("2 + 3 * 4");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ int64? value;
|
|
|
+ assert(result.try_get_as<int64?>(out value));
|
|
|
+ assert(value == 14);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/expressions/parser_comparison_chain", () => {
|
|
|
+ // Test chained comparisons with proper precedence
|
|
|
+ var expr = ExpressionParser.parse("5 < 10 && 10 > 5");
|
|
|
+ var props = new PropertyDictionary();
|
|
|
+ var context = new EvaluationContext(props);
|
|
|
+
|
|
|
+ var result = expr.evaluate(context);
|
|
|
+ assert(result != null);
|
|
|
+
|
|
|
+ bool? value;
|
|
|
+ assert(result.try_get_as<bool?>(out value));
|
|
|
+ assert(value == true);
|
|
|
+ });
|
|
|
+}
|