refactor(Lexer): refactor scanComplexOperator()

This commit is contained in:
Victor Berchet 2015-06-05 14:47:48 +02:00
parent d1b35f9174
commit 79f3f3b456
1 changed files with 62 additions and 38 deletions

View File

@ -1,6 +1,12 @@
import {Injectable} from 'angular2/src/di/decorators';
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_IDENTIFIER = 2;
@ -24,10 +30,10 @@ export class Lexer {
}
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) {}
isCharacter(code: int): boolean {
isCharacter(code: number): boolean {
return (this.type == TOKEN_TYPE_CHARACTER && this.numValue == code);
}
@ -63,7 +69,7 @@ export class Token {
}
toString(): string {
var t: int = this.type;
var t: number = this.type;
if (t >= TOKEN_TYPE_CHARACTER && t <= TOKEN_TYPE_STRING) {
return this.strValue;
} 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));
}
function newIdentifierToken(index: int, text: string): Token {
function newIdentifierToken(index: number, text: string): Token {
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);
}
function newOperatorToken(index: int, text: string): Token {
function newOperatorToken(index: number, text: string): Token {
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);
}
function newNumberToken(index: int, n: number): Token {
function newNumberToken(index: number, n: number): Token {
return new Token(index, TOKEN_TYPE_NUMBER, n, "");
}
@ -160,9 +166,9 @@ export class ScannerError extends BaseException {
}
class _Scanner {
length: int;
peek: int;
index: int;
length: number;
peek: number;
index: number;
constructor(public input: string) {
this.length = input.length;
@ -200,7 +206,7 @@ class _Scanner {
if (isIdentifierStart(peek)) return this.scanIdentifier();
if (isDigit(peek)) return this.scanNumber(index);
var start: int = index;
var start: number = index;
switch (peek) {
case $PERIOD:
this.advance();
@ -227,16 +233,18 @@ class _Scanner {
case $CARET:
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
case $QUESTION:
return this.scanComplexOperator(start, $PERIOD, '?', '.');
return this.scanComplexOperator(start, '?', $PERIOD, '.');
case $LT:
case $GT:
return this.scanComplexOperator(start, StringWrapper.fromCharCode(peek), $EQ, '=');
case $BANG:
case $EQ:
return this.scanComplexOperator(start, $EQ, StringWrapper.fromCharCode(peek), '=');
return this.scanComplexOperator(start, StringWrapper.fromCharCode(peek), $EQ, '=', $EQ,
'=');
case $AMPERSAND:
return this.scanComplexOperator(start, $AMPERSAND, '&', '&');
return this.scanComplexOperator(start, '&', $AMPERSAND, '&');
case $BAR:
return this.scanComplexOperator(start, $BAR, '|', '|');
return this.scanComplexOperator(start, '|', $BAR, '|');
case $NBSP:
while (isWhitespace(this.peek)) this.advance();
return this.scanToken();
@ -246,35 +254,51 @@ class _Scanner {
return null;
}
scanCharacter(start: int, code: int): Token {
scanCharacter(start: number, code: number): Token {
assert(this.peek == code);
this.advance();
return newCharacterToken(start, code);
}
scanOperator(start: int, str: string): Token {
scanOperator(start: number, str: string): Token {
assert(this.peek == StringWrapper.charCodeAt(str, 0));
assert(SetWrapper.has(OPERATORS, str));
this.advance();
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));
this.advance();
var str: string = one;
while (this.peek == code) {
if (this.peek == twoCode) {
this.advance();
str += two;
}
if (isPresent(threeCode) && this.peek == threeCode) {
this.advance();
str += three;
}
assert(SetWrapper.has(OPERATORS, str));
return newOperatorToken(start, str);
}
scanIdentifier(): Token {
assert(isIdentifierStart(this.peek));
var start: int = this.index;
var start: number = this.index;
this.advance();
while (isIdentifierPart(this.peek)) this.advance();
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));
var simple: boolean = (this.index === start);
this.advance(); // Skip initial digit.
@ -313,12 +337,12 @@ class _Scanner {
scanString(): Token {
assert(this.peek == $SQ || this.peek == $DQ);
var start: int = this.index;
var quote: int = this.peek;
var start: number = this.index;
var quote: number = this.peek;
this.advance(); // Skip initial quote.
var buffer: StringJoiner;
var marker: int = this.index;
var marker: number = this.index;
var input: string = this.input;
while (this.peek != quote) {
@ -326,7 +350,7 @@ class _Scanner {
if (buffer == null) buffer = new StringJoiner();
buffer.add(input.substring(marker, this.index));
this.advance();
var unescapedCode: int;
var unescapedCode: number;
if (this.peek == $u) {
// 4 character hex code for unicode character.
var hex: string = input.substring(this.index + 1, this.index + 5);
@ -335,7 +359,7 @@ class _Scanner {
} catch (e) {
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();
}
} else {
@ -363,39 +387,39 @@ class _Scanner {
return newStringToken(start, unescaped);
}
error(message: string, offset: int) {
var position: int = this.index + offset;
error(message: string, offset: number) {
var position: number = this.index + offset;
throw new ScannerError(
`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);
}
function isIdentifierStart(code: int): boolean {
function isIdentifierStart(code: number): boolean {
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) ||
(code == $_) || (code == $$);
}
function isDigit(code: int): boolean {
function isDigit(code: number): boolean {
return $0 <= code && code <= $9;
}
function isExponentStart(code: int): boolean {
function isExponentStart(code: number): boolean {
return code == $e || code == $E;
}
function isExponentSign(code: int): boolean {
function isExponentSign(code: number): boolean {
return code == $MINUS || code == $PLUS;
}
function unescape(code: int): int {
function unescape(code: number): number {
switch (code) {
case $n:
return $LF;