@xnoxs/flux-lang 3.1.1

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +1089 -0
  3. package/bin/flux.js +1397 -0
  4. package/dist/flux.cjs.js +6664 -0
  5. package/dist/flux.esm.js +6674 -0
  6. package/dist/flux.min.js +263 -0
  7. package/index.d.ts +202 -0
  8. package/index.js +26 -0
  9. package/package.json +77 -0
  10. package/scripts/build.js +76 -0
  11. package/src/bundler.js +216 -0
  12. package/src/checker.js +322 -0
  13. package/src/codegen.js +785 -0
  14. package/src/css-preprocessor.js +399 -0
  15. package/src/formatter.js +140 -0
  16. package/src/jsx.js +480 -0
  17. package/src/lexer.js +518 -0
  18. package/src/linter.js +758 -0
  19. package/src/mangler.js +280 -0
  20. package/src/parser.js +1671 -0
  21. package/src/self/bundler.flux +167 -0
  22. package/src/self/bundler.js +187 -0
  23. package/src/self/checker.flux +249 -0
  24. package/src/self/checker.js +338 -0
  25. package/src/self/codegen.flux +555 -0
  26. package/src/self/codegen.js +784 -0
  27. package/src/self/css-preprocessor.flux +373 -0
  28. package/src/self/css-preprocessor.js +387 -0
  29. package/src/self/formatter.flux +93 -0
  30. package/src/self/formatter.js +114 -0
  31. package/src/self/jsx.flux +430 -0
  32. package/src/self/jsx.js +396 -0
  33. package/src/self/lexer.flux +529 -0
  34. package/src/self/lexer.js +709 -0
  35. package/src/self/lexer.stage2.js +700 -0
  36. package/src/self/linter.flux +515 -0
  37. package/src/self/linter.js +804 -0
  38. package/src/self/mangler.flux +253 -0
  39. package/src/self/mangler.js +348 -0
  40. package/src/self/parser.flux +1146 -0
  41. package/src/self/parser.js +1571 -0
  42. package/src/self/sourcemap.flux +66 -0
  43. package/src/self/sourcemap.js +72 -0
  44. package/src/self/stdlib.flux +356 -0
  45. package/src/self/stdlib.js +396 -0
  46. package/src/self/test-runner.flux +201 -0
  47. package/src/self/test-runner.js +132 -0
  48. package/src/self/transpiler.flux +123 -0
  49. package/src/self/transpiler.js +83 -0
  50. package/src/self/type-checker.flux +821 -0
  51. package/src/self/type-checker.js +1106 -0
  52. package/src/sourcemap.js +82 -0
  53. package/src/stdlib.js +436 -0
  54. package/src/test-runner.js +239 -0
  55. package/src/transpiler.js +172 -0
  56. package/src/type-checker.js +1206 -0
