@jalvin/compiler 2.0.38 → 2.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codegen.d.ts +3 -56
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +252 -898
- package/dist/codegen.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/typechecker.d.ts.map +1 -1
- package/dist/typechecker.js +1 -3
- package/dist/typechecker.js.map +1 -1
- package/package.json +1 -1
- package/dist/codegen/index.d.ts +0 -179
- package/dist/codegen/index.d.ts.map +0 -1
- package/dist/codegen/index.js +0 -2207
- package/dist/codegen/index.js.map +0 -1
- package/dist/codegen_freestanding_c.d.ts +0 -9
- package/dist/codegen_freestanding_c.d.ts.map +0 -1
- package/dist/codegen_freestanding_c.js +0 -321
- package/dist/codegen_freestanding_c.js.map +0 -1
- package/dist/lexer/chars.d.ts +0 -5
- package/dist/lexer/chars.d.ts.map +0 -1
- package/dist/lexer/chars.js +0 -20
- package/dist/lexer/chars.js.map +0 -1
- package/dist/lexer/index.d.ts +0 -37
- package/dist/lexer/index.d.ts.map +0 -1
- package/dist/lexer/index.js +0 -630
- package/dist/lexer/index.js.map +0 -1
- package/dist/lexer/tokens.d.ts +0 -135
- package/dist/lexer/tokens.d.ts.map +0 -1
- package/dist/lexer/tokens.js +0 -89
- package/dist/lexer/tokens.js.map +0 -1
- package/dist/parser/constants.d.ts +0 -3
- package/dist/parser/constants.d.ts.map +0 -1
- package/dist/parser/constants.js +0 -7
- package/dist/parser/constants.js.map +0 -1
- package/dist/parser/helpers.d.ts +0 -2
- package/dist/parser/helpers.d.ts.map +0 -1
- package/dist/parser/helpers.js +0 -9
- package/dist/parser/helpers.js.map +0 -1
- package/dist/parser/index.d.ts +0 -118
- package/dist/parser/index.d.ts.map +0 -1
- package/dist/parser/index.js +0 -2387
- package/dist/parser/index.js.map +0 -1
- package/dist/typechecker/globals.d.ts +0 -2
- package/dist/typechecker/globals.d.ts.map +0 -1
- package/dist/typechecker/globals.js +0 -15
- package/dist/typechecker/globals.js.map +0 -1
- package/dist/typechecker/index.d.ts +0 -146
- package/dist/typechecker/index.d.ts.map +0 -1
- package/dist/typechecker/index.js +0 -2288
- package/dist/typechecker/index.js.map +0 -1
- package/dist/typechecker/types.d.ts +0 -66
- package/dist/typechecker/types.d.ts.map +0 -1
- package/dist/typechecker/types.js +0 -39
- package/dist/typechecker/types.js.map +0 -1
package/dist/lexer/index.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { DiagnosticBag } from "../diagnostics.js";
|
|
2
|
-
import { type Token } from "./tokens.js";
|
|
3
|
-
export { ASI_SET, KEYWORDS, TokenKind, type Token } from "./tokens.js";
|
|
4
|
-
export declare class Lexer {
|
|
5
|
-
private pos;
|
|
6
|
-
private line;
|
|
7
|
-
private col;
|
|
8
|
-
private readonly src;
|
|
9
|
-
private readonly file;
|
|
10
|
-
private readonly diag;
|
|
11
|
-
constructor(src: string, file: string, diag: DiagnosticBag);
|
|
12
|
-
tokenize(): Token[];
|
|
13
|
-
private nextToken;
|
|
14
|
-
private lexIdentOrKeyword;
|
|
15
|
-
private lexNumber;
|
|
16
|
-
private lexString;
|
|
17
|
-
private lexRawString;
|
|
18
|
-
private parseEscape;
|
|
19
|
-
private lexSymbol;
|
|
20
|
-
private skipLineComment;
|
|
21
|
-
private skipBlockComment;
|
|
22
|
-
/**
|
|
23
|
-
* Returns true when the next meaningful token after the current newline is a
|
|
24
|
-
* dot-continuation (`.member` or `?.member`), in which case ASI must NOT be
|
|
25
|
-
* inserted so that multi-line method chains work correctly.
|
|
26
|
-
*
|
|
27
|
-
* Scans forward from `this.pos + 1` skipping whitespace and comments.
|
|
28
|
-
* `.` followed by another `.` (range `..`) does NOT count as continuation.
|
|
29
|
-
*/
|
|
30
|
-
private peekIsDotContinuation;
|
|
31
|
-
private advance;
|
|
32
|
-
private peek;
|
|
33
|
-
private spanAt;
|
|
34
|
-
private makeSpan;
|
|
35
|
-
}
|
|
36
|
-
export declare function lex(src: string, file: string, diag: DiagnosticBag): Token[];
|
|
37
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lexer/index.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,aAAa,EAMd,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAgC,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAMvE,qBAAa,KAAK;IAChB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;gBAEzB,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa;IAM1D,QAAQ,IAAI,KAAK,EAAE;IAqDnB,OAAO,CAAC,SAAS;IAyBjB,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,SAAS;IA8FjB,OAAO,CAAC,SAAS;IAgFjB,OAAO,CAAC,YAAY;IA8BpB,OAAO,CAAC,WAAW;IAkDnB,OAAO,CAAC,SAAS;IAyFjB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,gBAAgB;IAuBxB;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAqC7B,OAAO,CAAC,OAAO;IAUf,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,MAAM;IAYd,OAAO,CAAC,QAAQ;CAWjB;AAMD,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,KAAK,EAAE,CAE3E"}
|
package/dist/lexer/index.js
DELETED
|
@@ -1,630 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
3
|
-
// Jalvin Lexer
|
|
4
|
-
//
|
|
5
|
-
// Tokenises a .jalvin source file. Key behaviours:
|
|
6
|
-
// • Automatic semicolon insertion (like Go) after certain tokens
|
|
7
|
-
// when a newline is encountered.
|
|
8
|
-
// • String templates: `"Hello $name"` and `"Hello ${expr}"`.
|
|
9
|
-
// • Triple-quoted raw strings: `"""..."""`.
|
|
10
|
-
// • Coroutine keywords: launch, async, suspend, await.
|
|
11
|
-
// • `component` keyword for UI declarations.
|
|
12
|
-
// • `Bibi` is just an identifier — treated normally by the lexer.
|
|
13
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.Lexer = exports.KEYWORDS = exports.ASI_SET = void 0;
|
|
16
|
-
exports.lex = lex;
|
|
17
|
-
const diagnostics_js_1 = require("../diagnostics.js");
|
|
18
|
-
const chars_js_1 = require("./chars.js");
|
|
19
|
-
const tokens_js_1 = require("./tokens.js");
|
|
20
|
-
var tokens_js_2 = require("./tokens.js");
|
|
21
|
-
Object.defineProperty(exports, "ASI_SET", { enumerable: true, get: function () { return tokens_js_2.ASI_SET; } });
|
|
22
|
-
Object.defineProperty(exports, "KEYWORDS", { enumerable: true, get: function () { return tokens_js_2.KEYWORDS; } });
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
// Lexer
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
class Lexer {
|
|
27
|
-
pos = 0;
|
|
28
|
-
line = 0;
|
|
29
|
-
col = 0;
|
|
30
|
-
src;
|
|
31
|
-
file;
|
|
32
|
-
diag;
|
|
33
|
-
constructor(src, file, diag) {
|
|
34
|
-
this.src = src;
|
|
35
|
-
this.file = file;
|
|
36
|
-
this.diag = diag;
|
|
37
|
-
}
|
|
38
|
-
tokenize() {
|
|
39
|
-
const tokens = [];
|
|
40
|
-
let prevKind = null;
|
|
41
|
-
while (this.pos < this.src.length) {
|
|
42
|
-
const ch = this.src[this.pos];
|
|
43
|
-
// ── Whitespace ───────────────────────────────────────────────────────
|
|
44
|
-
if (ch === " " || ch === "\t" || ch === "\r") {
|
|
45
|
-
this.advance();
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
// ── Newline → potential semicolon ────────────────────────────────────
|
|
49
|
-
if (ch === "\n") {
|
|
50
|
-
if (prevKind !== null && tokens_js_1.ASI_SET.has(prevKind) && !this.peekIsDotContinuation()) {
|
|
51
|
-
const span = this.spanAt(this.pos, this.pos);
|
|
52
|
-
tokens.push({ kind: "Semicolon" /* TokenKind.Semicolon */, span, text: "\n" });
|
|
53
|
-
prevKind = "Semicolon" /* TokenKind.Semicolon */;
|
|
54
|
-
}
|
|
55
|
-
this.advance();
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
// ── Comments ─────────────────────────────────────────────────────────
|
|
59
|
-
if (ch === "/" && this.peek(1) === "/") {
|
|
60
|
-
this.skipLineComment();
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
if (ch === "/" && this.peek(1) === "*") {
|
|
64
|
-
this.skipBlockComment();
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
const tok = this.nextToken();
|
|
68
|
-
if (tok) {
|
|
69
|
-
tokens.push(tok);
|
|
70
|
-
prevKind = tok.kind;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// EOF
|
|
74
|
-
tokens.push({
|
|
75
|
-
kind: "EOF" /* TokenKind.EOF */,
|
|
76
|
-
span: this.spanAt(this.pos, this.pos),
|
|
77
|
-
text: "",
|
|
78
|
-
});
|
|
79
|
-
return tokens;
|
|
80
|
-
}
|
|
81
|
-
// ── Token dispatch ─────────────────────────────────────────────────────────
|
|
82
|
-
nextToken() {
|
|
83
|
-
const start = this.pos;
|
|
84
|
-
const ch = this.src[start];
|
|
85
|
-
if (ch === undefined)
|
|
86
|
-
return null;
|
|
87
|
-
// Identifiers & keywords
|
|
88
|
-
if ((0, chars_js_1.isIdentStart)(ch))
|
|
89
|
-
return this.lexIdentOrKeyword();
|
|
90
|
-
// Numbers
|
|
91
|
-
if ((0, chars_js_1.isDigit)(ch))
|
|
92
|
-
return this.lexNumber();
|
|
93
|
-
// Strings
|
|
94
|
-
if (ch === '"') {
|
|
95
|
-
if (this.peek(1) === '"' && this.peek(2) === '"') {
|
|
96
|
-
return this.lexRawString();
|
|
97
|
-
}
|
|
98
|
-
return this.lexString();
|
|
99
|
-
}
|
|
100
|
-
// Operators & punctuation
|
|
101
|
-
return this.lexSymbol();
|
|
102
|
-
}
|
|
103
|
-
// ── Identifier / keyword ───────────────────────────────────────────────────
|
|
104
|
-
lexIdentOrKeyword() {
|
|
105
|
-
const start = this.pos;
|
|
106
|
-
const startLine = this.line;
|
|
107
|
-
const startCol = this.col;
|
|
108
|
-
while (this.pos < this.src.length && (0, chars_js_1.isIdentContinue)(this.src[this.pos])) {
|
|
109
|
-
this.advance();
|
|
110
|
-
}
|
|
111
|
-
const text = this.src.slice(start, this.pos);
|
|
112
|
-
const span = this.makeSpan(start, startLine, startCol);
|
|
113
|
-
if (text === "_") {
|
|
114
|
-
return { kind: "Underscore" /* TokenKind.Underscore */, span, text };
|
|
115
|
-
}
|
|
116
|
-
const kwKind = Object.prototype.hasOwnProperty.call(tokens_js_1.KEYWORDS, text) ? tokens_js_1.KEYWORDS[text] : undefined;
|
|
117
|
-
if (kwKind !== undefined) {
|
|
118
|
-
switch (kwKind) {
|
|
119
|
-
case "BooleanLiteral" /* TokenKind.BooleanLiteral */:
|
|
120
|
-
return { kind: kwKind, span, text, value: text === "true" };
|
|
121
|
-
case "NullLiteral" /* TokenKind.NullLiteral */:
|
|
122
|
-
return { kind: kwKind, span, text, value: null };
|
|
123
|
-
default:
|
|
124
|
-
return { kind: kwKind, span, text };
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return { kind: "Identifier" /* TokenKind.Identifier */, span, text };
|
|
128
|
-
}
|
|
129
|
-
// ── Number literals ────────────────────────────────────────────────────────
|
|
130
|
-
lexNumber() {
|
|
131
|
-
const start = this.pos;
|
|
132
|
-
const startLine = this.line;
|
|
133
|
-
const startCol = this.col;
|
|
134
|
-
let isHex = false;
|
|
135
|
-
let isBinary = false;
|
|
136
|
-
if (this.src[this.pos] === "0") {
|
|
137
|
-
const next = this.peek(1);
|
|
138
|
-
if (next === "x" || next === "X") {
|
|
139
|
-
this.advance();
|
|
140
|
-
this.advance();
|
|
141
|
-
isHex = true;
|
|
142
|
-
}
|
|
143
|
-
else if (next === "b" || next === "B") {
|
|
144
|
-
this.advance();
|
|
145
|
-
this.advance();
|
|
146
|
-
isBinary = true;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
if (isHex) {
|
|
150
|
-
while (this.pos < this.src.length && (0, chars_js_1.isHexDigit)(this.src[this.pos])) {
|
|
151
|
-
this.advance();
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
else if (isBinary) {
|
|
155
|
-
while (this.pos < this.src.length && (this.src[this.pos] === "0" || this.src[this.pos] === "1")) {
|
|
156
|
-
this.advance();
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
while (this.pos < this.src.length && ((0, chars_js_1.isDigit)(this.src[this.pos]) || this.src[this.pos] === "_")) {
|
|
161
|
-
this.advance();
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
let isFloat = false;
|
|
165
|
-
let isDouble = false;
|
|
166
|
-
if (!isHex && !isBinary) {
|
|
167
|
-
if (this.src[this.pos] === "." && (0, chars_js_1.isDigit)(this.peek(1) ?? "")) {
|
|
168
|
-
isDouble = true;
|
|
169
|
-
this.advance(); // consume '.'
|
|
170
|
-
while (this.pos < this.src.length && ((0, chars_js_1.isDigit)(this.src[this.pos]) || this.src[this.pos] === "_")) {
|
|
171
|
-
this.advance();
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
// Exponent
|
|
175
|
-
if (this.src[this.pos] === "e" || this.src[this.pos] === "E") {
|
|
176
|
-
isDouble = true;
|
|
177
|
-
this.advance();
|
|
178
|
-
if (this.src[this.pos] === "+" || this.src[this.pos] === "-")
|
|
179
|
-
this.advance();
|
|
180
|
-
while (this.pos < this.src.length && (0, chars_js_1.isDigit)(this.src[this.pos]))
|
|
181
|
-
this.advance();
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
let suffix = "";
|
|
185
|
-
const sc = this.src[this.pos];
|
|
186
|
-
if (sc === "L" || sc === "l") {
|
|
187
|
-
suffix = "L";
|
|
188
|
-
this.advance();
|
|
189
|
-
}
|
|
190
|
-
else if (sc === "f" || sc === "F") {
|
|
191
|
-
suffix = "f";
|
|
192
|
-
isFloat = true;
|
|
193
|
-
this.advance();
|
|
194
|
-
}
|
|
195
|
-
else if (sc === "d" || sc === "D") {
|
|
196
|
-
suffix = "d";
|
|
197
|
-
isDouble = true;
|
|
198
|
-
this.advance();
|
|
199
|
-
}
|
|
200
|
-
const text = this.src.slice(start, this.pos);
|
|
201
|
-
const span = this.makeSpan(start, startLine, startCol);
|
|
202
|
-
const clean = text.replace(/_/g, "").replace(/[LlfFdD]$/, "");
|
|
203
|
-
if (suffix === "L") {
|
|
204
|
-
let val;
|
|
205
|
-
try {
|
|
206
|
-
val = BigInt(clean);
|
|
207
|
-
}
|
|
208
|
-
catch {
|
|
209
|
-
this.diag.error(span, diagnostics_js_1.E_INVALID_NUMBER_LITERAL, `Invalid long literal: ${text}`);
|
|
210
|
-
val = 0n;
|
|
211
|
-
}
|
|
212
|
-
return { kind: "LongLiteral" /* TokenKind.LongLiteral */, span, text, value: val };
|
|
213
|
-
}
|
|
214
|
-
if (isFloat) {
|
|
215
|
-
return { kind: "FloatLiteral" /* TokenKind.FloatLiteral */, span, text, value: parseFloat(clean) };
|
|
216
|
-
}
|
|
217
|
-
if (isDouble || text.includes(".") || text.toLowerCase().includes("e")) {
|
|
218
|
-
return { kind: "DoubleLiteral" /* TokenKind.DoubleLiteral */, span, text, value: parseFloat(clean) };
|
|
219
|
-
}
|
|
220
|
-
const intVal = isHex
|
|
221
|
-
? parseInt(clean.slice(2), 16)
|
|
222
|
-
: isBinary
|
|
223
|
-
? parseInt(clean.slice(2), 2)
|
|
224
|
-
: parseInt(clean, 10);
|
|
225
|
-
if (isNaN(intVal)) {
|
|
226
|
-
this.diag.error(span, diagnostics_js_1.E_INVALID_NUMBER_LITERAL, `Invalid integer literal: ${text}`);
|
|
227
|
-
return { kind: "IntLiteral" /* TokenKind.IntLiteral */, span, text, value: 0 };
|
|
228
|
-
}
|
|
229
|
-
return { kind: "IntLiteral" /* TokenKind.IntLiteral */, span, text, value: intVal };
|
|
230
|
-
}
|
|
231
|
-
// ── String literals ────────────────────────────────────────────────────────
|
|
232
|
-
lexString() {
|
|
233
|
-
const start = this.pos;
|
|
234
|
-
const startLine = this.line;
|
|
235
|
-
const startCol = this.col;
|
|
236
|
-
this.advance(); // consume opening "
|
|
237
|
-
let value = "";
|
|
238
|
-
let isTemplate = false;
|
|
239
|
-
let closed = false;
|
|
240
|
-
const templateParts = [];
|
|
241
|
-
while (this.pos < this.src.length) {
|
|
242
|
-
const ch = this.src[this.pos];
|
|
243
|
-
if (ch === "\"") {
|
|
244
|
-
this.advance();
|
|
245
|
-
closed = true;
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
if (ch === "\n") {
|
|
249
|
-
break;
|
|
250
|
-
}
|
|
251
|
-
if (ch === "$") {
|
|
252
|
-
if (this.peek(1) === "{") {
|
|
253
|
-
isTemplate = true;
|
|
254
|
-
templateParts.push(value);
|
|
255
|
-
value = "";
|
|
256
|
-
// We just return the string fragment; the parser handles interpolation
|
|
257
|
-
// by re-lexing nested expressions. For simplicity we encode templates
|
|
258
|
-
// into a single StringTemplateExpr token with the raw text.
|
|
259
|
-
// Consume ${ block
|
|
260
|
-
this.advance();
|
|
261
|
-
this.advance();
|
|
262
|
-
let depth = 1;
|
|
263
|
-
let exprText = "";
|
|
264
|
-
while (this.pos < this.src.length && depth > 0) {
|
|
265
|
-
const ec = this.src[this.pos];
|
|
266
|
-
if (ec === "{")
|
|
267
|
-
depth++;
|
|
268
|
-
else if (ec === "}") {
|
|
269
|
-
depth--;
|
|
270
|
-
if (depth === 0) {
|
|
271
|
-
this.advance();
|
|
272
|
-
break;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
exprText += ec;
|
|
276
|
-
this.advance();
|
|
277
|
-
}
|
|
278
|
-
templateParts.push(`\${${exprText}}`);
|
|
279
|
-
}
|
|
280
|
-
else if ((0, chars_js_1.isIdentStart)(this.peek(1) ?? "")) {
|
|
281
|
-
isTemplate = true;
|
|
282
|
-
templateParts.push(value);
|
|
283
|
-
value = "";
|
|
284
|
-
this.advance(); // $
|
|
285
|
-
const identStart = this.pos;
|
|
286
|
-
while (this.pos < this.src.length && (0, chars_js_1.isIdentContinue)(this.src[this.pos]))
|
|
287
|
-
this.advance();
|
|
288
|
-
const ident = this.src.slice(identStart, this.pos);
|
|
289
|
-
templateParts.push(`\${${ident}}`);
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
value += ch;
|
|
293
|
-
this.advance();
|
|
294
|
-
}
|
|
295
|
-
continue;
|
|
296
|
-
}
|
|
297
|
-
if (ch === "\\") {
|
|
298
|
-
const span = this.makeSpan(this.pos, this.line, this.col);
|
|
299
|
-
value += this.parseEscape(span);
|
|
300
|
-
continue;
|
|
301
|
-
}
|
|
302
|
-
value += ch;
|
|
303
|
-
this.advance();
|
|
304
|
-
}
|
|
305
|
-
if (!closed) {
|
|
306
|
-
const span = this.makeSpan(start, startLine, startCol);
|
|
307
|
-
this.diag.error(span, diagnostics_js_1.E_UNTERMINATED_STRING, "Unterminated string literal");
|
|
308
|
-
}
|
|
309
|
-
const text = this.src.slice(start, this.pos);
|
|
310
|
-
const span = this.makeSpan(start, startLine, startCol);
|
|
311
|
-
if (isTemplate) {
|
|
312
|
-
const raw = templateParts.join("") + value;
|
|
313
|
-
return { kind: "StringLiteral" /* TokenKind.StringLiteral */, span, text, value: raw };
|
|
314
|
-
}
|
|
315
|
-
return { kind: "StringLiteral" /* TokenKind.StringLiteral */, span, text, value };
|
|
316
|
-
}
|
|
317
|
-
lexRawString() {
|
|
318
|
-
const start = this.pos;
|
|
319
|
-
const startLine = this.line;
|
|
320
|
-
const startCol = this.col;
|
|
321
|
-
this.advance();
|
|
322
|
-
this.advance();
|
|
323
|
-
this.advance(); // consume """
|
|
324
|
-
let value = "";
|
|
325
|
-
let closed = false;
|
|
326
|
-
while (this.pos < this.src.length) {
|
|
327
|
-
if (this.src[this.pos] === '"' && this.peek(1) === '"' && this.peek(2) === '"') {
|
|
328
|
-
this.advance();
|
|
329
|
-
this.advance();
|
|
330
|
-
this.advance();
|
|
331
|
-
closed = true;
|
|
332
|
-
break;
|
|
333
|
-
}
|
|
334
|
-
const c = this.src[this.pos];
|
|
335
|
-
if (c === "\n")
|
|
336
|
-
this.line++; // no col tracking in raw strings
|
|
337
|
-
value += c;
|
|
338
|
-
this.advance();
|
|
339
|
-
}
|
|
340
|
-
if (!closed) {
|
|
341
|
-
const span = this.makeSpan(start, startLine, startCol);
|
|
342
|
-
this.diag.error(span, diagnostics_js_1.E_UNTERMINATED_STRING, "Unterminated triple-quoted string");
|
|
343
|
-
}
|
|
344
|
-
const text = this.src.slice(start, this.pos);
|
|
345
|
-
const span = this.makeSpan(start, startLine, startCol);
|
|
346
|
-
return { kind: "RawStringLiteral" /* TokenKind.RawStringLiteral */, span, text, value };
|
|
347
|
-
}
|
|
348
|
-
parseEscape(span) {
|
|
349
|
-
this.advance(); // consume backslash
|
|
350
|
-
const ec = this.src[this.pos];
|
|
351
|
-
this.advance();
|
|
352
|
-
switch (ec) {
|
|
353
|
-
case "n": return "\n";
|
|
354
|
-
case "t": return "\t";
|
|
355
|
-
case "r": return "\r";
|
|
356
|
-
case "\\": return "\\";
|
|
357
|
-
case '"': return '"';
|
|
358
|
-
case "'": return "'";
|
|
359
|
-
case "0": return "\0";
|
|
360
|
-
case "$": return "$";
|
|
361
|
-
case "u": {
|
|
362
|
-
if (this.src[this.pos] === "{") {
|
|
363
|
-
this.advance(); // {
|
|
364
|
-
let hex = "";
|
|
365
|
-
while (this.pos < this.src.length && this.src[this.pos] !== "}") {
|
|
366
|
-
hex += this.src[this.pos];
|
|
367
|
-
this.advance();
|
|
368
|
-
}
|
|
369
|
-
this.advance(); // }
|
|
370
|
-
const cp = parseInt(hex, 16);
|
|
371
|
-
if (isNaN(cp)) {
|
|
372
|
-
this.diag.error(span, diagnostics_js_1.E_INVALID_ESCAPE, `Invalid unicode escape: \\u{${hex}}`);
|
|
373
|
-
return "";
|
|
374
|
-
}
|
|
375
|
-
return String.fromCodePoint(cp);
|
|
376
|
-
}
|
|
377
|
-
// \uXXXX
|
|
378
|
-
let hex = "";
|
|
379
|
-
for (let i = 0; i < 4; i++) {
|
|
380
|
-
hex += this.src[this.pos] ?? "";
|
|
381
|
-
this.advance();
|
|
382
|
-
}
|
|
383
|
-
const cp = parseInt(hex, 16);
|
|
384
|
-
if (isNaN(cp)) {
|
|
385
|
-
this.diag.error(span, diagnostics_js_1.E_INVALID_ESCAPE, `Invalid unicode escape: \\u${hex}`);
|
|
386
|
-
return "";
|
|
387
|
-
}
|
|
388
|
-
return String.fromCharCode(cp);
|
|
389
|
-
}
|
|
390
|
-
default:
|
|
391
|
-
this.diag.error(span, diagnostics_js_1.E_INVALID_ESCAPE, `Unknown escape sequence: \\${ec ?? ""}`);
|
|
392
|
-
return ec ?? "";
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
// ── Symbols & operators ────────────────────────────────────────────────────
|
|
396
|
-
lexSymbol() {
|
|
397
|
-
const start = this.pos;
|
|
398
|
-
const startLine = this.line;
|
|
399
|
-
const startCol = this.col;
|
|
400
|
-
const ch = this.src[this.pos];
|
|
401
|
-
const next = this.peek(1);
|
|
402
|
-
const next2 = this.peek(2);
|
|
403
|
-
const mk = (kind, len) => {
|
|
404
|
-
const text = this.src.slice(start, start + len);
|
|
405
|
-
for (let i = 0; i < len; i++)
|
|
406
|
-
this.advance();
|
|
407
|
-
return { kind, span: this.makeSpan(start, startLine, startCol), text };
|
|
408
|
-
};
|
|
409
|
-
switch (ch) {
|
|
410
|
-
case "+":
|
|
411
|
-
if (next === "+")
|
|
412
|
-
return mk("PlusPlus" /* TokenKind.PlusPlus */, 2);
|
|
413
|
-
if (next === "=")
|
|
414
|
-
return mk("PlusEq" /* TokenKind.PlusEq */, 2);
|
|
415
|
-
return mk("Plus" /* TokenKind.Plus */, 1);
|
|
416
|
-
case "-":
|
|
417
|
-
if (next === "-")
|
|
418
|
-
return mk("MinusMinus" /* TokenKind.MinusMinus */, 2);
|
|
419
|
-
if (next === "=")
|
|
420
|
-
return mk("MinusEq" /* TokenKind.MinusEq */, 2);
|
|
421
|
-
if (next === ">")
|
|
422
|
-
return mk("Arrow" /* TokenKind.Arrow */, 2);
|
|
423
|
-
return mk("Minus" /* TokenKind.Minus */, 1);
|
|
424
|
-
case "*":
|
|
425
|
-
if (next === "=")
|
|
426
|
-
return mk("StarEq" /* TokenKind.StarEq */, 2);
|
|
427
|
-
return mk("Star" /* TokenKind.Star */, 1);
|
|
428
|
-
case "/":
|
|
429
|
-
if (next === "=")
|
|
430
|
-
return mk("SlashEq" /* TokenKind.SlashEq */, 2);
|
|
431
|
-
return mk("Slash" /* TokenKind.Slash */, 1);
|
|
432
|
-
case "%":
|
|
433
|
-
if (next === "=")
|
|
434
|
-
return mk("PercentEq" /* TokenKind.PercentEq */, 2);
|
|
435
|
-
return mk("Percent" /* TokenKind.Percent */, 1);
|
|
436
|
-
case "=":
|
|
437
|
-
if (next === "=" && next2 === "=")
|
|
438
|
-
return mk("EqEqEq" /* TokenKind.EqEqEq */, 3);
|
|
439
|
-
if (next === "=")
|
|
440
|
-
return mk("EqEq" /* TokenKind.EqEq */, 2);
|
|
441
|
-
if (next === ">")
|
|
442
|
-
return mk("FatArrow" /* TokenKind.FatArrow */, 2);
|
|
443
|
-
return mk("Eq" /* TokenKind.Eq */, 1);
|
|
444
|
-
case "!":
|
|
445
|
-
if (next === "=" && next2 === "=")
|
|
446
|
-
return mk("BangEqEq" /* TokenKind.BangEqEq */, 3);
|
|
447
|
-
if (next === "=")
|
|
448
|
-
return mk("BangEq" /* TokenKind.BangEq */, 2);
|
|
449
|
-
if (next === "!")
|
|
450
|
-
return mk("BangBang" /* TokenKind.BangBang */, 2);
|
|
451
|
-
return mk("Bang" /* TokenKind.Bang */, 1);
|
|
452
|
-
case "<":
|
|
453
|
-
if (next === "=")
|
|
454
|
-
return mk("LtEq" /* TokenKind.LtEq */, 2);
|
|
455
|
-
return mk("Lt" /* TokenKind.Lt */, 1);
|
|
456
|
-
case ">":
|
|
457
|
-
if (next === "=")
|
|
458
|
-
return mk("GtEq" /* TokenKind.GtEq */, 2);
|
|
459
|
-
return mk("Gt" /* TokenKind.Gt */, 1);
|
|
460
|
-
case "&":
|
|
461
|
-
if (next === "&")
|
|
462
|
-
return mk("AmpAmp" /* TokenKind.AmpAmp */, 2);
|
|
463
|
-
return mk("Amp" /* TokenKind.Amp */, 1);
|
|
464
|
-
case "|":
|
|
465
|
-
if (next === "|")
|
|
466
|
-
return mk("PipePipe" /* TokenKind.PipePipe */, 2);
|
|
467
|
-
return mk("Pipe" /* TokenKind.Pipe */, 1);
|
|
468
|
-
case "?":
|
|
469
|
-
if (next === ":")
|
|
470
|
-
return mk("QuestionColon" /* TokenKind.QuestionColon */, 2);
|
|
471
|
-
if (next === ".")
|
|
472
|
-
return mk("QuestionDot" /* TokenKind.QuestionDot */, 2);
|
|
473
|
-
return mk("Question" /* TokenKind.Question */, 1);
|
|
474
|
-
case ".":
|
|
475
|
-
if (next === "." && next2 === "<")
|
|
476
|
-
return mk("DotDotLt" /* TokenKind.DotDotLt */, 3);
|
|
477
|
-
if (next === ".")
|
|
478
|
-
return mk("DotDot" /* TokenKind.DotDot */, 2);
|
|
479
|
-
return mk("Dot" /* TokenKind.Dot */, 1);
|
|
480
|
-
case ":":
|
|
481
|
-
if (next === ":")
|
|
482
|
-
return mk("ColonColon" /* TokenKind.ColonColon */, 2);
|
|
483
|
-
return mk("Colon" /* TokenKind.Colon */, 1);
|
|
484
|
-
case "{": return mk("LBrace" /* TokenKind.LBrace */, 1);
|
|
485
|
-
case "}": return mk("RBrace" /* TokenKind.RBrace */, 1);
|
|
486
|
-
case "(": return mk("LParen" /* TokenKind.LParen */, 1);
|
|
487
|
-
case ")": return mk("RParen" /* TokenKind.RParen */, 1);
|
|
488
|
-
case "[": return mk("LBracket" /* TokenKind.LBracket */, 1);
|
|
489
|
-
case "]": return mk("RBracket" /* TokenKind.RBracket */, 1);
|
|
490
|
-
case ",": return mk("Comma" /* TokenKind.Comma */, 1);
|
|
491
|
-
case ";": return mk("Semicolon" /* TokenKind.Semicolon */, 1);
|
|
492
|
-
case "@": return mk("At" /* TokenKind.At */, 1);
|
|
493
|
-
case "#": return mk("Hash" /* TokenKind.Hash */, 1);
|
|
494
|
-
case "^": return mk("Caret" /* TokenKind.Caret */, 1);
|
|
495
|
-
case "~": return mk("Tilde" /* TokenKind.Tilde */, 1);
|
|
496
|
-
default: {
|
|
497
|
-
const span = this.makeSpan(start, startLine, startCol);
|
|
498
|
-
this.diag.error(span, diagnostics_js_1.E_UNEXPECTED_CHAR, `Unexpected character: '${ch}'`);
|
|
499
|
-
this.advance();
|
|
500
|
-
return null;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
// ── Comment skipping ───────────────────────────────────────────────────────
|
|
505
|
-
skipLineComment() {
|
|
506
|
-
while (this.pos < this.src.length && this.src[this.pos] !== "\n") {
|
|
507
|
-
this.advance();
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
skipBlockComment() {
|
|
511
|
-
const start = this.pos;
|
|
512
|
-
const startLine = this.line;
|
|
513
|
-
const startCol = this.col;
|
|
514
|
-
this.advance();
|
|
515
|
-
this.advance(); // /*
|
|
516
|
-
let depth = 1;
|
|
517
|
-
while (this.pos < this.src.length) {
|
|
518
|
-
const c = this.src[this.pos];
|
|
519
|
-
if (c === "/" && this.peek(1) === "*") {
|
|
520
|
-
depth++;
|
|
521
|
-
this.advance();
|
|
522
|
-
this.advance();
|
|
523
|
-
continue;
|
|
524
|
-
}
|
|
525
|
-
if (c === "*" && this.peek(1) === "/") {
|
|
526
|
-
depth--;
|
|
527
|
-
this.advance();
|
|
528
|
-
this.advance();
|
|
529
|
-
if (depth === 0)
|
|
530
|
-
return;
|
|
531
|
-
continue;
|
|
532
|
-
}
|
|
533
|
-
this.advance();
|
|
534
|
-
}
|
|
535
|
-
const span = this.makeSpan(start, startLine, startCol);
|
|
536
|
-
this.diag.error(span, diagnostics_js_1.E_UNTERMINATED_BLOCK_COMMENT, "Unterminated block comment");
|
|
537
|
-
}
|
|
538
|
-
// ── Utilities ──────────────────────────────────────────────────────────────
|
|
539
|
-
/**
|
|
540
|
-
* Returns true when the next meaningful token after the current newline is a
|
|
541
|
-
* dot-continuation (`.member` or `?.member`), in which case ASI must NOT be
|
|
542
|
-
* inserted so that multi-line method chains work correctly.
|
|
543
|
-
*
|
|
544
|
-
* Scans forward from `this.pos + 1` skipping whitespace and comments.
|
|
545
|
-
* `.` followed by another `.` (range `..`) does NOT count as continuation.
|
|
546
|
-
*/
|
|
547
|
-
peekIsDotContinuation() {
|
|
548
|
-
let i = this.pos + 1; // start after current '\n'
|
|
549
|
-
while (i < this.src.length) {
|
|
550
|
-
const c = this.src[i];
|
|
551
|
-
// Skip horizontal whitespace and additional newlines
|
|
552
|
-
if (c === " " || c === "\t" || c === "\r" || c === "\n") {
|
|
553
|
-
i++;
|
|
554
|
-
continue;
|
|
555
|
-
}
|
|
556
|
-
// Skip line comment
|
|
557
|
-
if (c === "/" && this.src[i + 1] === "/") {
|
|
558
|
-
while (i < this.src.length && this.src[i] !== "\n")
|
|
559
|
-
i++;
|
|
560
|
-
continue;
|
|
561
|
-
}
|
|
562
|
-
// Skip block comment
|
|
563
|
-
if (c === "/" && this.src[i + 1] === "*") {
|
|
564
|
-
i += 2;
|
|
565
|
-
while (i < this.src.length - 1) {
|
|
566
|
-
if (this.src[i] === "*" && this.src[i + 1] === "/") {
|
|
567
|
-
i += 2;
|
|
568
|
-
break;
|
|
569
|
-
}
|
|
570
|
-
i++;
|
|
571
|
-
}
|
|
572
|
-
continue;
|
|
573
|
-
}
|
|
574
|
-
// Single '.' that is NOT part of '..' (range) is a chain continuation
|
|
575
|
-
if (c === ".") {
|
|
576
|
-
return this.src[i + 1] !== ".";
|
|
577
|
-
}
|
|
578
|
-
// '?.' is a safe-call chain continuation
|
|
579
|
-
if (c === "?" && this.src[i + 1] === ".") {
|
|
580
|
-
return true;
|
|
581
|
-
}
|
|
582
|
-
// Any other character — not a chain continuation
|
|
583
|
-
return false;
|
|
584
|
-
}
|
|
585
|
-
return false;
|
|
586
|
-
}
|
|
587
|
-
advance() {
|
|
588
|
-
if (this.src[this.pos] === "\n") {
|
|
589
|
-
this.line++;
|
|
590
|
-
this.col = 0;
|
|
591
|
-
}
|
|
592
|
-
else {
|
|
593
|
-
this.col++;
|
|
594
|
-
}
|
|
595
|
-
this.pos++;
|
|
596
|
-
}
|
|
597
|
-
peek(offset) {
|
|
598
|
-
return this.src[this.pos + offset];
|
|
599
|
-
}
|
|
600
|
-
spanAt(startPos, endPos) {
|
|
601
|
-
return {
|
|
602
|
-
file: this.file,
|
|
603
|
-
startLine: this.line,
|
|
604
|
-
startCol: this.col,
|
|
605
|
-
endLine: this.line,
|
|
606
|
-
endCol: this.col,
|
|
607
|
-
startOffset: startPos,
|
|
608
|
-
endOffset: endPos,
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
makeSpan(startOffset, startLine, startCol) {
|
|
612
|
-
return {
|
|
613
|
-
file: this.file,
|
|
614
|
-
startLine,
|
|
615
|
-
startCol,
|
|
616
|
-
endLine: this.line,
|
|
617
|
-
endCol: this.col,
|
|
618
|
-
startOffset,
|
|
619
|
-
endOffset: this.pos,
|
|
620
|
-
};
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
exports.Lexer = Lexer;
|
|
624
|
-
// ---------------------------------------------------------------------------
|
|
625
|
-
// Public helper: lex a source file
|
|
626
|
-
// ---------------------------------------------------------------------------
|
|
627
|
-
function lex(src, file, diag) {
|
|
628
|
-
return new Lexer(src, file, diag).tokenize();
|
|
629
|
-
}
|
|
630
|
-
//# sourceMappingURL=index.js.map
|