@misterscan/sesi 1.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/LICENSE +21 -0
- package/README.md +191 -0
- package/bin/sesi.js +56 -0
- package/dist/ai-runtime.d.ts +15 -0
- package/dist/ai-runtime.d.ts.map +1 -0
- package/dist/ai-runtime.js +214 -0
- package/dist/ai-runtime.js.map +1 -0
- package/dist/builtins.d.ts +7 -0
- package/dist/builtins.d.ts.map +1 -0
- package/dist/builtins.js +473 -0
- package/dist/builtins.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/interpreter.d.ts +36 -0
- package/dist/interpreter.d.ts.map +1 -0
- package/dist/interpreter.js +495 -0
- package/dist/interpreter.js.map +1 -0
- package/dist/lexer.d.ts +26 -0
- package/dist/lexer.d.ts.map +1 -0
- package/dist/lexer.js +340 -0
- package/dist/lexer.js.map +1 -0
- package/dist/parser.d.ts +55 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +1022 -0
- package/dist/parser.js.map +1 -0
- package/dist/types.d.ts +304 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +63 -0
- package/dist/types.js.map +1 -0
- package/docs/ARCHITECTURE.md +430 -0
- package/docs/BUILTINS.md +577 -0
- package/docs/COMPARISON.md +334 -0
- package/docs/DISTRIBUTED_SYSTEMS.md +71 -0
- package/docs/IMAGE_GENERATION.md +76 -0
- package/docs/IMPLEMENTATION_SUMMARY.md +533 -0
- package/docs/QUICKSTART.md +351 -0
- package/docs/README.md +191 -0
- package/docs/ROADMAP.md +408 -0
- package/docs/SPECIFICATION.md +462 -0
- package/docs/SYSTEMS_REASONING.md +522 -0
- package/examples/01_hello.sesi +2 -0
- package/examples/02_variables.sesi +11 -0
- package/examples/03_functions.sesi +6 -0
- package/examples/04_conditionals.sesi +6 -0
- package/examples/05_loops.sesi +12 -0
- package/examples/06_arrays_objects.sesi +18 -0
- package/examples/07_prompts.sesi +10 -0
- package/examples/08_model_call.sesi +5 -0
- package/examples/09_structured_output.sesi +7 -0
- package/examples/10_code_generation.sesi +5 -0
- package/examples/11_memory_conversation.sesi +16 -0
- package/examples/12_classification.sesi +8 -0
- package/examples/13_data_pipeline.sesi +35 -0
- package/examples/14_folder_explainer.sesi +58 -0
- package/examples/15_image_generation.sesi +17 -0
- package/main/atm_deposit.sesi +37 -0
- package/main/atm_withdraw.sesi +37 -0
- package/main/data.txt +1 -0
- package/main/math_aggregator.sesi +15 -0
- package/main/math_generator.sesi +7 -0
- package/main/math_processor.sesi +23 -0
- package/main/orchestrator.sesi +15 -0
- package/main/playground.sesi +1 -0
- package/main/setup_swarm.sesi +5 -0
- package/main/start.sesi +13 -0
- package/main/tax_calculator.sesi +15 -0
- package/main/tests/compare.sesi +23 -0
- package/main/tests/compare.ts +104 -0
- package/main/tests/debug.sesi +1 -0
- package/main/tests/demo.sesi +24 -0
- package/main/tests/primitive_validation.sesi +18 -0
- package/main/tests/test_connection.sesi +4 -0
- package/main/tests/test_failure_debug.sesi +2 -0
- package/main/tests/test_image.sesi +3 -0
- package/main/tests/test_parser_config.sesi +2 -0
- package/main/tests/test_syntax.sesi +3 -0
- package/main/tests/test_tool_call.sesi +14 -0
- package/main/tests/try.sesi +7 -0
- package/main/vault.sesi +15 -0
- package/package.json +50 -0
package/dist/parser.js
ADDED
|
@@ -0,0 +1,1022 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Parser = void 0;
|
|
4
|
+
class Parser {
|
|
5
|
+
constructor(tokens) {
|
|
6
|
+
this.current = 0;
|
|
7
|
+
this.tokens = tokens;
|
|
8
|
+
}
|
|
9
|
+
parse() {
|
|
10
|
+
const statements = [];
|
|
11
|
+
while (!this.isAtEnd()) {
|
|
12
|
+
const stmt = this.statement();
|
|
13
|
+
if (stmt)
|
|
14
|
+
statements.push(stmt);
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
type: 'Program',
|
|
18
|
+
statements,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
statement() {
|
|
22
|
+
this.skipNewlines();
|
|
23
|
+
if (this.isAtEnd())
|
|
24
|
+
return null;
|
|
25
|
+
try {
|
|
26
|
+
if (this.match('LET'))
|
|
27
|
+
return this.letStatement();
|
|
28
|
+
if (this.match('CONST'))
|
|
29
|
+
return this.constStatement();
|
|
30
|
+
if (this.match('FN'))
|
|
31
|
+
return this.functionStatement();
|
|
32
|
+
if (this.match('IF'))
|
|
33
|
+
return this.ifStatement();
|
|
34
|
+
if (this.match('WHILE'))
|
|
35
|
+
return this.whileStatement();
|
|
36
|
+
if (this.match('FOR'))
|
|
37
|
+
return this.forStatement();
|
|
38
|
+
if (this.match('RETURN'))
|
|
39
|
+
return this.returnStatement();
|
|
40
|
+
if (this.match('BREAK'))
|
|
41
|
+
return this.breakStatement();
|
|
42
|
+
if (this.match('CONTINUE'))
|
|
43
|
+
return this.continueStatement();
|
|
44
|
+
if (this.match('TRY'))
|
|
45
|
+
return this.tryStatement();
|
|
46
|
+
if (this.match('IMPORT'))
|
|
47
|
+
return this.importStatement();
|
|
48
|
+
if (this.match('EXPORT'))
|
|
49
|
+
return this.exportStatement();
|
|
50
|
+
if (this.match('MEMORY'))
|
|
51
|
+
return this.memoryStatement();
|
|
52
|
+
if (this.check('LEFT_BRACE'))
|
|
53
|
+
return this.blockStatement();
|
|
54
|
+
return this.expressionStatement();
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
console.error(error.message);
|
|
58
|
+
this.synchronize();
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
letStatement() {
|
|
63
|
+
const line = this.previous().line;
|
|
64
|
+
const name = this.consume('IDENTIFIER', 'Expected variable name').lexeme;
|
|
65
|
+
let typeAnnotation;
|
|
66
|
+
if (this.match('COLON')) {
|
|
67
|
+
typeAnnotation = this.typeAnnotation();
|
|
68
|
+
}
|
|
69
|
+
let value;
|
|
70
|
+
if (this.match('EQUAL')) {
|
|
71
|
+
value = this.expression();
|
|
72
|
+
}
|
|
73
|
+
this.consumeStatementEnd();
|
|
74
|
+
return {
|
|
75
|
+
type: 'LetStatement',
|
|
76
|
+
name,
|
|
77
|
+
typeAnnotation,
|
|
78
|
+
value,
|
|
79
|
+
line,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
constStatement() {
|
|
83
|
+
const line = this.previous().line;
|
|
84
|
+
const name = this.consume('IDENTIFIER', 'Expected variable name').lexeme;
|
|
85
|
+
let typeAnnotation;
|
|
86
|
+
if (this.match('COLON')) {
|
|
87
|
+
typeAnnotation = this.typeAnnotation();
|
|
88
|
+
}
|
|
89
|
+
this.consume('EQUAL', 'Expected = in const declaration');
|
|
90
|
+
const value = this.expression();
|
|
91
|
+
this.consumeStatementEnd();
|
|
92
|
+
return {
|
|
93
|
+
type: 'ConstStatement',
|
|
94
|
+
name,
|
|
95
|
+
typeAnnotation,
|
|
96
|
+
value,
|
|
97
|
+
line,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
functionStatement() {
|
|
101
|
+
const line = this.previous().line;
|
|
102
|
+
const name = this.consume('IDENTIFIER', 'Expected function name').lexeme;
|
|
103
|
+
this.consume('LEFT_PAREN', 'Expected ( after function name');
|
|
104
|
+
const parameters = [];
|
|
105
|
+
if (!this.check('RIGHT_PAREN')) {
|
|
106
|
+
do {
|
|
107
|
+
const paramName = this.consume('IDENTIFIER', 'Expected parameter name').lexeme;
|
|
108
|
+
let paramType;
|
|
109
|
+
if (this.match('COLON')) {
|
|
110
|
+
paramType = this.typeAnnotation();
|
|
111
|
+
}
|
|
112
|
+
let defaultValue;
|
|
113
|
+
if (this.match('EQUAL')) {
|
|
114
|
+
defaultValue = this.assignment();
|
|
115
|
+
}
|
|
116
|
+
parameters.push({ name: paramName, type: paramType, defaultValue });
|
|
117
|
+
} while (this.match('COMMA'));
|
|
118
|
+
}
|
|
119
|
+
this.consume('RIGHT_PAREN', 'Expected ) after parameters');
|
|
120
|
+
let returnType;
|
|
121
|
+
if (this.match('ARROW')) {
|
|
122
|
+
returnType = this.typeAnnotation();
|
|
123
|
+
}
|
|
124
|
+
this.skipNewlines();
|
|
125
|
+
const body = this.blockStatement();
|
|
126
|
+
return {
|
|
127
|
+
type: 'FunctionStatement',
|
|
128
|
+
name,
|
|
129
|
+
parameters,
|
|
130
|
+
returnType,
|
|
131
|
+
body,
|
|
132
|
+
line,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
ifStatement() {
|
|
136
|
+
const line = this.previous().line;
|
|
137
|
+
const condition = this.expression();
|
|
138
|
+
this.skipNewlines();
|
|
139
|
+
const thenBranch = this.blockStatement();
|
|
140
|
+
let elseBranch;
|
|
141
|
+
if (this.match('ELSE')) {
|
|
142
|
+
if (this.match('IF')) {
|
|
143
|
+
elseBranch = this.ifStatement();
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
this.skipNewlines();
|
|
147
|
+
elseBranch = this.blockStatement();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
type: 'IfStatement',
|
|
152
|
+
condition,
|
|
153
|
+
thenBranch,
|
|
154
|
+
elseBranch,
|
|
155
|
+
line,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
whileStatement() {
|
|
159
|
+
const line = this.previous().line;
|
|
160
|
+
const condition = this.expression();
|
|
161
|
+
this.skipNewlines();
|
|
162
|
+
const body = this.blockStatement();
|
|
163
|
+
return {
|
|
164
|
+
type: 'WhileStatement',
|
|
165
|
+
condition,
|
|
166
|
+
body,
|
|
167
|
+
line,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
forStatement() {
|
|
171
|
+
const line = this.previous().line;
|
|
172
|
+
const variable = this.consume('IDENTIFIER', 'Expected variable in for loop').lexeme;
|
|
173
|
+
let iterable;
|
|
174
|
+
let start;
|
|
175
|
+
let end;
|
|
176
|
+
if (this.match('IN')) {
|
|
177
|
+
iterable = this.expression();
|
|
178
|
+
}
|
|
179
|
+
else if (this.match('EQUAL')) {
|
|
180
|
+
start = this.expression();
|
|
181
|
+
this.consume('TO', 'Expected "to" in for loop (use: for i = 0 to 10)');
|
|
182
|
+
end = this.expression();
|
|
183
|
+
}
|
|
184
|
+
this.skipNewlines();
|
|
185
|
+
const body = this.blockStatement();
|
|
186
|
+
return {
|
|
187
|
+
type: 'ForStatement',
|
|
188
|
+
variable,
|
|
189
|
+
iterable,
|
|
190
|
+
start,
|
|
191
|
+
end,
|
|
192
|
+
body,
|
|
193
|
+
line,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
returnStatement() {
|
|
197
|
+
const line = this.previous().line;
|
|
198
|
+
let value;
|
|
199
|
+
if (!this.check('SEMICOLON') && !this.check('NEWLINE') && !this.check('RIGHT_BRACE') && !this.isAtEnd()) {
|
|
200
|
+
value = this.expression();
|
|
201
|
+
}
|
|
202
|
+
this.consumeStatementEnd();
|
|
203
|
+
return {
|
|
204
|
+
type: 'ReturnStatement',
|
|
205
|
+
value,
|
|
206
|
+
line,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
breakStatement() {
|
|
210
|
+
const line = this.previous().line;
|
|
211
|
+
this.consumeStatementEnd();
|
|
212
|
+
return {
|
|
213
|
+
type: 'BreakStatement',
|
|
214
|
+
line,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
continueStatement() {
|
|
218
|
+
const line = this.previous().line;
|
|
219
|
+
this.consumeStatementEnd();
|
|
220
|
+
return {
|
|
221
|
+
type: 'ContinueStatement',
|
|
222
|
+
line,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
tryStatement() {
|
|
226
|
+
const line = this.previous().line;
|
|
227
|
+
this.skipNewlines();
|
|
228
|
+
const tryBlock = this.blockStatement();
|
|
229
|
+
this.skipNewlines();
|
|
230
|
+
this.consume('CATCH', 'Expected "catch" after try block');
|
|
231
|
+
this.skipNewlines();
|
|
232
|
+
let catchParameter = 'e';
|
|
233
|
+
if (this.match('LEFT_PAREN')) {
|
|
234
|
+
catchParameter = this.consume('IDENTIFIER', 'Expected error variable name').lexeme;
|
|
235
|
+
this.consume('RIGHT_PAREN', 'Expected ")" after catch parameter');
|
|
236
|
+
}
|
|
237
|
+
this.skipNewlines();
|
|
238
|
+
const catchBlock = this.blockStatement();
|
|
239
|
+
return {
|
|
240
|
+
type: 'TryStatement',
|
|
241
|
+
tryBlock,
|
|
242
|
+
catchParameter,
|
|
243
|
+
catchBlock,
|
|
244
|
+
line,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
importStatement() {
|
|
248
|
+
const line = this.previous().line;
|
|
249
|
+
const names = [];
|
|
250
|
+
if (this.match('LEFT_BRACE')) {
|
|
251
|
+
do {
|
|
252
|
+
names.push(this.consume('IDENTIFIER', 'Expected import name').lexeme);
|
|
253
|
+
} while (this.match('COMMA'));
|
|
254
|
+
this.consume('RIGHT_BRACE', 'Expected } after imports');
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
names.push(this.consume('IDENTIFIER', 'Expected import name').lexeme);
|
|
258
|
+
}
|
|
259
|
+
this.consume('FROM', 'Expected "from" in import statement');
|
|
260
|
+
const source = this.consume('STRING', 'Expected module path').literal;
|
|
261
|
+
this.consumeStatementEnd();
|
|
262
|
+
return {
|
|
263
|
+
type: 'ImportStatement',
|
|
264
|
+
names,
|
|
265
|
+
source: source,
|
|
266
|
+
line,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
exportStatement() {
|
|
270
|
+
const line = this.previous().line;
|
|
271
|
+
let statement;
|
|
272
|
+
if (this.match('FN')) {
|
|
273
|
+
statement = this.functionStatement();
|
|
274
|
+
}
|
|
275
|
+
else if (this.match('LET')) {
|
|
276
|
+
statement = this.letStatement();
|
|
277
|
+
}
|
|
278
|
+
else if (this.match('CONST')) {
|
|
279
|
+
statement = this.constStatement();
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
throw new Error('Expected function or variable declaration after export');
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
type: 'ExportStatement',
|
|
286
|
+
statement,
|
|
287
|
+
line,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
memoryStatement() {
|
|
291
|
+
const line = this.previous().line;
|
|
292
|
+
const name = this.consume('IDENTIFIER', 'Expected memory name').lexeme;
|
|
293
|
+
let initialValue;
|
|
294
|
+
if (this.match('LEFT_BRACE')) {
|
|
295
|
+
const content = [];
|
|
296
|
+
while (!this.check('RIGHT_BRACE') && !this.isAtEnd()) {
|
|
297
|
+
content.push(this.expression());
|
|
298
|
+
}
|
|
299
|
+
this.consume('RIGHT_BRACE', 'Expected }');
|
|
300
|
+
if (content.length === 1) {
|
|
301
|
+
initialValue = content[0];
|
|
302
|
+
}
|
|
303
|
+
else if (content.length > 1) {
|
|
304
|
+
// Concatenate strings
|
|
305
|
+
let result = content[0];
|
|
306
|
+
for (let i = 1; i < content.length; i++) {
|
|
307
|
+
result = {
|
|
308
|
+
type: 'BinaryOp',
|
|
309
|
+
left: result,
|
|
310
|
+
operator: '+',
|
|
311
|
+
right: content[i],
|
|
312
|
+
line,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
initialValue = result;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
this.consumeStatementEnd();
|
|
319
|
+
return {
|
|
320
|
+
type: 'MemoryStatement',
|
|
321
|
+
name,
|
|
322
|
+
initialValue,
|
|
323
|
+
line,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
blockStatement() {
|
|
327
|
+
this.consume('LEFT_BRACE', 'Expected { to start block');
|
|
328
|
+
const line = this.previous().line;
|
|
329
|
+
const statements = [];
|
|
330
|
+
this.skipNewlines();
|
|
331
|
+
while (!this.check('RIGHT_BRACE') && !this.isAtEnd()) {
|
|
332
|
+
const stmt = this.statement();
|
|
333
|
+
if (stmt)
|
|
334
|
+
statements.push(stmt);
|
|
335
|
+
this.skipNewlines();
|
|
336
|
+
}
|
|
337
|
+
this.consume('RIGHT_BRACE', 'Expected } to end block');
|
|
338
|
+
return {
|
|
339
|
+
type: 'BlockStatement',
|
|
340
|
+
statements,
|
|
341
|
+
line,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
expressionStatement() {
|
|
345
|
+
const line = this.peek().line;
|
|
346
|
+
const expr = this.expression();
|
|
347
|
+
this.consumeStatementEnd();
|
|
348
|
+
return {
|
|
349
|
+
type: 'ExpressionStatement',
|
|
350
|
+
expression: expr,
|
|
351
|
+
line,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
expression() {
|
|
355
|
+
return this.assignment();
|
|
356
|
+
}
|
|
357
|
+
assignment() {
|
|
358
|
+
const expr = this.logicalOr();
|
|
359
|
+
if (this.match('EQUAL')) {
|
|
360
|
+
const value = this.assignment();
|
|
361
|
+
return {
|
|
362
|
+
type: 'Assignment',
|
|
363
|
+
left: expr,
|
|
364
|
+
right: value,
|
|
365
|
+
line: this.previous().line,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
return expr;
|
|
369
|
+
}
|
|
370
|
+
logicalOr() {
|
|
371
|
+
let expr = this.logicalAnd();
|
|
372
|
+
while (this.match('PIPE_PIPE')) {
|
|
373
|
+
const operator = this.previous().lexeme;
|
|
374
|
+
const right = this.logicalAnd();
|
|
375
|
+
expr = {
|
|
376
|
+
type: 'LogicalOp',
|
|
377
|
+
left: expr,
|
|
378
|
+
operator: '||',
|
|
379
|
+
right,
|
|
380
|
+
line: this.previous().line,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
return expr;
|
|
384
|
+
}
|
|
385
|
+
logicalAnd() {
|
|
386
|
+
let expr = this.equality();
|
|
387
|
+
while (this.match('AMPERSAND_AMPERSAND')) {
|
|
388
|
+
const right = this.equality();
|
|
389
|
+
expr = {
|
|
390
|
+
type: 'LogicalOp',
|
|
391
|
+
left: expr,
|
|
392
|
+
operator: '&&',
|
|
393
|
+
right,
|
|
394
|
+
line: this.previous().line,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
return expr;
|
|
398
|
+
}
|
|
399
|
+
equality() {
|
|
400
|
+
let expr = this.comparison();
|
|
401
|
+
while (this.match('BANG_EQUAL', 'EQUAL_EQUAL')) {
|
|
402
|
+
const operator = this.previous().lexeme;
|
|
403
|
+
const right = this.comparison();
|
|
404
|
+
expr = {
|
|
405
|
+
type: 'BinaryOp',
|
|
406
|
+
left: expr,
|
|
407
|
+
operator,
|
|
408
|
+
right,
|
|
409
|
+
line: this.previous().line,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
return expr;
|
|
413
|
+
}
|
|
414
|
+
comparison() {
|
|
415
|
+
let expr = this.addition();
|
|
416
|
+
while (this.match('GREATER', 'GREATER_EQUAL', 'LESS', 'LESS_EQUAL')) {
|
|
417
|
+
const operator = this.previous().lexeme;
|
|
418
|
+
const right = this.addition();
|
|
419
|
+
expr = {
|
|
420
|
+
type: 'BinaryOp',
|
|
421
|
+
left: expr,
|
|
422
|
+
operator,
|
|
423
|
+
right,
|
|
424
|
+
line: this.previous().line,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
return expr;
|
|
428
|
+
}
|
|
429
|
+
addition() {
|
|
430
|
+
let expr = this.multiplication();
|
|
431
|
+
while (this.match('MINUS', 'PLUS')) {
|
|
432
|
+
const operator = this.previous().lexeme;
|
|
433
|
+
const right = this.multiplication();
|
|
434
|
+
expr = {
|
|
435
|
+
type: 'BinaryOp',
|
|
436
|
+
left: expr,
|
|
437
|
+
operator,
|
|
438
|
+
right,
|
|
439
|
+
line: this.previous().line,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
return expr;
|
|
443
|
+
}
|
|
444
|
+
multiplication() {
|
|
445
|
+
let expr = this.unary();
|
|
446
|
+
while (this.match('SLASH', 'STAR', 'PERCENT')) {
|
|
447
|
+
const operator = this.previous().lexeme;
|
|
448
|
+
const right = this.unary();
|
|
449
|
+
expr = {
|
|
450
|
+
type: 'BinaryOp',
|
|
451
|
+
left: expr,
|
|
452
|
+
operator,
|
|
453
|
+
right,
|
|
454
|
+
line: this.previous().line,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
return expr;
|
|
458
|
+
}
|
|
459
|
+
unary() {
|
|
460
|
+
if (this.match('BANG', 'MINUS')) {
|
|
461
|
+
const operator = this.previous().lexeme;
|
|
462
|
+
const operand = this.unary();
|
|
463
|
+
return {
|
|
464
|
+
type: 'UnaryOp',
|
|
465
|
+
operator,
|
|
466
|
+
operand,
|
|
467
|
+
line: this.previous().line,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
return this.postfix();
|
|
471
|
+
}
|
|
472
|
+
postfix() {
|
|
473
|
+
let expr = this.primary();
|
|
474
|
+
while (true) {
|
|
475
|
+
if (this.match('LEFT_PAREN')) {
|
|
476
|
+
expr = this.finishCall(expr);
|
|
477
|
+
}
|
|
478
|
+
else if (this.match('LEFT_BRACKET')) {
|
|
479
|
+
const index = this.expression();
|
|
480
|
+
this.consume('RIGHT_BRACKET', 'Expected ] after index');
|
|
481
|
+
expr = {
|
|
482
|
+
type: 'IndexExpression',
|
|
483
|
+
object: expr,
|
|
484
|
+
index,
|
|
485
|
+
line: this.previous().line,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
else if (this.match('DOT')) {
|
|
489
|
+
const property = this.consume('IDENTIFIER', 'Expected property name').lexeme;
|
|
490
|
+
expr = {
|
|
491
|
+
type: 'MemberExpression',
|
|
492
|
+
object: expr,
|
|
493
|
+
property,
|
|
494
|
+
line: this.previous().line,
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return expr;
|
|
502
|
+
}
|
|
503
|
+
finishCall(callee) {
|
|
504
|
+
const args = [];
|
|
505
|
+
if (!this.check('RIGHT_PAREN')) {
|
|
506
|
+
do {
|
|
507
|
+
this.skipNewlines();
|
|
508
|
+
args.push(this.assignment());
|
|
509
|
+
this.skipNewlines();
|
|
510
|
+
this.match('COMMA');
|
|
511
|
+
this.skipNewlines();
|
|
512
|
+
} while (!this.check('RIGHT_PAREN') && !this.isAtEnd());
|
|
513
|
+
}
|
|
514
|
+
this.consume('RIGHT_PAREN', 'Expected ) after arguments');
|
|
515
|
+
return {
|
|
516
|
+
type: 'CallExpression',
|
|
517
|
+
callee,
|
|
518
|
+
arguments: args,
|
|
519
|
+
line: this.previous().line,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
primary() {
|
|
523
|
+
if (this.match('TRUE')) {
|
|
524
|
+
return {
|
|
525
|
+
type: 'Literal',
|
|
526
|
+
value: true,
|
|
527
|
+
rawType: 'bool',
|
|
528
|
+
line: this.previous().line,
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
if (this.match('FALSE')) {
|
|
532
|
+
return {
|
|
533
|
+
type: 'Literal',
|
|
534
|
+
value: false,
|
|
535
|
+
rawType: 'bool',
|
|
536
|
+
line: this.previous().line,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
if (this.match('NULL')) {
|
|
540
|
+
return {
|
|
541
|
+
type: 'Literal',
|
|
542
|
+
value: null,
|
|
543
|
+
rawType: 'null',
|
|
544
|
+
line: this.previous().line,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
if (this.match('NUMBER')) {
|
|
548
|
+
return {
|
|
549
|
+
type: 'Literal',
|
|
550
|
+
value: this.previous().literal,
|
|
551
|
+
rawType: 'number',
|
|
552
|
+
line: this.previous().line,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
if (this.match('STRING')) {
|
|
556
|
+
return {
|
|
557
|
+
type: 'Literal',
|
|
558
|
+
value: this.previous().literal,
|
|
559
|
+
rawType: 'string',
|
|
560
|
+
line: this.previous().line,
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
if (this.match('LEFT_BRACKET')) {
|
|
564
|
+
return this.arrayLiteral();
|
|
565
|
+
}
|
|
566
|
+
if (this.match('LEFT_BRACE')) {
|
|
567
|
+
return this.objectLiteral();
|
|
568
|
+
}
|
|
569
|
+
if (this.match('PRINT')) {
|
|
570
|
+
const args = [];
|
|
571
|
+
let hasParens = false;
|
|
572
|
+
if (this.match('LEFT_PAREN')) {
|
|
573
|
+
hasParens = true;
|
|
574
|
+
}
|
|
575
|
+
if (hasParens) {
|
|
576
|
+
if (!this.check('RIGHT_PAREN')) {
|
|
577
|
+
do {
|
|
578
|
+
args.push(this.assignment());
|
|
579
|
+
} while (this.match('COMMA'));
|
|
580
|
+
}
|
|
581
|
+
this.consume('RIGHT_PAREN', 'Expected ) after print arguments');
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
// Without parens, allow multiple expressions separated by commas or spaces (on the same line)
|
|
585
|
+
do {
|
|
586
|
+
args.push(this.assignment());
|
|
587
|
+
// Optional comma
|
|
588
|
+
this.match('COMMA');
|
|
589
|
+
} while (!this.check('SEMICOLON') && !this.check('NEWLINE') && !this.check('RIGHT_BRACE') && !this.isAtEnd());
|
|
590
|
+
}
|
|
591
|
+
return {
|
|
592
|
+
type: 'CallExpression',
|
|
593
|
+
callee: {
|
|
594
|
+
type: 'Identifier',
|
|
595
|
+
name: 'print',
|
|
596
|
+
line: this.previous().line,
|
|
597
|
+
},
|
|
598
|
+
arguments: args,
|
|
599
|
+
line: this.previous().line,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
if (this.match('PROMPT')) {
|
|
603
|
+
return this.promptExpression();
|
|
604
|
+
}
|
|
605
|
+
if (this.match('MODEL')) {
|
|
606
|
+
return this.modelCall();
|
|
607
|
+
}
|
|
608
|
+
if (this.match('IMAGE')) {
|
|
609
|
+
return this.imageCall();
|
|
610
|
+
}
|
|
611
|
+
if (this.match('STRUCTURED_OUTPUT')) {
|
|
612
|
+
return this.structuredOutput();
|
|
613
|
+
}
|
|
614
|
+
if (this.match('TOOL_CALL')) {
|
|
615
|
+
return this.toolCall();
|
|
616
|
+
}
|
|
617
|
+
if (this.match('IDENTIFIER')) {
|
|
618
|
+
return {
|
|
619
|
+
type: 'Identifier',
|
|
620
|
+
name: this.previous().lexeme,
|
|
621
|
+
line: this.previous().line,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
if (this.match('LEFT_PAREN')) {
|
|
625
|
+
const expr = this.expression();
|
|
626
|
+
this.consume('RIGHT_PAREN', 'Expected ) after expression');
|
|
627
|
+
return expr;
|
|
628
|
+
}
|
|
629
|
+
throw new Error(`Unexpected token: ${this.peek().lexeme} at line ${this.peek().line}`);
|
|
630
|
+
}
|
|
631
|
+
arrayLiteral() {
|
|
632
|
+
const line = this.previous().line;
|
|
633
|
+
const elements = [];
|
|
634
|
+
if (!this.check('RIGHT_BRACKET')) {
|
|
635
|
+
do {
|
|
636
|
+
elements.push(this.assignment());
|
|
637
|
+
} while (this.match('COMMA'));
|
|
638
|
+
}
|
|
639
|
+
this.consume('RIGHT_BRACKET', 'Expected ] after array elements');
|
|
640
|
+
return {
|
|
641
|
+
type: 'ArrayLiteral',
|
|
642
|
+
elements,
|
|
643
|
+
line,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
objectLiteral() {
|
|
647
|
+
const line = this.previous().line;
|
|
648
|
+
const properties = [];
|
|
649
|
+
if (!this.check('RIGHT_BRACE')) {
|
|
650
|
+
do {
|
|
651
|
+
const key = this.consume('STRING', 'Expected string key').literal;
|
|
652
|
+
this.consume('COLON', 'Expected : after object key');
|
|
653
|
+
const value = this.assignment();
|
|
654
|
+
properties.push({ key: key, value });
|
|
655
|
+
} while (this.match('COMMA'));
|
|
656
|
+
}
|
|
657
|
+
this.consume('RIGHT_BRACE', 'Expected } after object properties');
|
|
658
|
+
return {
|
|
659
|
+
type: 'ObjectLiteral',
|
|
660
|
+
properties,
|
|
661
|
+
line,
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
promptExpression() {
|
|
665
|
+
const line = this.previous().line;
|
|
666
|
+
const name = this.consume('IDENTIFIER', 'Expected prompt name').lexeme;
|
|
667
|
+
this.consume('LEFT_BRACE', 'Expected { after prompt name');
|
|
668
|
+
const content = [];
|
|
669
|
+
while (!this.check('RIGHT_BRACE') && !this.isAtEnd()) {
|
|
670
|
+
if (this.check('STRING')) {
|
|
671
|
+
this.advance();
|
|
672
|
+
content.push({
|
|
673
|
+
type: 'Literal',
|
|
674
|
+
value: this.previous().literal,
|
|
675
|
+
rawType: 'string',
|
|
676
|
+
line: this.previous().line,
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
content.push(this.expression());
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
this.consume('RIGHT_BRACE', 'Expected } after prompt content');
|
|
684
|
+
return {
|
|
685
|
+
type: 'PromptExpression',
|
|
686
|
+
name,
|
|
687
|
+
content,
|
|
688
|
+
line,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
imageCall() {
|
|
692
|
+
const line = this.previous().line;
|
|
693
|
+
this.consume('LEFT_PAREN', 'Expected ( after image');
|
|
694
|
+
const modelName = this.consume('STRING', 'Expected image model name').literal;
|
|
695
|
+
this.consume('RIGHT_PAREN', 'Expected ) after image model name');
|
|
696
|
+
let config;
|
|
697
|
+
this.skipNewlines();
|
|
698
|
+
let hasConfig = false;
|
|
699
|
+
if (this.check('LEFT_BRACE')) {
|
|
700
|
+
const insideToken = this.tokens[this.current + 1];
|
|
701
|
+
if (insideToken.type === 'RIGHT_BRACE' && this.current + 2 < this.tokens.length && this.tokens[this.current + 2].type === 'LEFT_BRACE') {
|
|
702
|
+
hasConfig = true;
|
|
703
|
+
}
|
|
704
|
+
else if (insideToken.type === 'STRING' || insideToken.type === 'IDENTIFIER') {
|
|
705
|
+
if (this.current + 2 < this.tokens.length && this.tokens[this.current + 2].type === 'COLON') {
|
|
706
|
+
hasConfig = true;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
if (hasConfig) {
|
|
711
|
+
this.advance(); // consume LEFT_BRACE
|
|
712
|
+
config = {};
|
|
713
|
+
if (!this.check('RIGHT_BRACE')) {
|
|
714
|
+
do {
|
|
715
|
+
let key;
|
|
716
|
+
if (this.check('STRING')) {
|
|
717
|
+
key = this.consume('STRING', '').literal;
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
key = this.consume('IDENTIFIER', 'Expected config key').lexeme;
|
|
721
|
+
}
|
|
722
|
+
this.consume('COLON', 'Expected : after config key');
|
|
723
|
+
config[key] = this.assignment();
|
|
724
|
+
} while (this.match('COMMA'));
|
|
725
|
+
}
|
|
726
|
+
this.consume('RIGHT_BRACE', 'Expected } after config');
|
|
727
|
+
}
|
|
728
|
+
this.skipNewlines();
|
|
729
|
+
// Prompt or block
|
|
730
|
+
let prompt;
|
|
731
|
+
this.skipNewlines();
|
|
732
|
+
if (this.check('LEFT_BRACE')) {
|
|
733
|
+
this.advance();
|
|
734
|
+
const content = [];
|
|
735
|
+
while (!this.check('RIGHT_BRACE') && !this.isAtEnd()) {
|
|
736
|
+
if (this.check('STRING')) {
|
|
737
|
+
this.advance();
|
|
738
|
+
content.push({
|
|
739
|
+
type: 'Literal',
|
|
740
|
+
value: this.previous().literal,
|
|
741
|
+
rawType: 'string',
|
|
742
|
+
line: this.previous().line,
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
content.push(this.expression());
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
this.consume('RIGHT_BRACE', 'Expected }');
|
|
750
|
+
if (content.length === 1) {
|
|
751
|
+
prompt = content[0];
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
let result = content[0];
|
|
755
|
+
for (let i = 1; i < content.length; i++) {
|
|
756
|
+
result = {
|
|
757
|
+
type: 'BinaryOp',
|
|
758
|
+
left: result,
|
|
759
|
+
operator: '+',
|
|
760
|
+
right: content[i],
|
|
761
|
+
line,
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
prompt = result;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
throw new Error('Expected prompt block after image call');
|
|
769
|
+
}
|
|
770
|
+
return {
|
|
771
|
+
type: 'ImageCallExpression',
|
|
772
|
+
modelName: modelName,
|
|
773
|
+
config,
|
|
774
|
+
prompt,
|
|
775
|
+
line,
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
modelCall() {
|
|
779
|
+
const line = this.previous().line;
|
|
780
|
+
this.consume('LEFT_PAREN', 'Expected ( after model');
|
|
781
|
+
const modelName = this.consume('STRING', 'Expected model name').literal;
|
|
782
|
+
this.consume('RIGHT_PAREN', 'Expected ) after model name');
|
|
783
|
+
let config;
|
|
784
|
+
this.skipNewlines();
|
|
785
|
+
let hasConfig = false;
|
|
786
|
+
if (this.check('LEFT_BRACE')) {
|
|
787
|
+
const insideToken = this.tokens[this.current + 1];
|
|
788
|
+
if (insideToken.type === 'RIGHT_BRACE' && this.current + 2 < this.tokens.length && this.tokens[this.current + 2].type === 'LEFT_BRACE') {
|
|
789
|
+
hasConfig = true;
|
|
790
|
+
}
|
|
791
|
+
else if (insideToken.type === 'STRING' || insideToken.type === 'IDENTIFIER') {
|
|
792
|
+
if (this.current + 2 < this.tokens.length && this.tokens[this.current + 2].type === 'COLON') {
|
|
793
|
+
hasConfig = true;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (hasConfig) {
|
|
798
|
+
this.advance(); // consume LEFT_BRACE
|
|
799
|
+
config = {};
|
|
800
|
+
if (!this.check('RIGHT_BRACE')) {
|
|
801
|
+
do {
|
|
802
|
+
let key;
|
|
803
|
+
if (this.check('STRING')) {
|
|
804
|
+
key = this.consume('STRING', '').literal;
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
key = this.consume('IDENTIFIER', 'Expected config key').lexeme;
|
|
808
|
+
}
|
|
809
|
+
this.consume('COLON', 'Expected : after config key');
|
|
810
|
+
config[key] = this.assignment();
|
|
811
|
+
} while (this.match('COMMA'));
|
|
812
|
+
}
|
|
813
|
+
this.consume('RIGHT_BRACE', 'Expected } after config');
|
|
814
|
+
}
|
|
815
|
+
this.skipNewlines();
|
|
816
|
+
// Prompt or block
|
|
817
|
+
let prompt;
|
|
818
|
+
this.skipNewlines();
|
|
819
|
+
if (this.check('LEFT_BRACE')) {
|
|
820
|
+
this.advance();
|
|
821
|
+
const content = [];
|
|
822
|
+
while (!this.check('RIGHT_BRACE') && !this.isAtEnd()) {
|
|
823
|
+
if (this.check('STRING')) {
|
|
824
|
+
this.advance();
|
|
825
|
+
content.push({
|
|
826
|
+
type: 'Literal',
|
|
827
|
+
value: this.previous().literal,
|
|
828
|
+
rawType: 'string',
|
|
829
|
+
line: this.previous().line,
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
content.push(this.expression());
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
this.consume('RIGHT_BRACE', 'Expected }');
|
|
837
|
+
if (content.length === 1) {
|
|
838
|
+
prompt = content[0];
|
|
839
|
+
}
|
|
840
|
+
else {
|
|
841
|
+
let result = content[0];
|
|
842
|
+
for (let i = 1; i < content.length; i++) {
|
|
843
|
+
result = {
|
|
844
|
+
type: 'BinaryOp',
|
|
845
|
+
left: result,
|
|
846
|
+
operator: '+',
|
|
847
|
+
right: content[i],
|
|
848
|
+
line,
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
prompt = result;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
else {
|
|
855
|
+
throw new Error('Expected prompt block after model call');
|
|
856
|
+
}
|
|
857
|
+
return {
|
|
858
|
+
type: 'ModelCallExpression',
|
|
859
|
+
modelName: modelName,
|
|
860
|
+
config,
|
|
861
|
+
prompt,
|
|
862
|
+
line,
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
structuredOutput() {
|
|
866
|
+
const line = this.previous().line;
|
|
867
|
+
this.consume('LEFT_PAREN', 'Expected ( after structured_output');
|
|
868
|
+
this.consume('LEFT_BRACE', 'Expected { for schema');
|
|
869
|
+
const schema = {};
|
|
870
|
+
if (!this.check('RIGHT_BRACE')) {
|
|
871
|
+
do {
|
|
872
|
+
const key = this.consume('IDENTIFIER', 'Expected field name').lexeme;
|
|
873
|
+
this.consume('COLON', 'Expected : after field name');
|
|
874
|
+
schema[key] = this.typeAnnotation();
|
|
875
|
+
} while (this.match('COMMA'));
|
|
876
|
+
}
|
|
877
|
+
this.consume('RIGHT_BRACE', 'Expected } after schema');
|
|
878
|
+
this.consume('RIGHT_PAREN', 'Expected ) after schema');
|
|
879
|
+
this.skipNewlines();
|
|
880
|
+
this.consume('LEFT_PAREN', 'Expected ( for model call');
|
|
881
|
+
this.skipNewlines();
|
|
882
|
+
this.consume('MODEL', 'Expected model expression in structured_output');
|
|
883
|
+
const modelCall = this.modelCall();
|
|
884
|
+
this.consume('RIGHT_PAREN', 'Expected ) after model call');
|
|
885
|
+
return {
|
|
886
|
+
type: 'StructuredOutputExpression',
|
|
887
|
+
schema,
|
|
888
|
+
modelCall,
|
|
889
|
+
line,
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
toolCall() {
|
|
893
|
+
const line = this.previous().line;
|
|
894
|
+
this.consume('LEFT_PAREN', 'Expected ( after tool_call');
|
|
895
|
+
const functionName = this.consume('IDENTIFIER', 'Expected function name').lexeme;
|
|
896
|
+
this.consume('RIGHT_PAREN', 'Expected ) after function name');
|
|
897
|
+
this.consume('LEFT_PAREN', 'Expected ( for arguments');
|
|
898
|
+
const args = [];
|
|
899
|
+
if (!this.check('RIGHT_PAREN')) {
|
|
900
|
+
do {
|
|
901
|
+
args.push(this.assignment());
|
|
902
|
+
} while (this.match('COMMA'));
|
|
903
|
+
}
|
|
904
|
+
this.consume('RIGHT_PAREN', 'Expected ) after arguments');
|
|
905
|
+
return {
|
|
906
|
+
type: 'ToolCallExpression',
|
|
907
|
+
functionName,
|
|
908
|
+
arguments: args,
|
|
909
|
+
line,
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
typeAnnotation() {
|
|
913
|
+
if (this.check('IDENTIFIER')) {
|
|
914
|
+
const name = this.advance().lexeme;
|
|
915
|
+
switch (name) {
|
|
916
|
+
case 'number':
|
|
917
|
+
case 'string':
|
|
918
|
+
case 'bool':
|
|
919
|
+
case 'null': {
|
|
920
|
+
let type = {
|
|
921
|
+
type: 'PrimitiveType',
|
|
922
|
+
name: name,
|
|
923
|
+
};
|
|
924
|
+
if (this.match('QUESTION')) {
|
|
925
|
+
type = {
|
|
926
|
+
type: 'OptionalType',
|
|
927
|
+
baseType: type,
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
return type;
|
|
931
|
+
}
|
|
932
|
+
case 'array':
|
|
933
|
+
if (this.match('LESS')) {
|
|
934
|
+
const elementType = this.typeAnnotation();
|
|
935
|
+
this.consume('GREATER', 'Expected > in array type');
|
|
936
|
+
return {
|
|
937
|
+
type: 'ArrayType',
|
|
938
|
+
elementType,
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
return { type: 'PrimitiveType', name: 'any' };
|
|
942
|
+
case 'object':
|
|
943
|
+
if (this.match('LESS')) {
|
|
944
|
+
const valueType = this.typeAnnotation();
|
|
945
|
+
this.consume('GREATER', 'Expected > in object type');
|
|
946
|
+
return {
|
|
947
|
+
type: 'ObjectType',
|
|
948
|
+
valueType,
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
return { type: 'PrimitiveType', name: 'any' };
|
|
952
|
+
default:
|
|
953
|
+
throw new Error(`Unknown type: ${name}`);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
throw new Error('Expected type annotation');
|
|
957
|
+
}
|
|
958
|
+
skipNewlines() {
|
|
959
|
+
while (this.match('NEWLINE'))
|
|
960
|
+
;
|
|
961
|
+
}
|
|
962
|
+
match(...types) {
|
|
963
|
+
for (const type of types) {
|
|
964
|
+
if (this.check(type)) {
|
|
965
|
+
this.advance();
|
|
966
|
+
return true;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
return false;
|
|
970
|
+
}
|
|
971
|
+
check(type) {
|
|
972
|
+
if (this.isAtEnd())
|
|
973
|
+
return false;
|
|
974
|
+
return this.peek().type === type;
|
|
975
|
+
}
|
|
976
|
+
advance() {
|
|
977
|
+
if (!this.isAtEnd())
|
|
978
|
+
this.current++;
|
|
979
|
+
return this.previous();
|
|
980
|
+
}
|
|
981
|
+
isAtEnd() {
|
|
982
|
+
return this.peek().type === 'EOF';
|
|
983
|
+
}
|
|
984
|
+
peek() {
|
|
985
|
+
return this.tokens[this.current];
|
|
986
|
+
}
|
|
987
|
+
previous() {
|
|
988
|
+
return this.tokens[this.current - 1];
|
|
989
|
+
}
|
|
990
|
+
consume(type, message) {
|
|
991
|
+
if (this.check(type))
|
|
992
|
+
return this.advance();
|
|
993
|
+
throw new Error(`${message} at line ${this.peek().line}, got ${this.peek().type}`);
|
|
994
|
+
}
|
|
995
|
+
consumeStatementEnd() {
|
|
996
|
+
if (this.match('SEMICOLON') || this.match('NEWLINE'))
|
|
997
|
+
return;
|
|
998
|
+
if (this.check('RIGHT_BRACE') || this.isAtEnd())
|
|
999
|
+
return;
|
|
1000
|
+
// Allow implicit end of statement after a block-ending expression
|
|
1001
|
+
if (this.previous().type === 'RIGHT_BRACE')
|
|
1002
|
+
return;
|
|
1003
|
+
throw new Error(`Expected end of statement, got ${this.peek().lexeme}`);
|
|
1004
|
+
}
|
|
1005
|
+
synchronize() {
|
|
1006
|
+
this.advance();
|
|
1007
|
+
const syncTokens = ['FN', 'LET', 'CONST', 'FOR', 'IF', 'WHILE', 'RETURN'];
|
|
1008
|
+
while (!this.isAtEnd()) {
|
|
1009
|
+
if (syncTokens.includes(this.peek().type)) {
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
this.advance();
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
checkNext(type) {
|
|
1016
|
+
if (this.current + 1 >= this.tokens.length)
|
|
1017
|
+
return false;
|
|
1018
|
+
return this.tokens[this.current + 1].type === type;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
exports.Parser = Parser;
|
|
1022
|
+
//# sourceMappingURL=parser.js.map
|