refactor(HtmlLexer): simplify the code

This commit is contained in:
Victor Berchet 2016-06-21 16:55:17 -07:00
parent 1a212259af
commit 397f5e2390
3 changed files with 51 additions and 40 deletions

View File

@ -18,9 +18,10 @@ export function assertArrayOfStrings(identifier: string, value: any) {
} }
const INTERPOLATION_BLACKLIST_REGEXPS = [ const INTERPOLATION_BLACKLIST_REGEXPS = [
/^\s*$/g, // empty /^\s*$/, // empty
/[<>]/g, // html tag /[<>]/, // html tag
/^[\{\}]$/g, // i18n expansion /^[{}]$/, // i18n expansion
/&(#|[a-z])/i, // character reference
]; ];
export function assertInterpolationSymbols(identifier: string, value: any): void { export function assertInterpolationSymbols(identifier: string, value: any): void {

View File

@ -1,5 +1,4 @@
import * as chars from './chars'; import * as chars from './chars';
import {ListWrapper} from './facade/collection';
import {NumberWrapper, StringWrapper, isBlank, isPresent} from './facade/lang'; import {NumberWrapper, StringWrapper, isBlank, isPresent} from './facade/lang';
import {HtmlTagContentType, NAMED_ENTITIES, getHtmlTagDefinition} from './html_tags'; import {HtmlTagContentType, NAMED_ENTITIES, getHtmlTagDefinition} from './html_tags';
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './interpolation_config'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './interpolation_config';
@ -80,6 +79,7 @@ class _HtmlTokenizer {
private _currentTokenStart: ParseLocation; private _currentTokenStart: ParseLocation;
private _currentTokenType: HtmlTokenType; private _currentTokenType: HtmlTokenType;
private _expansionCaseStack: HtmlTokenType[] = []; private _expansionCaseStack: HtmlTokenType[] = [];
private _inInterpolation: boolean = false;
tokens: HtmlToken[] = []; tokens: HtmlToken[] = [];
errors: HtmlTokenError[] = []; errors: HtmlTokenError[] = [];
@ -238,8 +238,12 @@ class _HtmlTokenizer {
} }
private _attemptStr(chars: string): boolean { private _attemptStr(chars: string): boolean {
const len = chars.length;
if (this._index + len > this._length) {
return false;
}
const initialPosition = this._savePosition(); const initialPosition = this._savePosition();
for (var i = 0; i < chars.length; i++) { for (var i = 0; i < len; i++) {
if (!this._attemptCharCode(StringWrapper.charCodeAt(chars, i))) { if (!this._attemptCharCode(StringWrapper.charCodeAt(chars, i))) {
// If attempting to parse the string fails, we want to reset the parser // If attempting to parse the string fails, we want to reset the parser
// to where it was before the attempt // to where it was before the attempt
@ -333,7 +337,7 @@ class _HtmlTokenizer {
} }
private _consumeRawText( private _consumeRawText(
decodeEntities: boolean, firstCharOfEnd: number, attemptEndRest: Function): HtmlToken { decodeEntities: boolean, firstCharOfEnd: number, attemptEndRest: () => boolean): HtmlToken {
var tagCloseStart: ParseLocation; var tagCloseStart: ParseLocation;
var textStart = this._getLocation(); var textStart = this._getLocation();
this._beginToken( this._beginToken(
@ -345,6 +349,7 @@ class _HtmlTokenizer {
break; break;
} }
if (this._index > tagCloseStart.offset) { if (this._index > tagCloseStart.offset) {
// add the characters consumed by the previous if statement to the output
parts.push(this._input.substring(tagCloseStart.offset, this._index)); parts.push(this._input.substring(tagCloseStart.offset, this._index));
} }
while (this._peek !== firstCharOfEnd) { while (this._peek !== firstCharOfEnd) {
@ -558,48 +563,43 @@ class _HtmlTokenizer {
this._beginToken(HtmlTokenType.TEXT, start); this._beginToken(HtmlTokenType.TEXT, start);
var parts: string[] = []; var parts: string[] = [];
let interpolation = false;
do { do {
const savedPos = this._savePosition();
// _attemptStr advances the position when it is true.
// To push interpolation symbols, we have to reset it.
if (this._attemptStr(this.interpolationConfig.start)) { if (this._attemptStr(this.interpolationConfig.start)) {
this._restorePosition(savedPos); parts.push(this.interpolationConfig.start);
for (let i = 0; i < this.interpolationConfig.start.length; i++) { this._inInterpolation = true;
parts.push(this._readChar(true)); } else if (this._attemptStr(this.interpolationConfig.end) && this._inInterpolation) {
} parts.push(this.interpolationConfig.end);
interpolation = true; this._inInterpolation = false;
} else if (this._attemptStr(this.interpolationConfig.end) && interpolation) {
this._restorePosition(savedPos);
for (let i = 0; i < this.interpolationConfig.end.length; i++) {
parts.push(this._readChar(true));
}
interpolation = false;
} else { } else {
this._restorePosition(savedPos);
parts.push(this._readChar(true)); parts.push(this._readChar(true));
} }
} while (!this._isTextEnd(interpolation)); } while (!this._isTextEnd());
this._endToken([this._processCarriageReturns(parts.join(''))]); this._endToken([this._processCarriageReturns(parts.join(''))]);
} }
private _isTextEnd(interpolation: boolean): boolean { private _isTextEnd(): boolean {
if (this._peek === chars.$LT || this._peek === chars.$EOF) return true; if (this._peek === chars.$LT || this._peek === chars.$EOF) {
if (this.tokenizeExpansionForms) { return true;
const savedPos = this._savePosition();
if (isExpansionFormStart(this._input, this._index, this.interpolationConfig.start))
return true;
this._restorePosition(savedPos);
if (this._peek === chars.$RBRACE && !interpolation &&
(this._isInExpansionCase() || this._isInExpansionForm()))
return true;
} }
if (this.tokenizeExpansionForms) {
if (isExpansionFormStart(this._input, this._index, this.interpolationConfig.start)) {
// start of an expansion form
return true;
}
if (this._peek === chars.$RBRACE && !this._inInterpolation && this._isInExpansionCase()) {
// end of and expansion case
return true;
}
}
return false; return false;
} }
private _savePosition(): number[] { private _savePosition(): [number, number, number, number, number] {
return [this._peek, this._index, this._column, this._line, this.tokens.length]; return [this._peek, this._index, this._column, this._line, this.tokens.length];
} }
@ -609,7 +609,7 @@ class _HtmlTokenizer {
return this._input.substring(start, this._index); return this._input.substring(start, this._index);
} }
private _restorePosition(position: number[]): void { private _restorePosition(position: [number, number, number, number, number]): void {
this._peek = position[0]; this._peek = position[0];
this._index = position[1]; this._index = position[1];
this._column = position[2]; this._column = position[2];
@ -617,7 +617,7 @@ class _HtmlTokenizer {
let nbTokens = position[4]; let nbTokens = position[4];
if (nbTokens < this.tokens.length) { if (nbTokens < this.tokens.length) {
// remove any extra tokens // remove any extra tokens
this.tokens = ListWrapper.slice(this.tokens, 0, nbTokens); this.tokens = this.tokens.slice(0, nbTokens);
} }
} }
@ -657,10 +657,8 @@ function isNamedEntityEnd(code: number): boolean {
} }
function isExpansionFormStart(input: string, offset: number, interpolationStart: string): boolean { function isExpansionFormStart(input: string, offset: number, interpolationStart: string): boolean {
const substr = input.substring(offset); return input.charCodeAt(offset) == chars.$LBRACE &&
return StringWrapper.charCodeAt(substr, 0) === chars.$LBRACE && input.indexOf(interpolationStart, offset) != offset;
StringWrapper.charCodeAt(substr, 1) !== chars.$LBRACE &&
!substr.startsWith(interpolationStart);
} }
function isExpansionCaseStart(peek: number): boolean { function isExpansionCaseStart(peek: number): boolean {

View File

@ -86,6 +86,10 @@ export function main() {
.toThrowError(`['{', '}'] contains unusable interpolation symbol.`); .toThrowError(`['{', '}'] contains unusable interpolation symbol.`);
expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation3)) expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation3))
.toThrowError(`['<%', '%>'] contains unusable interpolation symbol.`); .toThrowError(`['<%', '%>'] contains unusable interpolation symbol.`);
expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation4))
.toThrowError(`['&#', '}}'] contains unusable interpolation symbol.`);
expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation5))
.toThrowError(`['&lbrace;', '}}'] contains unusable interpolation symbol.`);
})); }));
}); });
@ -199,3 +203,11 @@ class ComponentWithInvalidInterpolation2 {
@Component({selector: 'someSelector', template: '', interpolation: ['<%', '%>']}) @Component({selector: 'someSelector', template: '', interpolation: ['<%', '%>']})
class ComponentWithInvalidInterpolation3 { class ComponentWithInvalidInterpolation3 {
} }
@Component({selector: 'someSelector', template: '', interpolation: ['&#', '}}']})
class ComponentWithInvalidInterpolation4 {
}
@Component({selector: 'someSelector', template: '', interpolation: ['&lbrace;', '}}']})
class ComponentWithInvalidInterpolation5 {
}