Skip to content

Commit fb36ff1

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 fb36ff1

2 files changed

Lines changed: 15 additions & 9 deletions

File tree

WSL/Lexer.js

Lines changed: 12 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,18 @@ 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 matches = str.substring(0, length).match(/\n/g);
58+
return matches?.length ?? 0;
5859
}
5960

60-
get state() { return {index: this._index, stack: this._stack.concat()}; }
61+
get state() { return {index: this._index, stack: this._stack.concat(), lineNumber: this._lineNumber}; }
6162
set state(value)
6263
{
6364
this._index = value.index;
6465
this._stack = value.stack;
66+
this._lineNumber = value.lineNumber;
6567
}
6668

6769
static _textIsIdentifierImpl(text)
@@ -87,7 +89,7 @@ class Lexer {
8789

8890
let result = (kind) => {
8991
let text = RegExp.lastMatch;
90-
let token = new LexerToken(this, this._index, kind, text);
92+
let token = new LexerToken(this, this._index, kind, text, this._lineNumber);
9193
this._index += text.length;
9294
return token;
9395
};
@@ -96,13 +98,16 @@ class Lexer {
9698
for (;;) {
9799
relevantText = this._text.substring(this._index);
98100
if (/^\s+/.test(relevantText)) {
99-
this._index += RegExp.lastMatch.length;
101+
let ws = RegExp.lastMatch;
102+
this._lineNumber += this._countNewlines(ws, ws.length);
103+
this._index += ws.length;
100104
continue;
101105
}
102106
if (/^\/\*/.test(relevantText)) {
103107
let endIndex = relevantText.search(/\*\//);
104108
if (endIndex < 0)
105109
this.fail("Unterminated comment");
110+
this._lineNumber += this._countNewlines(relevantText, endIndex);
106111
this._index += endIndex;
107112
continue;
108113
}

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)