Skip to content

Commit f1792e0

Browse files
committed
Rewrite line number computation in WSL
Instead of computing the line number on demand, keep track of it as we lex. Previously, we ended up spending a quadratic amount of time computing the line number because we'd do something like: for every index: count the number line breaks up to this index. Fixes #298
1 parent de88e36 commit f1792e0

2 files changed

Lines changed: 19 additions & 9 deletions

File tree

WSL/Lexer.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ class Lexer {
3535
this._text = text;
3636
this._index = 0;
3737
this._stack = [];
38+
this._lineNumber = lineNumberOffset;
3839
}
3940

4041
get lineNumber()
4142
{
42-
return this.lineNumberForIndex(this._index);
43+
return this._lineNumber;
4344
}
4445

4546
get origin() { return this._origin; }
@@ -51,17 +52,22 @@ class Lexer {
5152

5253
get originKind() { return this._originKind; }
5354

54-
lineNumberForIndex(index)
55+
_countNewlines(str, length)
5556
{
56-
let matches = this._text.substring(0, index).match(/\n/g);
57-
return (matches ? matches.length : 0) + this._lineNumberOffset;
57+
let count = 0;
58+
for (let i = 0; i < length; i++) {
59+
if (str.charCodeAt(i) === 10)
60+
count++;
61+
}
62+
return count;
5863
}
5964

60-
get state() { return {index: this._index, stack: this._stack.concat()}; }
65+
get state() { return {index: this._index, stack: this._stack.concat(), lineNumber: this._lineNumber}; }
6166
set state(value)
6267
{
6368
this._index = value.index;
6469
this._stack = value.stack;
70+
this._lineNumber = value.lineNumber;
6571
}
6672

6773
static _textIsIdentifierImpl(text)
@@ -87,7 +93,7 @@ class Lexer {
8793

8894
let result = (kind) => {
8995
let text = RegExp.lastMatch;
90-
let token = new LexerToken(this, this._index, kind, text);
96+
let token = new LexerToken(this, this._index, kind, text, this._lineNumber);
9197
this._index += text.length;
9298
return token;
9399
};
@@ -96,13 +102,16 @@ class Lexer {
96102
for (;;) {
97103
relevantText = this._text.substring(this._index);
98104
if (/^\s+/.test(relevantText)) {
99-
this._index += RegExp.lastMatch.length;
105+
let ws = RegExp.lastMatch;
106+
this._lineNumber += this._countNewlines(ws, ws.length);
107+
this._index += ws.length;
100108
continue;
101109
}
102110
if (/^\/\*/.test(relevantText)) {
103111
let endIndex = relevantText.search(/\*\//);
104112
if (endIndex < 0)
105113
this.fail("Unterminated comment");
114+
this._lineNumber += this._countNewlines(relevantText, endIndex);
106115
this._index += endIndex;
107116
continue;
108117
}

WSL/LexerToken.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@
2525
"use strict";
2626

2727
class LexerToken {
28-
constructor(lexer, index, kind, text)
28+
constructor(lexer, index, kind, text, lineNumber)
2929
{
3030
this._lexer = lexer;
3131
this._index = index;
3232
this._kind = kind;
3333
this._text = text;
34+
this._lineNumber = lineNumber;
3435
}
3536

3637
get lexer()
@@ -65,7 +66,7 @@ class LexerToken {
6566

6667
get lineNumber()
6768
{
68-
return this._lexer.lineNumberForIndex(this._index);
69+
return this._lineNumber;
6970
}
7071

7172
get originString()

0 commit comments

Comments
 (0)