Skip to content

Commit c5e8220

Browse files
committed
Fix escape sequence
1 parent bc76e30 commit c5e8220

File tree

5 files changed

+58
-6
lines changed

5 files changed

+58
-6
lines changed

src/check.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ export function check(module: Module) {
5454
error(expression.pos, 'Could not resolve ' + expression.text);
5555
return errorType;
5656
case Node.Literal:
57-
return typeof expression.value === 'string' ? stringType : numberType;
57+
return numberType;
58+
case Node.StringLiteral:
59+
return stringType;
5860
case Node.Assignment:
5961
const v = checkExpression(expression.value);
6062
const t = checkExpression(expression.name);

src/emit.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
import { Statement, Node, Expression } from './types';
22

3+
const singleQuoteRegex = /[\\\'\t\v\f\b\r\n]/g;
4+
const doubleQuoteRegex = /[\\\"\t\v\f\b\r\n]/g;
5+
6+
const escapedCharsMap = new Map(
7+
Object.entries({
8+
'\t': '\\t',
9+
'\v': '\\v',
10+
'\f': '\\f',
11+
'\b': '\\b',
12+
'\r': '\\r',
13+
'\n': '\\n',
14+
'\\': '\\\\',
15+
'"': '\\"',
16+
"'": "\\'",
17+
}),
18+
);
19+
320
export function emit(statements: Statement[]) {
421
return statements.map(emitStatement).join(';\n');
522
}
@@ -24,7 +41,22 @@ function emitExpression(expression: Expression): string {
2441
return expression.text;
2542
case Node.Literal:
2643
return '' + expression.value;
44+
case Node.StringLiteral:
45+
return expression.isSingleQuote
46+
? `'${escapeString(expression.value, true)}'`
47+
: `"${escapeString(expression.value, false)}"`;
2748
case Node.Assignment:
2849
return `${expression.name.text} = ${emitExpression(expression.value)}`;
2950
}
3051
}
52+
53+
function escapeString(string: string, isSingleQuote: boolean) {
54+
return string.replace(
55+
isSingleQuote ? singleQuoteRegex : doubleQuoteRegex,
56+
replacement,
57+
);
58+
}
59+
60+
function replacement(char: string) {
61+
return escapedCharsMap.get(char) || char;
62+
}

src/lex.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ export function lex(s: string): Lexer {
1111
let pos = 0;
1212
let text = '';
1313
let token = Token.BOF;
14+
let firstChar: string;
1415

1516
return {
1617
scan,
1718
token: () => token,
1819
pos: () => pos,
1920
text: () => text,
21+
isSingleQuote: () => firstChar === "'",
2022
};
2123

2224
function scan() {
@@ -41,6 +43,7 @@ export function lex(s: string): Lexer {
4143
? keywords[text as keyof typeof keywords]
4244
: Token.Identifier;
4345
} else if (['"', "'"].includes(s.charAt(pos))) {
46+
firstChar = s.charAt(pos);
4447
text = scanString();
4548
token = Token.String;
4649
} else {
@@ -114,9 +117,11 @@ export function lex(s: string): Lexer {
114117
case CharCodes.r:
115118
return '\r';
116119
case CharCodes.singleQuote:
117-
return "'";
120+
// prettier-ignore
121+
return "\'";
118122
case CharCodes.doubleQuote:
119-
return '"';
123+
// prettier-ignore
124+
return '\"';
120125
default:
121126
return String.fromCharCode(char);
122127
}

src/parse.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ export function parse(lexer: Lexer): Module {
3737
} else if (tryParseToken(Token.Literal)) {
3838
return { kind: Node.Literal, value: +lexer.text(), pos };
3939
} else if (tryParseToken(Token.String)) {
40-
return { kind: Node.Literal, value: lexer.text(), pos };
40+
return {
41+
kind: Node.StringLiteral,
42+
value: lexer.text(),
43+
pos,
44+
isSingleQuote: lexer.isSingleQuote(),
45+
};
4146
}
4247
error(
4348
pos,

src/types.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export type Lexer = {
2121
token(): Token;
2222
pos(): number;
2323
text(): string;
24+
isSingleQuote(): boolean;
2425
};
2526

2627
export enum Node {
@@ -30,6 +31,7 @@ export enum Node {
3031
ExpressionStatement,
3132
Var,
3233
TypeAlias,
34+
StringLiteral,
3335
}
3436

3537
export type Error = {
@@ -41,7 +43,7 @@ export interface Location {
4143
pos: number;
4244
}
4345

44-
export type Expression = Identifier | Literal | Assignment;
46+
export type Expression = Identifier | Literal | Assignment | StringLiteral;
4547

4648
export type Identifier = Location & {
4749
kind: Node.Identifier;
@@ -50,7 +52,13 @@ export type Identifier = Location & {
5052

5153
export type Literal = Location & {
5254
kind: Node.Literal;
53-
value: number | string;
55+
value: number;
56+
};
57+
58+
export type StringLiteral = Location & {
59+
kind: Node.StringLiteral;
60+
value: string;
61+
isSingleQuote: boolean;
5462
};
5563

5664
export type Assignment = Location & {

0 commit comments

Comments
 (0)