@@ -0,0 +1,529 @@
1
+ // ============================================================
2
+ // Flux Self-Hosted Lexer
3
+ // src/self/lexer.flux — written in Flux, compiled by stage-0
4
+ // ============================================================
5
+
6
+ export val T = {
7
+ NUMBER: 'NUMBER', STRING: 'STRING', BOOL: 'BOOL', NULL: 'NULL', IDENT: 'IDENT',
8
+ VAR: 'VAR', VAL: 'VAL', FN: 'FN', RETURN: 'RETURN',
9
+ IF: 'IF', ELSE: 'ELSE', FOR: 'FOR', IN: 'IN',
10
+ WHILE: 'WHILE', BREAK: 'BREAK', CONTINUE: 'CONTINUE', DO: 'DO',
11
+ CLASS: 'CLASS', EXTENDS: 'EXTENDS', SELF: 'SELF', NEW: 'NEW',
12
+ INTERFACE: 'INTERFACE', IMPLEMENTS: 'IMPLEMENTS',
13
+ PRIVATE: 'PRIVATE', PUBLIC: 'PUBLIC', PROTECTED: 'PROTECTED',
14
+ READONLY: 'READONLY', STATIC: 'STATIC', ABSTRACT: 'ABSTRACT', OVERRIDE: 'OVERRIDE',
15
+ MATCH: 'MATCH', WHEN: 'WHEN',
16
+ IMPORT: 'IMPORT', EXPORT: 'EXPORT', FROM: 'FROM', AS: 'AS', DEFAULT: 'DEFAULT',
17
+ AND: 'AND', OR: 'OR', NOT: 'NOT',
18
+ ASYNC: 'ASYNC', AWAIT: 'AWAIT',
19
+ TRY: 'TRY', CATCH: 'CATCH', FINALLY: 'FINALLY', THROW: 'THROW',
20
+ TYPEOF: 'TYPEOF', INSTANCEOF: 'INSTANCEOF', TYPE: 'TYPE',
21
+ ENUM: 'ENUM', SATISFIES: 'SATISFIES', IS: 'IS', CONST: 'CONST',
22
+ PLUS: 'PLUS', MINUS: 'MINUS', STAR: 'STAR', SLASH: 'SLASH', PERCENT: 'PERCENT',
23
+ REGEX: 'REGEX', STARSTAR: 'STARSTAR',
24
+ EQ: 'EQ', EQEQ: 'EQEQ', NEQ: 'NEQ', EQEQEQ: 'EQEQEQ', NEQEQ: 'NEQEQ',
25
+ LT: 'LT', LTE: 'LTE', GT: 'GT', GTE: 'GTE',
26
+ PLUSEQ: 'PLUSEQ', MINUSEQ: 'MINUSEQ', STAREQ: 'STAREQ', SLASHEQ: 'SLASHEQ',
27
+ PERCENTEQ: 'PERCENTEQ',
28
+ PLUSPLUS: 'PLUSPLUS', MINUSMINUS: 'MINUSMINUS',
29
+ AMPERSAND: 'AMPERSAND', ANDAND: 'ANDAND',
30
+ PIPEB: 'PIPEB', OROR: 'OROR',
31
+ CARET: 'CARET', TILDE: 'TILDE', LSHIFT: 'LSHIFT', RSHIFT: 'RSHIFT',
32
+ ARROW: 'ARROW', FATARROW: 'FATARROW',
33
+ PIPE: 'PIPE', DOTDOT: 'DOTDOT', DOTDOTDOT: 'DOTDOTDOT',
34
+ WILDCARD: 'WILDCARD', NULLISH: 'NULLISH',
35
+ QUESTIONDOT: 'QUESTIONDOT', BANG: 'BANG', AT: 'AT',
36
+ LPAREN: 'LPAREN', RPAREN: 'RPAREN',
37
+ LBRACKET: 'LBRACKET', RBRACKET: 'RBRACKET',
38
+ LBRACE: 'LBRACE', RBRACE: 'RBRACE',
39
+ COMMA: 'COMMA', DOT: 'DOT', COLON: 'COLON', QUESTION: 'QUESTION',
40
+ NEWLINE: 'NEWLINE', INDENT: 'INDENT', DEDENT: 'DEDENT', EOF: 'EOF',
41
+ }
42
+
43
+ export val TokenType = T
44
+
45
+ val KEYWORDS = {
46
+ "var": 'VAR', "val": 'VAL', "fn": 'FN', "return": 'RETURN',
47
+ "if": 'IF', "else": 'ELSE', "for": 'FOR', "in": 'IN',
48
+ "while": 'WHILE', "break": 'BREAK', "continue": 'CONTINUE', "do": 'DO',
49
+ "class": 'CLASS', "extends": 'EXTENDS', "self": 'SELF', "new": 'NEW',
50
+ "interface": 'INTERFACE', "implements": 'IMPLEMENTS',
51
+ "private": 'PRIVATE', "public": 'PUBLIC', "protected": 'PROTECTED',
52
+ "readonly": 'READONLY', "static": 'STATIC', "abstract": 'ABSTRACT', "override": 'OVERRIDE',
53
+ "match": 'MATCH', "when": 'WHEN',
54
+ "import": 'IMPORT', "export": 'EXPORT', "from": 'FROM', "as": 'AS', "default": 'DEFAULT',
55
+ "and": 'AND', "or": 'OR', "not": 'NOT',
56
+ "async": 'ASYNC', "await": 'AWAIT',
57
+ "try": 'TRY', "catch": 'CATCH', "finally": 'FINALLY', "throw": 'THROW',
58
+ "typeof": 'TYPEOF', "instanceof": 'INSTANCEOF', "type": 'TYPE',
59
+ "enum": 'ENUM', "satisfies": 'SATISFIES', "is": 'IS', "const": 'CONST',
60
+ "true": '__TRUE__', "false": '__FALSE__', "null": '__NULL__',
61
+ }
62
+
63
+ export class Lexer:
64
+ src: string
65
+ pos: int
66
+ line: int
67
+ col: int
68
+ tokens: any[]
69
+ indentStack: any[]
70
+ nestDepth: int
71
+
72
+ fn err(msg):
73
+ throw new Error("[Lexer {self.line}:{self.col}] {msg}")
74
+
75
+ fn ch(n = 0):
76
+ return self.src[self.pos + n] ?? ''
77
+
78
+ fn adv():
79
+ val c = self.src[self.pos]
80
+ self.pos = self.pos + 1
81
+ if c == '\n':
82
+ self.line = self.line + 1
83
+ self.col = 1
84
+ else:
85
+ self.col = self.col + 1
86
+ return c
87
+
88
+ fn tok(typ, value, l, c):
89
+ val v = value != undefined ? value : typ
90
+ val ln = l != undefined ? l : self.line
91
+ val co = c != undefined ? c : self.col
92
+ self.tokens.push({ type: typ, value: v, line: ln, col: co })
93
+
94
+ fn applyIndent(indent):
95
+ if self.nestDepth > 0: return
96
+ val top = self.indentStack[self.indentStack.length - 1]
97
+ if indent > top:
98
+ self.indentStack.push(indent)
99
+ self.tok(T.INDENT, indent, self.line, 1)
100
+ else if indent < top:
101
+ while self.indentStack.length > 1 and self.indentStack[self.indentStack.length - 1] > indent:
102
+ self.indentStack.pop()
103
+ self.tok(T.DEDENT, null, self.line, 1)
104
+ if self.indentStack[self.indentStack.length - 1] != indent:
105
+ self.err("Inconsistent indentation ({indent} spaces)")
106
+
107
+ fn scanBacktick(l, c):
108
+ self.adv()
109
+ var s = ''
110
+ while self.pos < self.src.length and self.ch() != '`':
111
+ if self.ch() == '\\':
112
+ self.adv()
113
+ val e = self.adv()
114
+ if e == 'n': s += '\n'
115
+ else if e == 't': s += '\t'
116
+ else if e == '`': s += '`'
117
+ else if e == '\\': s += '\\'
118
+ else: s += '\\' + e
119
+ else:
120
+ s += self.adv()
121
+ if self.ch() != '`': self.err('Unterminated backtick string')
122
+ self.adv()
123
+ val rawLines = s.split('\n')
124
+ val trimmed = rawLines.map(ln -> ln.trimEnd())
125
+ while trimmed.length and not trimmed[0].trim(): trimmed.shift()
126
+ while trimmed.length and not trimmed[trimmed.length - 1].trim(): trimmed.pop()
127
+ var minIndent = 999999
128
+ for ln in trimmed:
129
+ if ln.trim():
130
+ val m = ln.match(/^(\s*)/)
131
+ val ind = m[1].length
132
+ if ind < minIndent: minIndent = ind
133
+ if minIndent == 999999: minIndent = 0
134
+ val dedented = trimmed.map(ln -> ln.slice(minIndent))
135
+ self.tok(T.STRING, dedented.join('\n'), l, c)
136
+
137
+ fn scanStr(l, c):
138
+ self.adv()
139
+ val parts = []
140
+ var text = ''
141
+ while self.pos < self.src.length and self.ch() != '"':
142
+ if self.ch() == '\\':
143
+ self.adv()
144
+ val e = self.adv()
145
+ if e == 'n': text += '\n'
146
+ else if e == 't': text += '\t'
147
+ else if e == '"': text += '"'
148
+ else if e == "'": text += "'"
149
+ else if e == '\\': text += '\\'
150
+ else if e == '{': text += '{'
151
+ else if e == '}': text += '}'
152
+ else: text += '\\' + e
153
+ else if self.ch() == '{':
154
+ parts.push({ type: 'text', value: text })
155
+ text = ''
156
+ self.adv()
157
+ var depth = 1
158
+ var expr = ''
159
+ while self.pos < self.src.length:
160
+ val cc = self.ch()
161
+ if cc == '{': depth = depth + 1
162
+ if cc == '}':
163
+ depth = depth - 1
164
+ if depth == 0: break
165
+ if cc == '\\' and (self.src[self.pos + 1] == '"' or self.src[self.pos + 1] == "'"):
166
+ self.adv()
167
+ expr += self.adv()
168
+ else:
169
+ expr += self.adv()
170
+ if self.ch() != '}': self.err('Unclosed string interpolation')
171
+ self.adv()
172
+ parts.push({ type: 'expr', value: expr.trim() })
173
+ else:
174
+ text += self.adv()
175
+ if self.ch() != '"': self.err('Unterminated string')
176
+ self.adv()
177
+ parts.push({ type: 'text', value: text })
178
+ if parts.some(p -> p.type == 'expr'):
179
+ self.tok(T.STRING, { template: true, parts }, l, c)
180
+ else:
181
+ self.tok(T.STRING, parts.map(p -> p.value).join(''), l, c)
182
+
183
+ fn scanRegexBody(l, c):
184
+ var pattern = ''
185
+ var inClass = false
186
+ while self.pos < self.src.length:
187
+ val ch = self.ch()
188
+ if ch == '\n': self.err('Unterminated regex literal')
189
+ if ch == '\\':
190
+ pattern += self.adv()
191
+ if self.pos < self.src.length: pattern += self.adv()
192
+ continue
193
+ if ch == '[':
194
+ inClass = true
195
+ pattern += self.adv()
196
+ continue
197
+ if ch == ']':
198
+ inClass = false
199
+ pattern += self.adv()
200
+ continue
201
+ if ch == '/' and not inClass: break
202
+ pattern += self.adv()
203
+ if self.ch() != '/': self.err('Unterminated regex literal')
204
+ self.adv()
205
+ var flags = ''
206
+ while /[gimsuy]/.test(self.ch()): flags += self.adv()
207
+ self.tok(T.REGEX, { pattern, flags }, l, c)
208
+
209
+ fn scanBlockComment():
210
+ self.adv()
211
+ self.adv()
212
+ while self.pos < self.src.length:
213
+ if self.ch() == '*' and self.ch(1) == '/':
214
+ self.adv()
215
+ self.adv()
216
+ return
217
+ self.adv()
218
+ self.err('Unterminated block comment')
219
+
220
+ fn tokenize():
221
+ var bol = true
222
+ while self.pos < self.src.length:
223
+ if bol:
224
+ bol = false
225
+ var indent = 0
226
+ while self.ch() == ' ' or self.ch() == '\t':
227
+ if self.ch() == '\t':
228
+ indent = indent + 4
229
+ else:
230
+ indent = indent + 1
231
+ self.adv()
232
+ if self.ch() == '\n' or not self.ch():
233
+ if self.ch() == '\n':
234
+ self.adv()
235
+ bol = true
236
+ continue
237
+ if self.ch() == '/' and self.ch(1) == '/':
238
+ while self.pos < self.src.length and self.ch() != '\n': self.adv()
239
+ if self.ch() == '\n':
240
+ self.adv()
241
+ bol = true
242
+ continue
243
+ if self.ch() == '/' and self.ch(1) == '*':
244
+ self.scanBlockComment()
245
+ continue
246
+ val isContinuation = (self.ch() == '|' and self.ch(1) == '>') or self.ch() == '.' or (self.ch() == '?' and self.ch(1) == '.')
247
+ if isContinuation:
248
+ val lastTok = self.tokens[self.tokens.length - 1]
249
+ if lastTok and lastTok.type == T.NEWLINE: self.tokens.pop()
250
+ else:
251
+ self.applyIndent(indent)
252
+
253
+ val l = self.line
254
+ val c = self.col
255
+ val cur = self.ch()
256
+
257
+ if cur == '\n':
258
+ self.adv()
259
+ bol = true
260
+ if self.nestDepth == 0:
261
+ val lastTok = self.tokens[self.tokens.length - 1]
262
+ if lastTok and lastTok.type != T.NEWLINE and lastTok.type != T.INDENT and lastTok.type != T.DEDENT:
263
+ self.tok(T.NEWLINE, null, l, c)
264
+ continue
265
+
266
+ if cur == ' ' or cur == '\t':
267
+ self.adv()
268
+ continue
269
+
270
+ if cur == '/' and self.ch(1) == '/':
271
+ while self.pos < self.src.length and self.ch() != '\n': self.adv()
272
+ continue
273
+
274
+ if cur == '/' and self.ch(1) == '*':
275
+ self.scanBlockComment()
276
+ continue
277
+
278
+ if cur >= '0' and cur <= '9':
279
+ if cur == '0' and (self.ch(1) == 'x' or self.ch(1) == 'X'):
280
+ self.adv()
281
+ self.adv()
282
+ var h = ''
283
+ while /[0-9a-fA-F_]/.test(self.ch()):
284
+ val hc = self.adv()
285
+ if hc != '_': h += hc
286
+ self.tok(T.NUMBER, parseInt(h.length > 0 ? h : '0', 16), l, c)
287
+ continue
288
+ if cur == '0' and (self.ch(1) == 'b' or self.ch(1) == 'B'):
289
+ self.adv()
290
+ self.adv()
291
+ var b = ''
292
+ while /[01_]/.test(self.ch()):
293
+ val bc = self.adv()
294
+ if bc != '_': b += bc
295
+ self.tok(T.NUMBER, parseInt(b.length > 0 ? b : '0', 2), l, c)
296
+ continue
297
+ var numStr = ''
298
+ while self.pos < self.src.length:
299
+ if self.ch() == '.' and self.ch(1) == '.': break
300
+ if (self.ch() >= '0' and self.ch() <= '9') or self.ch() == '.':
301
+ numStr += self.adv()
302
+ else if self.ch() == '_':
303
+ self.adv()
304
+ else if (self.ch() == 'e' or self.ch() == 'E') and numStr.length > 0:
305
+ numStr += self.adv()
306
+ if self.ch() == '+' or self.ch() == '-': numStr += self.adv()
307
+ while self.ch() >= '0' and self.ch() <= '9': numStr += self.adv()
308
+ break
309
+ else:
310
+ break
311
+ self.tok(T.NUMBER, parseFloat(numStr), l, c)
312
+ continue
313
+
314
+ if cur == '"':
315
+ self.scanStr(l, c)
316
+ continue
317
+
318
+ if cur == '`':
319
+ self.scanBacktick(l, c)
320
+ continue
321
+
322
+ if cur == "'":
323
+ self.adv()
324
+ var sq = ''
325
+ while self.pos < self.src.length and self.ch() != "'":
326
+ if self.ch() == '\\':
327
+ self.adv()
328
+ val e = self.adv()
329
+ if e == 'n': sq += '\n'
330
+ else if e == 't': sq += '\t'
331
+ else if e == 'r': sq += '\r'
332
+ else if e == "'": sq += "'"
333
+ else if e == '\\': sq += '\\'
334
+ else: sq += '\\' + e
335
+ else:
336
+ sq += self.adv()
337
+ if self.ch() != "'": self.err('Unterminated string')
338
+ self.adv()
339
+ self.tok(T.STRING, sq, l, c)
340
+ continue
341
+
342
+ if (cur >= 'a' and cur <= 'z') or (cur >= 'A' and cur <= 'Z') or cur == '_':
343
+ var word = ''
344
+ while /[a-zA-Z0-9_]/.test(self.ch()): word += self.adv()
345
+ if word == '_' and not /[a-zA-Z0-9_]/.test(self.ch()):
346
+ self.tok(T.WILDCARD, '_', l, c)
347
+ continue
348
+ val kw = KEYWORDS[word]
349
+ if kw == '__TRUE__': self.tok(T.BOOL, true, l, c)
350
+ else if kw == '__FALSE__': self.tok(T.BOOL, false, l, c)
351
+ else if kw == '__NULL__': self.tok(T.NULL, null, l, c)
352
+ else if kw != undefined: self.tok(kw, word, l, c)
353
+ else: self.tok(T.IDENT, word, l, c)
354
+ continue
355
+
356
+ self.adv()
357
+
358
+ if cur == '+':
359
+ if self.ch() == '+':
360
+ self.adv()
361
+ self.tok(T.PLUSPLUS, '++', l, c)
362
+ else if self.ch() == '=':
363
+ self.adv()
364
+ self.tok(T.PLUSEQ, '+=', l, c)
365
+ else:
366
+ self.tok(T.PLUS, '+', l, c)
367
+ else if cur == '-':
368
+ if self.ch() == '-':
369
+ self.adv()
370
+ self.tok(T.MINUSMINUS, '--', l, c)
371
+ else if self.ch() == '>':
372
+ self.adv()
373
+ self.tok(T.ARROW, '->', l, c)
374
+ else if self.ch() == '=':
375
+ self.adv()
376
+ self.tok(T.MINUSEQ, '-=', l, c)
377
+ else:
378
+ self.tok(T.MINUS, '-', l, c)
379
+ else if cur == '*':
380
+ if self.ch() == '*':
381
+ self.adv()
382
+ self.tok(T.STARSTAR, '**', l, c)
383
+ else if self.ch() == '=':
384
+ self.adv()
385
+ self.tok(T.STAREQ, '*=', l, c)
386
+ else:
387
+ self.tok(T.STAR, '*', l, c)
388
+ else if cur == '/':
389
+ if self.ch() == '=':
390
+ self.adv()
391
+ self.tok(T.SLASHEQ, '/=', l, c)
392
+ else:
393
+ val lastTok = self.tokens[self.tokens.length - 1]
394
+ val afterVal = lastTok != null and (
395
+ lastTok.type == T.IDENT or lastTok.type == T.NUMBER or
396
+ lastTok.type == T.STRING or lastTok.type == T.BOOL or
397
+ lastTok.type == T.NULL or lastTok.type == T.REGEX or
398
+ lastTok.type == T.RPAREN or lastTok.type == T.RBRACKET or
399
+ lastTok.type == T.PLUSPLUS or lastTok.type == T.MINUSMINUS or
400
+ lastTok.type == T.BANG
401
+ )
402
+ if afterVal:
403
+ self.tok(T.SLASH, '/', l, c)
404
+ else:
405
+ self.scanRegexBody(l, c)
406
+ else if cur == '%':
407
+ if self.ch() == '=':
408
+ self.adv()
409
+ self.tok(T.PERCENTEQ, '%=', l, c)
410
+ else:
411
+ self.tok(T.PERCENT, '%', l, c)
412
+ else if cur == '=':
413
+ if self.ch() == '=' and self.src[self.pos + 1] == '=':
414
+ self.adv()
415
+ self.adv()
416
+ self.tok(T.EQEQEQ, '===', l, c)
417
+ else if self.ch() == '=':
418
+ self.adv()
419
+ self.tok(T.EQEQ, '==', l, c)
420
+ else if self.ch() == '>':
421
+ self.adv()
422
+ self.tok(T.FATARROW, '=>', l, c)
423
+ else:
424
+ self.tok(T.EQ, '=', l, c)
425
+ else if cur == '!':
426
+ if self.ch() == '=' and self.src[self.pos + 1] == '=':
427
+ self.adv()
428
+ self.adv()
429
+ self.tok(T.NEQEQ, '!==', l, c)
430
+ else if self.ch() == '=':
431
+ self.adv()
432
+ self.tok(T.NEQ, '!=', l, c)
433
+ else:
434
+ self.tok(T.BANG, '!', l, c)
435
+ else if cur == '<':
436
+ if self.ch() == '<':
437
+ self.adv()
438
+ self.tok(T.LSHIFT, '<<', l, c)
439
+ else if self.ch() == '=':
440
+ self.adv()
441
+ self.tok(T.LTE, '<=', l, c)
442
+ else:
443
+ self.tok(T.LT, '<', l, c)
444
+ else if cur == '>':
445
+ if self.ch() == '>':
446
+ self.adv()
447
+ self.tok(T.RSHIFT, '>>', l, c)
448
+ else if self.ch() == '=':
449
+ self.adv()
450
+ self.tok(T.GTE, '>=', l, c)
451
+ else:
452
+ self.tok(T.GT, '>', l, c)
453
+ else if cur == '.':
454
+ if self.ch() == '.' and self.src[self.pos + 1] == '.':
455
+ self.adv()
456
+ self.adv()
457
+ self.tok(T.DOTDOTDOT, '...', l, c)
458
+ else if self.ch() == '.':
459
+ self.adv()
460
+ self.tok(T.DOTDOT, '..', l, c)
461
+ else:
462
+ self.tok(T.DOT, '.', l, c)
463
+ else if cur == '?':
464
+ if self.ch() == '.':
465
+ self.adv()
466
+ self.tok(T.QUESTIONDOT, '?.', l, c)
467
+ else if self.ch() == '?':
468
+ self.adv()
469
+ self.tok(T.NULLISH, '??', l, c)
470
+ else:
471
+ self.tok(T.QUESTION, '?', l, c)
472
+ else if cur == '|':
473
+ if self.ch() == '>':
474
+ self.adv()
475
+ self.tok(T.PIPE, '|>', l, c)
476
+ else if self.ch() == '|':
477
+ self.adv()
478
+ self.tok(T.OROR, '||', l, c)
479
+ else:
480
+ self.tok(T.PIPEB, '|', l, c)
481
+ else if cur == '&':
482
+ if self.ch() == '&':
483
+ self.adv()
484
+ self.tok(T.ANDAND, '&&', l, c)
485
+ else:
486
+ self.tok(T.AMPERSAND, '&', l, c)
487
+ else if cur == '^':
488
+ self.tok(T.CARET, '^', l, c)
489
+ else if cur == '~':
490
+ self.tok(T.TILDE, '~', l, c)
491
+ else if cur == '@':
492
+ self.tok(T.AT, '@', l, c)
493
+ else if cur == '(':
494
+ self.nestDepth = self.nestDepth + 1
495
+ self.tok(T.LPAREN, '(', l, c)
496
+ else if cur == ')':
497
+ self.nestDepth = self.nestDepth - 1
498
+ self.tok(T.RPAREN, ')', l, c)
499
+ else if cur == '[':
500
+ self.nestDepth = self.nestDepth + 1
501
+ self.tok(T.LBRACKET, '[', l, c)
502
+ else if cur == ']':
503
+ self.nestDepth = self.nestDepth - 1
504
+ self.tok(T.RBRACKET, ']', l, c)
505
+ else if cur == '{':
506
+ self.nestDepth = self.nestDepth + 1
507
+ self.tok(T.LBRACE, '{', l, c)
508
+ else if cur == '}':
509
+ self.nestDepth = self.nestDepth - 1
510
+ self.tok(T.RBRACE, '}', l, c)
511
+ else if cur == ',':
512
+ self.tok(T.COMMA, ',', l, c)
513
+ else if cur == ':':
514
+ self.tok(T.COLON, ':', l, c)
515
+ else if cur != ';':
516
+ self.err("Unknown character: '{cur}'")
517
+
518
+ while self.indentStack.length > 1:
519
+ self.indentStack.pop()
520
+ self.tok(T.DEDENT, null, self.line, 1)
521
+ val lastTok = self.tokens[self.tokens.length - 1]
522
+ if lastTok and lastTok.type != T.NEWLINE and lastTok.type != T.DEDENT:
523
+ self.tok(T.NEWLINE, null, self.line, self.col)
524
+ self.tok(T.EOF, null, self.line, self.col)
525
+ return self.tokens
526
+
527
+ export fn lexerize(source):
528
+ val processed = source.replace(/\r\n/g, '\n').replace(/\r/g, '\n')
529
+ return new Lexer(processed, 0, 1, 1, [], [0], 0)