@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.
- package/CHANGELOG.md +103 -0
- package/README.md +1089 -0
- package/bin/flux.js +1397 -0
- package/dist/flux.cjs.js +6664 -0
- package/dist/flux.esm.js +6674 -0
- package/dist/flux.min.js +263 -0
- package/index.d.ts +202 -0
- package/index.js +26 -0
- package/package.json +77 -0
- package/scripts/build.js +76 -0
- package/src/bundler.js +216 -0
- package/src/checker.js +322 -0
- package/src/codegen.js +785 -0
- package/src/css-preprocessor.js +399 -0
- package/src/formatter.js +140 -0
- package/src/jsx.js +480 -0
- package/src/lexer.js +518 -0
- package/src/linter.js +758 -0
- package/src/mangler.js +280 -0
- package/src/parser.js +1671 -0
- package/src/self/bundler.flux +167 -0
- package/src/self/bundler.js +187 -0
- package/src/self/checker.flux +249 -0
- package/src/self/checker.js +338 -0
- package/src/self/codegen.flux +555 -0
- package/src/self/codegen.js +784 -0
- package/src/self/css-preprocessor.flux +373 -0
- package/src/self/css-preprocessor.js +387 -0
- package/src/self/formatter.flux +93 -0
- package/src/self/formatter.js +114 -0
- package/src/self/jsx.flux +430 -0
- package/src/self/jsx.js +396 -0
- package/src/self/lexer.flux +529 -0
- package/src/self/lexer.js +709 -0
- package/src/self/lexer.stage2.js +700 -0
- package/src/self/linter.flux +515 -0
- package/src/self/linter.js +804 -0
- package/src/self/mangler.flux +253 -0
- package/src/self/mangler.js +348 -0
- package/src/self/parser.flux +1146 -0
- package/src/self/parser.js +1571 -0
- package/src/self/sourcemap.flux +66 -0
- package/src/self/sourcemap.js +72 -0
- package/src/self/stdlib.flux +356 -0
- package/src/self/stdlib.js +396 -0
- package/src/self/test-runner.flux +201 -0
- package/src/self/test-runner.js +132 -0
- package/src/self/transpiler.flux +123 -0
- package/src/self/transpiler.js +83 -0
- package/src/self/type-checker.flux +821 -0
- package/src/self/type-checker.js +1106 -0
- package/src/sourcemap.js +82 -0
- package/src/stdlib.js +436 -0
- package/src/test-runner.js +239 -0
- package/src/transpiler.js +172 -0
- 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)
|