refactor(Lexer): refactor scanComplexOperator()

This commit is contained in:
Victor Berchet
2015-06-05 14:47:48 +02:00
parent d1b35f9174
commit 79f3f3b456

View File

@ -1,6 +1,12 @@
import {Injectable} from 'angular2/src/di/decorators'; import {Injectable} from 'angular2/src/di/decorators';
import {List, ListWrapper, SetWrapper} from "angular2/src/facade/collection"; import {List, ListWrapper, SetWrapper} from "angular2/src/facade/collection";
import {NumberWrapper, StringJoiner, StringWrapper, BaseException} from "angular2/src/facade/lang"; import {
NumberWrapper,
StringJoiner,
StringWrapper,
BaseException,
isPresent
} from "angular2/src/facade/lang";
export const TOKEN_TYPE_CHARACTER = 1; export const TOKEN_TYPE_CHARACTER = 1;
export const TOKEN_TYPE_IDENTIFIER = 2; export const TOKEN_TYPE_IDENTIFIER = 2;
@ -24,10 +30,10 @@ export class Lexer {
} }
export class Token { export class Token {
constructor(public index: int, public type: int, public numValue: number, constructor(public index: number, public type: number, public numValue: number,
public strValue: string) {} public strValue: string) {}
isCharacter(code: int): boolean { isCharacter(code: number): boolean {
return (this.type == TOKEN_TYPE_CHARACTER && this.numValue == code); return (this.type == TOKEN_TYPE_CHARACTER && this.numValue == code);
} }
@ -63,7 +69,7 @@ export class Token {
} }
toString(): string { toString(): string {
var t: int = this.type; var t: number = this.type;
if (t >= TOKEN_TYPE_CHARACTER && t <= TOKEN_TYPE_STRING) { if (t >= TOKEN_TYPE_CHARACTER && t <= TOKEN_TYPE_STRING) {
return this.strValue; return this.strValue;
} else if (t == TOKEN_TYPE_NUMBER) { } else if (t == TOKEN_TYPE_NUMBER) {
@ -74,27 +80,27 @@ export class Token {
} }
} }
function newCharacterToken(index: int, code: int): Token { function newCharacterToken(index: number, code: number): Token {
return new Token(index, TOKEN_TYPE_CHARACTER, code, StringWrapper.fromCharCode(code)); return new Token(index, TOKEN_TYPE_CHARACTER, code, StringWrapper.fromCharCode(code));
} }
function newIdentifierToken(index: int, text: string): Token { function newIdentifierToken(index: number, text: string): Token {
return new Token(index, TOKEN_TYPE_IDENTIFIER, 0, text); return new Token(index, TOKEN_TYPE_IDENTIFIER, 0, text);
} }
function newKeywordToken(index: int, text: string): Token { function newKeywordToken(index: number, text: string): Token {
return new Token(index, TOKEN_TYPE_KEYWORD, 0, text); return new Token(index, TOKEN_TYPE_KEYWORD, 0, text);
} }
function newOperatorToken(index: int, text: string): Token { function newOperatorToken(index: number, text: string): Token {
return new Token(index, TOKEN_TYPE_OPERATOR, 0, text); return new Token(index, TOKEN_TYPE_OPERATOR, 0, text);
} }
function newStringToken(index: int, text: string): Token { function newStringToken(index: number, text: string): Token {
return new Token(index, TOKEN_TYPE_STRING, 0, text); return new Token(index, TOKEN_TYPE_STRING, 0, text);
} }
function newNumberToken(index: int, n: number): Token { function newNumberToken(index: number, n: number): Token {
return new Token(index, TOKEN_TYPE_NUMBER, n, ""); return new Token(index, TOKEN_TYPE_NUMBER, n, "");
} }
@ -160,9 +166,9 @@ export class ScannerError extends BaseException {
} }
class _Scanner { class _Scanner {
length: int; length: number;
peek: int; peek: number;
index: int; index: number;
constructor(public input: string) { constructor(public input: string) {
this.length = input.length; this.length = input.length;
@ -200,7 +206,7 @@ class _Scanner {
if (isIdentifierStart(peek)) return this.scanIdentifier(); if (isIdentifierStart(peek)) return this.scanIdentifier();
if (isDigit(peek)) return this.scanNumber(index); if (isDigit(peek)) return this.scanNumber(index);
var start: int = index; var start: number = index;
switch (peek) { switch (peek) {
case $PERIOD: case $PERIOD:
this.advance(); this.advance();
@ -227,16 +233,18 @@ class _Scanner {
case $CARET: case $CARET:
return this.scanOperator(start, StringWrapper.fromCharCode(peek)); return this.scanOperator(start, StringWrapper.fromCharCode(peek));
case $QUESTION: case $QUESTION:
return this.scanComplexOperator(start, $PERIOD, '?', '.'); return this.scanComplexOperator(start, '?', $PERIOD, '.');
case $LT: case $LT:
case $GT: case $GT:
return this.scanComplexOperator(start, StringWrapper.fromCharCode(peek), $EQ, '=');
case $BANG: case $BANG:
case $EQ: case $EQ:
return this.scanComplexOperator(start, $EQ, StringWrapper.fromCharCode(peek), '='); return this.scanComplexOperator(start, StringWrapper.fromCharCode(peek), $EQ, '=', $EQ,
'=');
case $AMPERSAND: case $AMPERSAND:
return this.scanComplexOperator(start, $AMPERSAND, '&', '&'); return this.scanComplexOperator(start, '&', $AMPERSAND, '&');
case $BAR: case $BAR:
return this.scanComplexOperator(start, $BAR, '|', '|'); return this.scanComplexOperator(start, '|', $BAR, '|');
case $NBSP: case $NBSP:
while (isWhitespace(this.peek)) this.advance(); while (isWhitespace(this.peek)) this.advance();
return this.scanToken(); return this.scanToken();
@ -246,35 +254,51 @@ class _Scanner {
return null; return null;
} }
scanCharacter(start: int, code: int): Token { scanCharacter(start: number, code: number): Token {
assert(this.peek == code); assert(this.peek == code);
this.advance(); this.advance();
return newCharacterToken(start, code); return newCharacterToken(start, code);
} }
scanOperator(start: int, str: string): Token { scanOperator(start: number, str: string): Token {
assert(this.peek == StringWrapper.charCodeAt(str, 0)); assert(this.peek == StringWrapper.charCodeAt(str, 0));
assert(SetWrapper.has(OPERATORS, str)); assert(SetWrapper.has(OPERATORS, str));
this.advance(); this.advance();
return newOperatorToken(start, str); return newOperatorToken(start, str);
} }
scanComplexOperator(start: int, code: int, one: string, two: string): Token { /**
* Tokenize a 2/3 char long operator
*
* @param start start index in the expression
* @param one first symbol (always part of the operator)
* @param twoCode code point for the second symbol
* @param two second symbol (part of the operator when the second code point matches)
* @param threeCode code point for the third symbol
* @param three third symbol (part of the operator when provided and matches source expression)
* @returns {Token}
*/
scanComplexOperator(start: number, one: string, twoCode: number, two: string, threeCode?: number,
three?: string): Token {
assert(this.peek == StringWrapper.charCodeAt(one, 0)); assert(this.peek == StringWrapper.charCodeAt(one, 0));
this.advance(); this.advance();
var str: string = one; var str: string = one;
while (this.peek == code) { if (this.peek == twoCode) {
this.advance(); this.advance();
str += two; str += two;
} }
if (isPresent(threeCode) && this.peek == threeCode) {
this.advance();
str += three;
}
assert(SetWrapper.has(OPERATORS, str)); assert(SetWrapper.has(OPERATORS, str));
return newOperatorToken(start, str); return newOperatorToken(start, str);
} }
scanIdentifier(): Token { scanIdentifier(): Token {
assert(isIdentifierStart(this.peek)); assert(isIdentifierStart(this.peek));
var start: int = this.index; var start: number = this.index;
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);
@ -285,7 +309,7 @@ class _Scanner {
} }
} }
scanNumber(start: int): Token { scanNumber(start: number): Token {
assert(isDigit(this.peek)); assert(isDigit(this.peek));
var simple: boolean = (this.index === start); var simple: boolean = (this.index === start);
this.advance(); // Skip initial digit. this.advance(); // Skip initial digit.
@ -313,12 +337,12 @@ class _Scanner {
scanString(): Token { scanString(): Token {
assert(this.peek == $SQ || this.peek == $DQ); assert(this.peek == $SQ || this.peek == $DQ);
var start: int = this.index; var start: number = this.index;
var quote: int = this.peek; var quote: number = this.peek;
this.advance(); // Skip initial quote. this.advance(); // Skip initial quote.
var buffer: StringJoiner; var buffer: StringJoiner;
var marker: int = this.index; var marker: number = this.index;
var input: string = this.input; var input: string = this.input;
while (this.peek != quote) { while (this.peek != quote) {
@ -326,7 +350,7 @@ class _Scanner {
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: int; var unescapedCode: number;
if (this.peek == $u) { if (this.peek == $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);
@ -335,7 +359,7 @@ class _Scanner {
} catch (e) { } catch (e) {
this.error(`Invalid unicode escape [\\u${hex}]`, 0); this.error(`Invalid unicode escape [\\u${hex}]`, 0);
} }
for (var i: int = 0; i < 5; i++) { for (var i: number = 0; i < 5; i++) {
this.advance(); this.advance();
} }
} else { } else {
@ -363,39 +387,39 @@ class _Scanner {
return newStringToken(start, unescaped); return newStringToken(start, unescaped);
} }
error(message: string, offset: int) { error(message: string, offset: number) {
var position: int = this.index + offset; var position: number = this.index + offset;
throw new ScannerError( throw new ScannerError(
`Lexer Error: ${message} at column ${position} in expression [${this.input}]`); `Lexer Error: ${message} at column ${position} in expression [${this.input}]`);
} }
} }
function isWhitespace(code: int): boolean { function isWhitespace(code: number): boolean {
return (code >= $TAB && code <= $SPACE) || (code == $NBSP); return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
} }
function isIdentifierStart(code: int): boolean { function isIdentifierStart(code: number): boolean {
return ($a <= code && code <= $z) || ($A <= code && code <= $Z) || (code == $_) || (code == $$); return ($a <= code && code <= $z) || ($A <= code && code <= $Z) || (code == $_) || (code == $$);
} }
function isIdentifierPart(code: int): boolean { function isIdentifierPart(code: number): boolean {
return ($a <= code && code <= $z) || ($A <= code && code <= $Z) || ($0 <= code && code <= $9) || return ($a <= code && code <= $z) || ($A <= code && code <= $Z) || ($0 <= code && code <= $9) ||
(code == $_) || (code == $$); (code == $_) || (code == $$);
} }
function isDigit(code: int): boolean { function isDigit(code: number): boolean {
return $0 <= code && code <= $9; return $0 <= code && code <= $9;
} }
function isExponentStart(code: int): boolean { function isExponentStart(code: number): boolean {
return code == $e || code == $E; return code == $e || code == $E;
} }
function isExponentSign(code: int): boolean { function isExponentSign(code: number): boolean {
return code == $MINUS || code == $PLUS; return code == $MINUS || code == $PLUS;
} }
function unescape(code: int): int { function unescape(code: number): number {
switch (code) { switch (code) {
case $n: case $n:
return $LF; return $LF;