feat(compiler): new semantics for template attributes and view variables.

- Supports `<div template=“…”>`, including parsing the expressions within
  the attribute.
- Supports `<template let-ng-repeat=“rows”>`
- Adds attribute interpolation (was missing previously)
This commit is contained in:
Tobias Bosch
2014-11-18 16:38:36 -08:00
parent f864aa1f8e
commit c6846f1163
25 changed files with 586 additions and 283 deletions

View File

@ -111,7 +111,7 @@ export class KeyedAccess extends AST {
this.obj = obj;
this.key = key;
}
eval(context) {
var obj = this.obj.eval(context);
var key = this.key.eval(context);
@ -169,11 +169,11 @@ export class LiteralPrimitive extends AST {
constructor(value) {
this.value = value;
}
eval(context) {
return this.value;
}
visit(visitor, args) {
visitor.visitLiteralPrimitive(this, args);
}
@ -184,11 +184,11 @@ export class LiteralArray extends AST {
constructor(expressions:List) {
this.expressions = expressions;
}
eval(context) {
return ListWrapper.map(this.expressions, (e) => e.eval(context));
}
visit(visitor, args) {
visitor.visitLiteralArray(this, args);
}
@ -287,7 +287,7 @@ export class Assignment extends AST {
eval(context) {
return this.target.assign(context, this.value.eval(context));
}
visit(visitor, args) {
visitor.visitAssignment(this, args);
}
@ -336,6 +336,22 @@ export class FunctionCall extends AST {
}
}
export class ASTWithSource {
constructor(ast:AST, source:string) {
this.source = source;
this.ast = ast;
}
}
export class TemplateBinding {
constructor(key:string, name:string, expression:ASTWithSource) {
this.key = key;
// only either name or expression will be filled.
this.name = name;
this.expression = expression;
}
}
//INTERFACE
export class AstVisitor {
visitChain(ast:Chain, args){}

View File

@ -130,6 +130,7 @@ export const $CR = 13;
export const $SPACE = 32;
export const $BANG = 33;
export const $DQ = 34;
export const $HASH = 35;
export const $$ = 36;
export const $PERCENT = 37;
export const $AMPERSAND = 38;
@ -246,6 +247,8 @@ class _Scanner {
case $SQ:
case $DQ:
return this.scanString();
case $HASH:
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
case $PLUS:
case $MINUS:
case $STAR:
@ -459,7 +462,8 @@ var OPERATORS = SetWrapper.createFromList([
'&',
'|',
'!',
'?'
'?',
'#'
]);

View File

@ -19,7 +19,10 @@ import {
LiteralArray,
LiteralMap,
MethodCall,
FunctionCall
FunctionCall,
TemplateBindings,
TemplateBinding,
ASTWithSource
} from './ast';
var _implicitReceiver = new ImplicitReceiver();
@ -32,14 +35,21 @@ export class Parser {
this._closureMap = closureMap;
}
parseAction(input:string):AST {
parseAction(input:string):ASTWithSource {
var tokens = this._lexer.tokenize(input);
return new _ParseAST(input, tokens, this._closureMap, true).parseChain();
var ast = new _ParseAST(input, tokens, this._closureMap, true).parseChain();
return new ASTWithSource(ast, input);
}
parseBinding(input:string):AST {
parseBinding(input:string):ASTWithSource {
var tokens = this._lexer.tokenize(input);
return new _ParseAST(input, tokens, this._closureMap, false).parseChain();
var ast = new _ParseAST(input, tokens, this._closureMap, false).parseChain();
return new ASTWithSource(ast, input);
}
parseTemplateBindings(input:string):List<TemplateBinding> {
var tokens = this._lexer.tokenize(input);
return new _ParseAST(input, tokens, this._closureMap, false).parseTemplateBindings();
}
}
@ -407,6 +417,29 @@ class _ParseAST {
return positionals;
}
parseTemplateBindings() {
var bindings = [];
while (this.index < this.tokens.length) {
var key = this.expectIdentifierOrKeywordOrString();
this.optionalCharacter($COLON);
var name = null;
var expression = null;
if (this.optionalOperator("#")) {
name = this.expectIdentifierOrKeyword();
} else {
var start = this.inputIndex;
var ast = this.parseExpression();
var source = this.input.substring(start, this.inputIndex);
expression = new ASTWithSource(ast, source);
}
ListWrapper.push(bindings, new TemplateBinding(key, name, expression));
if (!this.optionalCharacter($SEMICOLON)) {
this.optionalCharacter($COMMA);
};
}
return bindings;
}
error(message:string, index:int = null) {
if (isBlank(index)) index = this.index;