X Tutup
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions modules/angular2/src/compiler/template_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
assertionsEnabled,
isBlank
} from 'angular2/src/facade/lang';
import {Injectable} from 'angular2/src/core/di';
import {Injectable, Inject, Injector, OpaqueToken, Optional} from 'angular2/core';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OOC, is Injector needed here ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch: #5424

import {CONST_EXPR} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions';
import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
Expand All @@ -28,6 +29,8 @@ import {
BoundEventAst,
VariableAst,
TemplateAst,
TemplateAstVisitor,
templateVisitAll,
TextAst,
BoundTextAst,
EmbeddedTemplateAst,
Expand Down Expand Up @@ -78,14 +81,17 @@ const STYLE_PREFIX = 'style';

var TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];

export const TEMPLATE_TRANSFORMS = CONST_EXPR(new OpaqueToken('TemplateTransforms'));

export class TemplateParseError extends ParseError {
constructor(message: string, location: ParseLocation) { super(location, message); }
}

@Injectable()
export class TemplateParser {
constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
private _htmlParser: HtmlParser) {}
private _htmlParser: HtmlParser,
@Optional() @Inject(TEMPLATE_TRANSFORMS) public transforms: TemplateAstVisitor[]) {}

parse(template: string, directives: CompileDirectiveMetadata[],
templateUrl: string): TemplateAst[] {
Expand All @@ -97,6 +103,10 @@ export class TemplateParser {
var errorString = errors.join('\n');
throw new BaseException(`Template parse errors:\n${errorString}`);
}
if (isPresent(this.transforms)) {
this.transforms.forEach(
(transform: TemplateAstVisitor) => { result = templateVisitAll(transform, result); });
}
return result;
}
}
Expand Down
41 changes: 19 additions & 22 deletions modules/angular2/src/core/change_detection/parser/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export class KeyedWrite extends AST {

export class BindingPipe extends AST {
constructor(public exp: AST, public name: string, public args: any[]) { super(); }

visit(visitor: AstVisitor): any { return visitor.visitPipe(this); }
}

Expand All @@ -79,7 +78,7 @@ export class LiteralMap extends AST {

export class Interpolation extends AST {
constructor(public strings: any[], public expressions: any[]) { super(); }
visit(visitor: AstVisitor) { visitor.visitInterpolation(this); }
visit(visitor: AstVisitor): any { return visitor.visitInterpolation(this); }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why :any and not :AST?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not every AstVisitor is transforming the AST. You can imagine a visitor that all it does is print the AST to a buffer, or maybe converts it to a 3rd-party AST structure. In contrast, AstTransformer is specifically for transforming AST into another AST and so it is more tightly typed.

}

export class Binary extends AST {
Expand Down Expand Up @@ -214,68 +213,66 @@ export class RecursiveAstVisitor implements AstVisitor {
}

export class AstTransformer implements AstVisitor {
visitImplicitReceiver(ast: ImplicitReceiver): ImplicitReceiver { return ast; }
visitImplicitReceiver(ast: ImplicitReceiver): AST { return ast; }

visitInterpolation(ast: Interpolation): Interpolation {
visitInterpolation(ast: Interpolation): AST {
return new Interpolation(ast.strings, this.visitAll(ast.expressions));
}

visitLiteralPrimitive(ast: LiteralPrimitive): LiteralPrimitive {
return new LiteralPrimitive(ast.value);
}
visitLiteralPrimitive(ast: LiteralPrimitive): AST { return new LiteralPrimitive(ast.value); }

visitPropertyRead(ast: PropertyRead): PropertyRead {
visitPropertyRead(ast: PropertyRead): AST {
return new PropertyRead(ast.receiver.visit(this), ast.name, ast.getter);
}

visitPropertyWrite(ast: PropertyWrite): PropertyWrite {
visitPropertyWrite(ast: PropertyWrite): AST {
return new PropertyWrite(ast.receiver.visit(this), ast.name, ast.setter, ast.value);
}

visitSafePropertyRead(ast: SafePropertyRead): SafePropertyRead {
visitSafePropertyRead(ast: SafePropertyRead): AST {
return new SafePropertyRead(ast.receiver.visit(this), ast.name, ast.getter);
}

visitMethodCall(ast: MethodCall): MethodCall {
visitMethodCall(ast: MethodCall): AST {
return new MethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args));
}

visitSafeMethodCall(ast: SafeMethodCall): SafeMethodCall {
visitSafeMethodCall(ast: SafeMethodCall): AST {
return new SafeMethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args));
}

visitFunctionCall(ast: FunctionCall): FunctionCall {
visitFunctionCall(ast: FunctionCall): AST {
return new FunctionCall(ast.target.visit(this), this.visitAll(ast.args));
}

visitLiteralArray(ast: LiteralArray): LiteralArray {
visitLiteralArray(ast: LiteralArray): AST {
return new LiteralArray(this.visitAll(ast.expressions));
}

visitLiteralMap(ast: LiteralMap): LiteralMap {
visitLiteralMap(ast: LiteralMap): AST {
return new LiteralMap(ast.keys, this.visitAll(ast.values));
}

visitBinary(ast: Binary): Binary {
visitBinary(ast: Binary): AST {
return new Binary(ast.operation, ast.left.visit(this), ast.right.visit(this));
}

visitPrefixNot(ast: PrefixNot): PrefixNot { return new PrefixNot(ast.expression.visit(this)); }
visitPrefixNot(ast: PrefixNot): AST { return new PrefixNot(ast.expression.visit(this)); }

visitConditional(ast: Conditional): Conditional {
visitConditional(ast: Conditional): AST {
return new Conditional(ast.condition.visit(this), ast.trueExp.visit(this),
ast.falseExp.visit(this));
}

visitPipe(ast: BindingPipe): BindingPipe {
visitPipe(ast: BindingPipe): AST {
return new BindingPipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args));
}

visitKeyedRead(ast: KeyedRead): KeyedRead {
visitKeyedRead(ast: KeyedRead): AST {
return new KeyedRead(ast.obj.visit(this), ast.key.visit(this));
}

visitKeyedWrite(ast: KeyedWrite): KeyedWrite {
visitKeyedWrite(ast: KeyedWrite): AST {
return new KeyedWrite(ast.obj.visit(this), ast.key.visit(this), ast.value.visit(this));
}

Expand All @@ -287,5 +284,5 @@ export class AstTransformer implements AstVisitor {
return res;
}

visitChain(ast: Chain): Chain { return new Chain(this.visitAll(ast.expressions)); }
visitChain(ast: Chain): AST { return new Chain(this.visitAll(ast.expressions)); }
}
48 changes: 47 additions & 1 deletion modules/angular2/test/compiler/template_parser_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import {provide} from 'angular2/src/core/di';

import {TEST_PROVIDERS} from './test_bindings';
import {isPresent} from 'angular2/src/facade/lang';
import {TemplateParser, splitClasses} from 'angular2/src/compiler/template_parser';
import {
TemplateParser,
splitClasses,
TEMPLATE_TRANSFORMS
} from 'angular2/src/compiler/template_parser';
import {
CompileDirectiveMetadata,
CompileTypeMetadata,
Expand Down Expand Up @@ -69,6 +73,22 @@ export function main() {
return parser.parse(template, directives, 'TestComp');
}

describe('template transform', () => {
beforeEachProviders(
() => [provide(TEMPLATE_TRANSFORMS, {useValue: new FooAstTransformer(), multi: true})]);

it('should transform TemplateAST',
() => { expect(humanizeTplAst(parse('<div>', []))).toEqual([[ElementAst, 'foo']]); });

describe('multiple', () => {
beforeEachProviders(
() => [provide(TEMPLATE_TRANSFORMS, {useValue: new BarAstTransformer(), multi: true})]);

it('should compose transformers',
() => { expect(humanizeTplAst(parse('<div>', []))).toEqual([[ElementAst, 'bar']]); });
});
});

describe('parse', () => {
describe('nodes without bindings', () => {

Expand Down Expand Up @@ -1068,3 +1088,29 @@ class TemplateContentProjectionHumanizer implements TemplateAstVisitor {
visitDirective(ast: DirectiveAst, context: any): any { return null; }
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
}

class FooAstTransformer implements TemplateAstVisitor {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's worth creating TemplateAstTransformer, similar to AstTransformer. The developer will probably only override a few methods in it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered it but decided not to because it would be speculative dead code. I was going to let @btford create the appropriate common utilities (if any) while he's working on a real AST transformer for the router.

visitNgContent(ast: NgContentAst, context: any): any { throw 'not implemented'; }
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { throw 'not implemented'; }
visitElement(ast: ElementAst, context: any): any {
if (ast.name != 'div') return ast;
return new ElementAst('foo', [], [], [], [], [], [], ast.ngContentIndex, ast.sourceSpan);
}
visitVariable(ast: VariableAst, context: any): any { throw 'not implemented'; }
visitEvent(ast: BoundEventAst, context: any): any { throw 'not implemented'; }
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { throw 'not implemented'; }
visitAttr(ast: AttrAst, context: any): any { throw 'not implemented'; }
visitBoundText(ast: BoundTextAst, context: any): any { throw 'not implemented'; }
visitText(ast: TextAst, context: any): any { throw 'not implemented'; }
visitDirective(ast: DirectiveAst, context: any): any { throw 'not implemented'; }
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {
throw 'not implemented';
}
}

class BarAstTransformer extends FooAstTransformer {
visitElement(ast: ElementAst, context: any): any {
if (ast.name != 'foo') return ast;
return new ElementAst('bar', [], [], [], [], [], [], ast.ngContentIndex, ast.sourceSpan);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ TemplateCompiler createTemplateCompiler(AssetReader reader,
var _htmlParser = new HtmlParser();
var _urlResolver = const TransformerUrlResolver();

// TODO(yjbanov): add router AST transformer when ready
var templateParser = new TemplateParser(new ng.Parser(new ng.Lexer()),
new DomElementSchemaRegistry(), _htmlParser);
new DomElementSchemaRegistry(), _htmlParser, null);

var cdCompiler = changeDetectionConfig != null
? new ChangeDetectionCompiler(changeDetectionConfig)
Expand Down
X Tutup