2016-06-23 12:47:54 -04:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2016-05-31 18:22:59 -04:00
|
|
|
import {ListWrapper} from '../facade/collection';
|
2016-07-06 17:06:47 -04:00
|
|
|
import {isBlank} from '../facade/lang';
|
|
|
|
|
|
|
|
export class ParserError {
|
|
|
|
public message: string;
|
|
|
|
constructor(
|
|
|
|
message: string, public input: string, public errLocation: string, public ctxLocation?: any) {
|
|
|
|
this.message = `Parser Error: ${message} ${errLocation} [${input}] in ${ctxLocation}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class ParseSpan {
|
|
|
|
constructor(public start: number, public end: number) {}
|
|
|
|
}
|
2016-01-06 17:13:44 -05:00
|
|
|
|
|
|
|
export class AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(public span: ParseSpan) {}
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any { return null; }
|
2016-06-08 19:38:52 -04:00
|
|
|
toString(): string { return 'AST'; }
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a quoted expression of the form:
|
|
|
|
*
|
|
|
|
* quote = prefix `:` uninterpretedExpression
|
|
|
|
* prefix = identifier
|
|
|
|
* uninterpretedExpression = arbitrary string
|
|
|
|
*
|
|
|
|
* A quoted expression is meant to be pre-processed by an AST transformer that
|
|
|
|
* converts it into another AST that no longer contains quoted expressions.
|
|
|
|
* It is meant to allow third-party developers to extend Angular template
|
|
|
|
* expression language. The `uninterpretedExpression` part of the quote is
|
|
|
|
* therefore not interpreted by the Angular's own expression parser.
|
|
|
|
*/
|
|
|
|
export class Quote extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(
|
|
|
|
span: ParseSpan, public prefix: string, public uninterpretedExpression: string,
|
|
|
|
public location: any) {
|
|
|
|
super(span);
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
visit(visitor: AstVisitor, context: any = null): any { return visitor.visitQuote(this, context); }
|
2016-06-08 19:38:52 -04:00
|
|
|
toString(): string { return 'Quote'; }
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
export class EmptyExpr extends AST {
|
|
|
|
visit(visitor: AstVisitor, context: any = null) {
|
|
|
|
// do nothing
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class ImplicitReceiver extends AST {
|
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitImplicitReceiver(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Multiple expressions separated by a semicolon.
|
|
|
|
*/
|
|
|
|
export class Chain extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public expressions: any[]) { super(span); }
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any { return visitor.visitChain(this, context); }
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Conditional extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public condition: AST, public trueExp: AST, public falseExp: AST) {
|
|
|
|
super(span);
|
|
|
|
}
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitConditional(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class PropertyRead extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public receiver: AST, public name: string) { super(span); }
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitPropertyRead(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class PropertyWrite extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public receiver: AST, public name: string, public value: AST) {
|
|
|
|
super(span);
|
|
|
|
}
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitPropertyWrite(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class SafePropertyRead extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public receiver: AST, public name: string) { super(span); }
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitSafePropertyRead(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class KeyedRead extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public obj: AST, public key: AST) { super(span); }
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitKeyedRead(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class KeyedWrite extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public obj: AST, public key: AST, public value: AST) { super(span); }
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitKeyedWrite(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class BindingPipe extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public exp: AST, public name: string, public args: any[]) {
|
|
|
|
super(span);
|
|
|
|
}
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any { return visitor.visitPipe(this, context); }
|
|
|
|
}
|
|
|
|
|
|
|
|
export class LiteralPrimitive extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public value: any) { super(span); }
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitLiteralPrimitive(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class LiteralArray extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public expressions: any[]) { super(span); }
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitLiteralArray(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class LiteralMap extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public keys: any[], public values: any[]) { super(span); }
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitLiteralMap(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Interpolation extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public strings: any[], public expressions: any[]) { super(span); }
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitInterpolation(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Binary extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public operation: string, public left: AST, public right: AST) {
|
|
|
|
super(span);
|
|
|
|
}
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitBinary(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class PrefixNot extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public expression: AST) { super(span); }
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitPrefixNot(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class MethodCall extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public receiver: AST, public name: string, public args: any[]) {
|
|
|
|
super(span);
|
|
|
|
}
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitMethodCall(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class SafeMethodCall extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public receiver: AST, public name: string, public args: any[]) {
|
|
|
|
super(span);
|
|
|
|
}
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitSafeMethodCall(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class FunctionCall extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(span: ParseSpan, public target: AST, public args: any[]) { super(span); }
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any {
|
|
|
|
return visitor.visitFunctionCall(this, context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class ASTWithSource extends AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
constructor(
|
|
|
|
public ast: AST, public source: string, public location: string,
|
|
|
|
public errors: ParserError[]) {
|
|
|
|
super(new ParseSpan(0, isBlank(source) ? 0 : source.length));
|
|
|
|
}
|
2016-01-06 17:13:44 -05:00
|
|
|
visit(visitor: AstVisitor, context: any = null): any { return this.ast.visit(visitor, context); }
|
|
|
|
toString(): string { return `${this.source} in ${this.location}`; }
|
|
|
|
}
|
|
|
|
|
|
|
|
export class TemplateBinding {
|
2016-06-08 19:38:52 -04:00
|
|
|
constructor(
|
|
|
|
public key: string, public keyIsVar: boolean, public name: string,
|
|
|
|
public expression: ASTWithSource) {}
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface AstVisitor {
|
|
|
|
visitBinary(ast: Binary, context: any): any;
|
|
|
|
visitChain(ast: Chain, context: any): any;
|
|
|
|
visitConditional(ast: Conditional, context: any): any;
|
|
|
|
visitFunctionCall(ast: FunctionCall, context: any): any;
|
|
|
|
visitImplicitReceiver(ast: ImplicitReceiver, context: any): any;
|
|
|
|
visitInterpolation(ast: Interpolation, context: any): any;
|
|
|
|
visitKeyedRead(ast: KeyedRead, context: any): any;
|
|
|
|
visitKeyedWrite(ast: KeyedWrite, context: any): any;
|
|
|
|
visitLiteralArray(ast: LiteralArray, context: any): any;
|
|
|
|
visitLiteralMap(ast: LiteralMap, context: any): any;
|
|
|
|
visitLiteralPrimitive(ast: LiteralPrimitive, context: any): any;
|
|
|
|
visitMethodCall(ast: MethodCall, context: any): any;
|
|
|
|
visitPipe(ast: BindingPipe, context: any): any;
|
|
|
|
visitPrefixNot(ast: PrefixNot, context: any): any;
|
|
|
|
visitPropertyRead(ast: PropertyRead, context: any): any;
|
|
|
|
visitPropertyWrite(ast: PropertyWrite, context: any): any;
|
|
|
|
visitQuote(ast: Quote, context: any): any;
|
|
|
|
visitSafeMethodCall(ast: SafeMethodCall, context: any): any;
|
|
|
|
visitSafePropertyRead(ast: SafePropertyRead, context: any): any;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class RecursiveAstVisitor implements AstVisitor {
|
|
|
|
visitBinary(ast: Binary, context: any): any {
|
|
|
|
ast.left.visit(this);
|
|
|
|
ast.right.visit(this);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
visitChain(ast: Chain, context: any): any { return this.visitAll(ast.expressions, context); }
|
|
|
|
visitConditional(ast: Conditional, context: any): any {
|
|
|
|
ast.condition.visit(this);
|
|
|
|
ast.trueExp.visit(this);
|
|
|
|
ast.falseExp.visit(this);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
visitPipe(ast: BindingPipe, context: any): any {
|
|
|
|
ast.exp.visit(this);
|
|
|
|
this.visitAll(ast.args, context);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
visitFunctionCall(ast: FunctionCall, context: any): any {
|
|
|
|
ast.target.visit(this);
|
|
|
|
this.visitAll(ast.args, context);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
visitImplicitReceiver(ast: ImplicitReceiver, context: any): any { return null; }
|
|
|
|
visitInterpolation(ast: Interpolation, context: any): any {
|
|
|
|
return this.visitAll(ast.expressions, context);
|
|
|
|
}
|
|
|
|
visitKeyedRead(ast: KeyedRead, context: any): any {
|
|
|
|
ast.obj.visit(this);
|
|
|
|
ast.key.visit(this);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
visitKeyedWrite(ast: KeyedWrite, context: any): any {
|
|
|
|
ast.obj.visit(this);
|
|
|
|
ast.key.visit(this);
|
|
|
|
ast.value.visit(this);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
visitLiteralArray(ast: LiteralArray, context: any): any {
|
|
|
|
return this.visitAll(ast.expressions, context);
|
|
|
|
}
|
|
|
|
visitLiteralMap(ast: LiteralMap, context: any): any { return this.visitAll(ast.values, context); }
|
|
|
|
visitLiteralPrimitive(ast: LiteralPrimitive, context: any): any { return null; }
|
|
|
|
visitMethodCall(ast: MethodCall, context: any): any {
|
|
|
|
ast.receiver.visit(this);
|
|
|
|
return this.visitAll(ast.args, context);
|
|
|
|
}
|
|
|
|
visitPrefixNot(ast: PrefixNot, context: any): any {
|
|
|
|
ast.expression.visit(this);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
visitPropertyRead(ast: PropertyRead, context: any): any {
|
|
|
|
ast.receiver.visit(this);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
visitPropertyWrite(ast: PropertyWrite, context: any): any {
|
|
|
|
ast.receiver.visit(this);
|
|
|
|
ast.value.visit(this);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
visitSafePropertyRead(ast: SafePropertyRead, context: any): any {
|
|
|
|
ast.receiver.visit(this);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
visitSafeMethodCall(ast: SafeMethodCall, context: any): any {
|
|
|
|
ast.receiver.visit(this);
|
|
|
|
return this.visitAll(ast.args, context);
|
|
|
|
}
|
|
|
|
visitAll(asts: AST[], context: any): any {
|
|
|
|
asts.forEach(ast => ast.visit(this, context));
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
visitQuote(ast: Quote, context: any): any { return null; }
|
|
|
|
}
|
|
|
|
|
|
|
|
export class AstTransformer implements AstVisitor {
|
|
|
|
visitImplicitReceiver(ast: ImplicitReceiver, context: any): AST { return ast; }
|
|
|
|
|
|
|
|
visitInterpolation(ast: Interpolation, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new Interpolation(ast.span, ast.strings, this.visitAll(ast.expressions));
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitLiteralPrimitive(ast: LiteralPrimitive, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new LiteralPrimitive(ast.span, ast.value);
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitPropertyRead(ast: PropertyRead, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new PropertyRead(ast.span, ast.receiver.visit(this), ast.name);
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitPropertyWrite(ast: PropertyWrite, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new PropertyWrite(ast.span, ast.receiver.visit(this), ast.name, ast.value);
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitSafePropertyRead(ast: SafePropertyRead, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new SafePropertyRead(ast.span, ast.receiver.visit(this), ast.name);
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitMethodCall(ast: MethodCall, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new MethodCall(ast.span, ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitSafeMethodCall(ast: SafeMethodCall, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new SafeMethodCall(
|
|
|
|
ast.span, ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitFunctionCall(ast: FunctionCall, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new FunctionCall(ast.span, ast.target.visit(this), this.visitAll(ast.args));
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitLiteralArray(ast: LiteralArray, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new LiteralArray(ast.span, this.visitAll(ast.expressions));
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitLiteralMap(ast: LiteralMap, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new LiteralMap(ast.span, ast.keys, this.visitAll(ast.values));
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitBinary(ast: Binary, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new Binary(ast.span, ast.operation, ast.left.visit(this), ast.right.visit(this));
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitPrefixNot(ast: PrefixNot, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new PrefixNot(ast.span, ast.expression.visit(this));
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitConditional(ast: Conditional, context: any): AST {
|
2016-06-08 19:38:52 -04:00
|
|
|
return new Conditional(
|
2016-07-06 17:06:47 -04:00
|
|
|
ast.span, ast.condition.visit(this), ast.trueExp.visit(this), ast.falseExp.visit(this));
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitPipe(ast: BindingPipe, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new BindingPipe(ast.span, ast.exp.visit(this), ast.name, this.visitAll(ast.args));
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitKeyedRead(ast: KeyedRead, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new KeyedRead(ast.span, ast.obj.visit(this), ast.key.visit(this));
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitKeyedWrite(ast: KeyedWrite, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new KeyedWrite(
|
|
|
|
ast.span, ast.obj.visit(this), ast.key.visit(this), ast.value.visit(this));
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visitAll(asts: any[]): any[] {
|
|
|
|
var res = ListWrapper.createFixedSize(asts.length);
|
|
|
|
for (var i = 0; i < asts.length; ++i) {
|
|
|
|
res[i] = asts[i].visit(this);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-07-06 17:06:47 -04:00
|
|
|
visitChain(ast: Chain, context: any): AST {
|
|
|
|
return new Chain(ast.span, this.visitAll(ast.expressions));
|
|
|
|
}
|
2016-01-06 17:13:44 -05:00
|
|
|
|
|
|
|
visitQuote(ast: Quote, context: any): AST {
|
2016-07-06 17:06:47 -04:00
|
|
|
return new Quote(ast.span, ast.prefix, ast.uninterpretedExpression, ast.location);
|
2016-01-06 17:13:44 -05:00
|
|
|
}
|
|
|
|
}
|