parent
d64cc8d87d
commit
7d32879929
|
@ -60,6 +60,20 @@ export class Conditional extends AST {
|
||||||
visit(visitor: AstVisitor) { return visitor.visitConditional(this); }
|
visit(visitor: AstVisitor) { return visitor.visitConditional(this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class If extends AST {
|
||||||
|
constructor(public condition: AST, public trueExp: AST, public falseExp?: AST) { super(); }
|
||||||
|
|
||||||
|
eval(context, locals) {
|
||||||
|
if (this.condition.eval(context, locals)) {
|
||||||
|
this.trueExp.eval(context, locals);
|
||||||
|
} else if (isPresent(this.falseExp)) {
|
||||||
|
this.falseExp.eval(context, locals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor: AstVisitor) { return visitor.visitIf(this); }
|
||||||
|
}
|
||||||
|
|
||||||
export class AccessMember extends AST {
|
export class AccessMember extends AST {
|
||||||
constructor(public receiver: AST, public name: string, public getter: Function,
|
constructor(public receiver: AST, public name: string, public getter: Function,
|
||||||
public setter: Function) {
|
public setter: Function) {
|
||||||
|
@ -321,6 +335,7 @@ export interface AstVisitor {
|
||||||
visitBinary(ast: Binary): any;
|
visitBinary(ast: Binary): any;
|
||||||
visitChain(ast: Chain): any;
|
visitChain(ast: Chain): any;
|
||||||
visitConditional(ast: Conditional): any;
|
visitConditional(ast: Conditional): any;
|
||||||
|
visitIf(ast: If): any;
|
||||||
visitPipe(ast: Pipe): any;
|
visitPipe(ast: Pipe): any;
|
||||||
visitFunctionCall(ast: FunctionCall): any;
|
visitFunctionCall(ast: FunctionCall): any;
|
||||||
visitImplicitReceiver(ast: ImplicitReceiver): any;
|
visitImplicitReceiver(ast: ImplicitReceiver): any;
|
||||||
|
@ -398,6 +413,8 @@ export class AstTransformer implements AstVisitor {
|
||||||
visitChain(ast: Chain) { throw new BaseException('Not implemented'); }
|
visitChain(ast: Chain) { throw new BaseException('Not implemented'); }
|
||||||
|
|
||||||
visitAssignment(ast: Assignment) { throw new BaseException('Not implemented'); }
|
visitAssignment(ast: Assignment) { throw new BaseException('Not implemented'); }
|
||||||
|
|
||||||
|
visitIf(ast: If) { throw new BaseException('Not implemented'); }
|
||||||
}
|
}
|
||||||
|
|
||||||
var _evalListCache = [
|
var _evalListCache = [
|
||||||
|
|
|
@ -59,6 +59,10 @@ export class Token {
|
||||||
|
|
||||||
isKeywordTrue(): boolean { return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "true"); }
|
isKeywordTrue(): boolean { return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "true"); }
|
||||||
|
|
||||||
|
isKeywordIf(): boolean { return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "if"); }
|
||||||
|
|
||||||
|
isKeywordElse(): boolean { return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "else"); }
|
||||||
|
|
||||||
isKeywordFalse(): boolean {
|
isKeywordFalse(): boolean {
|
||||||
return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "false");
|
return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "false");
|
||||||
}
|
}
|
||||||
|
@ -463,4 +467,5 @@ var OPERATORS = SetWrapper.createFromList([
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
var KEYWORDS = SetWrapper.createFromList(['var', 'null', 'undefined', 'true', 'false']);
|
var KEYWORDS =
|
||||||
|
SetWrapper.createFromList(['var', 'null', 'undefined', 'true', 'false', 'if', 'else']);
|
||||||
|
|
|
@ -33,6 +33,7 @@ import {
|
||||||
Binary,
|
Binary,
|
||||||
PrefixNot,
|
PrefixNot,
|
||||||
Conditional,
|
Conditional,
|
||||||
|
If,
|
||||||
Pipe,
|
Pipe,
|
||||||
Assignment,
|
Assignment,
|
||||||
Chain,
|
Chain,
|
||||||
|
@ -412,6 +413,19 @@ class _ParseAST {
|
||||||
this.advance();
|
this.advance();
|
||||||
return new LiteralPrimitive(false);
|
return new LiteralPrimitive(false);
|
||||||
|
|
||||||
|
} else if (this.parseAction && this.next.isKeywordIf()) {
|
||||||
|
this.advance();
|
||||||
|
this.expectCharacter($LPAREN);
|
||||||
|
let condition = this.parseExpression();
|
||||||
|
this.expectCharacter($RPAREN);
|
||||||
|
let ifExp = this.parseExpressionOrBlock();
|
||||||
|
let elseExp;
|
||||||
|
if (this.next.isKeywordElse()) {
|
||||||
|
this.advance();
|
||||||
|
elseExp = this.parseExpressionOrBlock();
|
||||||
|
}
|
||||||
|
return new If(condition, ifExp, elseExp)
|
||||||
|
|
||||||
} else if (this.optionalCharacter($LBRACKET)) {
|
} else if (this.optionalCharacter($LBRACKET)) {
|
||||||
var elements = this.parseExpressionList($RBRACKET);
|
var elements = this.parseExpressionList($RBRACKET);
|
||||||
this.expectCharacter($RBRACKET);
|
this.expectCharacter($RBRACKET);
|
||||||
|
@ -494,6 +508,37 @@ class _ParseAST {
|
||||||
return positionals;
|
return positionals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parseExpressionOrBlock(): AST {
|
||||||
|
if (this.optionalCharacter($LBRACE)) {
|
||||||
|
let block = this.parseBlockContent();
|
||||||
|
this.expectCharacter($RBRACE);
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.parseExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
parseBlockContent(): AST {
|
||||||
|
if (!this.parseAction) {
|
||||||
|
this.error("Binding expression cannot contain chained expression");
|
||||||
|
}
|
||||||
|
var exprs = [];
|
||||||
|
while (this.index < this.tokens.length && !this.next.isCharacter($RBRACE)) {
|
||||||
|
var expr = this.parseExpression();
|
||||||
|
ListWrapper.push(exprs, expr);
|
||||||
|
|
||||||
|
if (this.optionalCharacter($SEMICOLON)) {
|
||||||
|
while (this.optionalCharacter($SEMICOLON)) {
|
||||||
|
} // read all semicolons
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (exprs.length == 0) return new EmptyExpr();
|
||||||
|
if (exprs.length == 1) return exprs[0];
|
||||||
|
|
||||||
|
return new Chain(exprs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An identifier, a keyword, a string with an optional `-` inbetween.
|
* An identifier, a keyword, a string with an optional `-` inbetween.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
Binary,
|
Binary,
|
||||||
Chain,
|
Chain,
|
||||||
Conditional,
|
Conditional,
|
||||||
|
If,
|
||||||
Pipe,
|
Pipe,
|
||||||
FunctionCall,
|
FunctionCall,
|
||||||
ImplicitReceiver,
|
ImplicitReceiver,
|
||||||
|
@ -201,6 +202,8 @@ class _ConvertAstIntoProtoRecords implements AstVisitor {
|
||||||
|
|
||||||
visitChain(ast: Chain) { throw new BaseException('Not supported'); }
|
visitChain(ast: Chain) { throw new BaseException('Not supported'); }
|
||||||
|
|
||||||
|
visitIf(ast: If) { throw new BaseException('Not supported'); }
|
||||||
|
|
||||||
_visitAll(asts: List<any>) {
|
_visitAll(asts: List<any>) {
|
||||||
var res = ListWrapper.createFixedSize(asts.length);
|
var res = ListWrapper.createFixedSize(asts.length);
|
||||||
for (var i = 0; i < asts.length; ++i) {
|
for (var i = 0; i < asts.length; ++i) {
|
||||||
|
|
|
@ -43,10 +43,14 @@ export function main() {
|
||||||
|
|
||||||
function emptyLocals() { return new Locals(null, MapWrapper.create()); }
|
function emptyLocals() { return new Locals(null, MapWrapper.create()); }
|
||||||
|
|
||||||
function expectEval(text, passedInContext = null, passedInLocals = null) {
|
function evalAction(text, passedInContext = null, passedInLocals = null) {
|
||||||
var c = isBlank(passedInContext) ? td() : passedInContext;
|
var c = isBlank(passedInContext) ? td() : passedInContext;
|
||||||
var l = isBlank(passedInLocals) ? emptyLocals() : passedInLocals;
|
var l = isBlank(passedInLocals) ? emptyLocals() : passedInLocals;
|
||||||
return expect(parseAction(text).eval(c, l));
|
return parseAction(text).eval(c, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectEval(text, passedInContext = null, passedInLocals = null) {
|
||||||
|
return expect(evalAction(text, passedInContext, passedInLocals));
|
||||||
}
|
}
|
||||||
|
|
||||||
function expectEvalError(text, passedInContext = null, passedInLocals = null) {
|
function expectEvalError(text, passedInContext = null, passedInLocals = null) {
|
||||||
|
@ -280,6 +284,28 @@ export function main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("if", () => {
|
||||||
|
it('should parse if statements', () => {
|
||||||
|
|
||||||
|
var fixtures = [
|
||||||
|
['if (true) a = 0', 0, null],
|
||||||
|
['if (false) a = 0', null, null],
|
||||||
|
['if (a == null) b = 0', null, 0],
|
||||||
|
['if (true) { a = 0; b = 0 }', 0, 0],
|
||||||
|
['if (true) { a = 0; b = 0 } else { a = 1; b = 1; }', 0, 0],
|
||||||
|
['if (false) { a = 0; b = 0 } else { a = 1; b = 1; }', 1, 1],
|
||||||
|
['if (false) { } else { a = 1; b = 1; }', 1, 1],
|
||||||
|
];
|
||||||
|
|
||||||
|
fixtures.forEach(fix => {
|
||||||
|
var testData = td(null, null);
|
||||||
|
evalAction(fix[0], testData);
|
||||||
|
expect(testData.a).toEqual(fix[1]);
|
||||||
|
expect(testData.b).toEqual(fix[2]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("assignment", () => {
|
describe("assignment", () => {
|
||||||
it("should support field assignments", () => {
|
it("should support field assignments", () => {
|
||||||
var context = td();
|
var context = td();
|
||||||
|
|
|
@ -6,6 +6,8 @@ import {
|
||||||
Binary,
|
Binary,
|
||||||
Chain,
|
Chain,
|
||||||
Conditional,
|
Conditional,
|
||||||
|
EmptyExpr,
|
||||||
|
If,
|
||||||
Pipe,
|
Pipe,
|
||||||
FunctionCall,
|
FunctionCall,
|
||||||
ImplicitReceiver,
|
ImplicitReceiver,
|
||||||
|
@ -21,7 +23,7 @@ import {
|
||||||
} from 'angular2/src/change_detection/parser/ast';
|
} from 'angular2/src/change_detection/parser/ast';
|
||||||
|
|
||||||
|
|
||||||
import {StringWrapper, RegExpWrapper} from 'angular2/src/facade/lang';
|
import {StringWrapper, RegExpWrapper, isPresent} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
var quoteRegExp = RegExpWrapper.create('"');
|
var quoteRegExp = RegExpWrapper.create('"');
|
||||||
|
|
||||||
|
@ -53,10 +55,11 @@ export class Unparser implements AstVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
visitChain(ast: Chain) {
|
visitChain(ast: Chain) {
|
||||||
ast.expressions.forEach(expression => {
|
var len = ast.expressions.length;
|
||||||
this._visit(expression);
|
for (let i = 0; i < len; i++) {
|
||||||
this._expression += ';'
|
this._visit(ast.expressions[i]);
|
||||||
});
|
this._expression += i == len - 1 ? ';' : '; ';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitConditional(ast: Conditional) {
|
visitConditional(ast: Conditional) {
|
||||||
|
@ -67,6 +70,17 @@ export class Unparser implements AstVisitor {
|
||||||
this._visit(ast.falseExp);
|
this._visit(ast.falseExp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
visitIf(ast: If) {
|
||||||
|
this._expression += 'if (';
|
||||||
|
this._visit(ast.condition);
|
||||||
|
this._expression += ') ';
|
||||||
|
this._visitExpOrBlock(ast.trueExp);
|
||||||
|
if (isPresent(ast.falseExp)) {
|
||||||
|
this._expression += ' else ';
|
||||||
|
this._visitExpOrBlock(ast.falseExp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
visitPipe(ast: Pipe) {
|
visitPipe(ast: Pipe) {
|
||||||
this._expression += '(';
|
this._expression += '(';
|
||||||
this._visit(ast.exp);
|
this._visit(ast.exp);
|
||||||
|
@ -179,4 +193,11 @@ export class Unparser implements AstVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _visit(ast: AST) { ast.visit(this); }
|
private _visit(ast: AST) { ast.visit(this); }
|
||||||
|
|
||||||
|
private _visitExpOrBlock(ast: AST) {
|
||||||
|
var isBlock = ast instanceof Chain || ast instanceof EmptyExpr;
|
||||||
|
if (isBlock) this._expression += '{ ';
|
||||||
|
this._visit(ast);
|
||||||
|
if (isBlock) this._expression += ' }';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import {
|
||||||
Binary,
|
Binary,
|
||||||
Chain,
|
Chain,
|
||||||
Conditional,
|
Conditional,
|
||||||
|
EmptyExpr,
|
||||||
|
If,
|
||||||
Pipe,
|
Pipe,
|
||||||
ImplicitReceiver,
|
ImplicitReceiver,
|
||||||
Interpolation,
|
Interpolation,
|
||||||
|
@ -59,7 +61,7 @@ export function main() {
|
||||||
|
|
||||||
it('should support Binary', () => { check('a && b', Binary); });
|
it('should support Binary', () => { check('a && b', Binary); });
|
||||||
|
|
||||||
it('should support Chain', () => { check('a;b;', Chain); });
|
it('should support Chain', () => { check('a; b;', Chain); });
|
||||||
|
|
||||||
it('should support Conditional', () => { check('a ? b : c', Conditional); });
|
it('should support Conditional', () => { check('a ? b : c', Conditional); });
|
||||||
|
|
||||||
|
@ -93,6 +95,17 @@ export function main() {
|
||||||
|
|
||||||
it('should support SafeMethodCall', () => { check('a?.b(c, d)', SafeMethodCall); });
|
it('should support SafeMethodCall', () => { check('a?.b(c, d)', SafeMethodCall); });
|
||||||
|
|
||||||
|
it('should support if statements', () => {
|
||||||
|
var ifs = [
|
||||||
|
'if (true) a()',
|
||||||
|
'if (true) a() else b()',
|
||||||
|
'if (a()) { b = 1; c = 2; }',
|
||||||
|
'if (a()) b = 1 else { c = 2; d = e(); }'
|
||||||
|
];
|
||||||
|
|
||||||
|
ifs.forEach(ifStmt => check(ifStmt, If));
|
||||||
|
});
|
||||||
|
|
||||||
it('should support complex expression', () => {
|
it('should support complex expression', () => {
|
||||||
var originalExp = 'a + 3 * fn([(c + d | e).f], {a: 3})[g].h && i';
|
var originalExp = 'a + 3 * fn([(c + d | e).f], {a: 3})[g].h && i';
|
||||||
var ast = parseBinding(originalExp).ast;
|
var ast = parseBinding(originalExp).ast;
|
||||||
|
@ -108,5 +121,12 @@ export function main() {
|
||||||
expect(ast).toBeAnInstanceOf(Interpolation);
|
expect(ast).toBeAnInstanceOf(Interpolation);
|
||||||
expect(unparser.unparse(ast)).toEqual('a {{ b }} c');
|
expect(unparser.unparse(ast)).toEqual('a {{ b }} c');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support EmptyExpr', () => {
|
||||||
|
var ast = parser.parseAction('if (true) { }', null).ast;
|
||||||
|
expect(ast).toBeAnInstanceOf(If);
|
||||||
|
expect((<If>ast).trueExp).toBeAnInstanceOf(EmptyExpr);
|
||||||
|
expect(unparser.unparse(ast)).toEqual('if (true) { }');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue