Skip to content

Commit 60ceb41

Browse files
committed
Improve string literal
1 parent ba4c745 commit 60ceb41

File tree

4 files changed

+76
-7
lines changed

4 files changed

+76
-7
lines changed

baselines/reference/stringLIteral.tree.baseline

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
},
2323
"init": {
2424
"kind": "Literal",
25-
"value": "'singleQuote'"
25+
"value": "singleQuote"
2626
}
2727
},
2828
{
@@ -33,11 +33,15 @@
3333
},
3434
"init": {
3535
"kind": "Literal",
36-
"value": "\"doubleQuote\""
36+
"value": "double'Quote"
3737
}
3838
},
3939
{
40-
"kind": "EmptyStatement"
40+
"kind": "ExpressionStatement",
41+
"expr": {
42+
"kind": "Identifier",
43+
"text": "(missing)"
44+
}
4145
}
4246
]
4347
}

src/lex.ts

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Token, Lexer } from './types';
1+
import { Token, Lexer, CharCodes } from './types';
22

33
const keywords = {
44
function: Token.Function,
@@ -41,8 +41,7 @@ export function lex(s: string): Lexer {
4141
? keywords[text as keyof typeof keywords]
4242
: Token.Identifier;
4343
} else if (['"', "'"].includes(s.charAt(pos))) {
44-
scanForward((c) => /[_a-zA-Z0-9'"]/.test(c));
45-
text = s.slice(start, pos);
44+
text = scanString();
4645
token = Token.String;
4746
} else {
4847
pos++;
@@ -66,6 +65,62 @@ export function lex(s: string): Lexer {
6665
function scanForward(pred: (x: string) => boolean) {
6766
while (pos < s.length && pred(s.charAt(pos))) pos++;
6867
}
68+
69+
function scanString() {
70+
const quote = s.charCodeAt(pos);
71+
pos++;
72+
73+
let stringValue = '';
74+
let start = pos;
75+
76+
while (true) {
77+
if (pos >= s.length) {
78+
// report unterminated string literal error
79+
}
80+
81+
const char = s.charCodeAt(pos);
82+
83+
if (char === quote) {
84+
stringValue += s.slice(start, pos);
85+
pos++;
86+
break;
87+
}
88+
89+
if (char === CharCodes.backslash) {
90+
stringValue += s.slice(start, pos);
91+
stringValue += scanEscapeSequence();
92+
start = pos;
93+
continue;
94+
}
95+
96+
pos++;
97+
}
98+
99+
return stringValue;
100+
}
101+
102+
function scanEscapeSequence() {
103+
pos++;
104+
const char = s.charCodeAt(pos);
105+
pos++;
106+
107+
switch (char) {
108+
case CharCodes.b:
109+
return '\b';
110+
case CharCodes.t:
111+
return '\t';
112+
case CharCodes.n:
113+
return '\n';
114+
case CharCodes.r:
115+
return '\r';
116+
case CharCodes.singleQuote:
117+
return "'";
118+
case CharCodes.doubleQuote:
119+
return '"';
120+
default:
121+
return '';
122+
}
123+
}
69124
}
70125

71126
export function lexAll(s: string) {

src/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,13 @@ export type Module = {
9494
};
9595

9696
export type Type = { id: string };
97+
98+
export enum CharCodes {
99+
b = 98,
100+
t = 116,
101+
n = 110,
102+
r = 114,
103+
singleQuote = 39,
104+
doubleQuote = 34,
105+
backslash = 92,
106+
}

tests/stringLiteral.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
var singleQuote = 'singleQuote';
2-
var doubleQuote = "doubleQuote";
2+
var doubleQuote = 'double\'Quote';

0 commit comments

Comments
 (0)