@ -125,8 +125,7 @@ class _HtmlTokenizer {
|
||||
private currentTokenStart: ParseLocation;
|
||||
private currentTokenType: HtmlTokenType;
|
||||
|
||||
private inExpansionCase: boolean = false;
|
||||
private inExpansionForm: boolean = false;
|
||||
private expansionCaseStack = [];
|
||||
|
||||
tokens: HtmlToken[] = [];
|
||||
errors: HtmlTokenError[] = [];
|
||||
@ -169,10 +168,12 @@ class _HtmlTokenizer {
|
||||
} else if (this.peek === $EQ && this.tokenizeExpansionForms) {
|
||||
this._consumeExpansionCaseStart();
|
||||
|
||||
} else if (this.peek === $RBRACE && this.inExpansionCase && this.tokenizeExpansionForms) {
|
||||
} else if (this.peek === $RBRACE && this.isInExpansionCase() &&
|
||||
this.tokenizeExpansionForms) {
|
||||
this._consumeExpansionCaseEnd();
|
||||
|
||||
} else if (this.peek === $RBRACE && !this.inExpansionCase && this.tokenizeExpansionForms) {
|
||||
} else if (this.peek === $RBRACE && this.isInExpansionForm() &&
|
||||
this.tokenizeExpansionForms) {
|
||||
this._consumeExpansionFormEnd();
|
||||
|
||||
} else {
|
||||
@ -551,7 +552,7 @@ class _HtmlTokenizer {
|
||||
this._requireCharCode($COMMA);
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
|
||||
this.inExpansionForm = true;
|
||||
this.expansionCaseStack.push(HtmlTokenType.EXPANSION_FORM_START);
|
||||
}
|
||||
|
||||
private _consumeExpansionCaseStart() {
|
||||
@ -567,7 +568,7 @@ class _HtmlTokenizer {
|
||||
this._endToken([], this._getLocation());
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
|
||||
this.inExpansionCase = true;
|
||||
this.expansionCaseStack.push(HtmlTokenType.EXPANSION_CASE_EXP_START);
|
||||
}
|
||||
|
||||
private _consumeExpansionCaseEnd() {
|
||||
@ -576,7 +577,7 @@ class _HtmlTokenizer {
|
||||
this._endToken([], this._getLocation());
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
|
||||
this.inExpansionCase = false;
|
||||
this.expansionCaseStack.pop();
|
||||
}
|
||||
|
||||
private _consumeExpansionFormEnd() {
|
||||
@ -584,7 +585,7 @@ class _HtmlTokenizer {
|
||||
this._requireCharCode($RBRACE);
|
||||
this._endToken([]);
|
||||
|
||||
this.inExpansionForm = false;
|
||||
this.expansionCaseStack.pop();
|
||||
}
|
||||
|
||||
private _consumeText() {
|
||||
@ -622,7 +623,9 @@ class _HtmlTokenizer {
|
||||
if (this.peek === $LT || this.peek === $EOF) return true;
|
||||
if (this.tokenizeExpansionForms) {
|
||||
if (isSpecialFormStart(this.peek, this.nextPeek)) return true;
|
||||
if (this.peek === $RBRACE && !interpolation && this.inExpansionForm) return true;
|
||||
if (this.peek === $RBRACE && !interpolation &&
|
||||
(this.isInExpansionCase() || this.isInExpansionForm()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -648,6 +651,18 @@ class _HtmlTokenizer {
|
||||
this.tokens = ListWrapper.slice(this.tokens, 0, nbTokens);
|
||||
}
|
||||
}
|
||||
|
||||
private isInExpansionCase(): boolean {
|
||||
return this.expansionCaseStack.length > 0 &&
|
||||
this.expansionCaseStack[this.expansionCaseStack.length - 1] ===
|
||||
HtmlTokenType.EXPANSION_CASE_EXP_START;
|
||||
}
|
||||
|
||||
private isInExpansionForm(): boolean {
|
||||
return this.expansionCaseStack.length > 0 &&
|
||||
this.expansionCaseStack[this.expansionCaseStack.length - 1] ===
|
||||
HtmlTokenType.EXPANSION_FORM_START;
|
||||
}
|
||||
}
|
||||
|
||||
function isNotWhitespace(code: number): boolean {
|
||||
|
@ -124,40 +124,9 @@ class TreeBuilder {
|
||||
|
||||
// read =
|
||||
while (this.peek.type === HtmlTokenType.EXPANSION_CASE_VALUE) {
|
||||
let value = this._advance();
|
||||
|
||||
// read {
|
||||
let exp = [];
|
||||
if (this.peek.type !== HtmlTokenType.EXPANSION_CASE_EXP_START) {
|
||||
this.errors.push(HtmlTreeError.create(null, this.peek.sourceSpan,
|
||||
`Invalid expansion form. Missing '{'.,`));
|
||||
return;
|
||||
}
|
||||
|
||||
// read until }
|
||||
let start = this._advance();
|
||||
while (this.peek.type !== HtmlTokenType.EXPANSION_CASE_EXP_END) {
|
||||
exp.push(this._advance());
|
||||
if (this.peek.type === HtmlTokenType.EOF) {
|
||||
this.errors.push(
|
||||
HtmlTreeError.create(null, start.sourceSpan, `Invalid expansion form. Missing '}'.`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
let end = this._advance();
|
||||
exp.push(new HtmlToken(HtmlTokenType.EOF, [], end.sourceSpan));
|
||||
|
||||
// parse everything in between { and }
|
||||
let parsedExp = new TreeBuilder(exp).build();
|
||||
if (parsedExp.errors.length > 0) {
|
||||
this.errors = this.errors.concat(<HtmlTreeError[]>parsedExp.errors);
|
||||
return;
|
||||
}
|
||||
|
||||
let sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end);
|
||||
let expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end);
|
||||
cases.push(new HtmlExpansionCaseAst(value.parts[0], parsedExp.rootNodes, sourceSpan,
|
||||
value.sourceSpan, expSourceSpan));
|
||||
let expCase = this._parseExpansionCase();
|
||||
if (isBlank(expCase)) return; // error
|
||||
cases.push(expCase);
|
||||
}
|
||||
|
||||
// read the final }
|
||||
@ -173,6 +142,80 @@ class TreeBuilder {
|
||||
mainSourceSpan, switchValue.sourceSpan));
|
||||
}
|
||||
|
||||
private _parseExpansionCase(): HtmlExpansionCaseAst {
|
||||
let value = this._advance();
|
||||
|
||||
// read {
|
||||
if (this.peek.type !== HtmlTokenType.EXPANSION_CASE_EXP_START) {
|
||||
this.errors.push(HtmlTreeError.create(null, this.peek.sourceSpan,
|
||||
`Invalid expansion form. Missing '{'.,`));
|
||||
return null;
|
||||
}
|
||||
|
||||
// read until }
|
||||
let start = this._advance();
|
||||
|
||||
let exp = this._collectExpansionExpTokens(start);
|
||||
if (isBlank(exp)) return null;
|
||||
|
||||
let end = this._advance();
|
||||
exp.push(new HtmlToken(HtmlTokenType.EOF, [], end.sourceSpan));
|
||||
|
||||
// parse everything in between { and }
|
||||
let parsedExp = new TreeBuilder(exp).build();
|
||||
if (parsedExp.errors.length > 0) {
|
||||
this.errors = this.errors.concat(<HtmlTreeError[]>parsedExp.errors);
|
||||
return null;
|
||||
}
|
||||
|
||||
let sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end);
|
||||
let expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end);
|
||||
return new HtmlExpansionCaseAst(value.parts[0], parsedExp.rootNodes, sourceSpan,
|
||||
value.sourceSpan, expSourceSpan);
|
||||
}
|
||||
|
||||
private _collectExpansionExpTokens(start: HtmlToken): HtmlToken[] {
|
||||
let exp = [];
|
||||
let expansionFormStack = [HtmlTokenType.EXPANSION_CASE_EXP_START];
|
||||
|
||||
while (true) {
|
||||
if (this.peek.type === HtmlTokenType.EXPANSION_FORM_START ||
|
||||
this.peek.type === HtmlTokenType.EXPANSION_CASE_EXP_START) {
|
||||
expansionFormStack.push(this.peek.type);
|
||||
}
|
||||
|
||||
if (this.peek.type === HtmlTokenType.EXPANSION_CASE_EXP_END) {
|
||||
if (lastOnStack(expansionFormStack, HtmlTokenType.EXPANSION_CASE_EXP_START)) {
|
||||
expansionFormStack.pop();
|
||||
if (expansionFormStack.length == 0) return exp;
|
||||
|
||||
} else {
|
||||
this.errors.push(
|
||||
HtmlTreeError.create(null, start.sourceSpan, `Invalid expansion form. Missing '}'.`));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.peek.type === HtmlTokenType.EXPANSION_FORM_END) {
|
||||
if (lastOnStack(expansionFormStack, HtmlTokenType.EXPANSION_FORM_START)) {
|
||||
expansionFormStack.pop();
|
||||
} else {
|
||||
this.errors.push(
|
||||
HtmlTreeError.create(null, start.sourceSpan, `Invalid expansion form. Missing '}'.`));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.peek.type === HtmlTokenType.EOF) {
|
||||
this.errors.push(
|
||||
HtmlTreeError.create(null, start.sourceSpan, `Invalid expansion form. Missing '}'.`));
|
||||
return null;
|
||||
}
|
||||
|
||||
exp.push(this._advance());
|
||||
}
|
||||
}
|
||||
|
||||
private _consumeText(token: HtmlToken) {
|
||||
let text = token.parts[0];
|
||||
if (text.length > 0 && text[0] == '\n') {
|
||||
@ -321,3 +364,7 @@ function getElementFullName(prefix: string, localName: string,
|
||||
|
||||
return mergeNsAndName(prefix, localName);
|
||||
}
|
||||
|
||||
function lastOnStack(stack: any[], element: any): boolean {
|
||||
return stack.length > 0 && stack[stack.length - 1] === element;
|
||||
}
|
@ -254,6 +254,10 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
visitExpansion(ast: HtmlExpansionAst, context: any): any { return null; }
|
||||
|
||||
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any { return null; }
|
||||
|
||||
visitText(ast: HtmlTextAst, parent: ElementContext): any {
|
||||
var ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR);
|
||||
var expr = this._parseInterpolation(ast.value, ast.sourceSpan);
|
||||
@ -270,7 +274,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
|
||||
visitComment(ast: HtmlCommentAst, context: any): any { return null; }
|
||||
|
||||
visitElement(element: HtmlElementAst, component: ElementContext): any {
|
||||
visitElement(element: HtmlElementAst, parent: ElementContext): any {
|
||||
var nodeName = element.name;
|
||||
var preparsedElement = preparseElement(element);
|
||||
if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
||||
@ -773,7 +777,6 @@ class NonBindableVisitor implements HtmlAstVisitor {
|
||||
return new TextAst(ast.value, ngContentIndex, ast.sourceSpan);
|
||||
}
|
||||
visitExpansion(ast: HtmlExpansionAst, context: any): any { return ast; }
|
||||
|
||||
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any { return ast; }
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user