refactor(parser): align expression language with host language
Remove "enhancements" to the language from the parser, so the expression language mimics the host language.
This commit is contained in:
parent
90daca02cf
commit
3d534928b5
|
@ -1,5 +1,5 @@
|
||||||
import {FIELD, autoConvertAdd, isBlank, isPresent, FunctionWrapper, BaseException} from "facade/lang";
|
import {FIELD, autoConvertAdd, isBlank, isPresent, FunctionWrapper, BaseException} from "facade/lang";
|
||||||
import {List, Map, ListWrapper, MapWrapper} from "facade/collection";
|
import {List, Map, ListWrapper, StringMapWrapper} from "facade/collection";
|
||||||
import {ContextWithVariableBindings} from "./context_with_variable_bindings";
|
import {ContextWithVariableBindings} from "./context_with_variable_bindings";
|
||||||
|
|
||||||
export class AST {
|
export class AST {
|
||||||
|
@ -162,14 +162,7 @@ export class KeyedAccess extends AST {
|
||||||
eval(context) {
|
eval(context) {
|
||||||
var obj = this.obj.eval(context);
|
var obj = this.obj.eval(context);
|
||||||
var key = this.key.eval(context);
|
var key = this.key.eval(context);
|
||||||
|
return obj[key];
|
||||||
if (obj instanceof Map) {
|
|
||||||
return MapWrapper.get(obj, key);
|
|
||||||
} else if (obj instanceof List) {
|
|
||||||
return ListWrapper.get(obj, key);
|
|
||||||
} else {
|
|
||||||
return obj[key];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isAssignable() {
|
get isAssignable() {
|
||||||
|
@ -179,14 +172,7 @@ export class KeyedAccess extends AST {
|
||||||
assign(context, value) {
|
assign(context, value) {
|
||||||
var obj = this.obj.eval(context);
|
var obj = this.obj.eval(context);
|
||||||
var key = this.key.eval(context);
|
var key = this.key.eval(context);
|
||||||
|
obj[key] = value;
|
||||||
if (obj instanceof Map) {
|
|
||||||
MapWrapper.set(obj, key, value);
|
|
||||||
} else if (obj instanceof List) {
|
|
||||||
ListWrapper.set(obj, key, value);
|
|
||||||
} else {
|
|
||||||
obj[key] = value;
|
|
||||||
}
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,9 +237,9 @@ export class LiteralMap extends AST {
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(context) {
|
eval(context) {
|
||||||
var res = MapWrapper.create();
|
var res = StringMapWrapper.create();
|
||||||
for(var i = 0; i < this.keys.length; ++i) {
|
for(var i = 0; i < this.keys.length; ++i) {
|
||||||
MapWrapper.set(res, this.keys[i], this.values[i].eval(context));
|
StringMapWrapper.set(res, this.keys[i], this.values[i].eval(context));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -281,13 +267,8 @@ export class Binary extends AST {
|
||||||
}
|
}
|
||||||
var right = this.right.eval(context);
|
var right = this.right.eval(context);
|
||||||
|
|
||||||
// Null check for the operations.
|
|
||||||
if (isBlank(left)|| isBlank(right)) {
|
|
||||||
throw new BaseException("One of the operands is not defined");
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this.operation) {
|
switch (this.operation) {
|
||||||
case '+' : return autoConvertAdd(left, right);
|
case '+' : return left + right;
|
||||||
case '-' : return left - right;
|
case '-' : return left - right;
|
||||||
case '*' : return left * right;
|
case '*' : return left * right;
|
||||||
case '/' : return left / right;
|
case '/' : return left / right;
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
|
|
||||||
import {FIELD, IMPLEMENTS, isBlank, isPresent, int, toBool, autoConvertAdd, BaseException,
|
import {FIELD, IMPLEMENTS, isBlank, isPresent, int, toBool, autoConvertAdd, BaseException,
|
||||||
NumberWrapper} from 'facade/lang';
|
NumberWrapper} from 'facade/lang';
|
||||||
import {List, Map, ListWrapper, MapWrapper} from 'facade/collection';
|
import {List, Map, ListWrapper, MapWrapper, StringMapWrapper} from 'facade/collection';
|
||||||
import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
|
import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
|
||||||
import {
|
import {
|
||||||
AccessMember,
|
AccessMember,
|
||||||
|
@ -524,9 +524,9 @@ function _arrayFn(length:int) {
|
||||||
|
|
||||||
function _mapFn(keys:List, length:int) {
|
function _mapFn(keys:List, length:int) {
|
||||||
function buildMap(values) {
|
function buildMap(values) {
|
||||||
var res = MapWrapper.create();
|
var res = StringMapWrapper.create();
|
||||||
for(var i = 0; i < keys.length; ++i) {
|
for(var i = 0; i < keys.length; ++i) {
|
||||||
MapWrapper.set(res, keys[i], values[i]);
|
StringMapWrapper.set(res, keys[i], values[i]);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -554,6 +554,5 @@ function _mapGetter(key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function _keyedAccess(obj, args) {
|
function _keyedAccess(obj, args) {
|
||||||
var key = args[0];
|
return obj[args[0]];
|
||||||
return obj instanceof Map ? MapWrapper.get(obj, key):obj[key];
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,11 +118,11 @@ export function main() {
|
||||||
it("should support literal maps", () => {
|
it("should support literal maps", () => {
|
||||||
var c = createChangeDetector('map', '{z:1}');
|
var c = createChangeDetector('map', '{z:1}');
|
||||||
c["changeDetector"].detectChanges();
|
c["changeDetector"].detectChanges();
|
||||||
expect(MapWrapper.get(c["dispatcher"].loggedValues[0][0], 'z')).toEqual(1);
|
expect(c["dispatcher"].loggedValues[0][0]['z']).toEqual(1);
|
||||||
|
|
||||||
c = createChangeDetector('map', '{z:a}', new TestData(1));
|
c = createChangeDetector('map', '{z:a}', new TestData(1));
|
||||||
c["changeDetector"].detectChanges();
|
c["changeDetector"].detectChanges();
|
||||||
expect(MapWrapper.get(c["dispatcher"].loggedValues[0][0], 'z')).toEqual(1);
|
expect(c["dispatcher"].loggedValues[0][0]['z']).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should support binary operations", () => {
|
it("should support binary operations", () => {
|
||||||
|
|
|
@ -134,25 +134,6 @@ export function main() {
|
||||||
expectEval("(1+2)*3").toEqual((1+2)*3);
|
expectEval("(1+2)*3").toEqual((1+2)*3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should auto convert ints to strings', () => {
|
|
||||||
expectEval("'str ' + 4").toEqual("str 4");
|
|
||||||
expectEval("4 + ' str'").toEqual("4 str");
|
|
||||||
expectEval("4 + 4").toEqual(8);
|
|
||||||
expectEval("4 + 4 + ' str'").toEqual("8 str");
|
|
||||||
expectEval("'str ' + 4 + 4").toEqual("str 44");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw when one of the operands is null', () => {
|
|
||||||
expectEvalError("null < 0").toThrowError();
|
|
||||||
expectEvalError("null * 3").toThrowError();
|
|
||||||
expectEvalError("null + 6").toThrowError();
|
|
||||||
expectEvalError("5 + null").toThrowError();
|
|
||||||
expectEvalError("null - 4").toThrowError();
|
|
||||||
expectEvalError("3 - null").toThrowError();
|
|
||||||
expectEvalError("null + null").toThrowError();
|
|
||||||
expectEvalError("null - null").toThrowError();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse an empty string', () => {
|
it('should parse an empty string', () => {
|
||||||
expectEval('').toBeNull();
|
expectEval('').toBeNull();
|
||||||
});
|
});
|
||||||
|
@ -168,7 +149,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should evaluate map', () => {
|
it('should evaluate map', () => {
|
||||||
expectEval("{}").toEqual(MapWrapper.create());
|
expectEval("{}").toEqual({});
|
||||||
expectEval("{a:'b'}['a']").toEqual('b');
|
expectEval("{a:'b'}['a']").toEqual('b');
|
||||||
expectEval("{'a':'b'}['a']").toEqual('b');
|
expectEval("{'a':'b'}['a']").toEqual('b');
|
||||||
expectEval("{\"a\":'b'}['a']").toEqual('b');
|
expectEval("{\"a\":'b'}['a']").toEqual('b');
|
||||||
|
@ -285,15 +266,15 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should support map updates", () => {
|
it("should support map updates", () => {
|
||||||
var context = td(MapWrapper.createFromPairs([["key", 100]]));
|
var context = td({"key" : 100});
|
||||||
expectEval('a["key"] = 200', context).toEqual(200);
|
expectEval('a["key"] = 200', context).toEqual(200);
|
||||||
expect(MapWrapper.get(context.a, "key")).toEqual(200);
|
expect(context.a["key"]).toEqual(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should support array/map updates", () => {
|
it("should support array/map updates", () => {
|
||||||
var context = td([MapWrapper.createFromPairs([["key", 100]])]);
|
var context = td([{"key" : 100}]);
|
||||||
expectEval('a[0]["key"] = 200', context).toEqual(200);
|
expectEval('a[0]["key"] = 200', context).toEqual(200);
|
||||||
expect(MapWrapper.get(context.a[0], "key")).toEqual(200);
|
expect(context.a[0]["key"]).toEqual(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow assignment after array dereference', () => {
|
it('should allow assignment after array dereference', () => {
|
||||||
|
|
|
@ -33,21 +33,6 @@ bool toBool(x) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
autoConvertAdd(a, b) {
|
|
||||||
if (a != null && b != null) {
|
|
||||||
if (a is String && b is! String) {
|
|
||||||
return a + b.toString();
|
|
||||||
}
|
|
||||||
if (a is! String && b is String) {
|
|
||||||
return a.toString() + b;
|
|
||||||
}
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
if (a != null) return a;
|
|
||||||
if (b != null) return b;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
String stringify(obj) => obj.toString();
|
String stringify(obj) => obj.toString();
|
||||||
|
|
||||||
class StringWrapper {
|
class StringWrapper {
|
||||||
|
|
|
@ -30,10 +30,6 @@ export function toBool(obj) {
|
||||||
return !!obj;
|
return !!obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function autoConvertAdd(a, b) {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stringify(token):string {
|
export function stringify(token):string {
|
||||||
if (typeof token === 'string') {
|
if (typeof token === 'string') {
|
||||||
return token;
|
return token;
|
||||||
|
|
Loading…
Reference in New Issue