evalBinaryExpression method

  1. @override
dynamic evalBinaryExpression(
  1. BinaryExpression expression,
  2. Map<String, dynamic> context
)

Evaluates binary operations with null-safe logic.

This method implements custom null handling for various binary operators, providing spreadsheet-like behavior where null (empty cell) values are handled gracefully.

Operator Categories

Equality (==, !=)

Nulls can be compared to each other and other values:

  • null == nulltrue
  • null == 5false
  • null != 5true

Comparison (<, >, <=, >=)

Any comparison involving null returns false:

  • null < 5false
  • 10 > nullfalse

Logical (&&, ||, AND, OR)

Null is treated as falsy:

  • null && truefalse
  • null || truetrue
  • null && nullfalse

Arithmetic (+, -, *, /, %)

Any arithmetic with null produces null:

  • null + 5null
  • 10 * nullnull

Example

final evaluator = GridSheetExpressionEvaluator();
final parser = ExpressionParser();

// Equality with null
var expr = parser.parse('status == null');
debugPrint(evaluator.eval(expr, {'status': null}));  // true

// Comparison with null
expr = parser.parse('age > 18');
debugPrint(evaluator.eval(expr, {'age': null}));  // false

// Logical with null
expr = parser.parse('active || premium');
debugPrint(evaluator.eval(expr, {'active': null, 'premium': true}));  // true

// Arithmetic with null
expr = parser.parse('price * quantity');
debugPrint(evaluator.eval(expr, {'price': null, 'quantity': 5}));  // null

Implementation

@override
dynamic evalBinaryExpression(
  BinaryExpression expression,
  Map<String, dynamic> context,
) {
  var left = eval(expression.left, context);
  var right = eval(expression.right, context);

  // Apply null handling rules based on operator type
  switch (expression.operator) {
    // EQUALITY OPERATORS
    case '==':
      // null == null → true
      if (left == null && right == null) return true;
      // null == value → false
      if (left == null || right == null) return false;
      // Standard equality
      return left == right;

    case '!=':
      // null != null → false
      if (left == null && right == null) return false;
      // null != value → true
      if (left == null || right == null) return true;
      // Standard inequality
      return left != right;

    // COMPARISON OPERATORS
    case '<':
    case '>':
    case '<=':
    case '>=':
      // Any comparison with null returns false
      // This prevents exceptions and provides consistent behavior
      if (left == null || right == null) return false;
      // Delegate to parent for actual comparison
      return super.evalBinaryExpression(expression, context);

    // LOGICAL OPERATORS
    case '&&':
    case 'AND':
      // null is treated as false in AND operations
      if (left == null) return false;
      if (right == null) return false;
      // Standard AND logic
      return left && right;

    case '||':
    case 'OR':
      // null || null → false
      if (left == null && right == null) return false;
      // null || true → true, null || false → false
      if (left == null) return right == true;
      if (right == null) return left == true;
      // Standard OR logic
      return left || right;

    // ARITHMETIC AND OTHER OPERATORS
    default:
      // For arithmetic (+, -, *, /, %) and other operators,
      // any null operand produces null result
      if (left == null || right == null) return null;
      // Delegate to parent for actual operation
      return super.evalBinaryExpression(expression, context);
  }
}