evalBinaryExpression method
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 == null→truenull == 5→falsenull != 5→true
Comparison (<, >, <=, >=)
Any comparison involving null returns false:
null < 5→false10 > null→false
Logical (&&, ||, AND, OR)
Null is treated as falsy:
null && true→falsenull || true→truenull && null→false
Arithmetic (+, -, *, /, %)
Any arithmetic with null produces null:
null + 5→null10 * null→null
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);
}
}