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