rei-lang 0.2.0
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/LICENSE +21 -0
- package/README.md +246 -0
- package/bin/rei.js +212 -0
- package/dist/index.d.mts +275 -0
- package/dist/index.d.ts +275 -0
- package/dist/index.js +1739 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1729 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +66 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1729 @@
|
|
|
1
|
+
// src/lang/lexer.ts
|
|
2
|
+
var TokenType = {
|
|
3
|
+
// Literals
|
|
4
|
+
NUMBER: "NUMBER",
|
|
5
|
+
STRING: "STRING",
|
|
6
|
+
EXT_LIT: "EXT_LIT",
|
|
7
|
+
SYMBOL_0_0: "SYMBOL_0_0",
|
|
8
|
+
SYMBOL_DOT_PRIM: "SYMBOL_DOT_PRIM",
|
|
9
|
+
BOOL_TRUE: "BOOL_TRUE",
|
|
10
|
+
BOOL_FALSE: "BOOL_FALSE",
|
|
11
|
+
// Math constants
|
|
12
|
+
CONST_PI: "CONST_PI",
|
|
13
|
+
CONST_E: "CONST_E",
|
|
14
|
+
CONST_PHI: "CONST_PHI",
|
|
15
|
+
CONST_I: "CONST_I",
|
|
16
|
+
CONST_PHI_UP: "CONST_PHI_UP",
|
|
17
|
+
CONST_PSI_UP: "CONST_PSI_UP",
|
|
18
|
+
CONST_OMEGA_UP: "CONST_OMEGA_UP",
|
|
19
|
+
CONST_EMPTY: "CONST_EMPTY",
|
|
20
|
+
// Quad literals (v0.2)
|
|
21
|
+
QUAD_TOP: "QUAD_TOP",
|
|
22
|
+
QUAD_BOT: "QUAD_BOT",
|
|
23
|
+
QUAD_TOP_PI: "QUAD_TOP_PI",
|
|
24
|
+
QUAD_BOT_PI: "QUAD_BOT_PI",
|
|
25
|
+
// Keywords
|
|
26
|
+
LET: "LET",
|
|
27
|
+
MUT: "MUT",
|
|
28
|
+
COMPRESS: "COMPRESS",
|
|
29
|
+
WEIGHT: "WEIGHT",
|
|
30
|
+
GENESIS: "GENESIS",
|
|
31
|
+
IF: "IF",
|
|
32
|
+
THEN: "THEN",
|
|
33
|
+
ELSE: "ELSE",
|
|
34
|
+
MATCH: "MATCH",
|
|
35
|
+
CASE: "CASE",
|
|
36
|
+
WITNESSED: "WITNESSED",
|
|
37
|
+
BY: "BY",
|
|
38
|
+
TRUE: "TRUE",
|
|
39
|
+
FALSE: "FALSE",
|
|
40
|
+
NULL: "NULL",
|
|
41
|
+
TEMPORAL: "TEMPORAL",
|
|
42
|
+
TIMELESS: "TIMELESS",
|
|
43
|
+
// Identifiers
|
|
44
|
+
IDENT: "IDENT",
|
|
45
|
+
// Operators
|
|
46
|
+
PLUS: "PLUS",
|
|
47
|
+
MINUS: "MINUS",
|
|
48
|
+
STAR: "STAR",
|
|
49
|
+
SLASH: "SLASH",
|
|
50
|
+
OPLUS: "OPLUS",
|
|
51
|
+
OTIMES: "OTIMES",
|
|
52
|
+
CDOT: "CDOT",
|
|
53
|
+
PIPE_OP: "PIPE_OP",
|
|
54
|
+
EXTEND: "EXTEND",
|
|
55
|
+
REDUCE: "REDUCE",
|
|
56
|
+
ASSIGN: "ASSIGN",
|
|
57
|
+
DOT: "DOT",
|
|
58
|
+
ARROW: "ARROW",
|
|
59
|
+
SEMICOLON: "SEMICOLON",
|
|
60
|
+
COMMA: "COMMA",
|
|
61
|
+
COLON: "COLON",
|
|
62
|
+
LPAREN: "LPAREN",
|
|
63
|
+
RPAREN: "RPAREN",
|
|
64
|
+
LBRACE: "LBRACE",
|
|
65
|
+
RBRACE: "RBRACE",
|
|
66
|
+
LBRACKET: "LBRACKET",
|
|
67
|
+
RBRACKET: "RBRACKET",
|
|
68
|
+
CONVERGE: "CONVERGE",
|
|
69
|
+
DIVERGE: "DIVERGE",
|
|
70
|
+
REFLECT: "REFLECT",
|
|
71
|
+
AND: "AND",
|
|
72
|
+
OR: "OR",
|
|
73
|
+
NOT: "NOT",
|
|
74
|
+
GT_K: "GT_K",
|
|
75
|
+
LT_K: "LT_K",
|
|
76
|
+
EQ_K: "EQ_K",
|
|
77
|
+
GT: "GT",
|
|
78
|
+
LT: "LT",
|
|
79
|
+
EQ: "EQ",
|
|
80
|
+
NEQ: "NEQ",
|
|
81
|
+
GTE: "GTE",
|
|
82
|
+
LTE: "LTE",
|
|
83
|
+
MDIM_OPEN: "MDIM_OPEN",
|
|
84
|
+
// Special
|
|
85
|
+
NEWLINE: "NEWLINE",
|
|
86
|
+
EOF: "EOF"
|
|
87
|
+
};
|
|
88
|
+
var KEYWORDS = {
|
|
89
|
+
"let": TokenType.LET,
|
|
90
|
+
"mut": TokenType.MUT,
|
|
91
|
+
"compress": TokenType.COMPRESS,
|
|
92
|
+
"weight": TokenType.WEIGHT,
|
|
93
|
+
"genesis": TokenType.GENESIS,
|
|
94
|
+
"if": TokenType.IF,
|
|
95
|
+
"then": TokenType.THEN,
|
|
96
|
+
"else": TokenType.ELSE,
|
|
97
|
+
"match": TokenType.MATCH,
|
|
98
|
+
"case": TokenType.CASE,
|
|
99
|
+
"witnessed": TokenType.WITNESSED,
|
|
100
|
+
"by": TokenType.BY,
|
|
101
|
+
"true": TokenType.TRUE,
|
|
102
|
+
"false": TokenType.FALSE,
|
|
103
|
+
"null": TokenType.NULL,
|
|
104
|
+
"Temporal": TokenType.TEMPORAL,
|
|
105
|
+
"Timeless": TokenType.TIMELESS
|
|
106
|
+
};
|
|
107
|
+
var SUBSCRIPT_CHARS = new Set("oxzwensbua".split(""));
|
|
108
|
+
var Lexer = class {
|
|
109
|
+
constructor(source) {
|
|
110
|
+
this.source = source;
|
|
111
|
+
this.chars = Array.from(source);
|
|
112
|
+
}
|
|
113
|
+
chars;
|
|
114
|
+
pos = 0;
|
|
115
|
+
line = 1;
|
|
116
|
+
col = 1;
|
|
117
|
+
tokens = [];
|
|
118
|
+
tokenize() {
|
|
119
|
+
this.tokens = [];
|
|
120
|
+
while (this.pos < this.chars.length) {
|
|
121
|
+
this.skipWhitespaceAndComments();
|
|
122
|
+
if (this.pos >= this.chars.length) break;
|
|
123
|
+
const ch = this.chars[this.pos];
|
|
124
|
+
if (ch === "\n") {
|
|
125
|
+
this.emit(TokenType.NEWLINE, "\n");
|
|
126
|
+
this.advance();
|
|
127
|
+
this.line++;
|
|
128
|
+
this.col = 1;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (ch === '"') {
|
|
132
|
+
this.readString();
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (ch === "0" && this.peek(1) === "\u2080") {
|
|
136
|
+
this.emit(TokenType.SYMBOL_0_0, "0\u2080");
|
|
137
|
+
this.advance();
|
|
138
|
+
this.advance();
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (ch === "\u{1D544}" && this.peek(1) === "{") {
|
|
142
|
+
this.emit(TokenType.MDIM_OPEN, "\u{1D544}{");
|
|
143
|
+
this.advance();
|
|
144
|
+
this.advance();
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (this.isExtStart(ch)) {
|
|
148
|
+
const ext = this.readExtLit();
|
|
149
|
+
if (ext) continue;
|
|
150
|
+
}
|
|
151
|
+
if (this.isDigit(ch) || ch === "-" && this.isDigit(this.peek(1) ?? "") && this.shouldNegateBePrefix()) {
|
|
152
|
+
this.readNumber();
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (this.readUnicodeToken(ch)) continue;
|
|
156
|
+
if (this.readMultiCharOp(ch)) continue;
|
|
157
|
+
if (this.readSingleCharOp(ch)) continue;
|
|
158
|
+
if (this.isIdentStart(ch)) {
|
|
159
|
+
this.readIdentOrKeyword();
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
this.advance();
|
|
163
|
+
}
|
|
164
|
+
this.emit(TokenType.EOF, "");
|
|
165
|
+
return this.tokens.filter((t) => t.type !== TokenType.NEWLINE || false);
|
|
166
|
+
}
|
|
167
|
+
skipWhitespaceAndComments() {
|
|
168
|
+
while (this.pos < this.chars.length) {
|
|
169
|
+
const ch = this.chars[this.pos];
|
|
170
|
+
if (ch === " " || ch === " " || ch === "\r") {
|
|
171
|
+
this.advance();
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (ch === "/" && this.peek(1) === "/") {
|
|
175
|
+
while (this.pos < this.chars.length && this.chars[this.pos] !== "\n") this.advance();
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (ch === "/" && this.peek(1) === "*") {
|
|
179
|
+
this.advance();
|
|
180
|
+
this.advance();
|
|
181
|
+
while (this.pos < this.chars.length) {
|
|
182
|
+
if (this.chars[this.pos] === "*" && this.peek(1) === "/") {
|
|
183
|
+
this.advance();
|
|
184
|
+
this.advance();
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
if (this.chars[this.pos] === "\n") {
|
|
188
|
+
this.line++;
|
|
189
|
+
this.col = 0;
|
|
190
|
+
}
|
|
191
|
+
this.advance();
|
|
192
|
+
}
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
readString() {
|
|
199
|
+
const startCol = this.col;
|
|
200
|
+
this.advance();
|
|
201
|
+
let str = "";
|
|
202
|
+
while (this.pos < this.chars.length && this.chars[this.pos] !== '"') {
|
|
203
|
+
if (this.chars[this.pos] === "\\" && this.pos + 1 < this.chars.length) {
|
|
204
|
+
this.advance();
|
|
205
|
+
const esc = this.chars[this.pos];
|
|
206
|
+
if (esc === "n") str += "\n";
|
|
207
|
+
else if (esc === "t") str += " ";
|
|
208
|
+
else if (esc === "\\") str += "\\";
|
|
209
|
+
else if (esc === '"') str += '"';
|
|
210
|
+
else str += esc;
|
|
211
|
+
} else {
|
|
212
|
+
str += this.chars[this.pos];
|
|
213
|
+
}
|
|
214
|
+
this.advance();
|
|
215
|
+
}
|
|
216
|
+
if (this.pos < this.chars.length) this.advance();
|
|
217
|
+
this.tokens.push({ type: TokenType.STRING, value: str, line: this.line, col: startCol });
|
|
218
|
+
}
|
|
219
|
+
isExtStart(ch) {
|
|
220
|
+
if (ch === "0" && SUBSCRIPT_CHARS.has(this.peek(1) ?? "")) return true;
|
|
221
|
+
if (ch === "\u03C0" || ch === "\u03C6") {
|
|
222
|
+
const next = this.peek(1);
|
|
223
|
+
if (next && SUBSCRIPT_CHARS.has(next)) return true;
|
|
224
|
+
}
|
|
225
|
+
if (ch === "e" || ch === "i") {
|
|
226
|
+
const next = this.peek(1);
|
|
227
|
+
if (!next || !SUBSCRIPT_CHARS.has(next)) return false;
|
|
228
|
+
let offset = 1;
|
|
229
|
+
while (this.peek(offset) && SUBSCRIPT_CHARS.has(this.peek(offset))) offset++;
|
|
230
|
+
const afterSubs = this.peek(offset);
|
|
231
|
+
if (afterSubs && /[a-zA-Z0-9_]/.test(afterSubs)) return false;
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
readExtLit() {
|
|
237
|
+
const startCol = this.col;
|
|
238
|
+
const base = this.chars[this.pos];
|
|
239
|
+
this.advance();
|
|
240
|
+
let subs = "";
|
|
241
|
+
while (this.pos < this.chars.length && SUBSCRIPT_CHARS.has(this.chars[this.pos])) {
|
|
242
|
+
subs += this.chars[this.pos];
|
|
243
|
+
this.advance();
|
|
244
|
+
}
|
|
245
|
+
if (subs.length > 0) {
|
|
246
|
+
this.tokens.push({
|
|
247
|
+
type: TokenType.EXT_LIT,
|
|
248
|
+
value: base + subs,
|
|
249
|
+
line: this.line,
|
|
250
|
+
col: startCol
|
|
251
|
+
});
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
this.pos--;
|
|
255
|
+
this.col--;
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
readNumber() {
|
|
259
|
+
const startCol = this.col;
|
|
260
|
+
let num = "";
|
|
261
|
+
if (this.chars[this.pos] === "-") {
|
|
262
|
+
num += "-";
|
|
263
|
+
this.advance();
|
|
264
|
+
}
|
|
265
|
+
while (this.pos < this.chars.length && this.isDigit(this.chars[this.pos])) {
|
|
266
|
+
num += this.chars[this.pos];
|
|
267
|
+
this.advance();
|
|
268
|
+
}
|
|
269
|
+
if (this.pos < this.chars.length && this.chars[this.pos] === "." && this.isDigit(this.peek(1) ?? "")) {
|
|
270
|
+
num += ".";
|
|
271
|
+
this.advance();
|
|
272
|
+
while (this.pos < this.chars.length && this.isDigit(this.chars[this.pos])) {
|
|
273
|
+
num += this.chars[this.pos];
|
|
274
|
+
this.advance();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
this.tokens.push({ type: TokenType.NUMBER, value: num, line: this.line, col: startCol });
|
|
278
|
+
}
|
|
279
|
+
readUnicodeToken(ch) {
|
|
280
|
+
this.col;
|
|
281
|
+
const map = [
|
|
282
|
+
["\u2295", TokenType.OPLUS],
|
|
283
|
+
["\u2297", TokenType.OTIMES],
|
|
284
|
+
["\xB7", TokenType.CDOT],
|
|
285
|
+
["\u03C0", TokenType.CONST_PI],
|
|
286
|
+
["\u03C6", TokenType.CONST_PHI],
|
|
287
|
+
["\u03A6", TokenType.CONST_PHI_UP],
|
|
288
|
+
["\u03A8", TokenType.CONST_PSI_UP],
|
|
289
|
+
["\u03A9", TokenType.CONST_OMEGA_UP],
|
|
290
|
+
["\u2205", TokenType.CONST_EMPTY],
|
|
291
|
+
["\u22A4", TokenType.QUAD_TOP],
|
|
292
|
+
["\u22A5", TokenType.QUAD_BOT],
|
|
293
|
+
["\u290A", TokenType.CONVERGE],
|
|
294
|
+
["\u290B", TokenType.DIVERGE],
|
|
295
|
+
["\u25C1", TokenType.REFLECT],
|
|
296
|
+
["\u2227", TokenType.AND],
|
|
297
|
+
["\u2228", TokenType.OR],
|
|
298
|
+
["\xAC", TokenType.NOT],
|
|
299
|
+
["\u30FB", TokenType.SYMBOL_DOT_PRIM]
|
|
300
|
+
];
|
|
301
|
+
if (ch === "\u22A4" && this.peek(1) === "\u03C0") {
|
|
302
|
+
this.emit(TokenType.QUAD_TOP_PI, "\u22A4\u03C0");
|
|
303
|
+
this.advance();
|
|
304
|
+
this.advance();
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
if (ch === "\u22A5" && this.peek(1) === "\u03C0") {
|
|
308
|
+
this.emit(TokenType.QUAD_BOT_PI, "\u22A5\u03C0");
|
|
309
|
+
this.advance();
|
|
310
|
+
this.advance();
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
for (const [sym, type] of map) {
|
|
314
|
+
if (ch === sym) {
|
|
315
|
+
this.emit(type, sym);
|
|
316
|
+
this.advance();
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
readMultiCharOp(ch) {
|
|
323
|
+
const next = this.peek(1);
|
|
324
|
+
if (ch === "|" && next === ">") {
|
|
325
|
+
this.emit(TokenType.PIPE_OP, "|>");
|
|
326
|
+
this.advance();
|
|
327
|
+
this.advance();
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
if (ch === ">" && next === ">") {
|
|
331
|
+
this.emit(TokenType.EXTEND, ">>");
|
|
332
|
+
this.advance();
|
|
333
|
+
this.advance();
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
if (ch === "<" && next === "<") {
|
|
337
|
+
this.emit(TokenType.REDUCE, "<<");
|
|
338
|
+
this.advance();
|
|
339
|
+
this.advance();
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
if (ch === "-" && next === ">") {
|
|
343
|
+
this.emit(TokenType.ARROW, "->");
|
|
344
|
+
this.advance();
|
|
345
|
+
this.advance();
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
if (ch === "=" && next === "=") {
|
|
349
|
+
this.emit(TokenType.EQ, "==");
|
|
350
|
+
this.advance();
|
|
351
|
+
this.advance();
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
if (ch === "!" && next === "=") {
|
|
355
|
+
this.emit(TokenType.NEQ, "!=");
|
|
356
|
+
this.advance();
|
|
357
|
+
this.advance();
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
if (ch === ">" && next === "=") {
|
|
361
|
+
this.emit(TokenType.GTE, ">=");
|
|
362
|
+
this.advance();
|
|
363
|
+
this.advance();
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
if (ch === "<" && next === "=") {
|
|
367
|
+
this.emit(TokenType.LTE, "<=");
|
|
368
|
+
this.advance();
|
|
369
|
+
this.advance();
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
if (ch === ">" && next === "\u03BA") {
|
|
373
|
+
this.emit(TokenType.GT_K, ">\u03BA");
|
|
374
|
+
this.advance();
|
|
375
|
+
this.advance();
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
if (ch === "<" && next === "\u03BA") {
|
|
379
|
+
this.emit(TokenType.LT_K, "<\u03BA");
|
|
380
|
+
this.advance();
|
|
381
|
+
this.advance();
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
if (ch === "=" && next === "\u03BA") {
|
|
385
|
+
this.emit(TokenType.EQ_K, "=\u03BA");
|
|
386
|
+
this.advance();
|
|
387
|
+
this.advance();
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
readSingleCharOp(ch) {
|
|
393
|
+
const map = {
|
|
394
|
+
"+": TokenType.PLUS,
|
|
395
|
+
"-": TokenType.MINUS,
|
|
396
|
+
"*": TokenType.STAR,
|
|
397
|
+
"/": TokenType.SLASH,
|
|
398
|
+
"=": TokenType.ASSIGN,
|
|
399
|
+
".": TokenType.DOT,
|
|
400
|
+
",": TokenType.COMMA,
|
|
401
|
+
":": TokenType.COLON,
|
|
402
|
+
";": TokenType.SEMICOLON,
|
|
403
|
+
"(": TokenType.LPAREN,
|
|
404
|
+
")": TokenType.RPAREN,
|
|
405
|
+
"{": TokenType.LBRACE,
|
|
406
|
+
"}": TokenType.RBRACE,
|
|
407
|
+
"[": TokenType.LBRACKET,
|
|
408
|
+
"]": TokenType.RBRACKET,
|
|
409
|
+
"|": TokenType.PIPE_OP,
|
|
410
|
+
">": TokenType.GT,
|
|
411
|
+
"<": TokenType.LT
|
|
412
|
+
};
|
|
413
|
+
const type = map[ch];
|
|
414
|
+
if (type) {
|
|
415
|
+
this.emit(type, ch);
|
|
416
|
+
this.advance();
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
readIdentOrKeyword() {
|
|
422
|
+
const startCol = this.col;
|
|
423
|
+
let name = "";
|
|
424
|
+
while (this.pos < this.chars.length && this.isIdentPart(this.chars[this.pos])) {
|
|
425
|
+
name += this.chars[this.pos];
|
|
426
|
+
this.advance();
|
|
427
|
+
}
|
|
428
|
+
if (name.startsWith("compress") && name.length > 8) {
|
|
429
|
+
const suffix = name.slice(8);
|
|
430
|
+
if (["\u2070", "\xB9", "\xB2", "\xB3", "\u221E"].includes(suffix)) {
|
|
431
|
+
this.tokens.push({ type: TokenType.COMPRESS, value: name, line: this.line, col: startCol });
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
const kw = KEYWORDS[name];
|
|
436
|
+
if (kw) {
|
|
437
|
+
this.tokens.push({ type: kw, value: name, line: this.line, col: startCol });
|
|
438
|
+
} else {
|
|
439
|
+
this.tokens.push({ type: TokenType.IDENT, value: name, line: this.line, col: startCol });
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
// --- Helpers ---
|
|
443
|
+
advance() {
|
|
444
|
+
this.pos++;
|
|
445
|
+
this.col++;
|
|
446
|
+
}
|
|
447
|
+
peek(offset) {
|
|
448
|
+
return this.chars[this.pos + offset];
|
|
449
|
+
}
|
|
450
|
+
emit(type, value) {
|
|
451
|
+
this.tokens.push({ type, value, line: this.line, col: this.col });
|
|
452
|
+
}
|
|
453
|
+
isDigit(ch) {
|
|
454
|
+
return ch >= "0" && ch <= "9";
|
|
455
|
+
}
|
|
456
|
+
isIdentStart(ch) {
|
|
457
|
+
return /[a-zA-Z_α-ωΑ-Ω𝕄𝕌]/.test(ch);
|
|
458
|
+
}
|
|
459
|
+
isIdentPart(ch) {
|
|
460
|
+
return /[a-zA-Z0-9_α-ωΑ-Ω⁰¹²³∞𝕄𝕌]/.test(ch);
|
|
461
|
+
}
|
|
462
|
+
shouldNegateBePrefix() {
|
|
463
|
+
if (this.tokens.length === 0) return true;
|
|
464
|
+
const last = this.tokens[this.tokens.length - 1];
|
|
465
|
+
return [
|
|
466
|
+
TokenType.LPAREN,
|
|
467
|
+
TokenType.COMMA,
|
|
468
|
+
TokenType.ASSIGN,
|
|
469
|
+
TokenType.PLUS,
|
|
470
|
+
TokenType.MINUS,
|
|
471
|
+
TokenType.STAR,
|
|
472
|
+
TokenType.SLASH,
|
|
473
|
+
TokenType.OPLUS,
|
|
474
|
+
TokenType.OTIMES,
|
|
475
|
+
TokenType.PIPE_OP,
|
|
476
|
+
TokenType.SEMICOLON,
|
|
477
|
+
TokenType.LBRACKET,
|
|
478
|
+
TokenType.COLON
|
|
479
|
+
].includes(last.type);
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
// src/lang/parser.ts
|
|
484
|
+
function node(type, props = {}) {
|
|
485
|
+
return { type, ...props };
|
|
486
|
+
}
|
|
487
|
+
var Parser = class {
|
|
488
|
+
pos = 0;
|
|
489
|
+
tokens;
|
|
490
|
+
constructor(tokens) {
|
|
491
|
+
this.tokens = tokens.filter((t) => t.type !== TokenType.NEWLINE);
|
|
492
|
+
}
|
|
493
|
+
parseProgram() {
|
|
494
|
+
const stmts = [];
|
|
495
|
+
while (!this.isAtEnd()) {
|
|
496
|
+
while (this.check(TokenType.SEMICOLON)) this.advance();
|
|
497
|
+
if (this.isAtEnd()) break;
|
|
498
|
+
stmts.push(this.parseStatement());
|
|
499
|
+
while (this.check(TokenType.SEMICOLON)) this.advance();
|
|
500
|
+
}
|
|
501
|
+
return node("Program", { body: stmts });
|
|
502
|
+
}
|
|
503
|
+
parseStatement() {
|
|
504
|
+
if (this.check(TokenType.LET)) return this.parseLetStmt();
|
|
505
|
+
if (this.check(TokenType.COMPRESS)) return this.parseCompressDef();
|
|
506
|
+
return this.parseExpression();
|
|
507
|
+
}
|
|
508
|
+
// --- let [mut] name [: type] = expr [witnessed by "..."] ---
|
|
509
|
+
parseLetStmt() {
|
|
510
|
+
this.expect(TokenType.LET);
|
|
511
|
+
const mutable = this.match(TokenType.MUT);
|
|
512
|
+
const name = this.expect(TokenType.IDENT).value;
|
|
513
|
+
let typeAnnotation = null;
|
|
514
|
+
let phaseGuard = null;
|
|
515
|
+
if (this.match(TokenType.COLON)) {
|
|
516
|
+
typeAnnotation = this.expect(TokenType.IDENT).value;
|
|
517
|
+
if (this.check(TokenType.IDENT) && this.peek().value.startsWith("@")) {
|
|
518
|
+
phaseGuard = this.advance().value.slice(1);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
this.expect(TokenType.ASSIGN);
|
|
522
|
+
const init = this.parseExpression();
|
|
523
|
+
let witness = null;
|
|
524
|
+
if (this.match(TokenType.WITNESSED)) {
|
|
525
|
+
this.expect(TokenType.BY);
|
|
526
|
+
witness = this.expect(TokenType.STRING).value;
|
|
527
|
+
}
|
|
528
|
+
return node(mutable ? "MutStmt" : "LetStmt", {
|
|
529
|
+
name,
|
|
530
|
+
init,
|
|
531
|
+
typeAnnotation,
|
|
532
|
+
phaseGuard,
|
|
533
|
+
witness
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
// --- compress [level] name(params) [-> type] = body ---
|
|
537
|
+
parseCompressDef() {
|
|
538
|
+
const compressToken = this.expect(TokenType.COMPRESS);
|
|
539
|
+
const level = this.parseCompressLevel(compressToken.value);
|
|
540
|
+
const name = this.expect(TokenType.IDENT).value;
|
|
541
|
+
this.expect(TokenType.LPAREN);
|
|
542
|
+
const params = [];
|
|
543
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
544
|
+
params.push(this.parseParamDecl());
|
|
545
|
+
while (this.match(TokenType.COMMA)) {
|
|
546
|
+
params.push(this.parseParamDecl());
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
this.expect(TokenType.RPAREN);
|
|
550
|
+
let returnType = null;
|
|
551
|
+
if (this.match(TokenType.ARROW)) {
|
|
552
|
+
returnType = this.expect(TokenType.IDENT).value;
|
|
553
|
+
}
|
|
554
|
+
this.expect(TokenType.ASSIGN);
|
|
555
|
+
const body = this.parseExpression();
|
|
556
|
+
return node("CompressDef", { name, params, body, level, returnType });
|
|
557
|
+
}
|
|
558
|
+
parseCompressLevel(value) {
|
|
559
|
+
if (value === "compress") return -1;
|
|
560
|
+
const suffixMap = {
|
|
561
|
+
"compress\u2070": 0,
|
|
562
|
+
"compress\xB9": 1,
|
|
563
|
+
"compress\xB2": 2,
|
|
564
|
+
"compress\xB3": 3,
|
|
565
|
+
"compress\u221E": Infinity
|
|
566
|
+
};
|
|
567
|
+
return suffixMap[value] ?? -1;
|
|
568
|
+
}
|
|
569
|
+
parseParamDecl() {
|
|
570
|
+
let name;
|
|
571
|
+
if (this.check(TokenType.IDENT)) {
|
|
572
|
+
name = this.advance().value;
|
|
573
|
+
} else if (this.check(TokenType.CONST_E)) {
|
|
574
|
+
name = this.advance().value;
|
|
575
|
+
} else if (this.check(TokenType.CONST_I)) {
|
|
576
|
+
name = this.advance().value;
|
|
577
|
+
} else {
|
|
578
|
+
name = this.expect(TokenType.IDENT).value;
|
|
579
|
+
}
|
|
580
|
+
if (this.match(TokenType.COLON)) {
|
|
581
|
+
this.expect(TokenType.IDENT);
|
|
582
|
+
}
|
|
583
|
+
return name;
|
|
584
|
+
}
|
|
585
|
+
// --- Expression hierarchy (low → high precedence) ---
|
|
586
|
+
parseExpression() {
|
|
587
|
+
return this.parsePipe();
|
|
588
|
+
}
|
|
589
|
+
// Level 1: |> pipe, ◁ reflect
|
|
590
|
+
parsePipe() {
|
|
591
|
+
let left = this.parseLogicOr();
|
|
592
|
+
while (this.check(TokenType.PIPE_OP) || this.check(TokenType.REFLECT)) {
|
|
593
|
+
if (this.match(TokenType.PIPE_OP)) {
|
|
594
|
+
const cmd = this.parsePipeCommand();
|
|
595
|
+
left = node("Pipe", { input: left, command: cmd });
|
|
596
|
+
} else if (this.match(TokenType.REFLECT)) {
|
|
597
|
+
const right = this.parseLogicOr();
|
|
598
|
+
left = node("ReflectOp", { left, right });
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return left;
|
|
602
|
+
}
|
|
603
|
+
parsePipeCommand() {
|
|
604
|
+
if (this.check(TokenType.IDENT) || this.check(TokenType.GENESIS)) {
|
|
605
|
+
const cmd = this.advance().value;
|
|
606
|
+
let mode = null;
|
|
607
|
+
let args = [];
|
|
608
|
+
if (this.match(TokenType.COLON)) {
|
|
609
|
+
mode = this.expect(TokenType.IDENT).value;
|
|
610
|
+
}
|
|
611
|
+
if (this.match(TokenType.LPAREN)) {
|
|
612
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
613
|
+
args.push(this.parseExpression());
|
|
614
|
+
while (this.match(TokenType.COMMA)) args.push(this.parseExpression());
|
|
615
|
+
}
|
|
616
|
+
this.expect(TokenType.RPAREN);
|
|
617
|
+
}
|
|
618
|
+
return node("PipeCmd", { cmd, mode, args });
|
|
619
|
+
}
|
|
620
|
+
if (this.match(TokenType.CONVERGE)) return node("PipeCmd", { cmd: "\u290A", mode: null, args: [] });
|
|
621
|
+
if (this.match(TokenType.DIVERGE)) return node("PipeCmd", { cmd: "\u290B", mode: null, args: [] });
|
|
622
|
+
throw this.error("\u30D1\u30A4\u30D7\u30B3\u30DE\u30F3\u30C9\u304C\u5FC5\u8981");
|
|
623
|
+
}
|
|
624
|
+
// Level 2: ∧ ∨ (logic)
|
|
625
|
+
parseLogicOr() {
|
|
626
|
+
let left = this.parseLogicAnd();
|
|
627
|
+
while (this.match(TokenType.OR)) {
|
|
628
|
+
const right = this.parseLogicAnd();
|
|
629
|
+
left = node("BinOp", { op: "\u2228", left, right });
|
|
630
|
+
}
|
|
631
|
+
return left;
|
|
632
|
+
}
|
|
633
|
+
parseLogicAnd() {
|
|
634
|
+
let left = this.parseComparison();
|
|
635
|
+
while (this.match(TokenType.AND)) {
|
|
636
|
+
const right = this.parseComparison();
|
|
637
|
+
left = node("BinOp", { op: "\u2227", left, right });
|
|
638
|
+
}
|
|
639
|
+
return left;
|
|
640
|
+
}
|
|
641
|
+
// Level 3: >κ <κ =κ == != > < >= <=
|
|
642
|
+
parseComparison() {
|
|
643
|
+
let left = this.parseAddition();
|
|
644
|
+
const compOps = [
|
|
645
|
+
TokenType.GT_K,
|
|
646
|
+
TokenType.LT_K,
|
|
647
|
+
TokenType.EQ_K,
|
|
648
|
+
TokenType.EQ,
|
|
649
|
+
TokenType.NEQ,
|
|
650
|
+
TokenType.GT,
|
|
651
|
+
TokenType.LT,
|
|
652
|
+
TokenType.GTE,
|
|
653
|
+
TokenType.LTE
|
|
654
|
+
];
|
|
655
|
+
while (compOps.some((op) => this.check(op))) {
|
|
656
|
+
const opToken = this.advance();
|
|
657
|
+
const right = this.parseAddition();
|
|
658
|
+
left = node("BinOp", { op: opToken.value, left, right });
|
|
659
|
+
}
|
|
660
|
+
return left;
|
|
661
|
+
}
|
|
662
|
+
// Level 4: + - ⊕
|
|
663
|
+
parseAddition() {
|
|
664
|
+
let left = this.parseMultiplication();
|
|
665
|
+
while (this.check(TokenType.PLUS) || this.check(TokenType.MINUS) || this.check(TokenType.OPLUS)) {
|
|
666
|
+
const op = this.advance().value;
|
|
667
|
+
const right = this.parseMultiplication();
|
|
668
|
+
left = node("BinOp", { op, left, right });
|
|
669
|
+
}
|
|
670
|
+
return left;
|
|
671
|
+
}
|
|
672
|
+
// Level 5: * / ⊗ ·
|
|
673
|
+
parseMultiplication() {
|
|
674
|
+
let left = this.parseExtendReduce();
|
|
675
|
+
while (this.check(TokenType.STAR) || this.check(TokenType.SLASH) || this.check(TokenType.OTIMES) || this.check(TokenType.CDOT)) {
|
|
676
|
+
const op = this.advance().value;
|
|
677
|
+
const right = this.parseExtendReduce();
|
|
678
|
+
left = node("BinOp", { op, left, right });
|
|
679
|
+
}
|
|
680
|
+
return left;
|
|
681
|
+
}
|
|
682
|
+
// Level 6: >> << ⤊ ⤋
|
|
683
|
+
parseExtendReduce() {
|
|
684
|
+
let left = this.parseUnary();
|
|
685
|
+
while (true) {
|
|
686
|
+
if (this.match(TokenType.EXTEND)) {
|
|
687
|
+
if (this.match(TokenType.COLON)) {
|
|
688
|
+
const sub = this.expect(TokenType.IDENT).value;
|
|
689
|
+
left = node("Extend", { target: left, subscript: sub });
|
|
690
|
+
} else {
|
|
691
|
+
const right = this.parseUnary();
|
|
692
|
+
left = node("Extend", { target: left, expr: right });
|
|
693
|
+
}
|
|
694
|
+
} else if (this.match(TokenType.REDUCE)) {
|
|
695
|
+
left = node("Reduce", { target: left });
|
|
696
|
+
} else if (this.match(TokenType.CONVERGE)) {
|
|
697
|
+
const right = this.parseUnary();
|
|
698
|
+
left = node("ConvergeOp", { left, right });
|
|
699
|
+
} else if (this.match(TokenType.DIVERGE)) {
|
|
700
|
+
const right = this.parseUnary();
|
|
701
|
+
left = node("DivergeOp", { left, right });
|
|
702
|
+
} else {
|
|
703
|
+
break;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return left;
|
|
707
|
+
}
|
|
708
|
+
// Level 7: unary ¬ -
|
|
709
|
+
parseUnary() {
|
|
710
|
+
if (this.match(TokenType.NOT)) {
|
|
711
|
+
const operand = this.parseUnary();
|
|
712
|
+
return node("UnaryOp", { op: "\xAC", operand });
|
|
713
|
+
}
|
|
714
|
+
if (this.check(TokenType.MINUS) && this.shouldNegateBePrefix()) {
|
|
715
|
+
this.advance();
|
|
716
|
+
const operand = this.parseUnary();
|
|
717
|
+
return node("UnaryOp", { op: "-", operand });
|
|
718
|
+
}
|
|
719
|
+
return this.parsePostfix();
|
|
720
|
+
}
|
|
721
|
+
// Level 8: . member access, [index], (call)
|
|
722
|
+
parsePostfix() {
|
|
723
|
+
let left = this.parsePrimary();
|
|
724
|
+
while (true) {
|
|
725
|
+
if (this.match(TokenType.DOT)) {
|
|
726
|
+
if (this.check(TokenType.IDENT)) {
|
|
727
|
+
const member = this.advance().value;
|
|
728
|
+
if (this.match(TokenType.DOT) && this.check(TokenType.IDENT) && this.peek().value === "\u03BA") {
|
|
729
|
+
this.advance();
|
|
730
|
+
left = node("MemberAccess", { object: left, member, kappa: true });
|
|
731
|
+
} else {
|
|
732
|
+
left = node("MemberAccess", { object: left, member, kappa: false });
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
} else if (this.match(TokenType.LPAREN)) {
|
|
736
|
+
const args = [];
|
|
737
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
738
|
+
args.push(this.parseExpression());
|
|
739
|
+
while (this.match(TokenType.COMMA)) args.push(this.parseExpression());
|
|
740
|
+
}
|
|
741
|
+
this.expect(TokenType.RPAREN);
|
|
742
|
+
left = node("FnCall", { callee: left, args });
|
|
743
|
+
} else if (this.match(TokenType.LBRACKET)) {
|
|
744
|
+
const index = this.parseExpression();
|
|
745
|
+
this.expect(TokenType.RBRACKET);
|
|
746
|
+
left = node("IndexAccess", { object: left, index });
|
|
747
|
+
} else {
|
|
748
|
+
break;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return left;
|
|
752
|
+
}
|
|
753
|
+
// --- Primary expressions ---
|
|
754
|
+
parsePrimary() {
|
|
755
|
+
if (this.check(TokenType.NUMBER)) {
|
|
756
|
+
const val = this.advance().value;
|
|
757
|
+
return node("NumLit", { value: parseFloat(val) });
|
|
758
|
+
}
|
|
759
|
+
if (this.check(TokenType.STRING)) {
|
|
760
|
+
return node("StrLit", { value: this.advance().value });
|
|
761
|
+
}
|
|
762
|
+
if (this.match(TokenType.TRUE)) return node("BoolLit", { value: true });
|
|
763
|
+
if (this.match(TokenType.FALSE)) return node("BoolLit", { value: false });
|
|
764
|
+
if (this.match(TokenType.NULL)) return node("NullLit", {});
|
|
765
|
+
if (this.check(TokenType.EXT_LIT)) {
|
|
766
|
+
const val = this.advance().value;
|
|
767
|
+
return node("ExtLit", { raw: val });
|
|
768
|
+
}
|
|
769
|
+
if (this.match(TokenType.SYMBOL_0_0)) {
|
|
770
|
+
return node("ExtLit", { raw: "0\u2080" });
|
|
771
|
+
}
|
|
772
|
+
if (this.match(TokenType.SYMBOL_DOT_PRIM)) {
|
|
773
|
+
return node("ConstLit", { value: "\u30FB" });
|
|
774
|
+
}
|
|
775
|
+
if (this.match(TokenType.CONST_PI)) return node("NumLit", { value: Math.PI });
|
|
776
|
+
if (this.match(TokenType.CONST_E)) return node("NumLit", { value: Math.E });
|
|
777
|
+
if (this.match(TokenType.CONST_PHI)) return node("NumLit", { value: (1 + Math.sqrt(5)) / 2 });
|
|
778
|
+
if (this.match(TokenType.CONST_I)) return node("ConstLit", { value: "i" });
|
|
779
|
+
if (this.match(TokenType.CONST_EMPTY)) return node("ConstLit", { value: "\u2205" });
|
|
780
|
+
if (this.match(TokenType.CONST_PHI_UP)) return node("ConstLit", { value: "\u03A6" });
|
|
781
|
+
if (this.match(TokenType.CONST_PSI_UP)) return node("ConstLit", { value: "\u03A8" });
|
|
782
|
+
if (this.match(TokenType.CONST_OMEGA_UP)) return node("ConstLit", { value: "\u03A9" });
|
|
783
|
+
if (this.match(TokenType.QUAD_TOP)) return node("QuadLit", { value: "top" });
|
|
784
|
+
if (this.match(TokenType.QUAD_BOT)) return node("QuadLit", { value: "bottom" });
|
|
785
|
+
if (this.match(TokenType.QUAD_TOP_PI)) return node("QuadLit", { value: "topPi" });
|
|
786
|
+
if (this.match(TokenType.QUAD_BOT_PI)) return node("QuadLit", { value: "bottomPi" });
|
|
787
|
+
if (this.match(TokenType.MDIM_OPEN)) {
|
|
788
|
+
return this.parseMDimLit();
|
|
789
|
+
}
|
|
790
|
+
if (this.match(TokenType.LBRACKET)) {
|
|
791
|
+
const elems = [];
|
|
792
|
+
if (!this.check(TokenType.RBRACKET)) {
|
|
793
|
+
elems.push(this.parseExpression());
|
|
794
|
+
while (this.match(TokenType.COMMA)) elems.push(this.parseExpression());
|
|
795
|
+
}
|
|
796
|
+
this.expect(TokenType.RBRACKET);
|
|
797
|
+
return node("ArrayLit", { elements: elems });
|
|
798
|
+
}
|
|
799
|
+
if (this.match(TokenType.LPAREN)) {
|
|
800
|
+
const expr = this.parseExpression();
|
|
801
|
+
this.expect(TokenType.RPAREN);
|
|
802
|
+
return expr;
|
|
803
|
+
}
|
|
804
|
+
if (this.check(TokenType.IF)) return this.parseIfExpr();
|
|
805
|
+
if (this.check(TokenType.MATCH)) return this.parseMatchExpr();
|
|
806
|
+
if (this.check(TokenType.GENESIS)) {
|
|
807
|
+
this.advance();
|
|
808
|
+
if (this.match(TokenType.LPAREN)) {
|
|
809
|
+
this.expect(TokenType.RPAREN);
|
|
810
|
+
}
|
|
811
|
+
return node("FnCall", { callee: node("Ident", { name: "genesis" }), args: [] });
|
|
812
|
+
}
|
|
813
|
+
if (this.check(TokenType.IDENT)) {
|
|
814
|
+
const name = this.advance().value;
|
|
815
|
+
return node("Ident", { name });
|
|
816
|
+
}
|
|
817
|
+
throw this.error(`\u4E88\u671F\u3057\u306A\u3044\u30C8\u30FC\u30AF\u30F3: ${this.peek().value} (${this.peek().type})`);
|
|
818
|
+
}
|
|
819
|
+
// --- MDim literal: 𝕄{center; n1, n2, ... [weight w] [mode]} ---
|
|
820
|
+
parseMDimLit() {
|
|
821
|
+
const center = this.parseExpression();
|
|
822
|
+
this.expect(TokenType.SEMICOLON);
|
|
823
|
+
const neighbors = [];
|
|
824
|
+
neighbors.push(this.parseExpression());
|
|
825
|
+
while (this.match(TokenType.COMMA)) {
|
|
826
|
+
if (this.check(TokenType.RBRACE)) break;
|
|
827
|
+
if (this.check(TokenType.WEIGHT)) break;
|
|
828
|
+
neighbors.push(this.parseExpression());
|
|
829
|
+
}
|
|
830
|
+
let weight = null;
|
|
831
|
+
if (this.match(TokenType.WEIGHT)) {
|
|
832
|
+
weight = this.parseExpression();
|
|
833
|
+
}
|
|
834
|
+
let mode = "weighted";
|
|
835
|
+
if (this.match(TokenType.COLON)) {
|
|
836
|
+
if (this.check(TokenType.IDENT)) {
|
|
837
|
+
mode = this.advance().value;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
this.expect(TokenType.RBRACE);
|
|
841
|
+
return node("MDimLit", { center, neighbors, weight, mode });
|
|
842
|
+
}
|
|
843
|
+
// --- if expr then expr else expr ---
|
|
844
|
+
parseIfExpr() {
|
|
845
|
+
this.expect(TokenType.IF);
|
|
846
|
+
const cond = this.parseExpression();
|
|
847
|
+
this.expect(TokenType.THEN);
|
|
848
|
+
const then = this.parseExpression();
|
|
849
|
+
this.expect(TokenType.ELSE);
|
|
850
|
+
const elseExpr = this.parseExpression();
|
|
851
|
+
return node("IfExpr", { cond, then, else: elseExpr });
|
|
852
|
+
}
|
|
853
|
+
// --- match expr { case pat -> expr, ... } ---
|
|
854
|
+
parseMatchExpr() {
|
|
855
|
+
this.expect(TokenType.MATCH);
|
|
856
|
+
const target = this.parseExpression();
|
|
857
|
+
this.expect(TokenType.LBRACE);
|
|
858
|
+
const cases = [];
|
|
859
|
+
while (!this.check(TokenType.RBRACE) && !this.isAtEnd()) {
|
|
860
|
+
this.expect(TokenType.CASE);
|
|
861
|
+
const pattern = this.parsePrimary();
|
|
862
|
+
this.expect(TokenType.ARROW);
|
|
863
|
+
const body = this.parseExpression();
|
|
864
|
+
cases.push({ pattern, body });
|
|
865
|
+
this.match(TokenType.COMMA);
|
|
866
|
+
}
|
|
867
|
+
this.expect(TokenType.RBRACE);
|
|
868
|
+
return node("MatchExpr", { target, cases });
|
|
869
|
+
}
|
|
870
|
+
// --- Helpers ---
|
|
871
|
+
peek() {
|
|
872
|
+
return this.tokens[this.pos] || { type: TokenType.EOF, value: "", line: 0, col: 0 };
|
|
873
|
+
}
|
|
874
|
+
isAtEnd() {
|
|
875
|
+
return this.peek().type === TokenType.EOF;
|
|
876
|
+
}
|
|
877
|
+
check(type) {
|
|
878
|
+
return this.peek().type === type;
|
|
879
|
+
}
|
|
880
|
+
checkAhead(type, offset) {
|
|
881
|
+
const idx = this.pos + offset;
|
|
882
|
+
return idx < this.tokens.length && this.tokens[idx].type === type;
|
|
883
|
+
}
|
|
884
|
+
advance() {
|
|
885
|
+
const t = this.tokens[this.pos];
|
|
886
|
+
this.pos++;
|
|
887
|
+
return t;
|
|
888
|
+
}
|
|
889
|
+
match(type) {
|
|
890
|
+
if (this.check(type)) {
|
|
891
|
+
this.advance();
|
|
892
|
+
return true;
|
|
893
|
+
}
|
|
894
|
+
return false;
|
|
895
|
+
}
|
|
896
|
+
expect(type) {
|
|
897
|
+
if (this.check(type)) return this.advance();
|
|
898
|
+
const t = this.peek();
|
|
899
|
+
throw this.error(`\u671F\u5F85: ${type}, \u5B9F\u969B: ${t.type} ("${t.value}")`);
|
|
900
|
+
}
|
|
901
|
+
error(msg) {
|
|
902
|
+
const t = this.peek();
|
|
903
|
+
return new Error(`[\u884C ${t.line}:${t.col}] \u69CB\u6587\u30A8\u30E9\u30FC: ${msg}`);
|
|
904
|
+
}
|
|
905
|
+
shouldNegateBePrefix() {
|
|
906
|
+
if (this.pos === 0) return true;
|
|
907
|
+
const prev = this.tokens[this.pos - 1];
|
|
908
|
+
return [
|
|
909
|
+
TokenType.LPAREN,
|
|
910
|
+
TokenType.COMMA,
|
|
911
|
+
TokenType.ASSIGN,
|
|
912
|
+
TokenType.PLUS,
|
|
913
|
+
TokenType.MINUS,
|
|
914
|
+
TokenType.STAR,
|
|
915
|
+
TokenType.SLASH,
|
|
916
|
+
TokenType.OPLUS,
|
|
917
|
+
TokenType.OTIMES,
|
|
918
|
+
TokenType.PIPE_OP,
|
|
919
|
+
TokenType.SEMICOLON,
|
|
920
|
+
TokenType.LBRACKET,
|
|
921
|
+
TokenType.COLON
|
|
922
|
+
].includes(prev.type);
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
|
|
926
|
+
// src/core/types.ts
|
|
927
|
+
var Environment = class {
|
|
928
|
+
constructor(parent = null) {
|
|
929
|
+
this.parent = parent;
|
|
930
|
+
}
|
|
931
|
+
bindings = /* @__PURE__ */ new Map();
|
|
932
|
+
define(name, value, mutable = false) {
|
|
933
|
+
this.bindings.set(name, { value, mutable });
|
|
934
|
+
}
|
|
935
|
+
get(name) {
|
|
936
|
+
const b = this.bindings.get(name);
|
|
937
|
+
if (b) return b.value;
|
|
938
|
+
if (this.parent) return this.parent.get(name);
|
|
939
|
+
throw new Error(`\u672A\u5B9A\u7FA9\u306E\u5909\u6570: ${name}`);
|
|
940
|
+
}
|
|
941
|
+
set(name, value) {
|
|
942
|
+
const b = this.bindings.get(name);
|
|
943
|
+
if (b) {
|
|
944
|
+
if (!b.mutable) throw new Error(`\u4E0D\u5909\u306E\u5909\u6570\u306B\u4EE3\u5165: ${name}`);
|
|
945
|
+
b.value = value;
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
if (this.parent) {
|
|
949
|
+
this.parent.set(name, value);
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
throw new Error(`\u672A\u5B9A\u7FA9\u306E\u5909\u6570: ${name}`);
|
|
953
|
+
}
|
|
954
|
+
has(name) {
|
|
955
|
+
if (this.bindings.has(name)) return true;
|
|
956
|
+
if (this.parent) return this.parent.has(name);
|
|
957
|
+
return false;
|
|
958
|
+
}
|
|
959
|
+
getBinding(name) {
|
|
960
|
+
const b = this.bindings.get(name);
|
|
961
|
+
if (b) return b;
|
|
962
|
+
if (this.parent) return this.parent.getBinding(name);
|
|
963
|
+
return null;
|
|
964
|
+
}
|
|
965
|
+
allBindings() {
|
|
966
|
+
const all = /* @__PURE__ */ new Map();
|
|
967
|
+
if (this.parent) {
|
|
968
|
+
for (const [k, v] of this.parent.allBindings()) all.set(k, v);
|
|
969
|
+
}
|
|
970
|
+
for (const [k, v] of this.bindings) all.set(k, v);
|
|
971
|
+
return all;
|
|
972
|
+
}
|
|
973
|
+
};
|
|
974
|
+
|
|
975
|
+
// src/lang/evaluator.ts
|
|
976
|
+
function createExtended(base, subscripts) {
|
|
977
|
+
const order = subscripts.length;
|
|
978
|
+
return {
|
|
979
|
+
reiType: "Ext",
|
|
980
|
+
base,
|
|
981
|
+
order,
|
|
982
|
+
subscripts,
|
|
983
|
+
valStar() {
|
|
984
|
+
if (base === 0) return Math.pow(0.1, order);
|
|
985
|
+
return base * Math.pow(0.1, order);
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
function parseExtLit(raw) {
|
|
990
|
+
if (raw === "0\u2080") return createExtended(0, "o");
|
|
991
|
+
const baseChar = raw[0];
|
|
992
|
+
const subs = raw.slice(1);
|
|
993
|
+
const baseMap = {
|
|
994
|
+
"0": 0,
|
|
995
|
+
"\u03C0": Math.PI,
|
|
996
|
+
"e": Math.E,
|
|
997
|
+
"\u03C6": (1 + Math.sqrt(5)) / 2,
|
|
998
|
+
"i": NaN
|
|
999
|
+
};
|
|
1000
|
+
return createExtended(baseMap[baseChar] ?? 0, subs);
|
|
1001
|
+
}
|
|
1002
|
+
function computeMDim(md) {
|
|
1003
|
+
const { center, neighbors, mode } = md;
|
|
1004
|
+
const weights = md.weights ?? neighbors.map(() => 1);
|
|
1005
|
+
const n = neighbors.length;
|
|
1006
|
+
if (n === 0) return center;
|
|
1007
|
+
switch (mode) {
|
|
1008
|
+
case "weighted": {
|
|
1009
|
+
const wSum = weights.reduce((a, b) => a + b, 0);
|
|
1010
|
+
const wAvg = neighbors.reduce((sum, v, i) => sum + weights[i] * v, 0) / (wSum || 1);
|
|
1011
|
+
return center + wAvg;
|
|
1012
|
+
}
|
|
1013
|
+
case "multiplicative": {
|
|
1014
|
+
const prod = neighbors.reduce((p, v) => p * (1 + v), 1);
|
|
1015
|
+
return center * prod;
|
|
1016
|
+
}
|
|
1017
|
+
case "harmonic": {
|
|
1018
|
+
const harmSum = neighbors.reduce((s, v) => s + 1 / (Math.abs(v) || 1), 0);
|
|
1019
|
+
return center + n / harmSum;
|
|
1020
|
+
}
|
|
1021
|
+
case "exponential": {
|
|
1022
|
+
const expSum = neighbors.reduce((s, v) => s + Math.exp(v), 0);
|
|
1023
|
+
return center * (expSum / n);
|
|
1024
|
+
}
|
|
1025
|
+
default:
|
|
1026
|
+
return center;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
function quadNot(v) {
|
|
1030
|
+
const map = {
|
|
1031
|
+
"top": "bottom",
|
|
1032
|
+
"bottom": "top",
|
|
1033
|
+
"topPi": "bottomPi",
|
|
1034
|
+
"bottomPi": "topPi"
|
|
1035
|
+
};
|
|
1036
|
+
return map[v];
|
|
1037
|
+
}
|
|
1038
|
+
function quadAnd(a, b) {
|
|
1039
|
+
if (a === "bottom" || b === "bottom") return "bottom";
|
|
1040
|
+
if (a === "bottomPi" || b === "bottomPi") return "bottomPi";
|
|
1041
|
+
if (a === "top" && b === "top") return "top";
|
|
1042
|
+
if (a === "topPi" || b === "topPi") return "topPi";
|
|
1043
|
+
return "top";
|
|
1044
|
+
}
|
|
1045
|
+
function quadOr(a, b) {
|
|
1046
|
+
if (a === "top" || b === "top") return "top";
|
|
1047
|
+
if (a === "topPi" || b === "topPi") return "topPi";
|
|
1048
|
+
if (a === "bottom" && b === "bottom") return "bottom";
|
|
1049
|
+
return "bottomPi";
|
|
1050
|
+
}
|
|
1051
|
+
function createGenesis() {
|
|
1052
|
+
return { reiType: "State", state: "void", omega: 0, history: ["void"] };
|
|
1053
|
+
}
|
|
1054
|
+
var PHASE_ORDER = ["void", "dot", "line", "surface", "solid", "omega"];
|
|
1055
|
+
function genesisForward(g) {
|
|
1056
|
+
const idx = PHASE_ORDER.indexOf(g.state);
|
|
1057
|
+
if (idx < PHASE_ORDER.length - 1) {
|
|
1058
|
+
g.state = PHASE_ORDER[idx + 1];
|
|
1059
|
+
g.history.push(g.state);
|
|
1060
|
+
if (g.state === "omega") g.omega = 1;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
var Evaluator = class {
|
|
1064
|
+
env;
|
|
1065
|
+
constructor(parent) {
|
|
1066
|
+
this.env = new Environment(parent ?? null);
|
|
1067
|
+
this.registerBuiltins();
|
|
1068
|
+
}
|
|
1069
|
+
registerBuiltins() {
|
|
1070
|
+
this.env.define("e", Math.E);
|
|
1071
|
+
this.env.define("PI", Math.PI);
|
|
1072
|
+
this.env.define("genesis", {
|
|
1073
|
+
reiType: "Function",
|
|
1074
|
+
name: "genesis",
|
|
1075
|
+
params: [],
|
|
1076
|
+
body: null,
|
|
1077
|
+
closure: this.env
|
|
1078
|
+
});
|
|
1079
|
+
const mathFns = ["abs", "sqrt", "sin", "cos", "log", "exp", "floor", "ceil", "round", "min", "max", "len", "print"];
|
|
1080
|
+
for (const name of mathFns) {
|
|
1081
|
+
this.env.define(name, {
|
|
1082
|
+
reiType: "Function",
|
|
1083
|
+
name,
|
|
1084
|
+
params: ["x"],
|
|
1085
|
+
body: null,
|
|
1086
|
+
closure: this.env
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
eval(ast) {
|
|
1091
|
+
switch (ast.type) {
|
|
1092
|
+
case "Program":
|
|
1093
|
+
return this.evalProgram(ast);
|
|
1094
|
+
case "NumLit":
|
|
1095
|
+
return ast.value;
|
|
1096
|
+
case "StrLit":
|
|
1097
|
+
return ast.value;
|
|
1098
|
+
case "BoolLit":
|
|
1099
|
+
return ast.value;
|
|
1100
|
+
case "NullLit":
|
|
1101
|
+
return null;
|
|
1102
|
+
case "ExtLit":
|
|
1103
|
+
return parseExtLit(ast.raw);
|
|
1104
|
+
case "ConstLit":
|
|
1105
|
+
return this.evalConstLit(ast);
|
|
1106
|
+
case "QuadLit":
|
|
1107
|
+
return { reiType: "Quad", value: ast.value };
|
|
1108
|
+
case "MDimLit":
|
|
1109
|
+
return this.evalMDimLit(ast);
|
|
1110
|
+
case "ArrayLit":
|
|
1111
|
+
return ast.elements.map((e) => this.eval(e));
|
|
1112
|
+
case "Ident":
|
|
1113
|
+
return this.env.get(ast.name);
|
|
1114
|
+
case "LetStmt":
|
|
1115
|
+
return this.evalLetStmt(ast);
|
|
1116
|
+
case "MutStmt":
|
|
1117
|
+
return this.evalMutStmt(ast);
|
|
1118
|
+
case "CompressDef":
|
|
1119
|
+
return this.evalCompressDef(ast);
|
|
1120
|
+
case "BinOp":
|
|
1121
|
+
return this.evalBinOp(ast);
|
|
1122
|
+
case "UnaryOp":
|
|
1123
|
+
return this.evalUnaryOp(ast);
|
|
1124
|
+
case "Pipe":
|
|
1125
|
+
return this.evalPipe(ast);
|
|
1126
|
+
case "FnCall":
|
|
1127
|
+
return this.evalFnCall(ast);
|
|
1128
|
+
case "MemberAccess":
|
|
1129
|
+
return this.evalMemberAccess(ast);
|
|
1130
|
+
case "IndexAccess":
|
|
1131
|
+
return this.evalIndexAccess(ast);
|
|
1132
|
+
case "Extend":
|
|
1133
|
+
return this.evalExtend(ast);
|
|
1134
|
+
case "Reduce":
|
|
1135
|
+
return this.evalReduce(ast);
|
|
1136
|
+
case "ConvergeOp":
|
|
1137
|
+
return this.evalConverge(ast);
|
|
1138
|
+
case "DivergeOp":
|
|
1139
|
+
return this.evalDiverge(ast);
|
|
1140
|
+
case "ReflectOp":
|
|
1141
|
+
return this.evalReflect(ast);
|
|
1142
|
+
case "IfExpr":
|
|
1143
|
+
return this.evalIfExpr(ast);
|
|
1144
|
+
case "MatchExpr":
|
|
1145
|
+
return this.evalMatchExpr(ast);
|
|
1146
|
+
default:
|
|
1147
|
+
throw new Error(`\u672A\u5B9F\u88C5\u306E\u30CE\u30FC\u30C9\u578B: ${ast.type}`);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
evalProgram(ast) {
|
|
1151
|
+
let result = null;
|
|
1152
|
+
for (const stmt of ast.body) {
|
|
1153
|
+
result = this.eval(stmt);
|
|
1154
|
+
}
|
|
1155
|
+
return result;
|
|
1156
|
+
}
|
|
1157
|
+
evalConstLit(ast) {
|
|
1158
|
+
switch (ast.value) {
|
|
1159
|
+
case "\u30FB":
|
|
1160
|
+
return createGenesis();
|
|
1161
|
+
// 原初点
|
|
1162
|
+
case "\u2205":
|
|
1163
|
+
return null;
|
|
1164
|
+
case "i":
|
|
1165
|
+
return { reiType: "Ext", base: NaN, order: 0, subscripts: "", valStar: () => NaN };
|
|
1166
|
+
case "\u03A6":
|
|
1167
|
+
return "\u03A6";
|
|
1168
|
+
// phase constants
|
|
1169
|
+
case "\u03A8":
|
|
1170
|
+
return "\u03A8";
|
|
1171
|
+
case "\u03A9":
|
|
1172
|
+
return "\u03A9";
|
|
1173
|
+
default:
|
|
1174
|
+
return null;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
evalMDimLit(ast) {
|
|
1178
|
+
const center = this.toNumber(this.eval(ast.center));
|
|
1179
|
+
const neighbors = ast.neighbors.map((n) => this.toNumber(this.eval(n)));
|
|
1180
|
+
const weights = ast.weight ? [this.toNumber(this.eval(ast.weight))] : void 0;
|
|
1181
|
+
const mode = ast.mode || "weighted";
|
|
1182
|
+
return { reiType: "MDim", center, neighbors, mode, weights };
|
|
1183
|
+
}
|
|
1184
|
+
evalLetStmt(ast) {
|
|
1185
|
+
const val = this.eval(ast.init);
|
|
1186
|
+
this.env.define(ast.name, val, false);
|
|
1187
|
+
return val;
|
|
1188
|
+
}
|
|
1189
|
+
evalMutStmt(ast) {
|
|
1190
|
+
const val = this.eval(ast.init);
|
|
1191
|
+
this.env.define(ast.name, val, true);
|
|
1192
|
+
return val;
|
|
1193
|
+
}
|
|
1194
|
+
evalCompressDef(ast) {
|
|
1195
|
+
const fn = {
|
|
1196
|
+
reiType: "Function",
|
|
1197
|
+
name: ast.name,
|
|
1198
|
+
params: ast.params,
|
|
1199
|
+
body: ast.body,
|
|
1200
|
+
closure: this.env
|
|
1201
|
+
};
|
|
1202
|
+
this.env.define(ast.name, fn);
|
|
1203
|
+
return fn;
|
|
1204
|
+
}
|
|
1205
|
+
evalBinOp(ast) {
|
|
1206
|
+
const left = this.eval(ast.left);
|
|
1207
|
+
const right = this.eval(ast.right);
|
|
1208
|
+
if (this.isQuad(left) && this.isQuad(right)) {
|
|
1209
|
+
const lq = left.value;
|
|
1210
|
+
const rq = right.value;
|
|
1211
|
+
switch (ast.op) {
|
|
1212
|
+
case "\u2227":
|
|
1213
|
+
return { reiType: "Quad", value: quadAnd(lq, rq) };
|
|
1214
|
+
case "\u2228":
|
|
1215
|
+
return { reiType: "Quad", value: quadOr(lq, rq) };
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
if (this.isExt(left) && this.isExt(right) && ast.op === "\u2295") {
|
|
1219
|
+
const le = left;
|
|
1220
|
+
const re = right;
|
|
1221
|
+
return createExtended(le.base + re.base, le.subscripts + re.subscripts);
|
|
1222
|
+
}
|
|
1223
|
+
if (typeof left === "number" && this.isExt(right) && ast.op === "\xB7") {
|
|
1224
|
+
const re = right;
|
|
1225
|
+
return createExtended(left * re.base, re.subscripts);
|
|
1226
|
+
}
|
|
1227
|
+
if (this.isMDim(left) && this.isMDim(right) && ast.op === "\u2295") {
|
|
1228
|
+
const lm = left;
|
|
1229
|
+
const rm = right;
|
|
1230
|
+
const maxLen = Math.max(lm.neighbors.length, rm.neighbors.length);
|
|
1231
|
+
const neighbors = [];
|
|
1232
|
+
for (let i = 0; i < maxLen; i++) {
|
|
1233
|
+
neighbors.push((lm.neighbors[i] ?? 0) + (rm.neighbors[i] ?? 0));
|
|
1234
|
+
}
|
|
1235
|
+
return { reiType: "MDim", center: lm.center + rm.center, neighbors, mode: lm.mode };
|
|
1236
|
+
}
|
|
1237
|
+
if (typeof left === "string" && typeof right === "string" && ast.op === "+") {
|
|
1238
|
+
return left + right;
|
|
1239
|
+
}
|
|
1240
|
+
const l = this.toNumber(left);
|
|
1241
|
+
const r = this.toNumber(right);
|
|
1242
|
+
switch (ast.op) {
|
|
1243
|
+
case "+":
|
|
1244
|
+
return l + r;
|
|
1245
|
+
case "-":
|
|
1246
|
+
return l - r;
|
|
1247
|
+
case "*":
|
|
1248
|
+
return l * r;
|
|
1249
|
+
case "/":
|
|
1250
|
+
return r === 0 ? NaN : l / r;
|
|
1251
|
+
case "\u2295":
|
|
1252
|
+
return l + r;
|
|
1253
|
+
case "\u2297":
|
|
1254
|
+
return l * r;
|
|
1255
|
+
case "\xB7":
|
|
1256
|
+
return l * r;
|
|
1257
|
+
case "==":
|
|
1258
|
+
return l === r;
|
|
1259
|
+
case "!=":
|
|
1260
|
+
return l !== r;
|
|
1261
|
+
case ">":
|
|
1262
|
+
return l > r;
|
|
1263
|
+
case "<":
|
|
1264
|
+
return l < r;
|
|
1265
|
+
case ">=":
|
|
1266
|
+
return l >= r;
|
|
1267
|
+
case "<=":
|
|
1268
|
+
return l <= r;
|
|
1269
|
+
case ">\u03BA":
|
|
1270
|
+
return l > r;
|
|
1271
|
+
case "<\u03BA":
|
|
1272
|
+
return l < r;
|
|
1273
|
+
case "=\u03BA":
|
|
1274
|
+
return l === r;
|
|
1275
|
+
case "\u2227":
|
|
1276
|
+
return !!left && !!right;
|
|
1277
|
+
case "\u2228":
|
|
1278
|
+
return !!left || !!right;
|
|
1279
|
+
default:
|
|
1280
|
+
throw new Error(`\u672A\u77E5\u306E\u6F14\u7B97\u5B50: ${ast.op}`);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
evalUnaryOp(ast) {
|
|
1284
|
+
const operand = this.eval(ast.operand);
|
|
1285
|
+
switch (ast.op) {
|
|
1286
|
+
case "-":
|
|
1287
|
+
return -this.toNumber(operand);
|
|
1288
|
+
case "\xAC":
|
|
1289
|
+
if (this.isQuad(operand)) {
|
|
1290
|
+
return { reiType: "Quad", value: quadNot(operand.value) };
|
|
1291
|
+
}
|
|
1292
|
+
return !operand;
|
|
1293
|
+
default:
|
|
1294
|
+
throw new Error(`\u672A\u77E5\u306E\u5358\u9805\u6F14\u7B97\u5B50: ${ast.op}`);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
evalPipe(ast) {
|
|
1298
|
+
const input = this.eval(ast.input);
|
|
1299
|
+
const cmd = ast.command;
|
|
1300
|
+
if (cmd.type === "PipeCmd") {
|
|
1301
|
+
return this.execPipeCmd(input, cmd);
|
|
1302
|
+
}
|
|
1303
|
+
throw new Error("\u7121\u52B9\u306A\u30D1\u30A4\u30D7\u30B3\u30DE\u30F3\u30C9");
|
|
1304
|
+
}
|
|
1305
|
+
execPipeCmd(input, cmd) {
|
|
1306
|
+
const { cmd: cmdName, mode, args: argNodes } = cmd;
|
|
1307
|
+
const args = argNodes.map((a) => this.eval(a));
|
|
1308
|
+
if (this.isMDim(input)) {
|
|
1309
|
+
const md = input;
|
|
1310
|
+
switch (cmdName) {
|
|
1311
|
+
case "compute": {
|
|
1312
|
+
const m = mode || md.mode;
|
|
1313
|
+
return computeMDim({ ...md, mode: m });
|
|
1314
|
+
}
|
|
1315
|
+
case "center":
|
|
1316
|
+
return md.center;
|
|
1317
|
+
case "neighbors":
|
|
1318
|
+
return md.neighbors;
|
|
1319
|
+
case "dim":
|
|
1320
|
+
return md.neighbors.length;
|
|
1321
|
+
case "normalize": {
|
|
1322
|
+
const sum = md.neighbors.reduce((a, b) => a + Math.abs(b), 0) || 1;
|
|
1323
|
+
return {
|
|
1324
|
+
reiType: "MDim",
|
|
1325
|
+
center: md.center,
|
|
1326
|
+
neighbors: md.neighbors.map((n) => n / sum),
|
|
1327
|
+
mode: md.mode
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
case "flatten":
|
|
1331
|
+
return computeMDim(md);
|
|
1332
|
+
case "map": {
|
|
1333
|
+
if (args.length > 0 && this.isFunction(args[0])) {
|
|
1334
|
+
const fn = args[0];
|
|
1335
|
+
const newNeighbors = md.neighbors.map((n) => this.toNumber(this.callFunction(fn, [n])));
|
|
1336
|
+
return { ...md, neighbors: newNeighbors };
|
|
1337
|
+
}
|
|
1338
|
+
return md;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
if (this.isExt(input)) {
|
|
1343
|
+
const ext = input;
|
|
1344
|
+
switch (cmdName) {
|
|
1345
|
+
case "order":
|
|
1346
|
+
return ext.order;
|
|
1347
|
+
case "base":
|
|
1348
|
+
return ext.base;
|
|
1349
|
+
case "valStar":
|
|
1350
|
+
case "val":
|
|
1351
|
+
return ext.valStar();
|
|
1352
|
+
case "subscripts":
|
|
1353
|
+
return ext.subscripts;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
if (this.isGenesis(input)) {
|
|
1357
|
+
const g = input;
|
|
1358
|
+
switch (cmdName) {
|
|
1359
|
+
case "forward":
|
|
1360
|
+
genesisForward(g);
|
|
1361
|
+
return g;
|
|
1362
|
+
case "phase":
|
|
1363
|
+
return g.state;
|
|
1364
|
+
case "history":
|
|
1365
|
+
return g.history;
|
|
1366
|
+
case "omega":
|
|
1367
|
+
return g.omega;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
if (Array.isArray(input)) {
|
|
1371
|
+
switch (cmdName) {
|
|
1372
|
+
case "len":
|
|
1373
|
+
return input.length;
|
|
1374
|
+
case "sum":
|
|
1375
|
+
return input.reduce((a, b) => a + this.toNumber(b), 0);
|
|
1376
|
+
case "avg":
|
|
1377
|
+
return input.length === 0 ? 0 : input.reduce((a, b) => a + this.toNumber(b), 0) / input.length;
|
|
1378
|
+
case "first":
|
|
1379
|
+
return input[0] ?? null;
|
|
1380
|
+
case "last":
|
|
1381
|
+
return input[input.length - 1] ?? null;
|
|
1382
|
+
case "reverse":
|
|
1383
|
+
return [...input].reverse();
|
|
1384
|
+
case "sort":
|
|
1385
|
+
return [...input].sort((a, b) => this.toNumber(a) - this.toNumber(b));
|
|
1386
|
+
case "map": {
|
|
1387
|
+
if (args.length > 0 && this.isFunction(args[0])) {
|
|
1388
|
+
return input.map((v) => this.callFunction(args[0], [v]));
|
|
1389
|
+
}
|
|
1390
|
+
return input;
|
|
1391
|
+
}
|
|
1392
|
+
case "filter": {
|
|
1393
|
+
if (args.length > 0 && this.isFunction(args[0])) {
|
|
1394
|
+
return input.filter((v) => !!this.callFunction(args[0], [v]));
|
|
1395
|
+
}
|
|
1396
|
+
return input;
|
|
1397
|
+
}
|
|
1398
|
+
case "reduce": {
|
|
1399
|
+
if (args.length >= 2 && this.isFunction(args[0])) {
|
|
1400
|
+
return input.reduce((acc, v) => this.callFunction(args[0], [acc, v]), args[1]);
|
|
1401
|
+
}
|
|
1402
|
+
return input;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
if (typeof input === "number") {
|
|
1407
|
+
switch (cmdName) {
|
|
1408
|
+
case "abs":
|
|
1409
|
+
return Math.abs(input);
|
|
1410
|
+
case "sqrt":
|
|
1411
|
+
return Math.sqrt(input);
|
|
1412
|
+
case "round":
|
|
1413
|
+
return Math.round(input);
|
|
1414
|
+
case "floor":
|
|
1415
|
+
return Math.floor(input);
|
|
1416
|
+
case "ceil":
|
|
1417
|
+
return Math.ceil(input);
|
|
1418
|
+
case "negate":
|
|
1419
|
+
return -input;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
if (typeof input === "string") {
|
|
1423
|
+
switch (cmdName) {
|
|
1424
|
+
case "len":
|
|
1425
|
+
return input.length;
|
|
1426
|
+
case "upper":
|
|
1427
|
+
return input.toUpperCase();
|
|
1428
|
+
case "lower":
|
|
1429
|
+
return input.toLowerCase();
|
|
1430
|
+
case "trim":
|
|
1431
|
+
return input.trim();
|
|
1432
|
+
case "split":
|
|
1433
|
+
return input.split(args[0] ?? "");
|
|
1434
|
+
case "reverse":
|
|
1435
|
+
return Array.from(input).reverse().join("");
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
if (cmdName === "\u290A" || cmdName === "converge") {
|
|
1439
|
+
if (this.isMDim(input)) {
|
|
1440
|
+
return computeMDim(input);
|
|
1441
|
+
}
|
|
1442
|
+
return input;
|
|
1443
|
+
}
|
|
1444
|
+
if (cmdName === "\u290B" || cmdName === "diverge") {
|
|
1445
|
+
if (typeof input === "number") {
|
|
1446
|
+
return { reiType: "MDim", center: input, neighbors: [input, input, input, input], mode: "weighted" };
|
|
1447
|
+
}
|
|
1448
|
+
return input;
|
|
1449
|
+
}
|
|
1450
|
+
if (this.env.has(cmdName)) {
|
|
1451
|
+
const fn = this.env.get(cmdName);
|
|
1452
|
+
if (this.isFunction(fn)) {
|
|
1453
|
+
return this.callFunction(fn, [input, ...args]);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
throw new Error(`\u672A\u77E5\u306E\u30D1\u30A4\u30D7\u30B3\u30DE\u30F3\u30C9: ${cmdName}`);
|
|
1457
|
+
}
|
|
1458
|
+
evalFnCall(ast) {
|
|
1459
|
+
const callee = this.eval(ast.callee);
|
|
1460
|
+
const args = ast.args.map((a) => this.eval(a));
|
|
1461
|
+
if (ast.callee.type === "Ident" && ast.callee.name === "genesis") {
|
|
1462
|
+
return createGenesis();
|
|
1463
|
+
}
|
|
1464
|
+
if (this.isFunction(callee)) {
|
|
1465
|
+
const fn = callee;
|
|
1466
|
+
return this.callFunction(fn, args);
|
|
1467
|
+
}
|
|
1468
|
+
throw new Error(`\u547C\u3073\u51FA\u3057\u4E0D\u53EF\u80FD: ${JSON.stringify(callee)}`);
|
|
1469
|
+
}
|
|
1470
|
+
callFunction(fn, args) {
|
|
1471
|
+
if (fn.body === null || fn.body === void 0) {
|
|
1472
|
+
return this.callBuiltin(fn.name, args);
|
|
1473
|
+
}
|
|
1474
|
+
const callEnv = new Environment(fn.closure);
|
|
1475
|
+
for (let i = 0; i < fn.params.length; i++) {
|
|
1476
|
+
callEnv.define(fn.params[i], args[i] ?? null);
|
|
1477
|
+
}
|
|
1478
|
+
const savedEnv = this.env;
|
|
1479
|
+
this.env = callEnv;
|
|
1480
|
+
const result = this.eval(fn.body);
|
|
1481
|
+
this.env = savedEnv;
|
|
1482
|
+
return result;
|
|
1483
|
+
}
|
|
1484
|
+
callBuiltin(name, args) {
|
|
1485
|
+
if (name === "genesis") return createGenesis();
|
|
1486
|
+
const a = args[0] !== void 0 ? this.toNumber(args[0]) : 0;
|
|
1487
|
+
const b = args[1] !== void 0 ? this.toNumber(args[1]) : 0;
|
|
1488
|
+
switch (name) {
|
|
1489
|
+
case "abs":
|
|
1490
|
+
return Math.abs(a);
|
|
1491
|
+
case "sqrt":
|
|
1492
|
+
return Math.sqrt(a);
|
|
1493
|
+
case "sin":
|
|
1494
|
+
return Math.sin(a);
|
|
1495
|
+
case "cos":
|
|
1496
|
+
return Math.cos(a);
|
|
1497
|
+
case "log":
|
|
1498
|
+
return Math.log(a);
|
|
1499
|
+
case "exp":
|
|
1500
|
+
return Math.exp(a);
|
|
1501
|
+
case "floor":
|
|
1502
|
+
return Math.floor(a);
|
|
1503
|
+
case "ceil":
|
|
1504
|
+
return Math.ceil(a);
|
|
1505
|
+
case "round":
|
|
1506
|
+
return Math.round(a);
|
|
1507
|
+
case "min":
|
|
1508
|
+
return Math.min(a, b);
|
|
1509
|
+
case "max":
|
|
1510
|
+
return Math.max(a, b);
|
|
1511
|
+
case "len":
|
|
1512
|
+
if (Array.isArray(args[0])) return args[0].length;
|
|
1513
|
+
if (typeof args[0] === "string") return args[0].length;
|
|
1514
|
+
return 0;
|
|
1515
|
+
case "print":
|
|
1516
|
+
return args[0] ?? null;
|
|
1517
|
+
default:
|
|
1518
|
+
throw new Error(`\u672A\u77E5\u306E\u7D44\u8FBC\u307F\u95A2\u6570: ${name}`);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
evalMemberAccess(ast) {
|
|
1522
|
+
const obj = this.eval(ast.object);
|
|
1523
|
+
if (this.isMDim(obj)) {
|
|
1524
|
+
const md = obj;
|
|
1525
|
+
switch (ast.member) {
|
|
1526
|
+
case "center":
|
|
1527
|
+
return md.center;
|
|
1528
|
+
case "neighbors":
|
|
1529
|
+
return md.neighbors;
|
|
1530
|
+
case "mode":
|
|
1531
|
+
return md.mode;
|
|
1532
|
+
case "dim":
|
|
1533
|
+
return md.neighbors.length;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
if (this.isExt(obj)) {
|
|
1537
|
+
const ext = obj;
|
|
1538
|
+
switch (ast.member) {
|
|
1539
|
+
case "order":
|
|
1540
|
+
return ext.order;
|
|
1541
|
+
case "base":
|
|
1542
|
+
return ext.base;
|
|
1543
|
+
case "subscripts":
|
|
1544
|
+
return ext.subscripts;
|
|
1545
|
+
case "valStar":
|
|
1546
|
+
return ext.valStar();
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
if (this.isGenesis(obj)) {
|
|
1550
|
+
const g = obj;
|
|
1551
|
+
switch (ast.member) {
|
|
1552
|
+
case "state":
|
|
1553
|
+
case "phase":
|
|
1554
|
+
return g.state;
|
|
1555
|
+
case "omega":
|
|
1556
|
+
return g.omega;
|
|
1557
|
+
case "history":
|
|
1558
|
+
return g.history;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
if (Array.isArray(obj)) {
|
|
1562
|
+
switch (ast.member) {
|
|
1563
|
+
case "length":
|
|
1564
|
+
return obj.length;
|
|
1565
|
+
case "first":
|
|
1566
|
+
return obj[0] ?? null;
|
|
1567
|
+
case "last":
|
|
1568
|
+
return obj[obj.length - 1] ?? null;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
throw new Error(`\u30E1\u30F3\u30D0\u30FC ${ast.member} \u306B\u30A2\u30AF\u30BB\u30B9\u3067\u304D\u307E\u305B\u3093`);
|
|
1572
|
+
}
|
|
1573
|
+
evalIndexAccess(ast) {
|
|
1574
|
+
const obj = this.eval(ast.object);
|
|
1575
|
+
const idx = this.toNumber(this.eval(ast.index));
|
|
1576
|
+
if (Array.isArray(obj)) return obj[idx] ?? null;
|
|
1577
|
+
if (typeof obj === "string") return obj[idx] ?? null;
|
|
1578
|
+
if (this.isMDim(obj)) return obj.neighbors[idx] ?? null;
|
|
1579
|
+
throw new Error("\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u30A2\u30AF\u30BB\u30B9\u4E0D\u53EF");
|
|
1580
|
+
}
|
|
1581
|
+
evalExtend(ast) {
|
|
1582
|
+
const target = this.eval(ast.target);
|
|
1583
|
+
if (this.isExt(target)) {
|
|
1584
|
+
const ext = target;
|
|
1585
|
+
if (ast.subscript) {
|
|
1586
|
+
return createExtended(ext.base, ext.subscripts + ast.subscript);
|
|
1587
|
+
}
|
|
1588
|
+
return createExtended(ext.base, ext.subscripts + "o");
|
|
1589
|
+
}
|
|
1590
|
+
throw new Error("\u62E1\u5F35\u306F\u62E1\u5F35\u6570\u306B\u306E\u307F\u9069\u7528\u53EF\u80FD");
|
|
1591
|
+
}
|
|
1592
|
+
evalReduce(ast) {
|
|
1593
|
+
const target = this.eval(ast.target);
|
|
1594
|
+
if (this.isExt(target)) {
|
|
1595
|
+
const ext = target;
|
|
1596
|
+
if (ext.order <= 1) return ext.base;
|
|
1597
|
+
return createExtended(ext.base, ext.subscripts.slice(0, -1));
|
|
1598
|
+
}
|
|
1599
|
+
throw new Error("\u7E2E\u7D04\u306F\u62E1\u5F35\u6570\u306B\u306E\u307F\u9069\u7528\u53EF\u80FD");
|
|
1600
|
+
}
|
|
1601
|
+
evalConverge(ast) {
|
|
1602
|
+
const left = this.eval(ast.left);
|
|
1603
|
+
const right = this.eval(ast.right);
|
|
1604
|
+
if (this.isMDim(left) && this.isMDim(right)) {
|
|
1605
|
+
const lm = left;
|
|
1606
|
+
const rm = right;
|
|
1607
|
+
return {
|
|
1608
|
+
reiType: "MDim",
|
|
1609
|
+
center: (lm.center + rm.center) / 2,
|
|
1610
|
+
neighbors: [...lm.neighbors, ...rm.neighbors],
|
|
1611
|
+
mode: lm.mode
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
return this.toNumber(left) + this.toNumber(right);
|
|
1615
|
+
}
|
|
1616
|
+
evalDiverge(ast) {
|
|
1617
|
+
const left = this.eval(ast.left);
|
|
1618
|
+
const right = this.eval(ast.right);
|
|
1619
|
+
if (this.isMDim(left)) {
|
|
1620
|
+
const md = left;
|
|
1621
|
+
this.toNumber(right);
|
|
1622
|
+
const half = Math.floor(md.neighbors.length / 2);
|
|
1623
|
+
return [
|
|
1624
|
+
{ reiType: "MDim", center: md.center, neighbors: md.neighbors.slice(0, half), mode: md.mode },
|
|
1625
|
+
{ reiType: "MDim", center: md.center, neighbors: md.neighbors.slice(half), mode: md.mode }
|
|
1626
|
+
];
|
|
1627
|
+
}
|
|
1628
|
+
return this.toNumber(left) - this.toNumber(right);
|
|
1629
|
+
}
|
|
1630
|
+
evalReflect(ast) {
|
|
1631
|
+
const left = this.eval(ast.left);
|
|
1632
|
+
this.eval(ast.right);
|
|
1633
|
+
if (this.isMDim(left)) {
|
|
1634
|
+
const md = left;
|
|
1635
|
+
return {
|
|
1636
|
+
reiType: "MDim",
|
|
1637
|
+
center: md.center,
|
|
1638
|
+
neighbors: [...md.neighbors].reverse(),
|
|
1639
|
+
mode: md.mode
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
return this.toNumber(left);
|
|
1643
|
+
}
|
|
1644
|
+
evalIfExpr(ast) {
|
|
1645
|
+
const cond = this.eval(ast.cond);
|
|
1646
|
+
return this.isTruthy(cond) ? this.eval(ast.then) : this.eval(ast.else);
|
|
1647
|
+
}
|
|
1648
|
+
evalMatchExpr(ast) {
|
|
1649
|
+
const target = this.eval(ast.target);
|
|
1650
|
+
for (const { pattern, body } of ast.cases) {
|
|
1651
|
+
const patVal = this.eval(pattern);
|
|
1652
|
+
if (this.matches(target, patVal)) {
|
|
1653
|
+
return this.eval(body);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
throw new Error("\u30DE\u30C3\u30C1\u3059\u308B\u5206\u5C90\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093");
|
|
1657
|
+
}
|
|
1658
|
+
// --- Helpers ---
|
|
1659
|
+
toNumber(val) {
|
|
1660
|
+
if (typeof val === "number") return val;
|
|
1661
|
+
if (typeof val === "boolean") return val ? 1 : 0;
|
|
1662
|
+
if (val === null) return 0;
|
|
1663
|
+
if (this.isExt(val)) return val.valStar();
|
|
1664
|
+
if (this.isMDim(val)) return computeMDim(val);
|
|
1665
|
+
if (typeof val === "string") return parseFloat(val) || 0;
|
|
1666
|
+
return 0;
|
|
1667
|
+
}
|
|
1668
|
+
isTruthy(val) {
|
|
1669
|
+
if (val === null || val === false || val === 0) return false;
|
|
1670
|
+
if (this.isQuad(val)) return val.value === "top" || val.value === "topPi";
|
|
1671
|
+
return true;
|
|
1672
|
+
}
|
|
1673
|
+
matches(target, pattern) {
|
|
1674
|
+
if (typeof target === typeof pattern && target === pattern) return true;
|
|
1675
|
+
if (this.isQuad(target) && this.isQuad(pattern)) {
|
|
1676
|
+
return target.value === pattern.value;
|
|
1677
|
+
}
|
|
1678
|
+
return false;
|
|
1679
|
+
}
|
|
1680
|
+
isObj(v) {
|
|
1681
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
1682
|
+
}
|
|
1683
|
+
isMDim(v) {
|
|
1684
|
+
return this.isObj(v) && v.reiType === "MDim";
|
|
1685
|
+
}
|
|
1686
|
+
isExt(v) {
|
|
1687
|
+
return this.isObj(v) && v.reiType === "Ext";
|
|
1688
|
+
}
|
|
1689
|
+
isGenesis(v) {
|
|
1690
|
+
return this.isObj(v) && v.reiType === "State";
|
|
1691
|
+
}
|
|
1692
|
+
isFunction(v) {
|
|
1693
|
+
return this.isObj(v) && v.reiType === "Function";
|
|
1694
|
+
}
|
|
1695
|
+
isQuad(v) {
|
|
1696
|
+
return this.isObj(v) && v.reiType === "Quad";
|
|
1697
|
+
}
|
|
1698
|
+
};
|
|
1699
|
+
|
|
1700
|
+
// src/index.ts
|
|
1701
|
+
var _evaluator = new Evaluator();
|
|
1702
|
+
function rei(code) {
|
|
1703
|
+
const lexer = new Lexer(code);
|
|
1704
|
+
const tokens = lexer.tokenize();
|
|
1705
|
+
const parser = new Parser(tokens);
|
|
1706
|
+
const ast = parser.parseProgram();
|
|
1707
|
+
return _evaluator.eval(ast);
|
|
1708
|
+
}
|
|
1709
|
+
rei.reset = function reset() {
|
|
1710
|
+
_evaluator = new Evaluator();
|
|
1711
|
+
};
|
|
1712
|
+
rei.evaluator = function evaluator() {
|
|
1713
|
+
return _evaluator;
|
|
1714
|
+
};
|
|
1715
|
+
rei.parse = function parse(code) {
|
|
1716
|
+
const lexer = new Lexer(code);
|
|
1717
|
+
const tokens = lexer.tokenize();
|
|
1718
|
+
const parser = new Parser(tokens);
|
|
1719
|
+
return parser.parseProgram();
|
|
1720
|
+
};
|
|
1721
|
+
rei.tokenize = function tokenize(code) {
|
|
1722
|
+
const lexer = new Lexer(code);
|
|
1723
|
+
return lexer.tokenize();
|
|
1724
|
+
};
|
|
1725
|
+
var index_default = rei;
|
|
1726
|
+
|
|
1727
|
+
export { Environment, Evaluator, Lexer, Parser, TokenType, index_default as default, rei };
|
|
1728
|
+
//# sourceMappingURL=index.mjs.map
|
|
1729
|
+
//# sourceMappingURL=index.mjs.map
|