refactor: cleanup lexers & parsers
This commit is contained in:
parent
f114dd300b
commit
1a212259af
|
@ -62,6 +62,20 @@ export const $PIPE = 124;
|
||||||
export const $TILDA = 126;
|
export const $TILDA = 126;
|
||||||
export const $AT = 64;
|
export const $AT = 64;
|
||||||
|
|
||||||
|
export const $BT = 96;
|
||||||
|
|
||||||
export function isWhitespace(code: number): boolean {
|
export function isWhitespace(code: number): boolean {
|
||||||
return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
|
return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isDigit(code: number): boolean {
|
||||||
|
return $0 <= code && code <= $9;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAsciiLetter(code: number): boolean {
|
||||||
|
return code >= $a && code <= $z || code >= $A && code <= $Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAsciiHexDigit(code: number): boolean {
|
||||||
|
return code >= $a && code <= $f || code >= $A && code <= $F || isDigit(code);
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import {$$, $0, $9, $A, $AMPERSAND, $AT, $BACKSLASH, $BANG, $CARET, $COLON, $COMMA, $CR, $DQ, $EOF, $EQ, $FF, $GT, $HASH, $LBRACE, $LBRACKET, $LF, $LPAREN, $MINUS, $PERCENT, $PERIOD, $PIPE, $PLUS, $QUESTION, $RBRACE, $RBRACKET, $RPAREN, $SEMICOLON, $SLASH, $SPACE, $SQ, $STAR, $TAB, $TILDA, $VTAB, $Z, $_, $a, $z, isWhitespace} from './chars';
|
import * as chars from './chars';
|
||||||
import {BaseException} from './facade/exceptions';
|
import {BaseException} from './facade/exceptions';
|
||||||
import {StringWrapper, isPresent, resolveEnumToken} from './facade/lang';
|
import {StringWrapper, isPresent, resolveEnumToken} from './facade/lang';
|
||||||
|
|
||||||
export {$AT, $COLON, $COMMA, $EOF, $GT, $LBRACE, $LBRACKET, $LPAREN, $PLUS, $RBRACE, $RBRACKET, $RPAREN, $SEMICOLON, $SLASH, $SPACE, $TAB, $TILDA, isWhitespace} from './chars';
|
|
||||||
|
|
||||||
export enum CssTokenType {
|
export enum CssTokenType {
|
||||||
EOF,
|
EOF,
|
||||||
String,
|
String,
|
||||||
|
@ -148,25 +146,25 @@ export class CssScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
peekAt(index: number): number {
|
peekAt(index: number): number {
|
||||||
return index >= this.length ? $EOF : StringWrapper.charCodeAt(this.input, index);
|
return index >= this.length ? chars.$EOF : StringWrapper.charCodeAt(this.input, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
consumeEmptyStatements(): void {
|
consumeEmptyStatements(): void {
|
||||||
this.consumeWhitespace();
|
this.consumeWhitespace();
|
||||||
while (this.peek == $SEMICOLON) {
|
while (this.peek == chars.$SEMICOLON) {
|
||||||
this.advance();
|
this.advance();
|
||||||
this.consumeWhitespace();
|
this.consumeWhitespace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
consumeWhitespace(): void {
|
consumeWhitespace(): void {
|
||||||
while (isWhitespace(this.peek) || isNewline(this.peek)) {
|
while (chars.isWhitespace(this.peek) || isNewline(this.peek)) {
|
||||||
this.advance();
|
this.advance();
|
||||||
if (!this._trackComments && isCommentStart(this.peek, this.peekPeek)) {
|
if (!this._trackComments && isCommentStart(this.peek, this.peekPeek)) {
|
||||||
this.advance(); // /
|
this.advance(); // /
|
||||||
this.advance(); // *
|
this.advance(); // *
|
||||||
while (!isCommentEnd(this.peek, this.peekPeek)) {
|
while (!isCommentEnd(this.peek, this.peekPeek)) {
|
||||||
if (this.peek == $EOF) {
|
if (this.peek == chars.$EOF) {
|
||||||
this.error('Unterminated comment');
|
this.error('Unterminated comment');
|
||||||
}
|
}
|
||||||
this.advance();
|
this.advance();
|
||||||
|
@ -255,7 +253,7 @@ export class CssScanner {
|
||||||
_scan(): CssToken {
|
_scan(): CssToken {
|
||||||
var peek = this.peek;
|
var peek = this.peek;
|
||||||
var peekPeek = this.peekPeek;
|
var peekPeek = this.peekPeek;
|
||||||
if (peek == $EOF) return null;
|
if (peek == chars.$EOF) return null;
|
||||||
|
|
||||||
if (isCommentStart(peek, peekPeek)) {
|
if (isCommentStart(peek, peekPeek)) {
|
||||||
// even if comments are not tracked we still lex the
|
// even if comments are not tracked we still lex the
|
||||||
|
@ -266,13 +264,13 @@ export class CssScanner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_trackWhitespace(this._currentMode) && (isWhitespace(peek) || isNewline(peek))) {
|
if (_trackWhitespace(this._currentMode) && (chars.isWhitespace(peek) || isNewline(peek))) {
|
||||||
return this.scanWhitespace();
|
return this.scanWhitespace();
|
||||||
}
|
}
|
||||||
|
|
||||||
peek = this.peek;
|
peek = this.peek;
|
||||||
peekPeek = this.peekPeek;
|
peekPeek = this.peekPeek;
|
||||||
if (peek == $EOF) return null;
|
if (peek == chars.$EOF) return null;
|
||||||
|
|
||||||
if (isStringStart(peek, peekPeek)) {
|
if (isStringStart(peek, peekPeek)) {
|
||||||
return this.scanString();
|
return this.scanString();
|
||||||
|
@ -283,14 +281,15 @@ export class CssScanner {
|
||||||
return this.scanCssValueFunction();
|
return this.scanCssValueFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
var isModifier = peek == $PLUS || peek == $MINUS;
|
var isModifier = peek == chars.$PLUS || peek == chars.$MINUS;
|
||||||
var digitA = isModifier ? false : isDigit(peek);
|
var digitA = isModifier ? false : chars.isDigit(peek);
|
||||||
var digitB = isDigit(peekPeek);
|
var digitB = chars.isDigit(peekPeek);
|
||||||
if (digitA || (isModifier && (peekPeek == $PERIOD || digitB)) || (peek == $PERIOD && digitB)) {
|
if (digitA || (isModifier && (peekPeek == chars.$PERIOD || digitB)) ||
|
||||||
|
(peek == chars.$PERIOD && digitB)) {
|
||||||
return this.scanNumber();
|
return this.scanNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (peek == $AT) {
|
if (peek == chars.$AT) {
|
||||||
return this.scanAtExpression();
|
return this.scanAtExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,7 +318,7 @@ export class CssScanner {
|
||||||
this.advance(); // *
|
this.advance(); // *
|
||||||
|
|
||||||
while (!isCommentEnd(this.peek, this.peekPeek)) {
|
while (!isCommentEnd(this.peek, this.peekPeek)) {
|
||||||
if (this.peek == $EOF) {
|
if (this.peek == chars.$EOF) {
|
||||||
this.error('Unterminated comment');
|
this.error('Unterminated comment');
|
||||||
}
|
}
|
||||||
this.advance();
|
this.advance();
|
||||||
|
@ -336,7 +335,7 @@ export class CssScanner {
|
||||||
var start = this.index;
|
var start = this.index;
|
||||||
var startingColumn = this.column;
|
var startingColumn = this.column;
|
||||||
var startingLine = this.line;
|
var startingLine = this.line;
|
||||||
while (isWhitespace(this.peek) && this.peek != $EOF) {
|
while (chars.isWhitespace(this.peek) && this.peek != chars.$EOF) {
|
||||||
this.advance();
|
this.advance();
|
||||||
}
|
}
|
||||||
var str = this.input.substring(start, this.index);
|
var str = this.input.substring(start, this.index);
|
||||||
|
@ -357,7 +356,7 @@ export class CssScanner {
|
||||||
this.advance();
|
this.advance();
|
||||||
|
|
||||||
while (!isCharMatch(target, previous, this.peek)) {
|
while (!isCharMatch(target, previous, this.peek)) {
|
||||||
if (this.peek == $EOF || isNewline(this.peek)) {
|
if (this.peek == chars.$EOF || isNewline(this.peek)) {
|
||||||
this.error('Unterminated quote');
|
this.error('Unterminated quote');
|
||||||
}
|
}
|
||||||
previous = this.peek;
|
previous = this.peek;
|
||||||
|
@ -376,12 +375,12 @@ export class CssScanner {
|
||||||
scanNumber(): CssToken {
|
scanNumber(): CssToken {
|
||||||
var start = this.index;
|
var start = this.index;
|
||||||
var startingColumn = this.column;
|
var startingColumn = this.column;
|
||||||
if (this.peek == $PLUS || this.peek == $MINUS) {
|
if (this.peek == chars.$PLUS || this.peek == chars.$MINUS) {
|
||||||
this.advance();
|
this.advance();
|
||||||
}
|
}
|
||||||
var periodUsed = false;
|
var periodUsed = false;
|
||||||
while (isDigit(this.peek) || this.peek == $PERIOD) {
|
while (chars.isDigit(this.peek) || this.peek == chars.$PERIOD) {
|
||||||
if (this.peek == $PERIOD) {
|
if (this.peek == chars.$PERIOD) {
|
||||||
if (periodUsed) {
|
if (periodUsed) {
|
||||||
this.error('Unexpected use of a second period value');
|
this.error('Unexpected use of a second period value');
|
||||||
}
|
}
|
||||||
|
@ -412,11 +411,11 @@ export class CssScanner {
|
||||||
var start = this.index;
|
var start = this.index;
|
||||||
var startingColumn = this.column;
|
var startingColumn = this.column;
|
||||||
var parenBalance = 1;
|
var parenBalance = 1;
|
||||||
while (this.peek != $EOF && parenBalance > 0) {
|
while (this.peek != chars.$EOF && parenBalance > 0) {
|
||||||
this.advance();
|
this.advance();
|
||||||
if (this.peek == $LPAREN) {
|
if (this.peek == chars.$LPAREN) {
|
||||||
parenBalance++;
|
parenBalance++;
|
||||||
} else if (this.peek == $RPAREN) {
|
} else if (this.peek == chars.$RPAREN) {
|
||||||
parenBalance--;
|
parenBalance--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -440,7 +439,7 @@ export class CssScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
scanAtExpression(): CssToken {
|
scanAtExpression(): CssToken {
|
||||||
if (this.assertCondition(this.peek == $AT, 'Expected @ value')) {
|
if (this.assertCondition(this.peek == chars.$AT, 'Expected @ value')) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,53 +480,45 @@ export class CssScanner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAtKeyword(current: CssToken, next: CssToken): boolean {
|
|
||||||
return current.numValue == $AT && next.type == CssTokenType.Identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCharMatch(target: number, previous: number, code: number): boolean {
|
function isCharMatch(target: number, previous: number, code: number): boolean {
|
||||||
return code == target && previous != $BACKSLASH;
|
return code == target && previous != chars.$BACKSLASH;
|
||||||
}
|
|
||||||
|
|
||||||
function isDigit(code: number): boolean {
|
|
||||||
return $0 <= code && code <= $9;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCommentStart(code: number, next: number): boolean {
|
function isCommentStart(code: number, next: number): boolean {
|
||||||
return code == $SLASH && next == $STAR;
|
return code == chars.$SLASH && next == chars.$STAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCommentEnd(code: number, next: number): boolean {
|
function isCommentEnd(code: number, next: number): boolean {
|
||||||
return code == $STAR && next == $SLASH;
|
return code == chars.$STAR && next == chars.$SLASH;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isStringStart(code: number, next: number): boolean {
|
function isStringStart(code: number, next: number): boolean {
|
||||||
var target = code;
|
var target = code;
|
||||||
if (target == $BACKSLASH) {
|
if (target == chars.$BACKSLASH) {
|
||||||
target = next;
|
target = next;
|
||||||
}
|
}
|
||||||
return target == $DQ || target == $SQ;
|
return target == chars.$DQ || target == chars.$SQ;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isIdentifierStart(code: number, next: number): boolean {
|
function isIdentifierStart(code: number, next: number): boolean {
|
||||||
var target = code;
|
var target = code;
|
||||||
if (target == $MINUS) {
|
if (target == chars.$MINUS) {
|
||||||
target = next;
|
target = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ($a <= target && target <= $z) || ($A <= target && target <= $Z) || target == $BACKSLASH ||
|
return chars.isAsciiLetter(target) || target == chars.$BACKSLASH || target == chars.$MINUS ||
|
||||||
target == $MINUS || target == $_;
|
target == chars.$_;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isIdentifierPart(target: number): boolean {
|
function isIdentifierPart(target: number): boolean {
|
||||||
return ($a <= target && target <= $z) || ($A <= target && target <= $Z) || target == $BACKSLASH ||
|
return chars.isAsciiLetter(target) || target == chars.$BACKSLASH || target == chars.$MINUS ||
|
||||||
target == $MINUS || target == $_ || isDigit(target);
|
target == chars.$_ || chars.isDigit(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValidPseudoSelectorCharacter(code: number): boolean {
|
function isValidPseudoSelectorCharacter(code: number): boolean {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case $LPAREN:
|
case chars.$LPAREN:
|
||||||
case $RPAREN:
|
case chars.$RPAREN:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -535,18 +526,18 @@ function isValidPseudoSelectorCharacter(code: number): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValidKeyframeBlockCharacter(code: number): boolean {
|
function isValidKeyframeBlockCharacter(code: number): boolean {
|
||||||
return code == $PERCENT;
|
return code == chars.$PERCENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValidAttributeSelectorCharacter(code: number): boolean {
|
function isValidAttributeSelectorCharacter(code: number): boolean {
|
||||||
// value^*|$~=something
|
// value^*|$~=something
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case $$:
|
case chars.$$:
|
||||||
case $PIPE:
|
case chars.$PIPE:
|
||||||
case $CARET:
|
case chars.$CARET:
|
||||||
case $TILDA:
|
case chars.$TILDA:
|
||||||
case $STAR:
|
case chars.$STAR:
|
||||||
case $EQ:
|
case chars.$EQ:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -559,17 +550,17 @@ function isValidSelectorCharacter(code: number): boolean {
|
||||||
// #id, .class, *+~>
|
// #id, .class, *+~>
|
||||||
// tag:PSEUDO
|
// tag:PSEUDO
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case $HASH:
|
case chars.$HASH:
|
||||||
case $PERIOD:
|
case chars.$PERIOD:
|
||||||
case $TILDA:
|
case chars.$TILDA:
|
||||||
case $STAR:
|
case chars.$STAR:
|
||||||
case $PLUS:
|
case chars.$PLUS:
|
||||||
case $GT:
|
case chars.$GT:
|
||||||
case $COLON:
|
case chars.$COLON:
|
||||||
case $PIPE:
|
case chars.$PIPE:
|
||||||
case $COMMA:
|
case chars.$COMMA:
|
||||||
case $LBRACKET:
|
case chars.$LBRACKET:
|
||||||
case $RBRACKET:
|
case chars.$RBRACKET:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -580,16 +571,16 @@ function isValidStyleBlockCharacter(code: number): boolean {
|
||||||
// key:value;
|
// key:value;
|
||||||
// key:calc(something ... )
|
// key:calc(something ... )
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case $HASH:
|
case chars.$HASH:
|
||||||
case $SEMICOLON:
|
case chars.$SEMICOLON:
|
||||||
case $COLON:
|
case chars.$COLON:
|
||||||
case $PERCENT:
|
case chars.$PERCENT:
|
||||||
case $SLASH:
|
case chars.$SLASH:
|
||||||
case $BACKSLASH:
|
case chars.$BACKSLASH:
|
||||||
case $BANG:
|
case chars.$BANG:
|
||||||
case $PERIOD:
|
case chars.$PERIOD:
|
||||||
case $LPAREN:
|
case chars.$LPAREN:
|
||||||
case $RPAREN:
|
case chars.$RPAREN:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -599,11 +590,11 @@ function isValidStyleBlockCharacter(code: number): boolean {
|
||||||
function isValidMediaQueryRuleCharacter(code: number): boolean {
|
function isValidMediaQueryRuleCharacter(code: number): boolean {
|
||||||
// (min-width: 7.5em) and (orientation: landscape)
|
// (min-width: 7.5em) and (orientation: landscape)
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case $LPAREN:
|
case chars.$LPAREN:
|
||||||
case $RPAREN:
|
case chars.$RPAREN:
|
||||||
case $COLON:
|
case chars.$COLON:
|
||||||
case $PERCENT:
|
case chars.$PERCENT:
|
||||||
case $PERIOD:
|
case chars.$PERIOD:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -613,21 +604,21 @@ function isValidMediaQueryRuleCharacter(code: number): boolean {
|
||||||
function isValidAtRuleCharacter(code: number): boolean {
|
function isValidAtRuleCharacter(code: number): boolean {
|
||||||
// @document url(http://www.w3.org/page?something=on#hash),
|
// @document url(http://www.w3.org/page?something=on#hash),
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case $LPAREN:
|
case chars.$LPAREN:
|
||||||
case $RPAREN:
|
case chars.$RPAREN:
|
||||||
case $COLON:
|
case chars.$COLON:
|
||||||
case $PERCENT:
|
case chars.$PERCENT:
|
||||||
case $PERIOD:
|
case chars.$PERIOD:
|
||||||
case $SLASH:
|
case chars.$SLASH:
|
||||||
case $BACKSLASH:
|
case chars.$BACKSLASH:
|
||||||
case $HASH:
|
case chars.$HASH:
|
||||||
case $EQ:
|
case chars.$EQ:
|
||||||
case $QUESTION:
|
case chars.$QUESTION:
|
||||||
case $AMPERSAND:
|
case chars.$AMPERSAND:
|
||||||
case $STAR:
|
case chars.$STAR:
|
||||||
case $COMMA:
|
case chars.$COMMA:
|
||||||
case $MINUS:
|
case chars.$MINUS:
|
||||||
case $PLUS:
|
case chars.$PLUS:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -636,14 +627,14 @@ function isValidAtRuleCharacter(code: number): boolean {
|
||||||
|
|
||||||
function isValidStyleFunctionCharacter(code: number): boolean {
|
function isValidStyleFunctionCharacter(code: number): boolean {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case $PERIOD:
|
case chars.$PERIOD:
|
||||||
case $MINUS:
|
case chars.$MINUS:
|
||||||
case $PLUS:
|
case chars.$PLUS:
|
||||||
case $STAR:
|
case chars.$STAR:
|
||||||
case $SLASH:
|
case chars.$SLASH:
|
||||||
case $LPAREN:
|
case chars.$LPAREN:
|
||||||
case $RPAREN:
|
case chars.$RPAREN:
|
||||||
case $COMMA:
|
case chars.$COMMA:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -653,7 +644,7 @@ function isValidStyleFunctionCharacter(code: number): boolean {
|
||||||
function isValidBlockCharacter(code: number): boolean {
|
function isValidBlockCharacter(code: number): boolean {
|
||||||
// @something { }
|
// @something { }
|
||||||
// IDENT
|
// IDENT
|
||||||
return code == $AT;
|
return code == chars.$AT;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValidCssCharacter(code: number, mode: CssLexerMode): boolean {
|
function isValidCssCharacter(code: number, mode: CssLexerMode): boolean {
|
||||||
|
@ -695,20 +686,20 @@ function isValidCssCharacter(code: number, mode: CssLexerMode): boolean {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function charCode(input: any /** TODO #9100 */, index: any /** TODO #9100 */): number {
|
function charCode(input: string, index: number): number {
|
||||||
return index >= input.length ? $EOF : StringWrapper.charCodeAt(input, index);
|
return index >= input.length ? chars.$EOF : StringWrapper.charCodeAt(input, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
function charStr(code: number): string {
|
function charStr(code: number): string {
|
||||||
return StringWrapper.fromCharCode(code);
|
return StringWrapper.fromCharCode(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNewline(code: any /** TODO #9100 */): boolean {
|
export function isNewline(code: number): boolean {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case $FF:
|
case chars.$FF:
|
||||||
case $CR:
|
case chars.$CR:
|
||||||
case $LF:
|
case chars.$LF:
|
||||||
case $VTAB:
|
case chars.$VTAB:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {$AT, $COLON, $COMMA, $EOF, $GT, $LBRACE, $LBRACKET, $LPAREN, $PLUS, $RBRACE, $RBRACKET, $RPAREN, $SEMICOLON, $SLASH, $SPACE, $TAB, $TILDA, CssLexerMode, CssScanner, CssScannerError, CssToken, CssTokenType, generateErrorMessage, isNewline, isWhitespace} from './css_lexer';
|
import * as chars from './chars';
|
||||||
import {NumberWrapper, StringWrapper, isPresent} from './facade/lang';
|
import {CssLexerMode, CssScanner, CssToken, CssTokenType, generateErrorMessage, isNewline} from './css_lexer';
|
||||||
|
import {isPresent} from './facade/lang';
|
||||||
import {ParseError, ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util';
|
import {ParseError, ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util';
|
||||||
|
|
||||||
const SPACE_OPERATOR = ' ';
|
const SPACE_OPERATOR = ' ';
|
||||||
|
@ -43,13 +44,13 @@ function _pseudoSelectorSupportsInnerSelectors(name: string): boolean {
|
||||||
|
|
||||||
function isSelectorOperatorCharacter(code: number): boolean {
|
function isSelectorOperatorCharacter(code: number): boolean {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case $SLASH:
|
case chars.$SLASH:
|
||||||
case $TILDA:
|
case chars.$TILDA:
|
||||||
case $PLUS:
|
case chars.$PLUS:
|
||||||
case $GT:
|
case chars.$GT:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return isWhitespace(code);
|
return chars.isWhitespace(code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,22 +70,22 @@ function getDelimFromToken(token: CssToken): number {
|
||||||
|
|
||||||
function getDelimFromCharacter(code: number): number {
|
function getDelimFromCharacter(code: number): number {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case $EOF:
|
case chars.$EOF:
|
||||||
return EOF_DELIM_FLAG;
|
return EOF_DELIM_FLAG;
|
||||||
case $COMMA:
|
case chars.$COMMA:
|
||||||
return COMMA_DELIM_FLAG;
|
return COMMA_DELIM_FLAG;
|
||||||
case $COLON:
|
case chars.$COLON:
|
||||||
return COLON_DELIM_FLAG;
|
return COLON_DELIM_FLAG;
|
||||||
case $SEMICOLON:
|
case chars.$SEMICOLON:
|
||||||
return SEMICOLON_DELIM_FLAG;
|
return SEMICOLON_DELIM_FLAG;
|
||||||
case $RBRACE:
|
case chars.$RBRACE:
|
||||||
return RBRACE_DELIM_FLAG;
|
return RBRACE_DELIM_FLAG;
|
||||||
case $LBRACE:
|
case chars.$LBRACE:
|
||||||
return LBRACE_DELIM_FLAG;
|
return LBRACE_DELIM_FLAG;
|
||||||
case $RPAREN:
|
case chars.$RPAREN:
|
||||||
return RPAREN_DELIM_FLAG;
|
return RPAREN_DELIM_FLAG;
|
||||||
case $SPACE:
|
case chars.$SPACE:
|
||||||
case $TAB:
|
case chars.$TAB:
|
||||||
return SPACE_DELIM_FLAG;
|
return SPACE_DELIM_FLAG;
|
||||||
default:
|
default:
|
||||||
return isNewline(code) ? NEWLINE_DELIM_FLAG : 0;
|
return isNewline(code) ? NEWLINE_DELIM_FLAG : 0;
|
||||||
|
@ -187,7 +188,7 @@ export class CssParser {
|
||||||
const start = this._getScannerIndex();
|
const start = this._getScannerIndex();
|
||||||
var results: any[] /** TODO #9100 */ = [];
|
var results: any[] /** TODO #9100 */ = [];
|
||||||
this._scanner.consumeEmptyStatements();
|
this._scanner.consumeEmptyStatements();
|
||||||
while (this._scanner.peek != $EOF) {
|
while (this._scanner.peek != chars.$EOF) {
|
||||||
this._scanner.setMode(CssLexerMode.BLOCK);
|
this._scanner.setMode(CssLexerMode.BLOCK);
|
||||||
results.push(this._parseRule(delimiters));
|
results.push(this._parseRule(delimiters));
|
||||||
}
|
}
|
||||||
|
@ -197,7 +198,7 @@ export class CssParser {
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_parseRule(delimiters: number): CssRuleAst {
|
_parseRule(delimiters: number): CssRuleAst {
|
||||||
if (this._scanner.peek == $AT) {
|
if (this._scanner.peek == chars.$AT) {
|
||||||
return this._parseAtRule(delimiters);
|
return this._parseAtRule(delimiters);
|
||||||
}
|
}
|
||||||
return this._parseSelectorRule(delimiters);
|
return this._parseSelectorRule(delimiters);
|
||||||
|
@ -277,7 +278,7 @@ export class CssParser {
|
||||||
|
|
||||||
this._collectUntilDelim(delimiters | LBRACE_DELIM_FLAG | SEMICOLON_DELIM_FLAG)
|
this._collectUntilDelim(delimiters | LBRACE_DELIM_FLAG | SEMICOLON_DELIM_FLAG)
|
||||||
.forEach((token) => { listOfTokens.push(token); });
|
.forEach((token) => { listOfTokens.push(token); });
|
||||||
if (this._scanner.peek == $LBRACE) {
|
if (this._scanner.peek == chars.$LBRACE) {
|
||||||
listOfTokens.push(this._consume(CssTokenType.Character, '{'));
|
listOfTokens.push(this._consume(CssTokenType.Character, '{'));
|
||||||
this._collectUntilDelim(delimiters | RBRACE_DELIM_FLAG | LBRACE_DELIM_FLAG)
|
this._collectUntilDelim(delimiters | RBRACE_DELIM_FLAG | LBRACE_DELIM_FLAG)
|
||||||
.forEach((token) => { listOfTokens.push(token); });
|
.forEach((token) => { listOfTokens.push(token); });
|
||||||
|
@ -387,7 +388,7 @@ export class CssParser {
|
||||||
delimiters |= LBRACE_DELIM_FLAG;
|
delimiters |= LBRACE_DELIM_FLAG;
|
||||||
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
||||||
stepTokens.push(this._parseKeyframeLabel(delimiters | COMMA_DELIM_FLAG));
|
stepTokens.push(this._parseKeyframeLabel(delimiters | COMMA_DELIM_FLAG));
|
||||||
if (this._scanner.peek != $LBRACE) {
|
if (this._scanner.peek != chars.$LBRACE) {
|
||||||
this._consume(CssTokenType.Character, ',');
|
this._consume(CssTokenType.Character, ',');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -415,7 +416,7 @@ export class CssParser {
|
||||||
var startToken = this._consume(CssTokenType.Character, ':');
|
var startToken = this._consume(CssTokenType.Character, ':');
|
||||||
var tokens = [startToken];
|
var tokens = [startToken];
|
||||||
|
|
||||||
if (this._scanner.peek == $COLON) { // ::something
|
if (this._scanner.peek == chars.$COLON) { // ::something
|
||||||
startToken = this._consume(CssTokenType.Character, ':');
|
startToken = this._consume(CssTokenType.Character, ':');
|
||||||
tokens.push(startToken);
|
tokens.push(startToken);
|
||||||
}
|
}
|
||||||
|
@ -430,7 +431,7 @@ export class CssParser {
|
||||||
tokens.push(pseudoSelectorToken);
|
tokens.push(pseudoSelectorToken);
|
||||||
|
|
||||||
// host(), lang(), nth-child(), etc...
|
// host(), lang(), nth-child(), etc...
|
||||||
if (this._scanner.peek == $LPAREN) {
|
if (this._scanner.peek == chars.$LPAREN) {
|
||||||
this._scanner.setMode(CssLexerMode.PSEUDO_SELECTOR_WITH_ARGUMENTS);
|
this._scanner.setMode(CssLexerMode.PSEUDO_SELECTOR_WITH_ARGUMENTS);
|
||||||
|
|
||||||
var openParenToken = this._consume(CssTokenType.Character, '(');
|
var openParenToken = this._consume(CssTokenType.Character, '(');
|
||||||
|
@ -491,13 +492,13 @@ export class CssParser {
|
||||||
var peek = this._scanner.peek;
|
var peek = this._scanner.peek;
|
||||||
|
|
||||||
switch (peek) {
|
switch (peek) {
|
||||||
case $COLON:
|
case chars.$COLON:
|
||||||
var innerPseudo = this._parsePseudoSelector(delimiters);
|
var innerPseudo = this._parsePseudoSelector(delimiters);
|
||||||
pseudoSelectors.push(innerPseudo);
|
pseudoSelectors.push(innerPseudo);
|
||||||
this._scanner.setMode(CssLexerMode.SELECTOR);
|
this._scanner.setMode(CssLexerMode.SELECTOR);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case $LBRACKET:
|
case chars.$LBRACKET:
|
||||||
// we set the mode after the scan because attribute mode does not
|
// we set the mode after the scan because attribute mode does not
|
||||||
// allow attribute [] values. And this also will catch any errors
|
// allow attribute [] values. And this also will catch any errors
|
||||||
// if an extra "[" is used inside.
|
// if an extra "[" is used inside.
|
||||||
|
@ -505,7 +506,7 @@ export class CssParser {
|
||||||
this._scanner.setMode(CssLexerMode.ATTRIBUTE_SELECTOR);
|
this._scanner.setMode(CssLexerMode.ATTRIBUTE_SELECTOR);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case $RBRACKET:
|
case chars.$RBRACKET:
|
||||||
if (this._scanner.getMode() != CssLexerMode.ATTRIBUTE_SELECTOR) {
|
if (this._scanner.getMode() != CssLexerMode.ATTRIBUTE_SELECTOR) {
|
||||||
hasAttributeError = true;
|
hasAttributeError = true;
|
||||||
}
|
}
|
||||||
|
@ -580,7 +581,7 @@ export class CssParser {
|
||||||
|
|
||||||
case GT_CHARACTER:
|
case GT_CHARACTER:
|
||||||
// >>> operator
|
// >>> operator
|
||||||
if (this._scanner.peek == $GT && this._scanner.peekPeek == $GT) {
|
if (this._scanner.peek == chars.$GT && this._scanner.peekPeek == chars.$GT) {
|
||||||
this._consume(CssTokenType.Character, GT_CHARACTER);
|
this._consume(CssTokenType.Character, GT_CHARACTER);
|
||||||
this._consume(CssTokenType.Character, GT_CHARACTER);
|
this._consume(CssTokenType.Character, GT_CHARACTER);
|
||||||
token = new CssToken(
|
token = new CssToken(
|
||||||
|
@ -601,7 +602,7 @@ export class CssParser {
|
||||||
// if we do come across one or more spaces inside of
|
// if we do come across one or more spaces inside of
|
||||||
// the operators loop then an empty space is still a
|
// the operators loop then an empty space is still a
|
||||||
// valid operator to use if something else was not found
|
// valid operator to use if something else was not found
|
||||||
if (operator == null && operatorScanCount > 0 && this._scanner.peek != $LBRACE) {
|
if (operator == null && operatorScanCount > 0 && this._scanner.peek != chars.$LBRACE) {
|
||||||
operator = lastOperatorToken;
|
operator = lastOperatorToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,7 +648,7 @@ export class CssParser {
|
||||||
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
||||||
var token: CssToken;
|
var token: CssToken;
|
||||||
if (isPresent(previous) && previous.type == CssTokenType.Identifier &&
|
if (isPresent(previous) && previous.type == CssTokenType.Identifier &&
|
||||||
this._scanner.peek == $LPAREN) {
|
this._scanner.peek == chars.$LPAREN) {
|
||||||
token = this._consume(CssTokenType.Character, '(');
|
token = this._consume(CssTokenType.Character, '(');
|
||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
|
|
||||||
|
@ -676,9 +677,9 @@ export class CssParser {
|
||||||
this._scanner.consumeWhitespace();
|
this._scanner.consumeWhitespace();
|
||||||
|
|
||||||
var code = this._scanner.peek;
|
var code = this._scanner.peek;
|
||||||
if (code == $SEMICOLON) {
|
if (code == chars.$SEMICOLON) {
|
||||||
this._consume(CssTokenType.Character, ';');
|
this._consume(CssTokenType.Character, ';');
|
||||||
} else if (code != $RBRACE) {
|
} else if (code != chars.$RBRACE) {
|
||||||
this._error(
|
this._error(
|
||||||
generateErrorMessage(
|
generateErrorMessage(
|
||||||
this._scanner.input, `The CSS key/value definition did not end with a semicolon`,
|
this._scanner.input, `The CSS key/value definition did not end with a semicolon`,
|
||||||
|
@ -734,7 +735,7 @@ export class CssParser {
|
||||||
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
|
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
|
||||||
|
|
||||||
var result = this._consume(CssTokenType.Character, '{');
|
var result = this._consume(CssTokenType.Character, '{');
|
||||||
if (result.numValue != $LBRACE) {
|
if (result.numValue != chars.$LBRACE) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -767,20 +768,20 @@ export class CssParser {
|
||||||
// there are a few cases as to what could happen if it
|
// there are a few cases as to what could happen if it
|
||||||
// is missing
|
// is missing
|
||||||
switch (this._scanner.peek) {
|
switch (this._scanner.peek) {
|
||||||
case $COLON:
|
case chars.$COLON:
|
||||||
this._consume(CssTokenType.Character, ':');
|
this._consume(CssTokenType.Character, ':');
|
||||||
parseValue = true;
|
parseValue = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case $SEMICOLON:
|
case chars.$SEMICOLON:
|
||||||
case $RBRACE:
|
case chars.$RBRACE:
|
||||||
case $EOF:
|
case chars.$EOF:
|
||||||
parseValue = false;
|
parseValue = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
var propStr = [prop.strValue];
|
var propStr = [prop.strValue];
|
||||||
if (this._scanner.peek != $COLON) {
|
if (this._scanner.peek != chars.$COLON) {
|
||||||
// this will throw the error
|
// this will throw the error
|
||||||
var nextValue = this._consume(CssTokenType.Character, ':');
|
var nextValue = this._consume(CssTokenType.Character, ':');
|
||||||
propStr.push(nextValue.strValue);
|
propStr.push(nextValue.strValue);
|
||||||
|
@ -795,7 +796,7 @@ export class CssParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
// this means we've reached the end of the definition and/or block
|
// this means we've reached the end of the definition and/or block
|
||||||
if (this._scanner.peek == $COLON) {
|
if (this._scanner.peek == chars.$COLON) {
|
||||||
this._consume(CssTokenType.Character, ':');
|
this._consume(CssTokenType.Character, ':');
|
||||||
parseValue = true;
|
parseValue = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -960,8 +961,6 @@ export class CssSelectorAst extends CssSelectorPartAst {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CssSimpleSelectorAst extends CssSelectorPartAst {
|
export class CssSimpleSelectorAst extends CssSelectorPartAst {
|
||||||
public selectorStrValue: string;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
start: number, end: number, public tokens: CssToken[], public strValue: string,
|
start: number, end: number, public tokens: CssToken[], public strValue: string,
|
||||||
public pseudoSelectors: CssPseudoSelectorAst[], public operator: CssToken) {
|
public pseudoSelectors: CssPseudoSelectorAst[], public operator: CssToken) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
import * as chars from '../chars';
|
||||||
import {SetWrapper} from '../facade/collection';
|
import {SetWrapper} from '../facade/collection';
|
||||||
import {BaseException} from '../facade/exceptions';
|
import {BaseException} from '../facade/exceptions';
|
||||||
import {NumberWrapper, StringJoiner, StringWrapper, isPresent} from '../facade/lang';
|
import {NumberWrapper, StringJoiner, StringWrapper, isPresent} from '../facade/lang';
|
||||||
|
@ -13,6 +13,8 @@ export enum TokenType {
|
||||||
Number
|
Number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KEYWORDS = ['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else'];
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Lexer {
|
export class Lexer {
|
||||||
tokenize(text: string): any[] {
|
tokenize(text: string): any[] {
|
||||||
|
@ -109,58 +111,8 @@ function newNumberToken(index: number, n: number): Token {
|
||||||
return new Token(index, TokenType.Number, n, '');
|
return new Token(index, TokenType.Number, n, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export var EOF: Token = new Token(-1, TokenType.Character, 0, '');
|
export var EOF: Token = new Token(-1, TokenType.Character, 0, '');
|
||||||
|
|
||||||
export const $EOF = /*@ts2dart_const*/ 0;
|
|
||||||
export const $TAB = /*@ts2dart_const*/ 9;
|
|
||||||
export const $LF = /*@ts2dart_const*/ 10;
|
|
||||||
export const $VTAB = /*@ts2dart_const*/ 11;
|
|
||||||
export const $FF = /*@ts2dart_const*/ 12;
|
|
||||||
export const $CR = /*@ts2dart_const*/ 13;
|
|
||||||
export const $SPACE = /*@ts2dart_const*/ 32;
|
|
||||||
export const $BANG = /*@ts2dart_const*/ 33;
|
|
||||||
export const $DQ = /*@ts2dart_const*/ 34;
|
|
||||||
export const $HASH = /*@ts2dart_const*/ 35;
|
|
||||||
export const $$ = /*@ts2dart_const*/ 36;
|
|
||||||
export const $PERCENT = /*@ts2dart_const*/ 37;
|
|
||||||
export const $AMPERSAND = /*@ts2dart_const*/ 38;
|
|
||||||
export const $SQ = /*@ts2dart_const*/ 39;
|
|
||||||
export const $LPAREN = /*@ts2dart_const*/ 40;
|
|
||||||
export const $RPAREN = /*@ts2dart_const*/ 41;
|
|
||||||
export const $STAR = /*@ts2dart_const*/ 42;
|
|
||||||
export const $PLUS = /*@ts2dart_const*/ 43;
|
|
||||||
export const $COMMA = /*@ts2dart_const*/ 44;
|
|
||||||
export const $MINUS = /*@ts2dart_const*/ 45;
|
|
||||||
export const $PERIOD = /*@ts2dart_const*/ 46;
|
|
||||||
export const $SLASH = /*@ts2dart_const*/ 47;
|
|
||||||
export const $COLON = /*@ts2dart_const*/ 58;
|
|
||||||
export const $SEMICOLON = /*@ts2dart_const*/ 59;
|
|
||||||
export const $LT = /*@ts2dart_const*/ 60;
|
|
||||||
export const $EQ = /*@ts2dart_const*/ 61;
|
|
||||||
export const $GT = /*@ts2dart_const*/ 62;
|
|
||||||
export const $QUESTION = /*@ts2dart_const*/ 63;
|
|
||||||
|
|
||||||
const $0 = /*@ts2dart_const*/ 48;
|
|
||||||
const $9 = /*@ts2dart_const*/ 57;
|
|
||||||
|
|
||||||
const $A = /*@ts2dart_const*/ 65, $E = /*@ts2dart_const*/ 69, $Z = /*@ts2dart_const*/ 90;
|
|
||||||
|
|
||||||
export const $LBRACKET = /*@ts2dart_const*/ 91;
|
|
||||||
export const $BACKSLASH = /*@ts2dart_const*/ 92;
|
|
||||||
export const $RBRACKET = /*@ts2dart_const*/ 93;
|
|
||||||
const $CARET = /*@ts2dart_const*/ 94;
|
|
||||||
const $_ = /*@ts2dart_const*/ 95;
|
|
||||||
export const $BT = /*@ts2dart_const*/ 96;
|
|
||||||
const $a = /*@ts2dart_const*/ 97, $e = /*@ts2dart_const*/ 101, $f = /*@ts2dart_const*/ 102;
|
|
||||||
const $n = /*@ts2dart_const*/ 110, $r = /*@ts2dart_const*/ 114, $t = /*@ts2dart_const*/ 116,
|
|
||||||
$u = /*@ts2dart_const*/ 117, $v = /*@ts2dart_const*/ 118, $z = /*@ts2dart_const*/ 122;
|
|
||||||
|
|
||||||
export const $LBRACE = /*@ts2dart_const*/ 123;
|
|
||||||
export const $BAR = /*@ts2dart_const*/ 124;
|
|
||||||
export const $RBRACE = /*@ts2dart_const*/ 125;
|
|
||||||
const $NBSP = /*@ts2dart_const*/ 160;
|
|
||||||
|
|
||||||
export class ScannerError extends BaseException {
|
export class ScannerError extends BaseException {
|
||||||
constructor(public message: string) { super(); }
|
constructor(public message: string) { super(); }
|
||||||
|
|
||||||
|
@ -179,16 +131,16 @@ class _Scanner {
|
||||||
|
|
||||||
advance() {
|
advance() {
|
||||||
this.peek =
|
this.peek =
|
||||||
++this.index >= this.length ? $EOF : StringWrapper.charCodeAt(this.input, this.index);
|
++this.index >= this.length ? chars.$EOF : StringWrapper.charCodeAt(this.input, this.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
scanToken(): Token {
|
scanToken(): Token {
|
||||||
var input = this.input, length = this.length, peek = this.peek, index = this.index;
|
var input = this.input, length = this.length, peek = this.peek, index = this.index;
|
||||||
|
|
||||||
// Skip whitespace.
|
// Skip whitespace.
|
||||||
while (peek <= $SPACE) {
|
while (peek <= chars.$SPACE) {
|
||||||
if (++index >= length) {
|
if (++index >= length) {
|
||||||
peek = $EOF;
|
peek = chars.$EOF;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
peek = StringWrapper.charCodeAt(input, index);
|
peek = StringWrapper.charCodeAt(input, index);
|
||||||
|
@ -204,49 +156,50 @@ class _Scanner {
|
||||||
|
|
||||||
// Handle identifiers and numbers.
|
// Handle identifiers and numbers.
|
||||||
if (isIdentifierStart(peek)) return this.scanIdentifier();
|
if (isIdentifierStart(peek)) return this.scanIdentifier();
|
||||||
if (isDigit(peek)) return this.scanNumber(index);
|
if (chars.isDigit(peek)) return this.scanNumber(index);
|
||||||
|
|
||||||
var start: number = index;
|
var start: number = index;
|
||||||
switch (peek) {
|
switch (peek) {
|
||||||
case $PERIOD:
|
case chars.$PERIOD:
|
||||||
this.advance();
|
this.advance();
|
||||||
return isDigit(this.peek) ? this.scanNumber(start) : newCharacterToken(start, $PERIOD);
|
return chars.isDigit(this.peek) ? this.scanNumber(start) :
|
||||||
case $LPAREN:
|
newCharacterToken(start, chars.$PERIOD);
|
||||||
case $RPAREN:
|
case chars.$LPAREN:
|
||||||
case $LBRACE:
|
case chars.$RPAREN:
|
||||||
case $RBRACE:
|
case chars.$LBRACE:
|
||||||
case $LBRACKET:
|
case chars.$RBRACE:
|
||||||
case $RBRACKET:
|
case chars.$LBRACKET:
|
||||||
case $COMMA:
|
case chars.$RBRACKET:
|
||||||
case $COLON:
|
case chars.$COMMA:
|
||||||
case $SEMICOLON:
|
case chars.$COLON:
|
||||||
|
case chars.$SEMICOLON:
|
||||||
return this.scanCharacter(start, peek);
|
return this.scanCharacter(start, peek);
|
||||||
case $SQ:
|
case chars.$SQ:
|
||||||
case $DQ:
|
case chars.$DQ:
|
||||||
return this.scanString();
|
return this.scanString();
|
||||||
case $HASH:
|
case chars.$HASH:
|
||||||
case $PLUS:
|
case chars.$PLUS:
|
||||||
case $MINUS:
|
case chars.$MINUS:
|
||||||
case $STAR:
|
case chars.$STAR:
|
||||||
case $SLASH:
|
case chars.$SLASH:
|
||||||
case $PERCENT:
|
case chars.$PERCENT:
|
||||||
case $CARET:
|
case chars.$CARET:
|
||||||
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
|
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
|
||||||
case $QUESTION:
|
case chars.$QUESTION:
|
||||||
return this.scanComplexOperator(start, '?', $PERIOD, '.');
|
return this.scanComplexOperator(start, '?', chars.$PERIOD, '.');
|
||||||
case $LT:
|
case chars.$LT:
|
||||||
case $GT:
|
case chars.$GT:
|
||||||
return this.scanComplexOperator(start, StringWrapper.fromCharCode(peek), $EQ, '=');
|
return this.scanComplexOperator(start, StringWrapper.fromCharCode(peek), chars.$EQ, '=');
|
||||||
case $BANG:
|
case chars.$BANG:
|
||||||
case $EQ:
|
case chars.$EQ:
|
||||||
return this.scanComplexOperator(
|
return this.scanComplexOperator(
|
||||||
start, StringWrapper.fromCharCode(peek), $EQ, '=', $EQ, '=');
|
start, StringWrapper.fromCharCode(peek), chars.$EQ, '=', chars.$EQ, '=');
|
||||||
case $AMPERSAND:
|
case chars.$AMPERSAND:
|
||||||
return this.scanComplexOperator(start, '&', $AMPERSAND, '&');
|
return this.scanComplexOperator(start, '&', chars.$AMPERSAND, '&');
|
||||||
case $BAR:
|
case chars.$BAR:
|
||||||
return this.scanComplexOperator(start, '|', $BAR, '|');
|
return this.scanComplexOperator(start, '|', chars.$BAR, '|');
|
||||||
case $NBSP:
|
case chars.$NBSP:
|
||||||
while (isWhitespace(this.peek)) this.advance();
|
while (chars.isWhitespace(this.peek)) this.advance();
|
||||||
return this.scanToken();
|
return this.scanToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,25 +250,22 @@ class _Scanner {
|
||||||
this.advance();
|
this.advance();
|
||||||
while (isIdentifierPart(this.peek)) this.advance();
|
while (isIdentifierPart(this.peek)) this.advance();
|
||||||
var str: string = this.input.substring(start, this.index);
|
var str: string = this.input.substring(start, this.index);
|
||||||
if (SetWrapper.has(KEYWORDS, str)) {
|
return KEYWORDS.indexOf(str) > -1 ? newKeywordToken(start, str) :
|
||||||
return newKeywordToken(start, str);
|
newIdentifierToken(start, str);
|
||||||
} else {
|
|
||||||
return newIdentifierToken(start, str);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scanNumber(start: number): Token {
|
scanNumber(start: number): Token {
|
||||||
var simple: boolean = (this.index === start);
|
var simple: boolean = (this.index === start);
|
||||||
this.advance(); // Skip initial digit.
|
this.advance(); // Skip initial digit.
|
||||||
while (true) {
|
while (true) {
|
||||||
if (isDigit(this.peek)) {
|
if (chars.isDigit(this.peek)) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
} else if (this.peek == $PERIOD) {
|
} else if (this.peek == chars.$PERIOD) {
|
||||||
simple = false;
|
simple = false;
|
||||||
} else if (isExponentStart(this.peek)) {
|
} else if (isExponentStart(this.peek)) {
|
||||||
this.advance();
|
this.advance();
|
||||||
if (isExponentSign(this.peek)) this.advance();
|
if (isExponentSign(this.peek)) this.advance();
|
||||||
if (!isDigit(this.peek)) this.error('Invalid exponent', -1);
|
if (!chars.isDigit(this.peek)) this.error('Invalid exponent', -1);
|
||||||
simple = false;
|
simple = false;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
@ -323,7 +273,6 @@ class _Scanner {
|
||||||
this.advance();
|
this.advance();
|
||||||
}
|
}
|
||||||
var str: string = this.input.substring(start, this.index);
|
var str: string = this.input.substring(start, this.index);
|
||||||
// TODO
|
|
||||||
var value: number =
|
var value: number =
|
||||||
simple ? NumberWrapper.parseIntAutoRadix(str) : NumberWrapper.parseFloat(str);
|
simple ? NumberWrapper.parseIntAutoRadix(str) : NumberWrapper.parseFloat(str);
|
||||||
return newNumberToken(start, value);
|
return newNumberToken(start, value);
|
||||||
|
@ -339,12 +288,12 @@ class _Scanner {
|
||||||
var input: string = this.input;
|
var input: string = this.input;
|
||||||
|
|
||||||
while (this.peek != quote) {
|
while (this.peek != quote) {
|
||||||
if (this.peek == $BACKSLASH) {
|
if (this.peek == chars.$BACKSLASH) {
|
||||||
if (buffer == null) buffer = new StringJoiner();
|
if (buffer == null) buffer = new StringJoiner();
|
||||||
buffer.add(input.substring(marker, this.index));
|
buffer.add(input.substring(marker, this.index));
|
||||||
this.advance();
|
this.advance();
|
||||||
var unescapedCode: number;
|
var unescapedCode: number;
|
||||||
if (this.peek == $u) {
|
if (this.peek == chars.$u) {
|
||||||
// 4 character hex code for unicode character.
|
// 4 character hex code for unicode character.
|
||||||
var hex: string = input.substring(this.index + 1, this.index + 5);
|
var hex: string = input.substring(this.index + 1, this.index + 5);
|
||||||
try {
|
try {
|
||||||
|
@ -361,7 +310,7 @@ class _Scanner {
|
||||||
}
|
}
|
||||||
buffer.add(StringWrapper.fromCharCode(unescapedCode));
|
buffer.add(StringWrapper.fromCharCode(unescapedCode));
|
||||||
marker = this.index;
|
marker = this.index;
|
||||||
} else if (this.peek == $EOF) {
|
} else if (this.peek == chars.$EOF) {
|
||||||
this.error('Unterminated quote', 0);
|
this.error('Unterminated quote', 0);
|
||||||
} else {
|
} else {
|
||||||
this.advance();
|
this.advance();
|
||||||
|
@ -387,12 +336,9 @@ class _Scanner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWhitespace(code: number): boolean {
|
|
||||||
return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isIdentifierStart(code: number): boolean {
|
function isIdentifierStart(code: number): boolean {
|
||||||
return ($a <= code && code <= $z) || ($A <= code && code <= $Z) || (code == $_) || (code == $$);
|
return (chars.$a <= code && code <= chars.$z) || (chars.$A <= code && code <= chars.$Z) ||
|
||||||
|
(code == chars.$_) || (code == chars.$$);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isIdentifier(input: string): boolean {
|
export function isIdentifier(input: string): boolean {
|
||||||
|
@ -400,7 +346,7 @@ export function isIdentifier(input: string): boolean {
|
||||||
var scanner = new _Scanner(input);
|
var scanner = new _Scanner(input);
|
||||||
if (!isIdentifierStart(scanner.peek)) return false;
|
if (!isIdentifierStart(scanner.peek)) return false;
|
||||||
scanner.advance();
|
scanner.advance();
|
||||||
while (scanner.peek !== $EOF) {
|
while (scanner.peek !== chars.$EOF) {
|
||||||
if (!isIdentifierPart(scanner.peek)) return false;
|
if (!isIdentifierPart(scanner.peek)) return false;
|
||||||
scanner.advance();
|
scanner.advance();
|
||||||
}
|
}
|
||||||
|
@ -408,48 +354,35 @@ export function isIdentifier(input: string): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isIdentifierPart(code: number): boolean {
|
function isIdentifierPart(code: number): boolean {
|
||||||
return ($a <= code && code <= $z) || ($A <= code && code <= $Z) || ($0 <= code && code <= $9) ||
|
return chars.isAsciiLetter(code) || chars.isDigit(code) || (code == chars.$_) ||
|
||||||
(code == $_) || (code == $$);
|
(code == chars.$$);
|
||||||
}
|
|
||||||
|
|
||||||
function isDigit(code: number): boolean {
|
|
||||||
return $0 <= code && code <= $9;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isExponentStart(code: number): boolean {
|
function isExponentStart(code: number): boolean {
|
||||||
return code == $e || code == $E;
|
return code == chars.$e || code == chars.$E;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isExponentSign(code: number): boolean {
|
function isExponentSign(code: number): boolean {
|
||||||
return code == $MINUS || code == $PLUS;
|
return code == chars.$MINUS || code == chars.$PLUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isQuote(code: number): boolean {
|
export function isQuote(code: number): boolean {
|
||||||
return code === $SQ || code === $DQ || code === $BT;
|
return code === chars.$SQ || code === chars.$DQ || code === chars.$BT;
|
||||||
}
|
}
|
||||||
|
|
||||||
function unescape(code: number): number {
|
function unescape(code: number): number {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case $n:
|
case chars.$n:
|
||||||
return $LF;
|
return chars.$LF;
|
||||||
case $f:
|
case chars.$f:
|
||||||
return $FF;
|
return chars.$FF;
|
||||||
case $r:
|
case chars.$r:
|
||||||
return $CR;
|
return chars.$CR;
|
||||||
case $t:
|
case chars.$t:
|
||||||
return $TAB;
|
return chars.$TAB;
|
||||||
case $v:
|
case chars.$v:
|
||||||
return $VTAB;
|
return chars.$VTAB;
|
||||||
default:
|
default:
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var OPERATORS = SetWrapper.createFromList([
|
|
||||||
'+', '-', '*', '/', '%', '^', '=', '==', '!=', '===', '!==', '<',
|
|
||||||
'>', '<=', '>=', '&&', '||', '&', '|', '!', '?', '#', '?.'
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
|
||||||
var KEYWORDS =
|
|
||||||
SetWrapper.createFromList(['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else']);
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
|
import * as chars from '../chars';
|
||||||
import {ListWrapper} from '../facade/collection';
|
import {ListWrapper} from '../facade/collection';
|
||||||
import {BaseException} from '../facade/exceptions';
|
import {BaseException} from '../facade/exceptions';
|
||||||
import {RegExpWrapper, StringWrapper, escapeRegExp, isBlank, isPresent} from '../facade/lang';
|
import {RegExpWrapper, StringWrapper, escapeRegExp, isBlank, isPresent} from '../facade/lang';
|
||||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../interpolation_config';
|
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../interpolation_config';
|
||||||
|
|
||||||
import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, TemplateBinding} from './ast';
|
import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, TemplateBinding} from './ast';
|
||||||
import {$COLON, $COMMA, $LBRACE, $LBRACKET, $LPAREN, $PERIOD, $RBRACE, $RBRACKET, $RPAREN, $SEMICOLON, $SLASH, EOF, Lexer, Token, isIdentifier, isQuote} from './lexer';
|
import {EOF, Lexer, Token, isIdentifier, isQuote} from './lexer';
|
||||||
|
|
||||||
|
|
||||||
var _implicitReceiver = new ImplicitReceiver();
|
var _implicitReceiver = new ImplicitReceiver();
|
||||||
|
@ -152,7 +153,7 @@ export class Parser {
|
||||||
let char = StringWrapper.charCodeAt(input, i);
|
let char = StringWrapper.charCodeAt(input, i);
|
||||||
let nextChar = StringWrapper.charCodeAt(input, i + 1);
|
let nextChar = StringWrapper.charCodeAt(input, i + 1);
|
||||||
|
|
||||||
if (char === $SLASH && nextChar == $SLASH && isBlank(outerQuote)) return i;
|
if (char === chars.$SLASH && nextChar == chars.$SLASH && isBlank(outerQuote)) return i;
|
||||||
|
|
||||||
if (outerQuote === char) {
|
if (outerQuote === char) {
|
||||||
outerQuote = null;
|
outerQuote = null;
|
||||||
|
@ -267,11 +268,11 @@ export class _ParseAST {
|
||||||
var expr = this.parsePipe();
|
var expr = this.parsePipe();
|
||||||
exprs.push(expr);
|
exprs.push(expr);
|
||||||
|
|
||||||
if (this.optionalCharacter($SEMICOLON)) {
|
if (this.optionalCharacter(chars.$SEMICOLON)) {
|
||||||
if (!this.parseAction) {
|
if (!this.parseAction) {
|
||||||
this.error('Binding expression cannot contain chained expression');
|
this.error('Binding expression cannot contain chained expression');
|
||||||
}
|
}
|
||||||
while (this.optionalCharacter($SEMICOLON)) {
|
while (this.optionalCharacter(chars.$SEMICOLON)) {
|
||||||
} // read all semicolons
|
} // read all semicolons
|
||||||
} else if (this.index < this.tokens.length) {
|
} else if (this.index < this.tokens.length) {
|
||||||
this.error(`Unexpected token '${this.next}'`);
|
this.error(`Unexpected token '${this.next}'`);
|
||||||
|
@ -292,7 +293,7 @@ export class _ParseAST {
|
||||||
do {
|
do {
|
||||||
var name = this.expectIdentifierOrKeyword();
|
var name = this.expectIdentifierOrKeyword();
|
||||||
var args: AST[] = [];
|
var args: AST[] = [];
|
||||||
while (this.optionalCharacter($COLON)) {
|
while (this.optionalCharacter(chars.$COLON)) {
|
||||||
args.push(this.parseExpression());
|
args.push(this.parseExpression());
|
||||||
}
|
}
|
||||||
result = new BindingPipe(result, name, args);
|
result = new BindingPipe(result, name, args);
|
||||||
|
@ -310,7 +311,7 @@ export class _ParseAST {
|
||||||
|
|
||||||
if (this.optionalOperator('?')) {
|
if (this.optionalOperator('?')) {
|
||||||
var yes = this.parsePipe();
|
var yes = this.parsePipe();
|
||||||
if (!this.optionalCharacter($COLON)) {
|
if (!this.optionalCharacter(chars.$COLON)) {
|
||||||
var end = this.inputIndex;
|
var end = this.inputIndex;
|
||||||
var expression = this.input.substring(start, end);
|
var expression = this.input.substring(start, end);
|
||||||
this.error(`Conditional expression ${expression} requires all 3 expressions`);
|
this.error(`Conditional expression ${expression} requires all 3 expressions`);
|
||||||
|
@ -421,15 +422,15 @@ export class _ParseAST {
|
||||||
parseCallChain(): AST {
|
parseCallChain(): AST {
|
||||||
var result = this.parsePrimary();
|
var result = this.parsePrimary();
|
||||||
while (true) {
|
while (true) {
|
||||||
if (this.optionalCharacter($PERIOD)) {
|
if (this.optionalCharacter(chars.$PERIOD)) {
|
||||||
result = this.parseAccessMemberOrMethodCall(result, false);
|
result = this.parseAccessMemberOrMethodCall(result, false);
|
||||||
|
|
||||||
} else if (this.optionalOperator('?.')) {
|
} else if (this.optionalOperator('?.')) {
|
||||||
result = this.parseAccessMemberOrMethodCall(result, true);
|
result = this.parseAccessMemberOrMethodCall(result, true);
|
||||||
|
|
||||||
} else if (this.optionalCharacter($LBRACKET)) {
|
} else if (this.optionalCharacter(chars.$LBRACKET)) {
|
||||||
var key = this.parsePipe();
|
var key = this.parsePipe();
|
||||||
this.expectCharacter($RBRACKET);
|
this.expectCharacter(chars.$RBRACKET);
|
||||||
if (this.optionalOperator('=')) {
|
if (this.optionalOperator('=')) {
|
||||||
var value = this.parseConditional();
|
var value = this.parseConditional();
|
||||||
result = new KeyedWrite(result, key, value);
|
result = new KeyedWrite(result, key, value);
|
||||||
|
@ -437,9 +438,9 @@ export class _ParseAST {
|
||||||
result = new KeyedRead(result, key);
|
result = new KeyedRead(result, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (this.optionalCharacter($LPAREN)) {
|
} else if (this.optionalCharacter(chars.$LPAREN)) {
|
||||||
var args = this.parseCallArguments();
|
var args = this.parseCallArguments();
|
||||||
this.expectCharacter($RPAREN);
|
this.expectCharacter(chars.$RPAREN);
|
||||||
result = new FunctionCall(result, args);
|
result = new FunctionCall(result, args);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -449,9 +450,9 @@ export class _ParseAST {
|
||||||
}
|
}
|
||||||
|
|
||||||
parsePrimary(): AST {
|
parsePrimary(): AST {
|
||||||
if (this.optionalCharacter($LPAREN)) {
|
if (this.optionalCharacter(chars.$LPAREN)) {
|
||||||
let result = this.parsePipe();
|
let result = this.parsePipe();
|
||||||
this.expectCharacter($RPAREN);
|
this.expectCharacter(chars.$RPAREN);
|
||||||
return result;
|
return result;
|
||||||
} else if (this.next.isKeywordNull() || this.next.isKeywordUndefined()) {
|
} else if (this.next.isKeywordNull() || this.next.isKeywordUndefined()) {
|
||||||
this.advance();
|
this.advance();
|
||||||
|
@ -465,12 +466,12 @@ export class _ParseAST {
|
||||||
this.advance();
|
this.advance();
|
||||||
return new LiteralPrimitive(false);
|
return new LiteralPrimitive(false);
|
||||||
|
|
||||||
} else if (this.optionalCharacter($LBRACKET)) {
|
} else if (this.optionalCharacter(chars.$LBRACKET)) {
|
||||||
var elements = this.parseExpressionList($RBRACKET);
|
var elements = this.parseExpressionList(chars.$RBRACKET);
|
||||||
this.expectCharacter($RBRACKET);
|
this.expectCharacter(chars.$RBRACKET);
|
||||||
return new LiteralArray(elements);
|
return new LiteralArray(elements);
|
||||||
|
|
||||||
} else if (this.next.isCharacter($LBRACE)) {
|
} else if (this.next.isCharacter(chars.$LBRACE)) {
|
||||||
return this.parseLiteralMap();
|
return this.parseLiteralMap();
|
||||||
|
|
||||||
} else if (this.next.isIdentifier()) {
|
} else if (this.next.isIdentifier()) {
|
||||||
|
@ -501,7 +502,7 @@ export class _ParseAST {
|
||||||
if (!this.next.isCharacter(terminator)) {
|
if (!this.next.isCharacter(terminator)) {
|
||||||
do {
|
do {
|
||||||
result.push(this.parsePipe());
|
result.push(this.parsePipe());
|
||||||
} while (this.optionalCharacter($COMMA));
|
} while (this.optionalCharacter(chars.$COMMA));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -509,15 +510,15 @@ export class _ParseAST {
|
||||||
parseLiteralMap(): LiteralMap {
|
parseLiteralMap(): LiteralMap {
|
||||||
var keys: string[] = [];
|
var keys: string[] = [];
|
||||||
var values: AST[] = [];
|
var values: AST[] = [];
|
||||||
this.expectCharacter($LBRACE);
|
this.expectCharacter(chars.$LBRACE);
|
||||||
if (!this.optionalCharacter($RBRACE)) {
|
if (!this.optionalCharacter(chars.$RBRACE)) {
|
||||||
do {
|
do {
|
||||||
var key = this.expectIdentifierOrKeywordOrString();
|
var key = this.expectIdentifierOrKeywordOrString();
|
||||||
keys.push(key);
|
keys.push(key);
|
||||||
this.expectCharacter($COLON);
|
this.expectCharacter(chars.$COLON);
|
||||||
values.push(this.parsePipe());
|
values.push(this.parsePipe());
|
||||||
} while (this.optionalCharacter($COMMA));
|
} while (this.optionalCharacter(chars.$COMMA));
|
||||||
this.expectCharacter($RBRACE);
|
this.expectCharacter(chars.$RBRACE);
|
||||||
}
|
}
|
||||||
return new LiteralMap(keys, values);
|
return new LiteralMap(keys, values);
|
||||||
}
|
}
|
||||||
|
@ -525,9 +526,9 @@ export class _ParseAST {
|
||||||
parseAccessMemberOrMethodCall(receiver: AST, isSafe: boolean = false): AST {
|
parseAccessMemberOrMethodCall(receiver: AST, isSafe: boolean = false): AST {
|
||||||
let id = this.expectIdentifierOrKeyword();
|
let id = this.expectIdentifierOrKeyword();
|
||||||
|
|
||||||
if (this.optionalCharacter($LPAREN)) {
|
if (this.optionalCharacter(chars.$LPAREN)) {
|
||||||
let args = this.parseCallArguments();
|
let args = this.parseCallArguments();
|
||||||
this.expectCharacter($RPAREN);
|
this.expectCharacter(chars.$RPAREN);
|
||||||
return isSafe ? new SafeMethodCall(receiver, id, args) : new MethodCall(receiver, id, args);
|
return isSafe ? new SafeMethodCall(receiver, id, args) : new MethodCall(receiver, id, args);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -555,11 +556,11 @@ export class _ParseAST {
|
||||||
}
|
}
|
||||||
|
|
||||||
parseCallArguments(): BindingPipe[] {
|
parseCallArguments(): BindingPipe[] {
|
||||||
if (this.next.isCharacter($RPAREN)) return [];
|
if (this.next.isCharacter(chars.$RPAREN)) return [];
|
||||||
var positionals: AST[] = [];
|
var positionals: AST[] = [];
|
||||||
do {
|
do {
|
||||||
positionals.push(this.parsePipe());
|
positionals.push(this.parsePipe());
|
||||||
} while (this.optionalCharacter($COMMA));
|
} while (this.optionalCharacter(chars.$COMMA));
|
||||||
return positionals as BindingPipe[];
|
return positionals as BindingPipe[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,7 +606,7 @@ export class _ParseAST {
|
||||||
key = prefix + key[0].toUpperCase() + key.substring(1);
|
key = prefix + key[0].toUpperCase() + key.substring(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.optionalCharacter($COLON);
|
this.optionalCharacter(chars.$COLON);
|
||||||
var name: string = null;
|
var name: string = null;
|
||||||
var expression: ASTWithSource = null;
|
var expression: ASTWithSource = null;
|
||||||
if (keyIsVar) {
|
if (keyIsVar) {
|
||||||
|
@ -623,8 +624,8 @@ export class _ParseAST {
|
||||||
expression = new ASTWithSource(ast, source, this.location);
|
expression = new ASTWithSource(ast, source, this.location);
|
||||||
}
|
}
|
||||||
bindings.push(new TemplateBinding(key, keyIsVar, name, expression));
|
bindings.push(new TemplateBinding(key, keyIsVar, name, expression));
|
||||||
if (!this.optionalCharacter($SEMICOLON)) {
|
if (!this.optionalCharacter(chars.$SEMICOLON)) {
|
||||||
this.optionalCharacter($COMMA);
|
this.optionalCharacter(chars.$COMMA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new TemplateBindingParseResult(bindings, warnings);
|
return new TemplateBindingParseResult(bindings, warnings);
|
||||||
|
|
|
@ -266,13 +266,13 @@ class _HtmlTokenizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _attemptCharCodeUntilFn(predicate: Function) {
|
private _attemptCharCodeUntilFn(predicate: (code: number) => boolean) {
|
||||||
while (!predicate(this._peek)) {
|
while (!predicate(this._peek)) {
|
||||||
this._advance();
|
this._advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _requireCharCodeUntilFn(predicate: Function, len: number) {
|
private _requireCharCodeUntilFn(predicate: (code: number) => boolean, len: number) {
|
||||||
var start = this._getLocation();
|
var start = this._getLocation();
|
||||||
this._attemptCharCodeUntilFn(predicate);
|
this._attemptCharCodeUntilFn(predicate);
|
||||||
if (this._index - start.offset < len) {
|
if (this._index - start.offset < len) {
|
||||||
|
@ -402,7 +402,7 @@ class _HtmlTokenizer {
|
||||||
let savedPos = this._savePosition();
|
let savedPos = this._savePosition();
|
||||||
let lowercaseTagName: string;
|
let lowercaseTagName: string;
|
||||||
try {
|
try {
|
||||||
if (!isAsciiLetter(this._peek)) {
|
if (!chars.isAsciiLetter(this._peek)) {
|
||||||
throw this._createError(unexpectedCharacterErrorMsg(this._peek), this._getSpan());
|
throw this._createError(unexpectedCharacterErrorMsg(this._peek), this._getSpan());
|
||||||
}
|
}
|
||||||
var nameStart = this._index;
|
var nameStart = this._index;
|
||||||
|
@ -635,16 +635,12 @@ class _HtmlTokenizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNotWhitespace(code: number): boolean {
|
function isNotWhitespace(code: number): boolean {
|
||||||
return !isWhitespace(code) || code === chars.$EOF;
|
return !chars.isWhitespace(code) || code === chars.$EOF;
|
||||||
}
|
|
||||||
|
|
||||||
function isWhitespace(code: number): boolean {
|
|
||||||
return (code >= chars.$TAB && code <= chars.$SPACE) || (code === chars.$NBSP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNameEnd(code: number): boolean {
|
function isNameEnd(code: number): boolean {
|
||||||
return isWhitespace(code) || code === chars.$GT || code === chars.$SLASH || code === chars.$SQ ||
|
return chars.isWhitespace(code) || code === chars.$GT || code === chars.$SLASH ||
|
||||||
code === chars.$DQ || code === chars.$EQ;
|
code === chars.$SQ || code === chars.$DQ || code === chars.$EQ;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPrefixEnd(code: number): boolean {
|
function isPrefixEnd(code: number): boolean {
|
||||||
|
@ -653,11 +649,11 @@ function isPrefixEnd(code: number): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDigitEntityEnd(code: number): boolean {
|
function isDigitEntityEnd(code: number): boolean {
|
||||||
return code == chars.$SEMICOLON || code == chars.$EOF || !isAsciiHexDigit(code);
|
return code == chars.$SEMICOLON || code == chars.$EOF || !chars.isAsciiHexDigit(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNamedEntityEnd(code: number): boolean {
|
function isNamedEntityEnd(code: number): boolean {
|
||||||
return code == chars.$SEMICOLON || code == chars.$EOF || !isAsciiLetter(code);
|
return code == chars.$SEMICOLON || code == chars.$EOF || !chars.isAsciiLetter(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isExpansionFormStart(input: string, offset: number, interpolationStart: string): boolean {
|
function isExpansionFormStart(input: string, offset: number, interpolationStart: string): boolean {
|
||||||
|
@ -668,16 +664,7 @@ function isExpansionFormStart(input: string, offset: number, interpolationStart:
|
||||||
}
|
}
|
||||||
|
|
||||||
function isExpansionCaseStart(peek: number): boolean {
|
function isExpansionCaseStart(peek: number): boolean {
|
||||||
return peek === chars.$EQ || isAsciiLetter(peek);
|
return peek === chars.$EQ || chars.isAsciiLetter(peek);
|
||||||
}
|
|
||||||
|
|
||||||
function isAsciiLetter(code: number): boolean {
|
|
||||||
return code >= chars.$a && code <= chars.$z || code >= chars.$A && code <= chars.$Z;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAsciiHexDigit(code: number): boolean {
|
|
||||||
return code >= chars.$a && code <= chars.$f || code >= chars.$A && code <= chars.$F ||
|
|
||||||
code >= chars.$0 && code <= chars.$9;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function compareCharCodeCaseInsensitive(code1: number, code2: number): boolean {
|
function compareCharCodeCaseInsensitive(code1: number, code2: number): boolean {
|
||||||
|
|
Loading…
Reference in New Issue