Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Commit

Permalink
fix(parser, scope): Allow nulls in binary operations.
Browse files Browse the repository at this point in the history
Fixes #646
  • Loading branch information
jbdeboer committed Mar 5, 2014
1 parent 74ad60f commit 5981175
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 16 deletions.
8 changes: 8 additions & 0 deletions bin/parser_generator_for_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ main(arguments) {
'true',
'true || run()',
'undefined',
'null < 0',
'null * 3',
'null + 6',
'5 + null',
'null - 4',
'3 - null',
'null + null',
'null - null',

';;1;;',
'1==1',
Expand Down
16 changes: 16 additions & 0 deletions lib/core/parser/eval.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ class Binary extends syntax.Binary {
case '||': return toBool(left) || toBool(this.right.eval(scope));
}
var right = this.right.eval(scope);

// Null check for the operations.
if (left == null || right == null) {
switch (operation) {
case '+':
if (left != null) return left;
if (right != null) return right;
return 0;
case '-':
if (left != null) return left;
if (right != null) return 0 - right;
return 0;
}
return null;
}

switch (operation) {
case '+' : return autoConvertAdd(left, right);
case '-' : return left - right;
Expand Down
2 changes: 1 addition & 1 deletion lib/core/parser/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ autoConvertAdd(a, b) {
}
if (a != null) return a;
if (b != null) return b;
return null;
return 0;
}

/**
Expand Down
26 changes: 13 additions & 13 deletions lib/core/scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -954,19 +954,19 @@ Function _operationToFunction(String operation) {

_operation_negate(value) => !toBool(value);
_operation_add(left, right) => autoConvertAdd(left, right);
_operation_subtract(left, right) => left - right;
_operation_multiply(left, right) => left * right;
_operation_divide(left, right) => left / right;
_operation_divide_int(left, right) => left ~/ right;
_operation_remainder(left, right) => left % right;
_operation_equals(left, right) => left == right;
_operation_not_equals(left, right) => left != right;
_operation_less_then(left, right) => left < right;
_operation_greater_then(left, right) => (left == null || right == null) ? false : left > right;
_operation_less_or_equals_then(left, right) => left <= right;
_operation_greater_or_equals_then(left, right) => left >= right;
_operation_power(left, right) => left ^ right;
_operation_bitwise_and(left, right) => left & right;
_operation_subtract(left, right) => (left != null && right != null) ? left - right : (left != null ? left : (right != null ? 0 - right : 0));
_operation_multiply(left, right) => (left == null || right == null) ? null : left * right;
_operation_divide(left, right) => (left == null || right == null) ? null : left / right;
_operation_divide_int(left, right) => (left == null || right == null) ? null : left ~/ right;
_operation_remainder(left, right) => (left == null || right == null) ? null : left % right;
_operation_equals(left, right) => (left == null || right == null) ? null : left == right;
_operation_not_equals(left, right) => (left == null || right == null) ? null : left != right;
_operation_less_then(left, right) => (left == null || right == null) ? null : left < right;
_operation_greater_then(left, right) => (left == null || right == null) ? null : left > right;
_operation_less_or_equals_then(left, right) => (left == null || right == null) ? null : left <= right;
_operation_greater_or_equals_then(left, right) => (left == null || right == null) ? null : left >= right;
_operation_power(left, right) => (left == null || right == null) ? null : left ^ right;
_operation_bitwise_and(left, right) => (left == null || right == null) ? null : left & right;
// TODO(misko): these should short circuit the evaluation.
_operation_logical_and(left, right) => toBool(left) && toBool(right);
_operation_logical_or(left, right) => toBool(left) || toBool(right);
Expand Down
7 changes: 5 additions & 2 deletions lib/tools/parser_generator/dart_code_gen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,12 @@ class DartCodeGenVisitor extends Visitor {
String right = evaluate(binary.right, convertToBool: logical);
if (operation == '+') {
return 'autoConvertAdd($left, $right)';
} else {
return '($left $operation $right)';
} else if (operation == '-') {
return '(($left != null && $right != null) ? $left - $right : ($left != null ? $left : ($right != null ? 0 - $right : 0)))';
} else if (!logical) {
return '($left == null || $right == null ? null : $left $operation $right)';
}
return '($left $operation $right)';
}

visitPrefix(Prefix prefix) {
Expand Down
14 changes: 14 additions & 0 deletions test/core/parser/parser_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,20 @@ main() {
});


it('should eval binary operators with null as null', () {
expect(eval("null < 0")).toEqual(null);
expect(eval("null * 3")).toEqual(null);

// But + and - are special cases.
expect(eval("null + 6")).toEqual(6);
expect(eval("5 + null")).toEqual(5);
expect(eval("null - 4")).toEqual(-4);
expect(eval("3 - null")).toEqual(3);
expect(eval("null + null")).toEqual(0);
expect(eval("null - null")).toEqual(0);
});


it('should pass exceptions through getters', () {
expect(() {
parser('boo').eval(new ScopeWithErrors());
Expand Down
15 changes: 15 additions & 0 deletions test/core/scope_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,21 @@ void main() {
expect(logger).toEqual([[3, 2, 3], {'a': 3, 'b': 2}]);
}));

it('should watch nulls', inject((Logger logger, Map context, RootScope rootScope) {
var r = (value, _) => logger(value);
rootScope
..watch('null < 0',r)
..watch('null * 3', r)
..watch('null + 6', r)
..watch('5 + null', r)
..watch('null - 4', r)
..watch('3 - null', r)
..watch('null + null', r)
..watch('null - null', r)
..digest();
expect(logger).toEqual([null, null, 6, 5, -4, 3, 0, 0]);
}));

it('should invoke closures', inject((Logger logger, Map context, RootScope rootScope) {
context['fn'] = () {
logger('fn');
Expand Down

0 comments on commit 5981175

Please sign in to comment.