shell-lite 0.4.3__py3-none-any.whl → 0.4.5__py3-none-any.whl

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.
shell_lite/parser_new.py DELETED
@@ -1,2229 +0,0 @@
1
- from typing import List, Optional
2
- from .lexer_new import Token, Lexer
3
- from .ast_nodes import *
4
- import re
5
- class Parser:
6
- def __init__(self, tokens: List[Token]):
7
- self.tokens = [t for t in tokens if t.type != 'COMMENT']
8
- self.pos = 0
9
- def peek(self, offset: int = 0) -> Token:
10
- if self.pos + offset < len(self.tokens):
11
- return self.tokens[self.pos + offset]
12
- return self.tokens[-1]
13
- def consume(self, expected_type: str = None) -> Token:
14
- token = self.peek()
15
- if expected_type and token.type != expected_type:
16
- raise SyntaxError(f"Expected {expected_type} but got {token.type} on line {token.line}")
17
- self.pos += 1
18
- return token
19
- def check(self, token_type: str) -> bool:
20
- return self.peek().type == token_type
21
- def parse(self) -> List[Node]:
22
- statements = []
23
- while not self.check('EOF'):
24
- while self.check('NEWLINE'):
25
- self.consume()
26
- if self.check('EOF'): break
27
- if self.check('EOF'): break
28
- stmt = self.parse_statement()
29
- if stmt:
30
- statements.append(stmt)
31
- return statements
32
- def parse_statement(self) -> Node:
33
- if self.check('USE'):
34
- return self.parse_import()
35
- elif self.check('APP'):
36
- return self.parse_app()
37
- elif self.check('ON'):
38
- return self.parse_on()
39
- elif self.check('CONST'):
40
- return self.parse_const()
41
- elif self.check('PRINT') or self.check('SAY'):
42
- return self.parse_print()
43
- elif self.check('ALERT'):
44
- return self.parse_alert()
45
- elif self.check('IF'):
46
- return self.parse_if()
47
- elif self.check('UNLESS'):
48
- return self.parse_unless()
49
- elif self.check('WHILE'):
50
- return self.parse_while()
51
- elif self.check('UNTIL'):
52
- return self.parse_until()
53
- elif self.check('FOREVER'):
54
- return self.parse_forever()
55
- elif self.check('TRY'):
56
- return self.parse_try()
57
- elif self.check('FOR') or self.check('LOOP'):
58
- return self.parse_for()
59
- elif self.check('REPEAT'):
60
- return self.parse_repeat()
61
- elif self.check('WHEN'):
62
- return self.parse_when()
63
- elif self.check('TO'):
64
- return self.parse_function_def()
65
- elif self.check('STRUCTURE'):
66
- return self.parse_class_def()
67
- elif self.check('RETURN'):
68
- return self.parse_return()
69
- elif self.check('STOP'):
70
- return self.parse_stop()
71
- elif self.check('SKIP'):
72
- return self.parse_skip()
73
- elif self.check('EXIT'):
74
- return self.parse_exit()
75
- elif self.check('ERROR'):
76
- return self.parse_error()
77
- elif self.check('EXECUTE'):
78
- return self.parse_execute()
79
- elif self.check('MAKE'):
80
- return self.parse_make()
81
- elif self.check('INPUT'):
82
- next_t = self.peek(1)
83
- if next_t.type in ('ID', 'TYPE', 'STRING', 'NAME', 'VALUE', 'CLASS', 'STYLE', 'ONCLICK', 'SRC', 'HREF', 'ACTION', 'METHOD'):
84
- input_token = self.consume()
85
- return self.parse_id_start_statement(passed_name_token=input_token)
86
- return self.parse_expression_stmt()
87
- elif self.check('ID'):
88
- return self.parse_id_start_statement()
89
- elif self.check('SPAWN'):
90
- expr = self.parse_expression()
91
- self.consume('NEWLINE')
92
- return Print(expr)
93
- pass
94
- elif self.check('WAIT'):
95
- return self.parse_wait()
96
- elif self.check('EVERY'):
97
- return self.parse_every()
98
- elif self.check('IN'):
99
- return self.parse_after()
100
- elif self.check('LISTEN'):
101
- return self.parse_listen()
102
- elif self.check('SERVE'):
103
- return self.parse_serve()
104
- elif self.check('DOWNLOAD'):
105
- return self.parse_download()
106
- elif self.check('COMPRESS') or self.check('EXTRACT'):
107
- return self.parse_archive()
108
- elif self.check('LOAD') or self.check('SAVE'):
109
- if self.check('LOAD') and self.peek(1).type == 'CSV':
110
- return self.parse_csv_load()
111
- if self.check('SAVE'):
112
- return self.parse_csv_save()
113
- return self.parse_expression_stmt()
114
- elif self.check('COPY') or self.check('PASTE'):
115
- return self.parse_clipboard()
116
- elif self.check('WRITE'):
117
- return self.parse_write()
118
- elif self.check('APPEND'):
119
- return self.parse_append()
120
- elif self.check('DB'):
121
- return self.parse_db_op()
122
- elif self.check('PRESS') or self.check('TYPE') or self.check('CLICK') or self.check('NOTIFY'):
123
- return self.parse_automation()
124
- elif self.check('BEFORE'):
125
- return self.parse_middleware()
126
- elif self.check('DEFINE'):
127
- if self.peek(1).type == 'FUNCTION':
128
- return self.parse_function_def()
129
- return self.parse_define_page()
130
- elif self.check('ADD'):
131
- if self.peek(1).type == 'ID' or self.peek(1).type == 'NUMBER' or self.peek(1).type == 'STRING':
132
- # Heuristic: "Add X to Y" (List) vs "Add Component" (UI) maybe?
133
- # Assuming UI "add" uses 'ADD' keyword?
134
- # Lexer maps 'add' to 'ADD'.
135
- # parse_add_to -> UI.
136
- # parse_add_to_list -> List.
137
- # Let's check parse_add_to signature.
138
- pass
139
- # For now prioritize List if 'TO' is present later?
140
- # Or assume parse_add_to handles UI.
141
- # Let's peek(1). If it's an expression -> List op. If it's a Component?
142
- # Creating a unified parse_add dispatcher might be better.
143
- return self.parse_add_distinguish()
144
-
145
- elif self.check('START'):
146
- return self.parse_start_server()
147
- elif self.check('HEADING'):
148
- return self.parse_heading()
149
- elif self.check('PARAGRAPH'):
150
- return self.parse_paragraph()
151
-
152
- # English List/Time Ops
153
- if self.check('ADD'):
154
- return self.parse_add_to_list()
155
- if self.check('REMOVE'):
156
- return self.parse_remove_from_list()
157
- if self.check('WAIT'):
158
- return self.parse_wait()
159
-
160
- if self.check('INCREMENT'):
161
- return self.parse_increment()
162
- elif self.check('DECREMENT'):
163
- return self.parse_decrement()
164
- elif self.check('MULTIPLY'):
165
- return self.parse_multiply()
166
- elif self.check('DIVIDE'):
167
- return self.parse_divide()
168
- elif self.check('MAKE'):
169
- return self.parse_make_assignment()
170
- elif self.check('AS'):
171
- return self.parse_as_long_as()
172
- elif self.check('ASK'):
173
- # Standalone ask statement? e.g. ask "Questions?"
174
- # Or ask is expression. If statement, maybe just expression statement.
175
- return self.parse_expression_statement()
176
- elif self.check('SET'):
177
- return self.parse_set()
178
- else:
179
- return self.parse_expression_stmt()
180
- def parse_alert(self) -> Alert:
181
- token = self.consume('ALERT')
182
- message = self.parse_expression()
183
- self.consume('NEWLINE')
184
- node = Alert(message)
185
- node.line = token.line
186
- return node
187
- def parse_const(self) -> ConstAssign:
188
- token = self.consume('CONST')
189
- name = self.consume('ID').value
190
- self.consume('ASSIGN')
191
- value = self.parse_expression()
192
- self.consume('NEWLINE')
193
- node = ConstAssign(name, value)
194
- node.line = token.line
195
- return node
196
- def parse_on(self) -> Node:
197
- token = self.consume('ON')
198
- if self.check('REQUEST') or (self.check('ID') and self.peek().value == 'request'):
199
- self.consume()
200
- if self.check('TO'): self.consume('TO')
201
- path = self.parse_expression()
202
- self.consume('NEWLINE')
203
- self.consume('INDENT')
204
- body = []
205
- while not self.check('DEDENT') and not self.check('EOF'):
206
- while self.check('NEWLINE'): self.consume()
207
- if self.check('DEDENT'): break
208
- body.append(self.parse_statement())
209
- self.consume('DEDENT')
210
- node = OnRequest(path, body)
211
- node.line = token.line
212
- return node
213
- event_type = self.consume('ID').value
214
- path = self.parse_expression()
215
- self.consume('NEWLINE')
216
- self.consume('INDENT')
217
- body = []
218
- while not self.check('DEDENT') and not self.check('EOF'):
219
- while self.check('NEWLINE'): self.consume()
220
- if self.check('DEDENT'): break
221
- body.append(self.parse_statement())
222
- self.consume('DEDENT')
223
- return FileWatcher(path, body)
224
- def _parse_natural_list(self) -> ListVal:
225
- self.consume('ID')
226
- self.consume('LIST')
227
- self.consume('OF')
228
- elements = []
229
- if not self.check('NEWLINE') and not self.check('EOF'):
230
- elements.append(self.parse_expression())
231
- while self.check('COMMA'):
232
- self.consume('COMMA')
233
- if self.check('NEWLINE'): break
234
- elements.append(self.parse_expression())
235
- node = ListVal(elements)
236
- return node
237
- def _parse_natural_set(self) -> Node:
238
- self.consume('ID')
239
- self.consume('UNIQUE')
240
- self.consume('SET')
241
- self.consume('OF')
242
- elements = []
243
- if not self.check('NEWLINE') and not self.check('EOF'):
244
- elements.append(self.parse_expression())
245
- while self.check('COMMA'):
246
- self.consume('COMMA')
247
- if self.check('NEWLINE'): break
248
- elements.append(self.parse_expression())
249
- list_node = ListVal(elements)
250
- return Call('Set', [list_node])
251
- def parse_wait(self) -> Node:
252
- token = self.consume('WAIT')
253
- if self.check('FOR'):
254
- self.consume('FOR')
255
- time_expr = self.parse_expression()
256
- if self.check('SECOND'):
257
- self.consume()
258
- self.consume('NEWLINE')
259
- return Call('wait', [time_expr])
260
- def parse_stop(self) -> Stop:
261
- token = self.consume('STOP')
262
- self.consume('NEWLINE')
263
- node = Stop()
264
- node.line = token.line
265
- return node
266
- def parse_skip(self) -> Skip:
267
- token = self.consume('SKIP')
268
- self.consume('NEWLINE')
269
- node = Skip()
270
- node.line = token.line
271
- return node
272
- def parse_error(self) -> Throw:
273
- token = self.consume('ERROR')
274
- message = self.parse_expression()
275
- self.consume('NEWLINE')
276
- node = Throw(message)
277
- node.line = token.line
278
- return node
279
- def parse_execute(self) -> Execute:
280
- token = self.consume('EXECUTE')
281
- code = self.parse_expression()
282
- self.consume('NEWLINE')
283
- node = Execute(code)
284
- node.line = token.line
285
- return node
286
- def parse_unless(self) -> Unless:
287
- token = self.consume('UNLESS')
288
- condition = self.parse_expression()
289
- self.consume('NEWLINE')
290
- self.consume('INDENT')
291
- body = []
292
- while not self.check('DEDENT') and not self.check('EOF'):
293
- while self.check('NEWLINE'): self.consume()
294
- if self.check('DEDENT'): break
295
- body.append(self.parse_statement())
296
- self.consume('DEDENT')
297
- else_body = None
298
- if self.check('ELSE'):
299
- self.consume('ELSE')
300
- self.consume('NEWLINE')
301
- self.consume('INDENT')
302
- else_body = []
303
- while not self.check('DEDENT') and not self.check('EOF'):
304
- while self.check('NEWLINE'): self.consume()
305
- if self.check('DEDENT'): break
306
- else_body.append(self.parse_statement())
307
- self.consume('DEDENT')
308
- node = Unless(condition, body, else_body)
309
- node.line = token.line
310
- return node
311
- def parse_until(self) -> Until:
312
- token = self.consume('UNTIL')
313
- condition = self.parse_expression()
314
- self.consume('NEWLINE')
315
- self.consume('INDENT')
316
- body = []
317
- while not self.check('DEDENT') and not self.check('EOF'):
318
- while self.check('NEWLINE'): self.consume()
319
- if self.check('DEDENT'): break
320
- body.append(self.parse_statement())
321
- self.consume('DEDENT')
322
- node = Until(condition, body)
323
- node.line = token.line
324
- return node
325
- def parse_forever(self) -> Forever:
326
- token = self.consume('FOREVER')
327
- self.consume('NEWLINE')
328
- self.consume('INDENT')
329
- body = []
330
- while not self.check('DEDENT') and not self.check('EOF'):
331
- while self.check('NEWLINE'): self.consume()
332
- if self.check('DEDENT'): break
333
- body.append(self.parse_statement())
334
- self.consume('DEDENT')
335
- node = Forever(body)
336
- node.line = token.line
337
- return node
338
- def parse_exit(self) -> Exit:
339
- token = self.consume('EXIT')
340
- code = None
341
- if not self.check('NEWLINE'):
342
- code = self.parse_expression()
343
- self.consume('NEWLINE')
344
- node = Exit(code)
345
- node.line = token.line
346
- return node
347
- def parse_make(self) -> Node:
348
- node = self.parse_make_expr()
349
- self.consume('NEWLINE')
350
- return node
351
-
352
- def parse_make_expr(self) -> Node:
353
- token = self.consume('MAKE')
354
- class_name = self.consume('ID').value
355
-
356
- if self.check('BE'):
357
- self.consume('BE')
358
- value = self.parse_expression()
359
- node = Assign(class_name, value) # class_name is actually variable name here
360
- node.line = token.line
361
- return node
362
-
363
- args = []
364
- if self.check('LPAREN'):
365
- self.consume('LPAREN')
366
- if not self.check('RPAREN'):
367
- args.append(self.parse_expression())
368
- while self.check('COMMA'):
369
- self.consume('COMMA')
370
- args.append(self.parse_expression())
371
- self.consume('RPAREN')
372
- else:
373
- while not self.check('NEWLINE') and not self.check('EOF'):
374
- args.append(self.parse_expression())
375
- node = Make(class_name, args)
376
- node.line = token.line
377
- return node
378
- return node
379
- def parse_db_op(self) -> DatabaseOp:
380
- token = self.consume('DB')
381
- op = 'open'
382
- if self.check('OPEN'): op = 'open'; self.consume()
383
- elif self.check('QUERY'): op = 'query'; self.consume()
384
- elif self.check('EXEC'): op = 'exec'; self.consume()
385
- elif self.check('CLOSE'): op = 'close'; self.consume()
386
- else:
387
- if self.check('STRING'):
388
- op = 'open'
389
- else:
390
- raise SyntaxError(f"Unknown db operation at line {token.line}")
391
- args = []
392
- if op != 'close' and not self.check('NEWLINE'):
393
- args.append(self.parse_expression())
394
- while not self.check('NEWLINE'):
395
- args.append(self.parse_expression())
396
- node = DatabaseOp(op, args)
397
- node.line = token.line
398
- return node
399
- def parse_middleware(self) -> Node:
400
- token = self.consume('BEFORE')
401
- self.consume('REQUEST')
402
- self.consume('NEWLINE')
403
- self.consume('INDENT')
404
- body = []
405
- while not self.check('DEDENT') and not self.check('EOF'):
406
- while self.check('NEWLINE'): self.consume()
407
- if self.check('DEDENT'): break
408
- body.append(self.parse_statement())
409
- self.consume('DEDENT')
410
- return OnRequest(String('__middleware__'), body)
411
- def parse_when(self) -> Node:
412
- token = self.consume('WHEN')
413
- if self.check('SOMEONE'):
414
- self.consume('SOMEONE')
415
- if self.check('VISITS'):
416
- self.consume('VISITS')
417
- path = String(self.consume('STRING').value)
418
- self.consume('NEWLINE')
419
- self.consume('INDENT')
420
- body = []
421
- while not self.check('DEDENT') and not self.check('EOF'):
422
- while self.check('NEWLINE'): self.consume()
423
- if self.check('DEDENT'): break
424
- body.append(self.parse_statement())
425
- self.consume('DEDENT')
426
- node = OnRequest(path, body)
427
- node.line = token.line
428
- return node
429
- elif self.check('SUBMITS'):
430
- self.consume('SUBMITS')
431
- if self.check('TO'):
432
- self.consume('TO')
433
- path = String(self.consume('STRING').value)
434
- self.consume('NEWLINE')
435
- self.consume('INDENT')
436
- body = []
437
- while not self.check('DEDENT') and not self.check('EOF'):
438
- while self.check('NEWLINE'): self.consume()
439
- if self.check('DEDENT'): break
440
- body.append(self.parse_statement())
441
- self.consume('DEDENT')
442
- node = OnRequest(path, body)
443
- node.line = token.line
444
- return node
445
- condition_or_value = self.parse_expression()
446
- self.consume('NEWLINE')
447
- self.consume('INDENT')
448
- if self.check('IS'):
449
- cases = []
450
- otherwise = None
451
- while not self.check('DEDENT') and not self.check('EOF'):
452
- if self.check('IS'):
453
- self.consume('IS')
454
- match_val = self.parse_expression()
455
- self.consume('NEWLINE')
456
- self.consume('INDENT')
457
- case_body = []
458
- while not self.check('DEDENT') and not self.check('EOF'):
459
- while self.check('NEWLINE'): self.consume()
460
- if self.check('DEDENT'): break
461
- case_body.append(self.parse_statement())
462
- self.consume('DEDENT')
463
- cases.append((match_val, case_body))
464
- elif self.check('OTHERWISE'):
465
- self.consume('OTHERWISE')
466
- self.consume('NEWLINE')
467
- self.consume('INDENT')
468
- otherwise = []
469
- while not self.check('DEDENT') and not self.check('EOF'):
470
- while self.check('NEWLINE'): self.consume()
471
- if self.check('DEDENT'): break
472
- otherwise.append(self.parse_statement())
473
- self.consume('DEDENT')
474
- elif self.check('NEWLINE'):
475
- self.consume('NEWLINE')
476
- else:
477
- break
478
- self.consume('DEDENT')
479
- node = When(condition_or_value, cases, otherwise)
480
- node.line = token.line
481
- return node
482
- else:
483
- body = []
484
- while not self.check('DEDENT') and not self.check('EOF'):
485
- while self.check('NEWLINE'): self.consume()
486
- if self.check('DEDENT'): break
487
- body.append(self.parse_statement())
488
- self.consume('DEDENT')
489
- else_body = None
490
- if self.check('ELSE'):
491
- self.consume('ELSE')
492
- self.consume('NEWLINE')
493
- self.consume('INDENT')
494
- else_body = []
495
- while not self.check('DEDENT') and not self.check('EOF'):
496
- while self.check('NEWLINE'): self.consume()
497
- if self.check('DEDENT'): break
498
- else_body.append(self.parse_statement())
499
- self.consume('DEDENT')
500
- node = If(condition_or_value, body, else_body)
501
- node.line = token.line
502
- return node
503
- def parse_return(self) -> Return:
504
- token = self.consume('RETURN')
505
- expr = self.parse_expression()
506
- self.consume('NEWLINE')
507
- node = Return(expr)
508
- node.line = token.line
509
- return node
510
- def parse_function_def(self) -> FunctionDef:
511
- start_token = None
512
- if self.check('DEFINE'):
513
- start_token = self.consume('DEFINE')
514
- self.consume('FUNCTION')
515
- else:
516
- start_token = self.consume('TO') # Fallback to existing 'TO' if not 'DEFINE'
517
-
518
- name = self.consume('ID').value
519
- args = []
520
- while self.check('ID'):
521
- arg_name = self.consume('ID').value
522
- type_hint = None
523
- if self.check('COLON'):
524
- if self.peek(1).type == 'NEWLINE':
525
- pass
526
- else:
527
- self.consume('COLON')
528
- if self.check('ID'):
529
- type_hint = self.consume('ID').value
530
- elif self.check('STRING'):
531
- type_hint = "str"
532
- self.consume()
533
- else:
534
- type_hint = self.consume().value
535
- default_val = None
536
- if self.check('ASSIGN'):
537
- self.consume('ASSIGN')
538
- default_val = self.parse_expression()
539
- args.append((arg_name, default_val, type_hint))
540
- if self.check('DOING'): self.consume('DOING')
541
- if self.check('COLON'):
542
- self.consume('COLON')
543
- self.consume('NEWLINE')
544
- self.consume('INDENT')
545
- body = []
546
- while not self.check('DEDENT') and not self.check('EOF'):
547
- while self.check('NEWLINE'): self.consume()
548
- if self.check('DEDENT'): break
549
- body.append(self.parse_statement())
550
- self.consume('DEDENT')
551
- node = FunctionDef(name, args, body)
552
- node.line = start_token.line
553
- return node
554
- def parse_class_def(self) -> ClassDef:
555
- start_token = self.consume('STRUCTURE')
556
- name = self.consume('ID').value
557
- parent = None
558
- if self.check('EXTENDS'):
559
- self.consume('EXTENDS')
560
- parent = self.consume('ID').value
561
- elif self.check('LPAREN'):
562
- self.consume('LPAREN')
563
- parent = self.consume('ID').value
564
- self.consume('RPAREN')
565
- self.consume('NEWLINE')
566
- self.consume('INDENT')
567
- properties = []
568
- methods = []
569
- while not self.check('DEDENT') and not self.check('EOF'):
570
- if self.check('HAS'):
571
- self.consume()
572
- prop_name = None
573
- if self.check('MAKE'):
574
- prop_name = self.consume().value
575
- else:
576
- prop_name = self.consume('ID').value
577
-
578
- default_val = None
579
- if self.check('ASSIGN'):
580
- self.consume('ASSIGN')
581
- default_val = self.parse_expression()
582
-
583
- properties.append((prop_name, default_val))
584
- self.consume('NEWLINE')
585
- elif self.check('TO'):
586
- methods.append(self.parse_function_def())
587
- elif self.check('ID'):
588
- prop_name = self.consume('ID').value
589
- default_val = None
590
- if self.check('ASSIGN'):
591
- self.consume('ASSIGN')
592
- default_val = self.parse_expression()
593
- properties.append((prop_name, default_val))
594
- self.consume('NEWLINE')
595
- elif self.check('NEWLINE'):
596
- self.consume()
597
- else:
598
- self.consume('DEDENT')
599
- break
600
- self.consume('DEDENT')
601
- node = ClassDef(name, properties, methods, parent)
602
- node.line = start_token.line
603
- return node
604
- def parse_id_start_statement(self, passed_name_token=None) -> Node:
605
- if passed_name_token:
606
- name_token = passed_name_token
607
- else:
608
- name_token = self.consume('ID')
609
- name = name_token.value
610
- if self.check('ASSIGN'):
611
- self.consume('ASSIGN')
612
- value = self.parse_expression()
613
- self.consume('NEWLINE')
614
- node = Assign(name, value)
615
- node.line = name_token.line
616
- return node
617
- elif self.check('PLUSEQ'):
618
- self.consume('PLUSEQ')
619
- value = self.parse_expression()
620
- self.consume('NEWLINE')
621
- node = Assign(name, BinOp(VarAccess(name), '+', value))
622
- node.line = name_token.line
623
- return node
624
- elif self.check('MINUSEQ'):
625
- self.consume('MINUSEQ')
626
- value = self.parse_expression()
627
- self.consume('NEWLINE')
628
- node = Assign(name, BinOp(VarAccess(name), '-', value))
629
- node.line = name_token.line
630
- return node
631
- elif self.check('MULEQ'):
632
- self.consume('MULEQ')
633
- value = self.parse_expression()
634
- self.consume('NEWLINE')
635
- node = Assign(name, BinOp(VarAccess(name), '*', value))
636
- node.line = name_token.line
637
- return node
638
- elif self.check('DIVEQ'):
639
- self.consume('DIVEQ')
640
- value = self.parse_expression()
641
- self.consume('NEWLINE')
642
- node = Assign(name, BinOp(VarAccess(name), '/', value))
643
- node.line = name_token.line
644
- return node
645
- elif self.check('IS'):
646
- token_is = self.consume('IS')
647
- if self.check('ID') and self.peek().value == 'a':
648
- self.consume()
649
- if self.check('LIST'):
650
- self.consume('LIST')
651
- self.consume('NEWLINE')
652
- node = Assign(name, ListVal([]))
653
- node.line = token_is.line
654
- return node
655
- if self.check('ID') and self.peek().value in ('dictionary', 'map', 'dict'):
656
- self.consume()
657
- self.consume('NEWLINE')
658
- node = Assign(name, Dictionary([]))
659
- node.line = token_is.line
660
- return node
661
- if self.check('ID') and not self.peek().value in ('{', '['):
662
- class_name = self.consume('ID').value
663
- args = []
664
- while not self.check('NEWLINE') and not self.check('EOF'):
665
- args.append(self.parse_expression())
666
- self.consume('NEWLINE')
667
- node = Instantiation(name, class_name, args)
668
- node.line = token_is.line
669
- return node
670
- else:
671
- value = self.parse_expression()
672
- self.consume('NEWLINE')
673
- node = Assign(name, value)
674
- node.line = token_is.line
675
- return node
676
- elif self.check('LBRACKET'):
677
- self.consume('LBRACKET')
678
- index = self.parse_expression()
679
- self.consume('RBRACKET')
680
- self.consume('ASSIGN')
681
- val = self.parse_expression()
682
- self.consume('NEWLINE')
683
- # Convert simple assignment to PropertyAssign-like or specific set call?
684
- # interpreter.visit_IndexAccess handles get.
685
- # We need a node for IndexAssign. parser snippet doesn't show one.
686
- # But visit_PropertyAssign handles dict keys.
687
- # Let's map d[k] = v to PropertyAssign(d, k, v)?
688
- # No, PropertyAssign takes member name as string. Here index handles expressions.
689
- # I need to check if there is a node for this.
690
- # If not, I'll return Call('set', [VarAccess(name), index, val])
691
- return Call('set', [VarAccess(name), index, val])
692
- elif self.check('DOT'):
693
- self.consume('DOT')
694
- member_token = self.consume()
695
- member = member_token.value
696
- if self.check('ASSIGN'):
697
- self.consume('ASSIGN')
698
- value = self.parse_expression()
699
- self.consume('NEWLINE')
700
- return PropertyAssign(name, member, value)
701
-
702
- args = []
703
- if self.check('LPAREN'):
704
- self.consume('LPAREN')
705
- if not self.check('RPAREN'):
706
- args.append(self.parse_expression())
707
- while self.check('COMMA'):
708
- self.consume('COMMA')
709
- args.append(self.parse_expression())
710
- self.consume('RPAREN')
711
- self.consume('NEWLINE')
712
- node = MethodCall(name, member, args)
713
- node.line = name_token.line
714
- return node
715
-
716
- while not self.check('NEWLINE') and not self.check('EOF'):
717
- args.append(self.parse_expression())
718
- self.consume('NEWLINE')
719
- node = MethodCall(name, member, args)
720
- node.line = name_token.line
721
- return node
722
- elif self.check('LPAREN'):
723
- self.consume('LPAREN')
724
- args = []
725
- if not self.check('RPAREN'):
726
- args.append(self.parse_expression())
727
- while self.check('COMMA'):
728
- self.consume('COMMA')
729
- args.append(self.parse_expression())
730
- self.consume('RPAREN')
731
- self.consume('NEWLINE')
732
- node = Call(name, args)
733
- node.line = name_token.line
734
- return node
735
- else:
736
- if not self.check('NEWLINE') and not self.check('EOF') and not self.check('EQ') and not self.check('IS'):
737
- args = []
738
- while not self.check('NEWLINE') and not self.check('EOF') and not self.check('IS'):
739
- is_named_arg = False
740
- if self.peek(1).type == 'ASSIGN':
741
- t_type = self.peek().type
742
- if t_type in ('ID', 'STRUCTURE', 'TYPE', 'FOR', 'IN', 'WHILE', 'IF', 'ELSE', 'FROM', 'TO', 'STRING', 'EXTENDS', 'WITH', 'PLACEHOLDER', 'NAME', 'VALUE', 'ACTION', 'METHOD', 'HREF', 'SRC', 'CLASS', 'STYLE'):
743
- is_named_arg = True
744
- if is_named_arg:
745
- key_token = self.consume()
746
- key = key_token.value
747
- self.consume('ASSIGN')
748
- val = self.parse_expression()
749
- args.append(Dictionary([ (String(key), val) ]))
750
- else:
751
- if self.check('USING'):
752
- self.consume('USING')
753
- args.append(self.parse_expression())
754
- if self.check('NEWLINE'):
755
- self.consume('NEWLINE')
756
- elif self.check('INDENT'):
757
- pass
758
- else:
759
- self.consume('NEWLINE')
760
- body = None
761
- if self.check('INDENT'):
762
- self.consume('INDENT')
763
- body = []
764
- while not self.check('DEDENT') and not self.check('EOF'):
765
- while self.check('NEWLINE'): self.consume()
766
- if self.check('DEDENT'): break
767
- body.append(self.parse_statement())
768
- self.consume('DEDENT')
769
- node = Call(name, args, body)
770
- node.line = name_token.line
771
- return node
772
- node = Call(name, args, body)
773
- node.line = name_token.line
774
- return node
775
- if self.check('NEWLINE'):
776
- self.consume('NEWLINE')
777
- elif self.check('INDENT'):
778
- pass
779
- else:
780
- self.consume('NEWLINE')
781
- if self.check('INDENT'):
782
- self.consume('INDENT')
783
- body = []
784
- while not self.check('DEDENT') and not self.check('EOF'):
785
- while self.check('NEWLINE'): self.consume()
786
- if self.check('DEDENT'): break
787
- body.append(self.parse_statement())
788
- self.consume('DEDENT')
789
- node = Call(name, [], body)
790
- node.line = name_token.line
791
- return node
792
- node = VarAccess(name)
793
- node.line = name_token.line
794
- return node
795
- def parse_print(self) -> Node:
796
- if self.check('PRINT'):
797
- token = self.consume('PRINT')
798
- else:
799
- token = self.consume('SAY')
800
- if self.check('PROGRESS'):
801
- return self.parse_progress_loop(token)
802
- style = None
803
- color = None
804
- if self.check('IN'):
805
- self.consume('IN')
806
- if self.peek().type in ('RED', 'GREEN', 'BLUE', 'YELLOW', 'CYAN', 'MAGENTA'):
807
- color = self.consume().value
808
- if self.check('BOLD'):
809
- self.consume('BOLD')
810
- style = 'bold'
811
- if self.peek().type in ('RED', 'GREEN', 'BLUE', 'YELLOW', 'CYAN', 'MAGENTA'):
812
- color = self.consume().value
813
- expr = self.parse_expression()
814
- self.consume('NEWLINE')
815
- node = Print(expression=expr, style=style, color=color)
816
- node.line = token.line
817
- return node
818
- def parse_progress_loop(self, start_token: Token) -> ProgressLoop:
819
- self.consume('PROGRESS')
820
- if not (self.check('FOR') or self.check('REPEAT') or self.check('LOOP')):
821
- raise SyntaxError(f"Expected loop after 'show progress' on line {start_token.line}")
822
- if self.check('FOR') or self.check('LOOP'):
823
- loop_node = self.parse_for()
824
- else:
825
- loop_node = self.parse_repeat()
826
- node = ProgressLoop(loop_node)
827
- node.line = start_token.line
828
- return node
829
- def parse_serve(self) -> ServeStatic:
830
- token = self.consume('SERVE')
831
- if self.check('FILES'):
832
- self.consume('FILES')
833
- if self.check('FROM'):
834
- self.consume('FROM')
835
- folder = self.parse_expression()
836
- url = String('/static')
837
- if self.check('AT'):
838
- self.consume('AT')
839
- url = self.parse_expression()
840
- if self.check('FOLDER'):
841
- self.consume('FOLDER')
842
- self.consume('NEWLINE')
843
- node = ServeStatic(folder, url)
844
- node.line = token.line
845
- return node
846
- self.consume('STATIC')
847
- folder = self.parse_expression()
848
- self.consume('AT')
849
- url = self.parse_expression()
850
- self.consume('NEWLINE')
851
- node = ServeStatic(folder, url)
852
- node.line = token.line
853
- return node
854
- def parse_listen(self) -> Listen:
855
- token = self.consume('LISTEN')
856
- if self.check('ON'): self.consume('ON')
857
- if self.check('PORT'): self.consume('PORT')
858
- port_num = self.parse_expression()
859
- self.consume('NEWLINE')
860
- node = Listen(port_num)
861
- node.line = token.line
862
- return node
863
- def parse_every(self) -> Every:
864
- token = self.consume('EVERY')
865
- interval = self.parse_expression()
866
- unit = 'seconds'
867
- if self.check('MINUTE'):
868
- self.consume()
869
- unit = 'minutes'
870
- elif self.check('SECOND'):
871
- self.consume()
872
- unit = 'seconds'
873
- self.consume('NEWLINE')
874
- self.consume('INDENT')
875
- body = []
876
- while not self.check('DEDENT') and not self.check('EOF'):
877
- while self.check('NEWLINE'): self.consume()
878
- if self.check('DEDENT'): break
879
- body.append(self.parse_statement())
880
- self.consume('DEDENT')
881
- node = Every(interval, unit, body)
882
- node.line = token.line
883
- return node
884
- def parse_after(self) -> After:
885
- token = self.consume('IN')
886
- delay = self.parse_expression()
887
- unit = 'seconds'
888
- if self.check('MINUTE'):
889
- self.consume()
890
- unit = 'minutes'
891
- elif self.check('SECOND'):
892
- self.consume()
893
- unit = 'seconds'
894
- self.consume('NEWLINE')
895
- self.consume('INDENT')
896
- body = []
897
- while not self.check('DEDENT') and not self.check('EOF'):
898
- while self.check('NEWLINE'): self.consume()
899
- if self.check('DEDENT'): break
900
- body.append(self.parse_statement())
901
- self.consume('DEDENT')
902
- node = After(delay, unit, body)
903
- node.line = token.line
904
- return node
905
- def parse_define_page(self) -> Node:
906
- token = self.consume('DEFINE')
907
- if self.check('PAGE'):
908
- self.consume('PAGE')
909
- name = self.consume('ID').value
910
- args = []
911
- if self.check('USING'):
912
- self.consume('USING')
913
- args.append((self.consume('ID').value, None, None))
914
- while self.check('COMMA'):
915
- self.consume('COMMA')
916
- args.append((self.consume('ID').value, None, None))
917
- self.consume('NEWLINE')
918
- self.consume('INDENT')
919
- body = []
920
- while not self.check('DEDENT') and not self.check('EOF'):
921
- while self.check('NEWLINE'): self.consume()
922
- if self.check('DEDENT'): break
923
- body.append(self.parse_statement())
924
- self.consume('DEDENT')
925
- node = FunctionDef(name, args, body)
926
- node.line = token.line
927
- return node
928
- def parse_add_to(self) -> Node:
929
- token = self.consume('ADD')
930
- item_expr = self.parse_factor_simple()
931
- if self.check('TO') or self.check('INTO'):
932
- self.consume()
933
- list_name = self.consume('ID').value
934
- self.consume('NEWLINE')
935
- list_access = VarAccess(list_name)
936
- item_list = ListVal([item_expr])
937
- concat = BinOp(list_access, '+', item_list)
938
- node = Assign(list_name, concat)
939
- node.line = token.line
940
- return node
941
- def parse_start_server(self) -> Node:
942
- token = self.consume('START')
943
- if self.check('SERVER'):
944
- self.consume('SERVER')
945
- port = Number(8080)
946
- if self.check('ON'):
947
- self.consume('ON')
948
- if self.check('PORT'):
949
- self.consume('PORT')
950
- port = self.parse_expression()
951
- self.consume('NEWLINE')
952
- node = Listen(port)
953
- node.line = token.line
954
- return node
955
- def parse_heading(self) -> Node:
956
- token = self.consume('HEADING')
957
- text = self.parse_expression()
958
- self.consume('NEWLINE')
959
- node = Call('h1', [text])
960
- node.line = token.line
961
- return node
962
- def parse_paragraph(self) -> Node:
963
- token = self.consume('PARAGRAPH')
964
- text = self.parse_expression()
965
- self.consume('NEWLINE')
966
- node = Call('p', [text])
967
- node.line = token.line
968
- return node
969
- def parse_assign(self) -> Assign:
970
- name = self.consume('ID').value
971
- self.consume('ASSIGN')
972
- value = self.parse_expression()
973
- self.consume('NEWLINE')
974
- return Assign(name, value)
975
- def parse_import(self) -> Node:
976
- token = self.consume('USE')
977
-
978
- # Check for Python Import: use python "numpy" as np
979
- if self.check('ID') and self.peek().value == 'python':
980
- self.consume('ID') # consume 'python'
981
- if self.check('STRING'):
982
- module_name = self.consume('STRING').value
983
- alias = None
984
- if self.check('AS'):
985
- self.consume('AS')
986
- alias = self.consume('ID').value
987
- self.consume('NEWLINE')
988
- node = PythonImport(module_name, alias)
989
- node.line = token.line
990
- return node
991
- else:
992
- raise SyntaxError(f"Expected module name string after 'use python' at line {token.line}")
993
-
994
- if self.check('STRING'):
995
- path = self.consume('STRING').value
996
- else:
997
- path = self.consume('ID').value
998
- if self.check('AS'):
999
- self.consume('AS')
1000
- alias = self.consume('ID').value
1001
- self.consume('NEWLINE')
1002
- node = ImportAs(path, alias)
1003
- else:
1004
- self.consume('NEWLINE')
1005
- node = Import(path)
1006
- node.line = token.line
1007
- return node
1008
- def parse_if(self) -> If:
1009
- self.consume('IF')
1010
- condition = self.parse_expression()
1011
- if self.check('COLON'): self.consume('COLON')
1012
- self.consume('NEWLINE')
1013
- self.consume('INDENT')
1014
- body = []
1015
- while not self.check('DEDENT') and not self.check('EOF'):
1016
- while self.check('NEWLINE'): self.consume()
1017
- if self.check('DEDENT'): break
1018
- body.append(self.parse_statement())
1019
- self.consume('DEDENT')
1020
- else_body = None
1021
- if self.check('ELIF'):
1022
- else_body = [self.parse_elif()]
1023
- elif self.check('ELSE') or self.check('OTHERWISE'):
1024
- if self.check('ELSE'): self.consume('ELSE')
1025
- else: self.consume('OTHERWISE')
1026
-
1027
- if self.check('COLON'): self.consume('COLON')
1028
- self.consume('NEWLINE')
1029
- self.consume('INDENT')
1030
- else_body = []
1031
- while not self.check('DEDENT') and not self.check('EOF'):
1032
- while self.check('NEWLINE'): self.consume()
1033
- if self.check('DEDENT'): break
1034
- else_body.append(self.parse_statement())
1035
- self.consume('DEDENT')
1036
- return If(condition, body, else_body)
1037
-
1038
- def parse_elif(self) -> If:
1039
- token = self.consume('ELIF')
1040
- condition = self.parse_expression()
1041
- if self.check('COLON'): self.consume('COLON')
1042
- self.consume('NEWLINE')
1043
- self.consume('INDENT')
1044
- body = []
1045
- while not self.check('DEDENT') and not self.check('EOF'):
1046
- while self.check('NEWLINE'): self.consume()
1047
- if self.check('DEDENT'): break
1048
- body.append(self.parse_statement())
1049
- self.consume('DEDENT')
1050
- else_body = None
1051
- if self.check('ELIF'):
1052
- else_body = [self.parse_elif()]
1053
- elif self.check('ELSE'):
1054
- self.consume('ELSE')
1055
- if self.check('COLON'): self.consume('COLON')
1056
- self.consume('NEWLINE')
1057
- self.consume('INDENT')
1058
- else_body = []
1059
- while not self.check('DEDENT') and not self.check('EOF'):
1060
- while self.check('NEWLINE'): self.consume()
1061
- if self.check('DEDENT'): break
1062
- else_body.append(self.parse_statement())
1063
- self.consume('DEDENT')
1064
- return If(condition, body, else_body)
1065
-
1066
- def parse_while(self) -> While:
1067
- start_token = self.consume('WHILE')
1068
- condition = self.parse_expression()
1069
- if self.check('COLON'): self.consume('COLON')
1070
- self.consume('NEWLINE')
1071
- self.consume('INDENT')
1072
- body = []
1073
- while not self.check('DEDENT') and not self.check('EOF'):
1074
- while self.check('NEWLINE'): self.consume()
1075
- if self.check('DEDENT'): break
1076
- body.append(self.parse_statement())
1077
- self.consume('DEDENT')
1078
- node = While(condition, body)
1079
- node.line = start_token.line
1080
- return node
1081
-
1082
- def parse_repeat(self) -> Repeat:
1083
- start_token = self.consume('REPEAT')
1084
- count = self.parse_expression()
1085
- self.consume('TIMES')
1086
- if self.check('COLON'): self.consume('COLON')
1087
- self.consume('NEWLINE')
1088
- self.consume('INDENT')
1089
- body = []
1090
- while not self.check('DEDENT') and not self.check('EOF'):
1091
- while self.check('NEWLINE'): self.consume()
1092
- if self.check('DEDENT'): break
1093
- body.append(self.parse_statement())
1094
- self.consume('DEDENT')
1095
- node = Repeat(count, body)
1096
- node.line = start_token.line
1097
- return node
1098
-
1099
- def parse_try(self) -> Try:
1100
- start_token = self.consume('TRY')
1101
- if self.check('COLON'):
1102
- self.consume('COLON')
1103
- self.consume('NEWLINE')
1104
- self.consume('INDENT')
1105
- try_body = []
1106
- while not self.check('DEDENT') and not self.check('EOF'):
1107
- while self.check('NEWLINE'): self.consume()
1108
- if self.check('DEDENT'): break
1109
- try_body.append(self.parse_statement())
1110
- self.consume('DEDENT')
1111
- self.consume('CATCH')
1112
- catch_var = self.consume('ID').value
1113
- if self.check('COLON'):
1114
- self.consume('COLON')
1115
- self.consume('NEWLINE')
1116
- self.consume('INDENT')
1117
- catch_body = []
1118
- while not self.check('DEDENT') and not self.check('EOF'):
1119
- while self.check('NEWLINE'): self.consume()
1120
- if self.check('DEDENT'): break
1121
- catch_body.append(self.parse_statement())
1122
- self.consume('DEDENT')
1123
- always_body = []
1124
- if self.check('ALWAYS'):
1125
- self.consume('ALWAYS')
1126
- self.consume('NEWLINE')
1127
- self.consume('INDENT')
1128
- while not self.check('DEDENT') and not self.check('EOF'):
1129
- while self.check('NEWLINE'): self.consume()
1130
- if self.check('DEDENT'): break
1131
- always_body.append(self.parse_statement())
1132
- self.consume('DEDENT')
1133
- if always_body:
1134
- node = TryAlways(try_body, catch_var, catch_body, always_body)
1135
- else:
1136
- node = Try(try_body, catch_var, catch_body)
1137
- node.line = start_token.line
1138
- return node
1139
- def parse_list(self) -> Node:
1140
- token = self.consume('LBRACKET')
1141
- def skip_formatted():
1142
- while self.check('NEWLINE') or self.check('INDENT') or self.check('DEDENT'):
1143
- self.consume()
1144
- skip_formatted()
1145
- if self.check('RBRACKET'):
1146
- self.consume('RBRACKET')
1147
- node = ListVal([])
1148
- node.line = token.line
1149
- return node
1150
- if self.check('DOTDOTDOT'):
1151
- node = self._parse_list_with_spread(token)
1152
- skip_formatted()
1153
- return node
1154
- first_expr = self.parse_expression()
1155
- skip_formatted()
1156
-
1157
- if self.check('TO'):
1158
- self.consume('TO')
1159
- end_val = self.parse_expression()
1160
- skip_formatted()
1161
- self.consume('RBRACKET')
1162
- node = Call('range', [first_expr, end_val])
1163
- node.line = token.line
1164
- return node
1165
-
1166
- if self.check('FOR'):
1167
- self.consume('FOR')
1168
- var_name = self.consume('ID').value
1169
- self.consume('IN')
1170
- iterable = self.parse_expression()
1171
- condition = None
1172
- if self.check('IF'):
1173
- self.consume('IF')
1174
- condition = self.parse_expression()
1175
- self.consume('RBRACKET')
1176
- node = ListComprehension(first_expr, var_name, iterable, condition)
1177
- node.line = token.line
1178
- return node
1179
- elements = [first_expr]
1180
- while self.check('COMMA'):
1181
- self.consume('COMMA')
1182
- skip_formatted()
1183
- if self.check('RBRACKET'):
1184
- break
1185
- if self.check('DOTDOTDOT'):
1186
- self.consume('DOTDOTDOT')
1187
- spread_val = self.parse_expression()
1188
- spread_node = Spread(spread_val)
1189
- spread_node.line = token.line
1190
- elements.append(spread_node)
1191
- else:
1192
- elements.append(self.parse_expression())
1193
- skip_formatted()
1194
- skip_formatted()
1195
- self.consume('RBRACKET')
1196
- node = ListVal(elements)
1197
- node.line = token.line
1198
- return node
1199
- def _parse_list_with_spread(self, token: Token) -> ListVal:
1200
- elements = []
1201
- self.consume('DOTDOTDOT')
1202
- spread_val = self.parse_expression()
1203
- spread_node = Spread(spread_val)
1204
- spread_node.line = token.line
1205
- elements.append(spread_node)
1206
- while self.check('COMMA'):
1207
- self.consume('COMMA')
1208
- if self.check('RBRACKET'):
1209
- break
1210
- if self.check('DOTDOTDOT'):
1211
- self.consume('DOTDOTDOT')
1212
- spread_val = self.parse_expression()
1213
- spread_node = Spread(spread_val)
1214
- spread_node.line = token.line
1215
- elements.append(spread_node)
1216
- else:
1217
- elements.append(self.parse_expression())
1218
- self.consume('RBRACKET')
1219
- node = ListVal(elements)
1220
- node.line = token.line
1221
- return node
1222
- def parse_dict(self) -> Dictionary:
1223
- token = self.consume('LBRACE')
1224
- def skip_formatted():
1225
- while self.check('NEWLINE') or self.check('INDENT') or self.check('DEDENT'):
1226
- self.consume()
1227
- skip_formatted()
1228
- pairs = []
1229
- if not self.check('RBRACE'):
1230
- if self.check('ID') and self.peek(1).type == 'COLON':
1231
- key_token = self.consume('ID')
1232
- key = String(key_token.value)
1233
- key.line = key_token.line
1234
- else:
1235
- key = self.parse_expression()
1236
- self.consume('COLON')
1237
- skip_formatted()
1238
- value = self.parse_expression()
1239
- pairs.append((key, value))
1240
- skip_formatted()
1241
- while self.check('COMMA'):
1242
- self.consume('COMMA')
1243
- skip_formatted()
1244
- if self.check('RBRACE'): break
1245
- if self.check('ID') and self.peek(1).type == 'COLON':
1246
- key_token = self.consume('ID')
1247
- key = String(key_token.value)
1248
- key.line = key_token.line
1249
- else:
1250
- key = self.parse_expression()
1251
- self.consume('COLON')
1252
- skip_formatted()
1253
- value = self.parse_expression()
1254
- pairs.append((key, value))
1255
- skip_formatted()
1256
- skip_formatted()
1257
- self.consume('RBRACE')
1258
- node = Dictionary(pairs)
1259
- node.line = token.line
1260
- return node
1261
- def parse_app(self) -> Node:
1262
- token = self.consume('APP')
1263
- title = self.consume('STRING').value
1264
-
1265
- width = 500
1266
- height = 400
1267
- if self.check('SIZE'):
1268
- self.consume('SIZE')
1269
- width = int(self.consume('NUMBER').value)
1270
- height = int(self.consume('NUMBER').value)
1271
-
1272
- self.consume('COLON')
1273
- self.consume('NEWLINE')
1274
- self.consume('INDENT')
1275
-
1276
- body = []
1277
- while not self.check('DEDENT'):
1278
- body.append(self.parse_ui_block())
1279
- if self.check('NEWLINE'):
1280
- self.consume('NEWLINE')
1281
-
1282
- self.consume('DEDENT')
1283
- return App(title, width, height, body)
1284
-
1285
- def parse_ui_block(self) -> Node:
1286
- token = self.peek()
1287
-
1288
- if token.type in ('COLUMN', 'ROW'):
1289
- layout_type = self.consume().value # column or row
1290
- self.consume('COLON')
1291
- self.consume('NEWLINE')
1292
- self.consume('INDENT')
1293
- children = []
1294
- while not self.check('DEDENT'):
1295
- children.append(self.parse_ui_block())
1296
- if self.check('NEWLINE'): self.consume('NEWLINE')
1297
- self.consume('DEDENT')
1298
- return Layout(layout_type, children)
1299
-
1300
- elif (token.type in ('BUTTON', 'INPUT', 'HEADING') or
1301
- (token.type == 'ID' and token.value == 'text')):
1302
- if token.type == 'ID' and token.value == 'text':
1303
- widget_type = 'TEXT'
1304
- self.consume() # consume 'text' ID
1305
- else:
1306
- widget_type = self.consume().value
1307
- label = self.consume('STRING').value
1308
-
1309
- var_name = None
1310
- if self.check('AS'):
1311
- self.consume('AS')
1312
- var_name = self.consume('ID').value
1313
-
1314
- event_handler = None
1315
- if token.type == 'BUTTON' and self.check('DO'):
1316
- self.consume('DO')
1317
- self.consume('COLON')
1318
- self.consume('NEWLINE')
1319
- self.consume('INDENT')
1320
- event_handler = []
1321
- while not self.check('DEDENT'):
1322
- event_handler.append(self.parse_statement())
1323
- self.consume('DEDENT')
1324
-
1325
- return Widget(widget_type, label, var_name, event_handler)
1326
-
1327
- else:
1328
- raise SyntaxError(f"Unexpected token in UI block: {token.type} at line {token.line}")
1329
-
1330
- def parse_factor_simple(self) -> Node:
1331
- token = self.peek()
1332
- if token.type == 'ASK':
1333
- self.consume('ASK')
1334
- prompt = self.parse_expression()
1335
- node = Call('input', [prompt]) # Alias to input
1336
- node.line = token.line
1337
- return node
1338
- elif token.type == 'NUMBER':
1339
- self.consume()
1340
- val = token.value
1341
- if '.' in val:
1342
- node = Number(float(val))
1343
- else:
1344
- node = Number(int(val))
1345
- node.line = token.line
1346
- return node
1347
- elif token.type == 'STRING':
1348
- self.consume()
1349
- val = token.value
1350
- if '{' in val and '}' in val:
1351
- parts = re.split(r'\{([^}]+)\}', val)
1352
- if len(parts) == 1:
1353
- node = String(val)
1354
- node.line = token.line
1355
- return node
1356
- current_node = None
1357
- for i, part in enumerate(parts):
1358
- if i % 2 == 0:
1359
- if not part: continue
1360
- expr = String(part)
1361
- expr.line = token.line
1362
- else:
1363
- snippet = part.strip()
1364
- if snippet:
1365
- sub_lexer = Lexer(snippet)
1366
- sub_tokens = sub_lexer.tokenize()
1367
- sub_parser = Parser(sub_tokens)
1368
- try:
1369
- expr = sub_parser.parse_expression()
1370
- expr.line = token.line
1371
- except Exception as e:
1372
- raise SyntaxError(f"Invalid interpolation expression: '{snippet}' on line {token.line}")
1373
- else:
1374
- continue
1375
- if current_node is None:
1376
- current_node = expr
1377
- else:
1378
- current_node = BinOp(current_node, '+', expr)
1379
- current_node.line = token.line
1380
- return current_node if current_node else String("")
1381
- node = String(token.value)
1382
- node.line = token.line
1383
- return node
1384
- elif token.type == 'YES':
1385
- self.consume()
1386
- node = Boolean(True)
1387
- node.line = token.line
1388
- return node
1389
- elif token.type == 'NO':
1390
- self.consume()
1391
- node = Boolean(False)
1392
- node.line = token.line
1393
- return node
1394
- elif token.type == 'LBRACKET':
1395
- return self.parse_list()
1396
- elif token.type == 'LBRACE':
1397
- return self.parse_dict()
1398
- elif token.type == 'ID':
1399
- self.consume()
1400
- if self.check('DOT'):
1401
- self.consume('DOT')
1402
- prop_token = self.consume()
1403
- prop = prop_token.value
1404
- node = PropertyAccess(token.value, prop)
1405
- node.line = token.line
1406
- return node
1407
- node = VarAccess(token.value)
1408
- node.line = token.line
1409
- return node
1410
- elif token.type == 'LPAREN':
1411
- self.consume()
1412
- expr = self.parse_expression()
1413
- self.consume('RPAREN')
1414
- return expr
1415
- elif token.type == 'INPUT':
1416
- is_tag = False
1417
- next_t = self.peek(1)
1418
- if next_t.type in ('ID', 'TYPE', 'STRING', 'NAME', 'VALUE', 'CLASS', 'STYLE', 'ONCLICK', 'SRC', 'HREF', 'ACTION', 'METHOD'):
1419
- is_tag = True
1420
- if is_tag:
1421
- return self.parse_id_start_statement(passed_name_token=token)
1422
- self.consume()
1423
- prompt = None
1424
- if self.check('STRING'):
1425
- prompt = self.consume('STRING').value
1426
- node = Input(prompt)
1427
- node.line = token.line
1428
- return node
1429
- raise SyntaxError(f"Unexpected argument token {token.type} at line {token.line}")
1430
- def parse_factor(self) -> Node:
1431
- token = self.peek()
1432
- if token.type == 'MINUS':
1433
- op = self.consume()
1434
- right = self.parse_factor()
1435
- node = UnaryOp('-', right)
1436
- node.line = op.line
1437
- return node
1438
- elif token.type == 'NOT':
1439
- op = self.consume()
1440
- right = self.parse_factor()
1441
- node = UnaryOp('not', right)
1442
- node.line = op.line
1443
- return node
1444
- elif token.type == 'ASK':
1445
- return self.parse_factor_simple()
1446
- elif token.type == 'LPAREN':
1447
- self.consume('LPAREN')
1448
- node = self.parse_expression()
1449
- self.consume('RPAREN')
1450
- return node
1451
- elif token.type == 'DB':
1452
- return self.parse_db_op()
1453
- elif token.type == 'SPAWN':
1454
- op = self.consume()
1455
- right = self.parse_factor()
1456
- node = Spawn(right)
1457
- node.line = op.line
1458
- return node
1459
- elif token.type == 'EXECUTE':
1460
- op = self.consume()
1461
- right = self.parse_expression()
1462
- node = Call('run', [right])
1463
- node.line = op.line
1464
- return node
1465
- elif token.type == 'COUNT' or token.type == 'HOW':
1466
- token = self.consume()
1467
- if token.type == 'HOW':
1468
- self.consume('MANY')
1469
- if self.check('OF'):
1470
- self.consume('OF')
1471
- expr = self.parse_expression()
1472
- node = Call('len', [expr])
1473
- node.line = token.line
1474
- return node
1475
- elif token.type == 'AWAIT':
1476
- op = self.consume()
1477
- right = self.parse_factor()
1478
- node = Await(right)
1479
- node.line = op.line
1480
- return node
1481
- elif token.type == 'CONVERT':
1482
- return self.parse_convert()
1483
- elif token.type == 'LOAD' and self.peek(1).type == 'CSV':
1484
- self.consume('LOAD')
1485
- self.consume('CSV')
1486
- path = self.parse_factor()
1487
- node = CsvOp('load', None, path)
1488
- node.line = token.line
1489
- return node
1490
- elif token.type == 'PASTE':
1491
- token = self.consume('PASTE')
1492
- self.consume('FROM')
1493
- self.consume('CLIPBOARD')
1494
- node = ClipboardOp('paste', None)
1495
- node.line = token.line
1496
- return node
1497
- elif token.type == 'READ':
1498
- token = self.consume('READ')
1499
- if self.check('FILE'):
1500
- self.consume('FILE')
1501
- path = self.parse_factor()
1502
- node = FileRead(path)
1503
- node.line = token.line
1504
- return node
1505
- elif token.type == 'SUM':
1506
- return self.parse_sum()
1507
- elif token.type == 'UPPER':
1508
- return self.parse_upper()
1509
- elif token.type == 'DATE':
1510
- token = self.consume('DATE')
1511
- s = self.consume('STRING').value
1512
- node = DateOp(s)
1513
- node.line = token.line
1514
- return node
1515
- elif token.type == 'TODAY':
1516
- token = self.consume('TODAY')
1517
- node = DateOp('today')
1518
- node.line = token.line
1519
- return node
1520
- if token.type == 'NUMBER':
1521
- self.consume()
1522
- val = token.value
1523
- if '.' in val:
1524
- node = Number(float(val))
1525
- else:
1526
- node = Number(int(val))
1527
- node.line = token.line
1528
- return node
1529
- elif token.type == 'REGEX':
1530
- self.consume()
1531
- node = Regex(token.value)
1532
- node.line = token.line
1533
- return node
1534
- elif token.type == 'STRING':
1535
- return self.parse_factor_simple()
1536
- elif token.type == 'YES':
1537
- self.consume()
1538
- node = Boolean(True)
1539
- node.line = token.line
1540
- return node
1541
- elif token.type == 'NO':
1542
- self.consume()
1543
- node = Boolean(False)
1544
- node.line = token.line
1545
- return node
1546
- elif token.type == 'LBRACKET':
1547
- return self.parse_list()
1548
- elif token.type == 'LBRACE':
1549
- return self.parse_dict()
1550
- elif token.type == 'ID':
1551
- if token.value == 'a':
1552
- if self.peek(1).type == 'LIST' and self.peek(2).type == 'OF':
1553
- return self._parse_natural_list()
1554
- elif self.peek(1).type == 'UNIQUE' and self.peek(2).type == 'SET' and self.peek(3).type == 'OF':
1555
- return self._parse_natural_set()
1556
- if token.value == 'numbers' and self.peek(1).type == 'FROM':
1557
- return self.parse_numbers_range()
1558
- self.consume()
1559
- instance_name = token.value
1560
- method_name = None
1561
- if self.check('DOT'):
1562
- self.consume('DOT')
1563
- method_name = self.consume().value
1564
-
1565
- args = []
1566
- # Check for C-style function call: func(arg1, arg2)
1567
- if self.check('LPAREN'):
1568
- self.consume('LPAREN')
1569
- if not self.check('RPAREN'):
1570
- args.append(self.parse_expression())
1571
- while self.check('COMMA'):
1572
- self.consume('COMMA')
1573
- args.append(self.parse_expression())
1574
- self.consume('RPAREN')
1575
- if method_name:
1576
- node = MethodCall(instance_name, method_name, args)
1577
- else:
1578
- node = Call(instance_name, args)
1579
- node.line = token.line
1580
- return node
1581
-
1582
- # Fallback to command-style arguments: func arg1 arg2
1583
- # But only if NOT a property access (method_name w/o args is property access,
1584
- # but if it was method call it would use parens ideally, or spaces?)
1585
- # Existing logic supported 'func arg1 arg2'.
1586
- # We keep it for backward compatibility e.g. 'echo 123'.
1587
-
1588
- force_call = False
1589
- while True:
1590
- next_t = self.peek()
1591
- # If we see LPAREN now, it's ambiguous if we didn't handle it above.
1592
- # But 'func (1)' is func called with 1 arg which is (1).
1593
- # 'func (1, 2)' would fail in old logic too.
1594
- # So the above block handles the explicit invocation.
1595
- # This loop is for 'func 1 2'.
1596
-
1597
- # Check for keywords or invalid start tokens
1598
- if next_t.type not in ('NUMBER', 'STRING', 'REGEX', 'ID', 'LPAREN', 'INPUT', 'ASK', 'YES', 'NO', 'LBRACKET', 'LBRACE'):
1599
- break
1600
-
1601
- # Special case: if we see LPAREN, is it part of command args?
1602
- # e.g. 'func (1+2)' -> args=[(1+2)].
1603
- args.append(self.parse_factor_simple())
1604
-
1605
- if method_name:
1606
- if args:
1607
- node = MethodCall(instance_name, method_name, args)
1608
- else:
1609
- node = PropertyAccess(instance_name, method_name)
1610
- node.line = token.line
1611
- return node
1612
-
1613
- if args:
1614
- node = Call(instance_name, args)
1615
- node.line = token.line
1616
- return node
1617
-
1618
- node = VarAccess(instance_name)
1619
- node.line = token.line
1620
- return node
1621
- elif token.type == 'LPAREN':
1622
- self.consume()
1623
- expr = self.parse_expression()
1624
- self.consume('RPAREN')
1625
- return expr
1626
- elif token.type == 'INPUT' or token.type == 'ASK':
1627
- next_t = self.peek(1)
1628
- if next_t.type in ('ID', 'TYPE', 'STRING', 'NAME', 'VALUE', 'CLASS', 'STYLE', 'ONCLICK', 'SRC', 'HREF', 'ACTION', 'METHOD'):
1629
- self.consume()
1630
- return self.parse_id_start_statement(passed_name_token=token)
1631
- self.consume()
1632
- prompt = None
1633
- if self.check('STRING'):
1634
- prompt = self.consume('STRING').value
1635
- node = Input(prompt)
1636
- node.line = token.line
1637
- return node
1638
- elif token.type == 'PROMPT':
1639
- self.consume()
1640
- prompt_expr = self.parse_factor()
1641
- node = Prompt(prompt_expr)
1642
- node.line = token.line
1643
- return node
1644
- elif token.type == 'CONFIRM':
1645
- self.consume()
1646
- prompt_expr = self.parse_factor()
1647
- node = Confirm(prompt_expr)
1648
- node.line = token.line
1649
- return node
1650
- elif token.type == 'MAKE':
1651
- return self.parse_make_expr()
1652
- raise SyntaxError(f"Unexpected token {token.type} at line {token.line}")
1653
- def parse_for(self) -> Node:
1654
- if self.check('LOOP'):
1655
- start_token = self.consume('LOOP')
1656
- count_expr = self.parse_expression()
1657
- self.consume('TIMES')
1658
- self.consume('NEWLINE')
1659
- self.consume('INDENT')
1660
- body = []
1661
- while not self.check('DEDENT') and not self.check('EOF'):
1662
- while self.check('NEWLINE'): self.consume()
1663
- if self.check('DEDENT'): break
1664
- body.append(self.parse_statement())
1665
- self.consume('DEDENT')
1666
- node = For(count_expr, body)
1667
- node.line = start_token.line
1668
- return node
1669
- start_token = self.consume('FOR')
1670
- if self.check('ID') and self.peek(1).type == 'IN':
1671
- var_name = self.consume('ID').value
1672
- self.consume('IN')
1673
- if self.check('RANGE'):
1674
- self.consume('RANGE')
1675
- start_val = self.parse_expression()
1676
- end_val = self.parse_expression()
1677
- self.consume('NEWLINE')
1678
- self.consume('INDENT')
1679
- body = []
1680
- while not self.check('DEDENT') and not self.check('EOF'):
1681
- while self.check('NEWLINE'): self.consume()
1682
- if self.check('DEDENT'): break
1683
- body.append(self.parse_statement())
1684
- self.consume('DEDENT')
1685
- iterable = Call('range', [start_val, end_val])
1686
- node = ForIn(var_name, iterable, body)
1687
- node.line = start_token.line
1688
- return node
1689
- else:
1690
- iterable = self.parse_expression()
1691
- self.consume('NEWLINE')
1692
- self.consume('INDENT')
1693
- body = []
1694
- while not self.check('DEDENT') and not self.check('EOF'):
1695
- while self.check('NEWLINE'): self.consume()
1696
- if self.check('DEDENT'): break
1697
- body.append(self.parse_statement())
1698
- self.consume('DEDENT')
1699
- node = ForIn(var_name, iterable, body)
1700
- node.line = start_token.line
1701
- return node
1702
- else:
1703
- count_expr = self.parse_expression()
1704
- self.consume('IN')
1705
- self.consume('RANGE')
1706
- if self.check('COLON'):
1707
- self.consume('COLON')
1708
- self.consume('NEWLINE')
1709
- self.consume('INDENT')
1710
- body = []
1711
- while not self.check('DEDENT') and not self.check('EOF'):
1712
- while self.check('NEWLINE'): self.consume()
1713
- if self.check('DEDENT'): break
1714
- body.append(self.parse_statement())
1715
- self.consume('DEDENT')
1716
- node = For(count_expr, body)
1717
- node.line = start_token.line
1718
- return node
1719
- def parse_expression_stmt(self) -> Node:
1720
- expr = self.parse_expression()
1721
- self.consume('NEWLINE')
1722
- node = Print(expression=expr)
1723
- node.line = expr.line
1724
- return node
1725
- def parse_expression(self) -> Node:
1726
- if self.check('FN'):
1727
- return self.parse_lambda()
1728
- return self.parse_ternary()
1729
- def parse_lambda(self) -> Lambda:
1730
- token = self.consume('FN')
1731
- params = []
1732
- while self.check('ID'):
1733
- params.append(self.consume('ID').value)
1734
- self.consume('ARROW')
1735
- body = self.parse_expression()
1736
- node = Lambda(params, body)
1737
- node.line = token.line
1738
- return node
1739
- def parse_ternary(self) -> Node:
1740
- condition = self.parse_logic_or()
1741
- if self.check('QUESTION'):
1742
- self.consume('QUESTION')
1743
- true_expr = self.parse_expression()
1744
- self.consume('COLON')
1745
- false_expr = self.parse_expression()
1746
- node = Ternary(condition, true_expr, false_expr)
1747
- node.line = condition.line
1748
- return node
1749
- return condition
1750
- def parse_logic_or(self) -> Node:
1751
- left = self.parse_logic_and()
1752
- while self.check('OR'):
1753
- op_token = self.consume()
1754
- right = self.parse_logic_and()
1755
- new_node = BinOp(left, op_token.value, right)
1756
- new_node.line = op_token.line
1757
- left = new_node
1758
- return left
1759
- def parse_logic_and(self) -> Node:
1760
- left = self.parse_comparison()
1761
- while self.check('AND'):
1762
- op_token = self.consume()
1763
- right = self.parse_comparison()
1764
- new_node = BinOp(left, op_token.value, right)
1765
- new_node.line = op_token.line
1766
- left = new_node
1767
- return left
1768
- def parse_comparison(self) -> Node:
1769
- left = self.parse_arithmetic()
1770
- if self.peek().type in ('EQ', 'NEQ', 'GT', 'LT', 'GE', 'LE', 'IS', 'MATCHES', 'GREATER', 'LESS', 'EQUAL', 'CONTAINS', 'EMPTY'):
1771
- op_token = self.consume()
1772
- op_val = op_token.value
1773
-
1774
- # Handle "is greater/less/equal"
1775
- if op_token.type == 'IS':
1776
- if self.check('GREATER'):
1777
- self.consume('GREATER'); self.consume('THAN')
1778
- op_val = '>'
1779
- elif self.check('LESS'):
1780
- self.consume('LESS'); self.consume('THAN')
1781
- op_val = '<'
1782
- elif self.check('EQUAL'):
1783
- self.consume('EQUAL'); self.consume('TO')
1784
- op_val = '=='
1785
- elif self.check('NOT'):
1786
- self.consume('NOT'); self.consume('EQUAL'); self.consume('TO')
1787
- op_val = '!='
1788
- elif self.check('EMPTY'):
1789
- self.consume('EMPTY')
1790
- # is empty -> Call('empty', [left])
1791
- node = Call('empty', [left])
1792
- node.line = op_token.line
1793
- return node
1794
- else:
1795
- op_val = '=='
1796
- elif op_token.type == 'CONTAINS':
1797
- # list contains item -> Call('contains', [list, item])
1798
- right = self.parse_arithmetic()
1799
- node = Call('contains', [left, right])
1800
- node.line = op_token.line
1801
- return node
1802
-
1803
- right = self.parse_arithmetic()
1804
- node = BinOp(left, op_val, right)
1805
- node.line = op_token.line
1806
- return node
1807
- return left
1808
- def parse_arithmetic(self) -> Node:
1809
- left = self.parse_term()
1810
- while self.peek().type in ('PLUS', 'MINUS'):
1811
- op_token = self.consume()
1812
- op_val = op_token.value
1813
- # Normalize to symbols
1814
- if op_token.type == 'PLUS': op_val = '+'
1815
- if op_token.type == 'MINUS': op_val = '-'
1816
-
1817
- right = self.parse_term()
1818
- new_node = BinOp(left, op_val, right)
1819
- new_node.line = op_token.line
1820
- left = new_node
1821
- return left
1822
- def parse_term(self) -> Node:
1823
- left = self.parse_factor()
1824
- while self.peek().type in ('MUL', 'DIV', 'MOD', 'TIMES'):
1825
- # Disambiguate "repeat 3 TIMES" vs "3 TIMES 4"
1826
- if self.peek().type == 'TIMES':
1827
- next_tok = self.peek(1)
1828
- if next_tok.type in ('COLON', 'NEWLINE'):
1829
- break
1830
-
1831
- op_token = self.consume()
1832
- op_val = op_token.value
1833
-
1834
- # Normalize
1835
- if op_token.type == 'MUL': op_val = '*'
1836
- if op_token.type == 'TIMES': op_val = '*'
1837
- if op_token.type == 'DIV':
1838
- op_val = '/'
1839
- if self.check('BY'): self.consume('BY') # Handle "divided by"
1840
-
1841
- if op_token.type == 'MOD': op_val = '%'
1842
-
1843
- right = self.parse_factor()
1844
- new_node = BinOp(left, op_val, right)
1845
- new_node.line = op_token.line
1846
- left = new_node
1847
- return left
1848
- def parse_convert(self) -> Convert:
1849
- token = self.consume('CONVERT')
1850
- expr = self.parse_factor()
1851
- self.consume('TO')
1852
- target_format = 'json'
1853
- if self.check('JSON'):
1854
- self.consume('JSON')
1855
- elif self.check('ID'):
1856
- target_format = self.consume('ID').value
1857
- node = Convert(expr, target_format)
1858
- node.line = token.line
1859
- return node
1860
- def parse_download(self) -> Download:
1861
- token = self.consume('DOWNLOAD')
1862
- url = self.parse_expression()
1863
- self.consume('NEWLINE')
1864
- node = Download(url)
1865
- node.line = token.line
1866
- return node
1867
- def parse_archive(self) -> ArchiveOp:
1868
- op = None
1869
- token = None
1870
- if self.check('COMPRESS'):
1871
- token = self.consume('COMPRESS')
1872
- op = 'compress'
1873
- if self.check('FOLDER'): self.consume('FOLDER')
1874
- else:
1875
- token = self.consume('EXTRACT')
1876
- op = 'extract'
1877
- source = self.parse_expression()
1878
- self.consume('TO')
1879
- target = self.parse_expression()
1880
- self.consume('NEWLINE')
1881
- node = ArchiveOp(op, source, target)
1882
- node.line = token.line
1883
- return node
1884
- def parse_csv_load(self) -> CsvOp:
1885
- token = self.consume('LOAD')
1886
- self.consume('CSV')
1887
- path = self.parse_expression()
1888
- self.consume('NEWLINE')
1889
- node = CsvOp('load', None, path)
1890
- node.line = token.line
1891
- return node
1892
- def parse_csv_save(self) -> CsvOp:
1893
- token = self.consume('SAVE')
1894
- data = self.parse_expression()
1895
- self.consume('TO')
1896
- self.consume('CSV')
1897
- path = self.parse_expression()
1898
- self.consume('NEWLINE')
1899
- node = CsvOp('save', data, path)
1900
- node.line = token.line
1901
- return node
1902
- def parse_clipboard(self) -> Node:
1903
- if self.check('COPY'):
1904
- token = self.consume('COPY')
1905
- content = self.parse_expression()
1906
- self.consume('TO')
1907
- self.consume('CLIPBOARD')
1908
- self.consume('NEWLINE')
1909
- node = ClipboardOp('copy', content)
1910
- node.line = token.line
1911
- return node
1912
- else:
1913
- token = self.consume('PASTE')
1914
- self.consume('FROM')
1915
- self.consume('CLIPBOARD')
1916
- self.consume('NEWLINE')
1917
- node = ClipboardOp('paste', None)
1918
- node.line = token.line
1919
- return node
1920
- def parse_automation(self) -> AutomationOp:
1921
- if self.check('PRESS'):
1922
- token = self.consume('PRESS')
1923
- keys = self.parse_expression()
1924
- self.consume('NEWLINE')
1925
- return AutomationOp('press', [keys])
1926
- elif self.check('TYPE'):
1927
- token = self.consume('TYPE')
1928
- text = self.parse_expression()
1929
- self.consume('NEWLINE')
1930
- return AutomationOp('type', [text])
1931
- elif self.check('CLICK'):
1932
- token = self.consume('CLICK')
1933
- self.consume('AT')
1934
- x = self.parse_expression()
1935
- if self.check('COMMA'): self.consume('COMMA')
1936
- y = self.parse_expression()
1937
- self.consume('NEWLINE')
1938
- return AutomationOp('click', [x, y])
1939
- elif self.check('NOTIFY'):
1940
- token = self.consume('NOTIFY')
1941
- title = self.parse_expression()
1942
- msg = self.parse_expression()
1943
- self.consume('NEWLINE')
1944
- return AutomationOp('notify', [title, msg])
1945
- def parse_write(self) -> FileWrite:
1946
- token = self.consume('WRITE')
1947
- content = self.parse_expression()
1948
- self.consume('TO')
1949
- self.consume('FILE')
1950
- path = self.parse_expression()
1951
- self.consume('NEWLINE')
1952
- node = FileWrite(path, content, 'w')
1953
- node.line = token.line
1954
- return node
1955
- def parse_append(self) -> FileWrite:
1956
- token = self.consume('APPEND')
1957
- content = self.parse_expression()
1958
- self.consume('TO')
1959
- self.consume('FILE')
1960
- path = self.parse_expression()
1961
- self.consume('NEWLINE')
1962
- node = FileWrite(path, content, 'a')
1963
- node.line = token.line
1964
- return node
1965
-
1966
-
1967
- def parse_increment(self) -> Assign:
1968
-
1969
- token = self.consume('INCREMENT')
1970
-
1971
- name = self.consume('ID').value
1972
-
1973
- amount = Number(1)
1974
-
1975
- if self.check('BY'):
1976
-
1977
- self.consume('BY')
1978
-
1979
- amount = self.parse_expression()
1980
-
1981
- self.consume('NEWLINE')
1982
-
1983
- node = Assign(name, BinOp(VarAccess(name), '+', amount))
1984
-
1985
- node.line = token.line
1986
-
1987
- return node
1988
-
1989
-
1990
-
1991
- def parse_decrement(self) -> Assign:
1992
-
1993
- token = self.consume('DECREMENT')
1994
-
1995
- name = self.consume('ID').value
1996
-
1997
- amount = Number(1)
1998
-
1999
- if self.check('BY'):
2000
-
2001
- self.consume('BY')
2002
-
2003
- amount = self.parse_expression()
2004
-
2005
- self.consume('NEWLINE')
2006
-
2007
- node = Assign(name, BinOp(VarAccess(name), '-', amount))
2008
-
2009
- node.line = token.line
2010
-
2011
- return node
2012
-
2013
-
2014
-
2015
- def parse_multiply(self) -> Assign:
2016
-
2017
- token = self.consume('MULTIPLY')
2018
-
2019
- name = self.consume('ID').value
2020
-
2021
- self.consume('BY')
2022
-
2023
- amount = self.parse_expression()
2024
-
2025
- self.consume('NEWLINE')
2026
-
2027
- node = Assign(name, BinOp(VarAccess(name), '*', amount))
2028
-
2029
- node.line = token.line
2030
-
2031
- return node
2032
-
2033
-
2034
-
2035
- def parse_divide(self) -> Assign:
2036
-
2037
- token = self.consume('DIVIDE')
2038
-
2039
- name = self.consume('ID').value
2040
-
2041
- self.consume('BY')
2042
-
2043
- amount = self.parse_expression()
2044
-
2045
- self.consume('NEWLINE')
2046
-
2047
- node = Assign(name, BinOp(VarAccess(name), '/', amount))
2048
-
2049
- node.line = token.line
2050
-
2051
- return node
2052
-
2053
-
2054
-
2055
- def parse_set(self) -> Assign:
2056
-
2057
- token = self.consume('SET')
2058
-
2059
- name = self.consume('ID').value
2060
-
2061
- self.consume('TO')
2062
-
2063
- value = self.parse_expression()
2064
-
2065
- self.consume('NEWLINE')
2066
-
2067
- node = Assign(name, value)
2068
-
2069
- node.line = token.line
2070
-
2071
- return node
2072
-
2073
-
2074
-
2075
- def parse_sum(self) -> Node:
2076
- token = self.consume('SUM')
2077
- self.consume('OF')
2078
-
2079
- # Check for 'numbers from ...' (contextual keyword 'numbers')
2080
- if self.check('ID') and self.peek().value == 'numbers':
2081
- range_node = self.parse_numbers_range()
2082
- # range_node is Call('range_list', ...)
2083
- # We want Call('sum', [range_node])
2084
- node = Call('sum', [range_node])
2085
- node.line = token.line
2086
- return node
2087
-
2088
- expr = self.parse_expression()
2089
- node = Call('sum', [expr])
2090
- node.line = token.line
2091
- return node
2092
-
2093
-
2094
-
2095
- def parse_upper(self) -> Node:
2096
- token = self.consume('UPPER')
2097
- expr = self.parse_expression()
2098
- only_letters = Boolean(False)
2099
-
2100
- if self.check('ID') and self.peek().value == 'only':
2101
- self.consume() # consume 'only'
2102
- if self.check('ID') and self.peek().value == 'letters':
2103
- self.consume() # consume 'letters'
2104
- only_letters = Boolean(True)
2105
- node = Call('upper', [expr, only_letters])
2106
- node.line = token.line
2107
- return node
2108
-
2109
-
2110
-
2111
- def parse_numbers_range(self) -> Node:
2112
- # Expect 'numbers' as ID
2113
- token = self.peek()
2114
- if self.check('ID') and self.peek().value == 'numbers':
2115
- self.consume()
2116
- else:
2117
- # Should be 'numbers' but if called from parse_sum we assume check passed.
2118
- # If called from Factor loop...
2119
- pass
2120
-
2121
- self.consume('FROM')
2122
- start = self.parse_expression()
2123
- self.consume('TO')
2124
- end = self.parse_expression()
2125
-
2126
- condition = None
2127
- if self.check('ID') and self.peek().value == 'that':
2128
- self.consume() # that
2129
- if self.check('ID') and self.peek().value == 'are':
2130
- self.consume() # are
2131
-
2132
- if self.check('ID') and self.peek().value == 'prime':
2133
- self.consume() # prime
2134
- condition = String('prime')
2135
- elif self.check('ID') and self.peek().value == 'digits':
2136
- self.consume() # digits
2137
- condition = String('digits')
2138
-
2139
- elif self.check('WHEN'):
2140
- self.consume('WHEN')
2141
- # 'when even' -> check for ID 'even' or expression?
2142
- # User example: 'when even'. Implicit variable?
2143
- # Let's verify repro: 'when even'
2144
- if self.check('ID') and self.peek().value == 'even':
2145
- self.consume()
2146
- condition = String('even')
2147
- elif self.check('ID') and self.peek().value == 'odd':
2148
- self.consume()
2149
- condition = String('odd')
2150
- else:
2151
- # TODO: handle generic expression filter if needed
2152
- pass
2153
-
2154
- node = Call('range_list', [start, end, condition if condition else Boolean(False)])
2155
- node.line = token.line
2156
- return node
2157
- def parse_add_to_list(self) -> Node:
2158
- token = self.consume('ADD')
2159
- item = self.parse_expression()
2160
- self.consume('TO')
2161
- list_expr = self.parse_expression()
2162
- self.consume('NEWLINE')
2163
- node = Call('append', [list_expr, item])
2164
- node.line = token.line
2165
- return node
2166
-
2167
- def parse_remove_from_list(self) -> Node:
2168
- token = self.consume('REMOVE')
2169
- item = self.parse_expression()
2170
- self.consume('FROM')
2171
- list_expr = self.parse_expression()
2172
- self.consume('NEWLINE')
2173
- node = Call('remove', [list_expr, item])
2174
- node.line = token.line
2175
- return node
2176
-
2177
- def parse_wait(self) -> Node:
2178
- token = self.consume('WAIT')
2179
- value = self.parse_expression()
2180
- if self.check('SECOND'): self.consume('SECOND')
2181
- elif self.check('SECONDS'): self.consume('SECONDS') # Assuming 'SECONDS' token maps to SECOND?
2182
- # Actually I need to check lexer mapping for MINUTES/SECONDS.
2183
- # Lexer has: 'minute': 'MINUTE', 'minutes': 'MINUTE', 'second': 'SECOND', 'seconds': 'SECOND'
2184
-
2185
- elif self.check('MINUTE'):
2186
- self.consume('MINUTE')
2187
- value = BinOp(value, '*', Number(60))
2188
-
2189
- self.consume('NEWLINE')
2190
- node = Call('wait', [value])
2191
- node.line = token.line
2192
- return node
2193
-
2194
- def parse_add_distinguish(self) -> Node:
2195
- # Distinguish "ADD <expr> TO <list>" vs "ADD <component> ..."
2196
- tok = self.peek(1)
2197
- if tok.type in ('BUTTON', 'HEADING', 'PARAGRAPH', 'IMAGE', 'APP', 'PAGE', 'Use', 'INPUT', 'TEXT'):
2198
- return self.parse_add_to()
2199
- else:
2200
- return self.parse_add_to_list()
2201
-
2202
- def parse_make_assignment(self) -> Node:
2203
- token = self.consume('MAKE')
2204
- name = self.consume('ID').value
2205
- if self.check('BE'): self.consume('BE')
2206
- value = self.parse_expression()
2207
- self.consume('NEWLINE')
2208
- node = Assign(name, value)
2209
- node.line = token.line
2210
- return node
2211
-
2212
- def parse_as_long_as(self) -> While:
2213
- start_token = self.consume('AS')
2214
- self.consume('LONG')
2215
- self.consume('AS')
2216
- condition = self.parse_expression()
2217
- if self.check('COLON'): self.consume('COLON')
2218
- self.consume('NEWLINE')
2219
- self.consume('INDENT')
2220
- body = []
2221
- while not self.check('DEDENT') and not self.check('EOF'):
2222
- while self.check('NEWLINE'): self.consume()
2223
- if self.check('DEDENT'): break
2224
- body.append(self.parse_statement())
2225
- self.consume('DEDENT')
2226
- node = While(condition, body)
2227
- node.line = start_token.line
2228
- return node
2229
-