提交 1139f8bb 编写于 作者: M Martin Aeschlimann

Fixes #3048 [css] Uncaught RangeError: Maximum call stack size exceeded

上级 5598ee09
...@@ -46,6 +46,7 @@ export enum TokenType { ...@@ -46,6 +46,7 @@ export enum TokenType {
EscapedJavaScript, EscapedJavaScript,
BadEscapedJavaScript, BadEscapedJavaScript,
Comment,
SingleLineComment, SingleLineComment,
EOF, EOF,
CustomToken CustomToken
...@@ -115,7 +116,7 @@ export class MultiLineStream { ...@@ -115,7 +116,7 @@ export class MultiLineStream {
} }
public advanceIfChars(ch:number[]):boolean { public advanceIfChars(ch:number[]):boolean {
var i:number; let i:number;
if (this.position + ch.length > this.source.length) { if (this.position + ch.length > this.source.length) {
return false; return false;
} }
...@@ -129,7 +130,7 @@ export class MultiLineStream { ...@@ -129,7 +130,7 @@ export class MultiLineStream {
} }
public advanceWhileChar(condition:(ch:number)=>boolean):number { public advanceWhileChar(condition:(ch:number)=>boolean):number {
var posNow = this.position; let posNow = this.position;
while (this.position < this.len && condition(this.source.charCodeAt(this.position))) { while (this.position < this.len && condition(this.source.charCodeAt(this.position))) {
this.position++; this.position++;
} }
...@@ -137,66 +138,66 @@ export class MultiLineStream { ...@@ -137,66 +138,66 @@ export class MultiLineStream {
} }
} }
var _a = 'a'.charCodeAt(0); const _a = 'a'.charCodeAt(0);
var _e = 'e'.charCodeAt(0); const _e = 'e'.charCodeAt(0);
var _f = 'f'.charCodeAt(0); const _f = 'f'.charCodeAt(0);
var _i = 'i'.charCodeAt(0); const _i = 'i'.charCodeAt(0);
var _l = 'l'.charCodeAt(0); const _l = 'l'.charCodeAt(0);
var _p = 'p'.charCodeAt(0); const _p = 'p'.charCodeAt(0);
var _r = 'r'.charCodeAt(0); const _r = 'r'.charCodeAt(0);
var _u = 'u'.charCodeAt(0); const _u = 'u'.charCodeAt(0);
var _x = 'x'.charCodeAt(0); const _x = 'x'.charCodeAt(0);
var _z = 'z'.charCodeAt(0); const _z = 'z'.charCodeAt(0);
var _A = 'A'.charCodeAt(0); const _A = 'A'.charCodeAt(0);
var _E = 'E'.charCodeAt(0); const _E = 'E'.charCodeAt(0);
var _F = 'F'.charCodeAt(0); const _F = 'F'.charCodeAt(0);
var _I = 'I'.charCodeAt(0); const _I = 'I'.charCodeAt(0);
var _L = 'L'.charCodeAt(0); const _L = 'L'.charCodeAt(0);
var _P = 'P'.charCodeAt(0); const _P = 'P'.charCodeAt(0);
var _R = 'R'.charCodeAt(0); const _R = 'R'.charCodeAt(0);
var _U = 'U'.charCodeAt(0); const _U = 'U'.charCodeAt(0);
var _X = 'X'.charCodeAt(0); const _X = 'X'.charCodeAt(0);
var _Z = 'Z'.charCodeAt(0); const _Z = 'Z'.charCodeAt(0);
var _0 = '0'.charCodeAt(0); const _0 = '0'.charCodeAt(0);
var _9 = '9'.charCodeAt(0); const _9 = '9'.charCodeAt(0);
var _TLD = '~'.charCodeAt(0); const _TLD = '~'.charCodeAt(0);
var _HAT = '^'.charCodeAt(0); const _HAT = '^'.charCodeAt(0);
var _EQS = '='.charCodeAt(0); const _EQS = '='.charCodeAt(0);
var _PIP = '|'.charCodeAt(0); const _PIP = '|'.charCodeAt(0);
var _MIN = '-'.charCodeAt(0); const _MIN = '-'.charCodeAt(0);
var _USC = '_'.charCodeAt(0); const _USC = '_'.charCodeAt(0);
var _PRC = '%'.charCodeAt(0); const _PRC = '%'.charCodeAt(0);
var _MUL = '*'.charCodeAt(0); const _MUL = '*'.charCodeAt(0);
var _LPA = '('.charCodeAt(0); const _LPA = '('.charCodeAt(0);
var _RPA = ')'.charCodeAt(0); const _RPA = ')'.charCodeAt(0);
var _LAN = '<'.charCodeAt(0); const _LAN = '<'.charCodeAt(0);
var _RAN = '>'.charCodeAt(0); const _RAN = '>'.charCodeAt(0);
var _ATS = '@'.charCodeAt(0); const _ATS = '@'.charCodeAt(0);
var _HSH = '#'.charCodeAt(0); const _HSH = '#'.charCodeAt(0);
var _DLR = '$'.charCodeAt(0); const _DLR = '$'.charCodeAt(0);
var _BSL = '\\'.charCodeAt(0); const _BSL = '\\'.charCodeAt(0);
var _FSL = '/'.charCodeAt(0); const _FSL = '/'.charCodeAt(0);
var _NWL = '\n'.charCodeAt(0); const _NWL = '\n'.charCodeAt(0);
var _CAR = '\r'.charCodeAt(0); const _CAR = '\r'.charCodeAt(0);
var _LFD = '\f'.charCodeAt(0); const _LFD = '\f'.charCodeAt(0);
var _DQO = '"'.charCodeAt(0); const _DQO = '"'.charCodeAt(0);
var _SQO = '\''.charCodeAt(0); const _SQO = '\''.charCodeAt(0);
var _WSP = ' '.charCodeAt(0); const _WSP = ' '.charCodeAt(0);
var _TAB = '\t'.charCodeAt(0); const _TAB = '\t'.charCodeAt(0);
var _SEM = ';'.charCodeAt(0); const _SEM = ';'.charCodeAt(0);
var _COL = ':'.charCodeAt(0); const _COL = ':'.charCodeAt(0);
var _CUL = '{'.charCodeAt(0); const _CUL = '{'.charCodeAt(0);
var _CUR = '}'.charCodeAt(0); const _CUR = '}'.charCodeAt(0);
var _BRL = '['.charCodeAt(0); const _BRL = '['.charCodeAt(0);
var _BRR = ']'.charCodeAt(0); const _BRR = ']'.charCodeAt(0);
var _CMA = ','.charCodeAt(0); const _CMA = ','.charCodeAt(0);
var _DOT = '.'.charCodeAt(0); const _DOT = '.'.charCodeAt(0);
var _BNG = '!'.charCodeAt(0); const _BNG = '!'.charCodeAt(0);
var _url = [_u, _U, _r, _R, _l, _L, _LPA, _LPA]; const _url = [_u, _U, _r, _R, _l, _L, _LPA, _LPA];
var _url_prefix = [_u, _U, _r, _R, _l, _L, _MIN, _MIN, _p, _P, _r, _R, _e, _E, _f, _F, _i, _I, _x, _X, _LPA, _LPA]; const _url_prefix = [_u, _U, _r, _R, _l, _L, _MIN, _MIN, _p, _P, _r, _R, _e, _E, _f, _F, _i, _I, _x, _X, _LPA, _LPA];
var staticTokenTable:{[code:number]:TokenType;} = {}; const staticTokenTable:{[code:number]:TokenType;} = {};
staticTokenTable[_SEM] = TokenType.SemiColon; staticTokenTable[_SEM] = TokenType.SemiColon;
staticTokenTable[_COL] = TokenType.Colon; staticTokenTable[_COL] = TokenType.Colon;
staticTokenTable[_CUL] = TokenType.CurlyL; staticTokenTable[_CUL] = TokenType.CurlyL;
...@@ -207,7 +208,7 @@ staticTokenTable[_LPA] = TokenType.ParenthesisL; ...@@ -207,7 +208,7 @@ staticTokenTable[_LPA] = TokenType.ParenthesisL;
staticTokenTable[_RPA] = TokenType.ParenthesisR; staticTokenTable[_RPA] = TokenType.ParenthesisR;
staticTokenTable[_CMA] = TokenType.Comma; staticTokenTable[_CMA] = TokenType.Comma;
var staticUnitTable:{[code:number]:TokenType;} = {}; const staticUnitTable:{[code:number]:TokenType;} = {};
staticUnitTable['em'] = TokenType.EMS; staticUnitTable['em'] = TokenType.EMS;
staticUnitTable['ex'] = TokenType.EXS; staticUnitTable['ex'] = TokenType.EXS;
staticUnitTable['px'] = TokenType.Length; staticUnitTable['px'] = TokenType.Length;
...@@ -231,16 +232,19 @@ export class Scanner { ...@@ -231,16 +232,19 @@ export class Scanner {
public stream: MultiLineStream; public stream: MultiLineStream;
public ignoreComment = true; public ignoreComment = true;
public ignoreWhitespace = true;
public setSource(input: string): void { public setSource(input: string): void {
this.stream = new MultiLineStream(input); this.stream = new MultiLineStream(input);
} }
public finishToken(token: IToken, type: TokenType, text?: string): IToken { public finishToken(offset: number, type: TokenType, text?: string): IToken {
token.len = this.stream.pos() - token.offset; return {
token.type = type; offset: offset,
token.text = text || this.stream.substring(token.offset); len: this.stream.pos() - offset,
return token; type: type,
text: text || this.stream.substring(offset)
};
} }
public substring(offset:number, len:number):string { public substring(offset:number, len:number):string {
...@@ -255,71 +259,51 @@ export class Scanner { ...@@ -255,71 +259,51 @@ export class Scanner {
this.stream.goBackTo(pos); this.stream.goBackTo(pos);
} }
public scan(ignoreWhitespace:boolean=true): IToken { public scan(): IToken {
// processes all whitespaces and comments
var result:IToken = { let triviaToken = this.trivia();
type: undefined, if (triviaToken !== null) {
text: undefined, return triviaToken;
offset: this.stream.pos(),
len: 0
};
// Whitespace - if asked for
if (this._whitespace()) {
if (!ignoreWhitespace) {
return this.finishToken(result, TokenType.Whitespace);
} else {
return this.scan(ignoreWhitespace);
} }
}
let tokenType: TokenType = void 0;
// Comment - CSS let offset = this.stream.pos();
if (this._comment()) {
if (!this.ignoreComment) {
return this.finishToken(result, tokenType);
} else {
return this.scan(ignoreWhitespace);
}
}
// End of file/input // End of file/input
if (this.stream.eos()) { if (this.stream.eos()) {
return this.finishToken(result, TokenType.EOF); return this.finishToken(offset, TokenType.EOF);
} }
// CDO <!-- // CDO <!--
if (this.stream.advanceIfChars([_LAN, _BNG, _MIN, _MIN])) { if (this.stream.advanceIfChars([_LAN, _BNG, _MIN, _MIN])) {
return this.finishToken(result, TokenType.CDO); return this.finishToken(offset, TokenType.CDO);
} }
// CDC --> // CDC -->
if (this.stream.advanceIfChars([_MIN, _MIN, _RAN])) { if (this.stream.advanceIfChars([_MIN, _MIN, _RAN])) {
return this.finishToken(result, TokenType.CDC); return this.finishToken(offset, TokenType.CDC);
} }
// URL // URL
tokenType = this._url(); let tokenType = this._url();
if (tokenType !== null) { if (tokenType !== null) {
return this.finishToken(result, tokenType); return this.finishToken(offset, tokenType);
} }
var content: string[] = []; let content: string[] = [];
if (this.ident(content)) { if (this.ident(content)) {
return this.finishToken(result, TokenType.Ident, content.join('')); return this.finishToken(offset, TokenType.Ident, content.join(''));
} }
// at-keyword // at-keyword
if (this.stream.advanceIfChar(_ATS)) { if (this.stream.advanceIfChar(_ATS)) {
content = [ '@' ]; content = [ '@' ];
if (this.ident(content)) { if (this.ident(content)) {
var keywordText = content.join(''); let keywordText = content.join('');
if (keywordText === '@charset') { if (keywordText === '@charset') {
return this.finishToken(result, TokenType.Charset, keywordText); return this.finishToken(offset, TokenType.Charset, keywordText);
} }
return this.finishToken(result, TokenType.AtKeyword, keywordText); return this.finishToken(offset, TokenType.AtKeyword, keywordText);
} else { } else {
return this.finishToken(result, TokenType.Delim); return this.finishToken(offset, TokenType.Delim);
} }
} }
...@@ -327,93 +311,93 @@ export class Scanner { ...@@ -327,93 +311,93 @@ export class Scanner {
if (this.stream.advanceIfChar(_HSH)) { if (this.stream.advanceIfChar(_HSH)) {
content = [ '#' ]; content = [ '#' ];
if (this._name(content)) { if (this._name(content)) {
return this.finishToken(result, TokenType.Hash, content.join('')); return this.finishToken(offset, TokenType.Hash, content.join(''));
} else { } else {
return this.finishToken(result, TokenType.Delim); return this.finishToken(offset, TokenType.Delim);
} }
} }
// Important // Important
if (this.stream.advanceIfChar(_BNG)) { if (this.stream.advanceIfChar(_BNG)) {
return this.finishToken(result, TokenType.Exclamation); return this.finishToken(offset, TokenType.Exclamation);
} }
// Numbers // Numbers
if (this._number()) { if (this._number()) {
var pos = this.stream.pos(); let pos = this.stream.pos();
content = [ this.stream.substring(result.offset, pos) ]; content = [ this.stream.substring(offset, pos) ];
if (this.stream.advanceIfChar(_PRC)) { if (this.stream.advanceIfChar(_PRC)) {
// Percentage 43% // Percentage 43%
return this.finishToken(result, TokenType.Percentage); return this.finishToken(offset, TokenType.Percentage);
} else if (this.ident(content)) { } else if (this.ident(content)) {
let dim = this.stream.substring(pos).toLowerCase(); let dim = this.stream.substring(pos).toLowerCase();
tokenType = <TokenType>staticUnitTable[dim]; tokenType = <TokenType>staticUnitTable[dim];
if (typeof tokenType !== 'undefined') { if (typeof tokenType !== 'undefined') {
// Known dimension 43px // Known dimension 43px
return this.finishToken(result, tokenType, content.join('')); return this.finishToken(offset, tokenType, content.join(''));
} else { } else {
// Unknown dimension 43ft // Unknown dimension 43ft
return this.finishToken(result, TokenType.Dimension, content.join('')); return this.finishToken(offset, TokenType.Dimension, content.join(''));
} }
} }
return this.finishToken(result, TokenType.Num); return this.finishToken(offset, TokenType.Num);
} }
// String, BadString // String, BadString
content = []; content = [];
tokenType = this._string(content); tokenType = this._string(content);
if (tokenType !== null) { if (tokenType !== null) {
return this.finishToken(result, tokenType, content.join('')); return this.finishToken(offset, tokenType, content.join(''));
} }
// single character tokens // single character tokens
tokenType = <TokenType>staticTokenTable[this.stream.peekChar()]; tokenType = <TokenType>staticTokenTable[this.stream.peekChar()];
if (typeof tokenType !== 'undefined') { if (typeof tokenType !== 'undefined') {
this.stream.advance(1); this.stream.advance(1);
return this.finishToken(result, tokenType); return this.finishToken(offset, tokenType);
} }
// includes ~= // includes ~=
if (this.stream.peekChar(0) === _TLD && this.stream.peekChar(1) === _EQS) { if (this.stream.peekChar(0) === _TLD && this.stream.peekChar(1) === _EQS) {
this.stream.advance(2); this.stream.advance(2);
return this.finishToken(result, TokenType.Includes); return this.finishToken(offset, TokenType.Includes);
} }
// DashMatch |= // DashMatch |=
if (this.stream.peekChar(0) === _PIP && this.stream.peekChar(1) === _EQS) { if (this.stream.peekChar(0) === _PIP && this.stream.peekChar(1) === _EQS) {
this.stream.advance(2); this.stream.advance(2);
return this.finishToken(result, TokenType.Dashmatch); return this.finishToken(offset, TokenType.Dashmatch);
} }
// Substring operator *= // Substring operator *=
if (this.stream.peekChar(0) === _MUL && this.stream.peekChar(1) === _EQS) { if (this.stream.peekChar(0) === _MUL && this.stream.peekChar(1) === _EQS) {
this.stream.advance(2); this.stream.advance(2);
return this.finishToken(result, TokenType.SubstringOperator); return this.finishToken(offset, TokenType.SubstringOperator);
} }
// Substring operator ^= // Substring operator ^=
if (this.stream.peekChar(0) === _HAT && this.stream.peekChar(1) === _EQS) { if (this.stream.peekChar(0) === _HAT && this.stream.peekChar(1) === _EQS) {
this.stream.advance(2); this.stream.advance(2);
return this.finishToken(result, TokenType.PrefixOperator); return this.finishToken(offset, TokenType.PrefixOperator);
} }
// Substring operator $= // Substring operator $=
if (this.stream.peekChar(0) === _DLR && this.stream.peekChar(1) === _EQS) { if (this.stream.peekChar(0) === _DLR && this.stream.peekChar(1) === _EQS) {
this.stream.advance(2); this.stream.advance(2);
return this.finishToken(result, TokenType.SuffixOperator); return this.finishToken(offset, TokenType.SuffixOperator);
} }
// Delim // Delim
this.stream.nextChar(); this.stream.nextChar();
return this.finishToken(result, TokenType.Delim); return this.finishToken(offset, TokenType.Delim);
} }
private _matchWordAnyCase(characters:number[]): boolean { private _matchWordAnyCase(characters:number[]): boolean {
var index = 0; let index = 0;
this.stream.advanceWhileChar((ch:number) => { this.stream.advanceWhileChar((ch:number) => {
var result = characters[index] === ch || characters[index+1] === ch; let result = characters[index] === ch || characters[index+1] === ch;
if (result) { if (result) {
index += 2; index += 2;
} }
...@@ -427,9 +411,26 @@ export class Scanner { ...@@ -427,9 +411,26 @@ export class Scanner {
} }
} }
private _comment():boolean { protected trivia(): IToken {
while (true) {
let offset = this.stream.pos();
if (this._whitespace()) {
if (!this.ignoreWhitespace) {
return this.finishToken(offset, TokenType.Whitespace);
}
} else if (this.comment()) {
if (!this.ignoreComment) {
return this.finishToken(offset, TokenType.Comment);
}
} else {
return null;
}
}
}
protected comment():boolean {
if (this.stream.advanceIfChars([_FSL, _MUL])) { if (this.stream.advanceIfChars([_FSL, _MUL])) {
var success = false, hot = false; let success = false, hot = false;
this.stream.advanceWhileChar((ch) => { this.stream.advanceWhileChar((ch) => {
if (hot && ch === _FSL) { if (hot && ch === _FSL) {
success = true; success = true;
...@@ -448,7 +449,7 @@ export class Scanner { ...@@ -448,7 +449,7 @@ export class Scanner {
} }
private _number():boolean { private _number():boolean {
var npeek = 0, ch:number; let npeek = 0, ch:number;
if (this.stream.peekChar() === _DOT) { if (this.stream.peekChar() === _DOT) {
npeek = 1; npeek = 1;
} }
...@@ -464,7 +465,7 @@ export class Scanner { ...@@ -464,7 +465,7 @@ export class Scanner {
} }
private _newline(result: string[]):boolean { private _newline(result: string[]):boolean {
var ch = this.stream.peekChar(); let ch = this.stream.peekChar();
switch (ch) { switch (ch) {
case _CAR: case _CAR:
case _LFD: case _LFD:
...@@ -482,11 +483,11 @@ export class Scanner { ...@@ -482,11 +483,11 @@ export class Scanner {
} }
private _escape(result: string[], includeNewLines?:boolean):boolean { private _escape(result: string[], includeNewLines?:boolean):boolean {
var ch = this.stream.peekChar(); let ch = this.stream.peekChar();
if (ch === _BSL) { if (ch === _BSL) {
this.stream.advance(1); this.stream.advance(1);
ch = this.stream.peekChar(); ch = this.stream.peekChar();
var hexNumCount = 0; let hexNumCount = 0;
while (hexNumCount < 6 && (ch >= _0 && ch <= _9 || ch >= _a && ch <= _f || ch >= _A && ch <= _F)) { while (hexNumCount < 6 && (ch >= _0 && ch <= _9 || ch >= _a && ch <= _f || ch >= _A && ch <= _F)) {
this.stream.advance(1); this.stream.advance(1);
ch = this.stream.peekChar(); ch = this.stream.peekChar();
...@@ -494,7 +495,7 @@ export class Scanner { ...@@ -494,7 +495,7 @@ export class Scanner {
} }
if (hexNumCount > 0) { if (hexNumCount > 0) {
try { try {
var hexVal= parseInt(this.stream.substring(this.stream.pos() - hexNumCount), 16); let hexVal= parseInt(this.stream.substring(this.stream.pos() - hexNumCount), 16);
if (hexVal) { if (hexVal) {
result.push(String.fromCharCode(hexVal)); result.push(String.fromCharCode(hexVal));
} }
...@@ -523,7 +524,7 @@ export class Scanner { ...@@ -523,7 +524,7 @@ export class Scanner {
private _stringChar(closeQuote: number, result: string[]) { private _stringChar(closeQuote: number, result: string[]) {
// not closeQuote, not backslash, not newline // not closeQuote, not backslash, not newline
var ch = this.stream.peekChar(); let ch = this.stream.peekChar();
if (ch !== 0 && ch !== closeQuote && ch !== _BSL && ch !== _CAR && ch !== _LFD && ch !== _NWL) { if (ch !== 0 && ch !== closeQuote && ch !== _BSL && ch !== _CAR && ch !== _LFD && ch !== _NWL) {
this.stream.advance(1); this.stream.advance(1);
result.push(String.fromCharCode(ch)); result.push(String.fromCharCode(ch));
...@@ -534,7 +535,7 @@ export class Scanner { ...@@ -534,7 +535,7 @@ export class Scanner {
private _string(result: string[]):TokenType { private _string(result: string[]):TokenType {
if (this.stream.peekChar() === _SQO || this.stream.peekChar() === _DQO) { if (this.stream.peekChar() === _SQO || this.stream.peekChar() === _DQO) {
var closeQuote = this.stream.nextChar(); let closeQuote = this.stream.nextChar();
result.push(String.fromCharCode(closeQuote)); result.push(String.fromCharCode(closeQuote));
while (this._stringChar(closeQuote, result) || this._escape(result, true)) { while (this._stringChar(closeQuote, result) || this._escape(result, true)) {
...@@ -555,7 +556,7 @@ export class Scanner { ...@@ -555,7 +556,7 @@ export class Scanner {
private _url():TokenType { private _url():TokenType {
if (this._matchWordAnyCase(_url) || this._matchWordAnyCase(_url_prefix)) { if (this._matchWordAnyCase(_url) || this._matchWordAnyCase(_url_prefix)) {
this._whitespace(); this._whitespace();
var tokenType = TokenType.URI, stringType = this._string([]); let tokenType = TokenType.URI, stringType = this._string([]);
if (stringType === TokenType.BadString) { if (stringType === TokenType.BadString) {
tokenType = TokenType.BadUri; tokenType = TokenType.BadUri;
...@@ -577,14 +578,14 @@ export class Scanner { ...@@ -577,14 +578,14 @@ export class Scanner {
} }
private _whitespace():boolean { private _whitespace():boolean {
var n = this.stream.advanceWhileChar((ch) => { let n = this.stream.advanceWhileChar((ch) => {
return ch === _WSP || ch === _TAB || ch === _NWL || ch === _LFD || ch === _CAR; return ch === _WSP || ch === _TAB || ch === _NWL || ch === _LFD || ch === _CAR;
}); });
return n > 0; return n > 0;
} }
private _name(result:string[]):boolean { private _name(result:string[]):boolean {
var matched = false; let matched = false;
while (this._identChar(result) || this._escape(result)) { while (this._identChar(result) || this._escape(result)) {
matched = true; matched = true;
} }
...@@ -592,10 +593,10 @@ export class Scanner { ...@@ -592,10 +593,10 @@ export class Scanner {
} }
protected ident(result:string[]):boolean { protected ident(result:string[]):boolean {
var pos = this.stream.pos(); let pos = this.stream.pos();
var hasMinus = this._minus(result); let hasMinus = this._minus(result);
if (hasMinus && this._minus(result) /* -- */) { if (hasMinus && this._minus(result) /* -- */) {
var hasContent = false; let hasContent = false;
while (this._identChar(result) || this._escape(result)) { while (this._identChar(result) || this._escape(result)) {
hasContent = true; hasContent = true;
} }
...@@ -613,7 +614,7 @@ export class Scanner { ...@@ -613,7 +614,7 @@ export class Scanner {
} }
private _identFirstChar(result:string[]):boolean { private _identFirstChar(result:string[]):boolean {
var ch = this.stream.peekChar(); let ch = this.stream.peekChar();
if (ch === _USC || // _ if (ch === _USC || // _
ch >= _a && ch <= _z || // a-z ch >= _a && ch <= _z || // a-z
ch >= _A && ch <= _Z || // A-Z ch >= _A && ch <= _Z || // A-Z
...@@ -627,7 +628,7 @@ export class Scanner { ...@@ -627,7 +628,7 @@ export class Scanner {
private _minus(result:string[]):boolean { private _minus(result:string[]):boolean {
var ch = this.stream.peekChar(); let ch = this.stream.peekChar();
if (ch === _MIN) { if (ch === _MIN) {
this.stream.advance(1); this.stream.advance(1);
result.push(String.fromCharCode(ch)); result.push(String.fromCharCode(ch));
...@@ -637,7 +638,7 @@ export class Scanner { ...@@ -637,7 +638,7 @@ export class Scanner {
} }
private _identChar(result:string[]):boolean { private _identChar(result:string[]):boolean {
var ch = this.stream.peekChar(); let ch = this.stream.peekChar();
if (ch === _USC || // _ if (ch === _USC || // _
ch === _MIN || // - ch === _MIN || // -
ch >= _a && ch <= _z || // a-z ch >= _a && ch <= _z || // a-z
......
...@@ -10,9 +10,9 @@ import Scanner = require('vs/languages/css/common/parser/cssScanner'); ...@@ -10,9 +10,9 @@ import Scanner = require('vs/languages/css/common/parser/cssScanner');
suite('CSS - Scanner', () => { suite('CSS - Scanner', () => {
function assertSingleToken(scan: Scanner.Scanner, source: string, len: number, offset: number, text: string, type: Scanner.TokenType, ignoreWhitespace?:boolean):void { function assertSingleToken(scan: Scanner.Scanner, source: string, len: number, offset: number, text: string, type: Scanner.TokenType):void {
scan.setSource(source); scan.setSource(source);
var token = scan.scan(ignoreWhitespace); var token = scan.scan();
assert.equal(token.len, len); assert.equal(token.len, len);
assert.equal(token.offset, offset); assert.equal(token.offset, offset);
assert.equal(token.text, text); assert.equal(token.text, text);
...@@ -22,7 +22,17 @@ suite('CSS - Scanner', () => { ...@@ -22,7 +22,17 @@ suite('CSS - Scanner', () => {
test('Test Whitespace', function() { test('Test Whitespace', function() {
var scanner = new Scanner.Scanner(); var scanner = new Scanner.Scanner();
assertSingleToken(scanner, ' @', 1, 1, '@', Scanner.TokenType.Delim); assertSingleToken(scanner, ' @', 1, 1, '@', Scanner.TokenType.Delim);
assertSingleToken(scanner, ' @', 1, 0, ' ', Scanner.TokenType.Whitespace, false); assertSingleToken(scanner, ' /* comment*/ \n/*comment*/@', 1, 26, '@', Scanner.TokenType.Delim);
scanner = new Scanner.Scanner();
scanner.ignoreWhitespace = false;
assertSingleToken(scanner, ' @', 1, 0, ' ', Scanner.TokenType.Whitespace);
assertSingleToken(scanner, '/*comment*/ @', 1, 11, ' ', Scanner.TokenType.Whitespace);
scanner = new Scanner.Scanner();
scanner.ignoreComment = false;
assertSingleToken(scanner, ' /*comment*/@', 11, 1, '/*comment*/', Scanner.TokenType.Comment);
assertSingleToken(scanner, '/*comment*/ @', 11, 0, '/*comment*/', Scanner.TokenType.Comment);
}); });
test('Test Token Ident', function() { test('Test Token Ident', function() {
......
...@@ -6,50 +6,44 @@ ...@@ -6,50 +6,44 @@
import scanner = require('vs/languages/css/common/parser/cssScanner'); import scanner = require('vs/languages/css/common/parser/cssScanner');
var _FSL = '/'.charCodeAt(0); const _FSL = '/'.charCodeAt(0);
var _NWL = '\n'.charCodeAt(0); const _NWL = '\n'.charCodeAt(0);
var _CAR = '\r'.charCodeAt(0); const _CAR = '\r'.charCodeAt(0);
var _LFD = '\f'.charCodeAt(0); const _LFD = '\f'.charCodeAt(0);
var _TIC = '`'.charCodeAt(0); const _TIC = '`'.charCodeAt(0);
var _DOT = '.'.charCodeAt(0); const _DOT = '.'.charCodeAt(0);
var customTokenValue = scanner.TokenType.CustomToken; let customTokenValue = scanner.TokenType.CustomToken;
export var Ellipsis: scanner.TokenType = customTokenValue++; export const Ellipsis: scanner.TokenType = customTokenValue++;
export class LessScanner extends scanner.Scanner { export class LessScanner extends scanner.Scanner {
public scan(ignoreWhitespace:boolean=true): scanner.IToken { public scan(): scanner.IToken {
var result:scanner.IToken = { let triviaToken = this.trivia();
type: undefined, if (triviaToken !== null) {
text: undefined, return triviaToken;
offset: this.stream.pos(),
len: 0
};
// SingleLine Comments
if (this.lessComment()) {
if (!this.ignoreComment) {
return this.finishToken(result, scanner.TokenType.SingleLineComment);
} else {
return this.scan(ignoreWhitespace);
}
} }
// LESS: escaped JavaScript code `var a = "dddd"` let offset = this.stream.pos();
var tokenType = this.escapedJavaScript();
if(tokenType !== null) { // LESS: escaped JavaScript code `let a = "dddd"`
return this.finishToken(result, tokenType); let tokenType = this.escapedJavaScript();
if (tokenType !== null) {
return this.finishToken(offset, tokenType);
} }
if(this.stream.advanceIfChars([_DOT, _DOT, _DOT])) { if (this.stream.advanceIfChars([_DOT, _DOT, _DOT])) {
return this.finishToken(result, Ellipsis); return this.finishToken(offset, Ellipsis);
} }
return super.scan(ignoreWhitespace); return super.scan();
} }
private lessComment():boolean { protected comment():boolean {
if (super.comment()) {
return true;
}
if (this.stream.advanceIfChars([_FSL, _FSL])) { if (this.stream.advanceIfChars([_FSL, _FSL])) {
this.stream.advanceWhileChar((ch:number) => { this.stream.advanceWhileChar((ch:number) => {
switch(ch) { switch(ch) {
...@@ -68,8 +62,8 @@ export class LessScanner extends scanner.Scanner { ...@@ -68,8 +62,8 @@ export class LessScanner extends scanner.Scanner {
} }
private escapedJavaScript():scanner.TokenType { private escapedJavaScript():scanner.TokenType {
var ch = this.stream.peekChar(); let ch = this.stream.peekChar();
if(ch === _TIC) { if (ch === _TIC) {
this.stream.advance(1); this.stream.advance(1);
this.stream.advanceWhileChar((ch) => { return ch !== _TIC; }); this.stream.advanceWhileChar((ch) => { return ch !== _TIC; });
return this.stream.advanceIfChar(_TIC) ? scanner.TokenType.EscapedJavaScript : scanner.TokenType.BadEscapedJavaScript; return this.stream.advanceIfChar(_TIC) ? scanner.TokenType.EscapedJavaScript : scanner.TokenType.BadEscapedJavaScript;
......
...@@ -9,10 +9,10 @@ import assert = require('assert'); ...@@ -9,10 +9,10 @@ import assert = require('assert');
import scanner = require('vs/languages/css/common/parser/cssScanner'); import scanner = require('vs/languages/css/common/parser/cssScanner');
import lessScanner = require('vs/languages/less/common/parser/lessScanner'); import lessScanner = require('vs/languages/less/common/parser/lessScanner');
function assertSingleToken(source: string, len: number, offset: number, text: string, type: scanner.TokenType, ignoreWhitespace?:boolean):void { function assertSingleToken(source: string, len: number, offset: number, text: string, type: scanner.TokenType):void {
var scan = new lessScanner.LessScanner(); var scan = new lessScanner.LessScanner();
scan.setSource(source); scan.setSource(source);
var token = scan.scan(ignoreWhitespace); var token = scan.scan();
assert.equal(token.len, len); assert.equal(token.len, len);
assert.equal(token.offset, offset); assert.equal(token.offset, offset);
assert.equal(token.text, text); assert.equal(token.text, text);
...@@ -35,5 +35,6 @@ suite('LESS - Scanner', () => { ...@@ -35,5 +35,6 @@ suite('LESS - Scanner', () => {
assertSingleToken('//this is a comment test', 0, 24, '', scanner.TokenType.EOF); assertSingleToken('//this is a comment test', 0, 24, '', scanner.TokenType.EOF);
assertSingleToken('// this is a comment test', 0, 25, '', scanner.TokenType.EOF); assertSingleToken('// this is a comment test', 0, 25, '', scanner.TokenType.EOF);
assertSingleToken('// this is a\na', 1, 13, 'a', scanner.TokenType.Ident); assertSingleToken('// this is a\na', 1, 13, 'a', scanner.TokenType.Ident);
assertSingleToken('// this is a\n// more\n \n/* comment */a', 1, 38, 'a', scanner.TokenType.Ident);
}); });
}); });
...@@ -6,101 +6,96 @@ ...@@ -6,101 +6,96 @@
import scanner = require('vs/languages/css/common/parser/cssScanner'); import scanner = require('vs/languages/css/common/parser/cssScanner');
var _FSL = '/'.charCodeAt(0); const _FSL = '/'.charCodeAt(0);
var _NWL = '\n'.charCodeAt(0); const _NWL = '\n'.charCodeAt(0);
var _CAR = '\r'.charCodeAt(0); const _CAR = '\r'.charCodeAt(0);
var _LFD = '\f'.charCodeAt(0); const _LFD = '\f'.charCodeAt(0);
var _DLR = '$'.charCodeAt(0); const _DLR = '$'.charCodeAt(0);
var _HSH = '#'.charCodeAt(0); const _HSH = '#'.charCodeAt(0);
var _CUL = '{'.charCodeAt(0); const _CUL = '{'.charCodeAt(0);
var _EQS = '='.charCodeAt(0); const _EQS = '='.charCodeAt(0);
var _BNG = '!'.charCodeAt(0); const _BNG = '!'.charCodeAt(0);
var _LAN = '<'.charCodeAt(0); const _LAN = '<'.charCodeAt(0);
var _RAN = '>'.charCodeAt(0); const _RAN = '>'.charCodeAt(0);
var _DOT = '.'.charCodeAt(0); const _DOT = '.'.charCodeAt(0);
var customTokenValue = scanner.TokenType.CustomToken; let customTokenValue = scanner.TokenType.CustomToken;
export var VariableName = customTokenValue++; export const VariableName = customTokenValue++;
export var InterpolationFunction: scanner.TokenType = customTokenValue++; export const InterpolationFunction: scanner.TokenType = customTokenValue++;
export var Default: scanner.TokenType = customTokenValue++; export const Default: scanner.TokenType = customTokenValue++;
export var EqualsOperator: scanner.TokenType = customTokenValue++; export const EqualsOperator: scanner.TokenType = customTokenValue++;
export var NotEqualsOperator: scanner.TokenType = customTokenValue++; export const NotEqualsOperator: scanner.TokenType = customTokenValue++;
export var GreaterEqualsOperator: scanner.TokenType = customTokenValue++; export const GreaterEqualsOperator: scanner.TokenType = customTokenValue++;
export var SmallerEqualsOperator: scanner.TokenType = customTokenValue++; export const SmallerEqualsOperator: scanner.TokenType = customTokenValue++;
export var Ellipsis: scanner.TokenType = customTokenValue++; export const Ellipsis: scanner.TokenType = customTokenValue++;
export class SassScanner extends scanner.Scanner { export class SassScanner extends scanner.Scanner {
public scan(ignoreWhitespace:boolean=true): scanner.IToken { public scan(): scanner.IToken {
var result:scanner.IToken = { // processes all whitespaces and comments
type: undefined, const triviaToken = this.trivia();
text: undefined, if (triviaToken !== null) {
offset: this.stream.pos(), return triviaToken;
len: 0
};
// SingleLine Comments
if (this.sassComment()) {
if (!this.ignoreComment) {
return this.finishToken(result, scanner.TokenType.SingleLineComment);
} else {
return this.scan(ignoreWhitespace);
}
} }
const offset = this.stream.pos();
// sass variable // sass variable
if (this.stream.advanceIfChar(_DLR)) { if (this.stream.advanceIfChar(_DLR)) {
var content = [ '$' ]; const content = [ '$' ];
if (this.ident(content)) { if (this.ident(content)) {
return this.finishToken(result, VariableName, content.join('')); return this.finishToken(offset, VariableName, content.join(''));
} else { } else {
this.stream.goBackTo(result.offset); this.stream.goBackTo(offset);
} }
} }
// Sass: interpolation function #{..}) // Sass: interpolation function #{..})
if (this.stream.advanceIfChars([_HSH, _CUL])) { if (this.stream.advanceIfChars([_HSH, _CUL])) {
return this.finishToken(result, InterpolationFunction); return this.finishToken(offset, InterpolationFunction);
} }
// operator == // operator ==
if (this.stream.advanceIfChars([_EQS, _EQS])) { if (this.stream.advanceIfChars([_EQS, _EQS])) {
return this.finishToken(result, EqualsOperator); return this.finishToken(offset, EqualsOperator);
} }
// operator != // operator !=
if (this.stream.advanceIfChars([_BNG, _EQS])) { if (this.stream.advanceIfChars([_BNG, _EQS])) {
return this.finishToken(result, NotEqualsOperator); return this.finishToken(offset, NotEqualsOperator);
} }
// operators <, <= // operators <, <=
if (this.stream.advanceIfChar(_LAN)) { if (this.stream.advanceIfChar(_LAN)) {
if (this.stream.advanceIfChar(_EQS)) { if (this.stream.advanceIfChar(_EQS)) {
return this.finishToken(result, SmallerEqualsOperator); return this.finishToken(offset, SmallerEqualsOperator);
} }
return this.finishToken(result, scanner.TokenType.Delim); return this.finishToken(offset, scanner.TokenType.Delim);
} }
// ooperators >, >= // ooperators >, >=
if (this.stream.advanceIfChar(_RAN)) { if (this.stream.advanceIfChar(_RAN)) {
if (this.stream.advanceIfChar(_EQS)) { if (this.stream.advanceIfChar(_EQS)) {
return this.finishToken(result, GreaterEqualsOperator); return this.finishToken(offset, GreaterEqualsOperator);
} }
return this.finishToken(result, scanner.TokenType.Delim); return this.finishToken(offset, scanner.TokenType.Delim);
} }
// ellipis // ellipis
if (this.stream.advanceIfChars([_DOT, _DOT, _DOT])) { if (this.stream.advanceIfChars([_DOT, _DOT, _DOT])) {
return this.finishToken(result, Ellipsis); return this.finishToken(offset, Ellipsis);
} }
return super.scan(ignoreWhitespace); return super.scan();
} }
private sassComment():boolean { protected comment():boolean {
if (super.comment()) {
return true;
}
if (this.stream.advanceIfChars([_FSL, _FSL])) { if (this.stream.advanceIfChars([_FSL, _FSL])) {
this.stream.advanceWhileChar((ch:number) => { this.stream.advanceWhileChar((ch:number) => {
switch(ch) { switch(ch) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册