Clarkoer commited on
Commit
e6b283b
·
1 Parent(s): b61beb8

add comments ehehe

Browse files
Backend/_smoke_test.py CHANGED
@@ -1,132 +1,253 @@
 
1
  import sys, os
 
2
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
3
 
 
4
  from lexer import lex
 
5
  from parser import LL1Parser
 
6
  from cfg import cfg, first_sets, predict_sets
 
7
  from semantic import validate_ast
 
8
  from interpreter import Interpreter
9
 
10
 
 
11
  class _Collector:
 
12
  def __init__(self): self.outputs = []
 
13
  def emit(self, event, data=None, **kw):
 
14
  if event == 'output' and data:
 
15
  self.outputs.append(data.get('output', ''))
16
 
17
 
 
18
  PROGRAMS = [
 
19
  ('hello', 'root() { plant("hello"); reclaim; }',
 
20
  ['hello']),
 
21
  ('arith', 'root() { seed a = ~5 + 3; plant("b={}", a); reclaim; }',
 
22
  ['b=-2']),
 
23
  ('unary', 'root() { tree x = 2.5; tree y = ~x; seed n = ~(2 + 3); plant("{} {}", y, n); reclaim; }',
 
24
  ['-2.5 -5']),
 
25
  ('strings', 'root() { vine hi = "Hi"; leaf mark = \'!\'; vine msg = hi ` mark; branch same = msg == "Hi!"; plant("{} {}", msg, same); reclaim; }',
 
26
  ['Hi! True']),
 
27
  ('float', 'root() { tree pi = 3.14; tree r = 2.0; tree area = pi * r * r; plant("area={}", area); reclaim; }',
 
28
  ['area=12.56']),
 
29
  ('forloop', 'root() { seed s = 0; cultivate(seed i = 1; i <= 3; i++) { s += i; } plant("sum={}", s); reclaim; }',
 
30
  ['sum=6']),
 
31
  ('assignexpr', 'root() { seed a = 0; seed b = 0; seed c = (a = b = 5) + 2; plant("a={} b={} c={}", a, b, c); reclaim; }',
 
32
  ['a=5 b=5 c=7']),
 
33
  ('switch', 'root() { seed x = 2; harvest(x) { variety 1: { plant("one"); prune; } variety 2: { plant("two"); prune; } soil: { plant("other"); } } reclaim; }',
 
34
  ['two']),
 
35
  ('declfirst', 'root() { seed num = 5; plant("begin"); num += 2; plant("num={}", num); reclaim; }',
 
36
  ['begin', 'num=7']),
 
37
  ('bundlevar', 'bundle Student { seed age; }; root() { bundle Student student; student.age = 20; plant("age={}", student.age); reclaim; }',
 
38
  ['age=20']),
 
39
  ('nestedreturn', 'root() { branch stop = frost; spring (stop) { reclaim; } plant("continued"); reclaim; }',
 
40
  ['continued']),
 
41
  ('arr_postfix', 'root() { seed arr[3]; arr[0] = 1; arr[0]++; plant("v={}", arr[0]); reclaim; }',
 
42
  ['v=2']),
 
43
  ('arr_prefix', 'root() { seed arr[3]; arr[0] = 5; ++arr[0]; plant("v={}", arr[0]); reclaim; }',
 
44
  ['v=6']),
 
45
  ('arr_postdec', 'root() { seed arr[3]; arr[0] = 7; arr[0]--; plant("v={}", arr[0]); reclaim; }',
 
46
  ['v=6']),
 
47
  ('struct_postinc', 'bundle P { seed a; }; root() { bundle P p; p.a = 1; p.a++; plant("v={}", p.a); reclaim; }',
 
48
  ['v=2']),
 
49
  ('struct_preinc', 'bundle P { seed a; }; root() { bundle P p; p.a = 5; ++p.a; plant("v={}", p.a); reclaim; }',
 
50
  ['v=6']),
 
51
  ('struct_postdec', 'bundle P { seed a; }; root() { bundle P p; p.a = 10; p.a--; plant("v={}", p.a); reclaim; }',
 
52
  ['v=9']),
 
53
  ('arr_in_loop', 'root() { seed arr[3]; arr[0]=0; arr[1]=0; arr[2]=0; cultivate(seed i = 0; i < 3; i++) { arr[i]++; } plant("a={} b={} c={}", arr[0], arr[1], arr[2]); reclaim; }',
 
54
  ['a=1 b=1 c=1']),
 
55
  ('exp_assign_seed', 'root() { seed x = 2; x **= 3; plant("{}", x); reclaim; }',
 
56
  ['8']),
 
57
  ('exp_assign_chain', 'root() { seed x = 2; x **= 3; x **= 2; plant("{}", x); reclaim; }',
 
58
  ['64']),
 
59
  ('exp_assign_member', 'bundle P { seed v; }; root() { bundle P p; p.v = 5; p.v **= 2; plant("{}", p.v); reclaim; }',
 
60
  ['25']),
 
61
  ]
62
 
 
63
  REJECTED_PROGRAMS = [
 
64
  ('late_decl', 'root() { plant("begin"); seed num = 5; reclaim; }',
 
65
  'Local declarations must appear first in the block.'),
 
66
  ('late_nested', 'root() { branch ok = sunshine; spring (ok) { plant("begin"); seed num = 5; } reclaim; }',
 
67
  'Local declarations must appear first in the block.'),
 
68
  ('late_case', 'root() { seed n = 1; harvest (n) { variety 1: plant("begin"); seed late = 5; prune; } reclaim; }',
 
69
  'Local declarations must appear first in the block.'),
 
70
  ('bundle_no_var', 'bundle Student { seed age; }; root() { bundle Student; reclaim; }',
 
71
  'Expected: id'),
 
72
  ('bundle_leading_comma', 'bundle Student { seed age; }; root() { bundle Student, student; reclaim; }',
 
73
  'Expected: id'),
 
74
  ('bundle_multiple', 'bundle Student { seed age; }; root() { bundle Student first, second; reclaim; }',
 
75
  "Expected: ';'"),
 
76
  ('missing_root_reclaim', 'root() { }',
 
77
  "expected 'reclaim;' before '}'"),
 
78
  ('missing_final_reclaim', 'root() { branch stop = sunshine; spring (stop) { reclaim; } }',
 
79
  "expected 'reclaim;' before '}'"),
 
80
  ('missing_function_reclaim', 'pollinate empty greet() { plant("hi"); } root() { reclaim; }',
 
81
  "expected 'reclaim;' before '}'"),
 
82
  ('array_decimal_size', 'root() { seed arr[2.5]; reclaim; }',
 
83
  "Expected: ']', intlit"),
 
84
  ('empty_initializer', 'root() { seed x = ; reclaim; }',
 
85
  "Missing value after '=' operator."),
 
86
  ('string_case_literal', 'root() { seed x = 1; harvest (x) { variety "a": { prune; } } reclaim; }',
 
87
  "Expected: chrlit, 'frost', intlit, 'sunshine'"),
 
88
  ]
89
 
90
 
 
91
  def run():
 
92
  parser = LL1Parser(cfg=cfg, predict_sets=predict_sets, first_sets=first_sets,
 
93
  start_symbol='<program>', end_marker='EOF',
 
94
  skip_token_types={'\n', 'comment', 'mcommentlit'})
 
95
  ok = 0
 
96
  for name, src, expected in PROGRAMS:
 
97
  tokens, lex_errs = lex(src)
 
98
  if lex_errs:
 
99
  print(f'{name:10s} LEX FAIL {lex_errs[:1]}'); continue
 
100
  pr = parser.parse_and_build(tokens)
 
101
  if not pr['success']:
 
102
  print(f'{name:10s} PARSE FAIL {pr["errors"][:1]}'); continue
 
103
  sr = validate_ast(pr['ast'], pr['symbol_table'])
 
104
  if not sr.get('success', True):
 
105
  print(f'{name:10s} SEM FAIL {sr.get("errors", [])[:1]}'); continue
 
106
  c = _Collector()
 
107
  try:
 
108
  Interpreter(socketio=c).interpret(sr['ast'])
 
109
  status = 'OK' if c.outputs == expected else f'WRONG (expected {expected})'
 
110
  print(f'{name:10s} {status:30s} got {c.outputs}')
 
111
  if c.outputs == expected: ok += 1
 
112
  except Exception as e:
 
113
  print(f'{name:10s} RUN FAIL {e}')
114
 
 
115
  reject_ok = 0
 
116
  for name, src, expected_error in REJECTED_PROGRAMS:
 
117
  tokens, lex_errs = lex(src)
 
118
  if lex_errs:
 
119
  print(f'{name:10s} LEX FAIL {lex_errs[:1]}'); continue
 
120
  pr = parser.parse_and_build(tokens)
 
121
  message = pr.get('errors', [''])[0] if not pr['success'] else ''
 
122
  status = 'OK' if expected_error in message else f'WRONG (expected rejection containing {expected_error!r})'
 
123
  print(f'{name:10s} {status:30s} got {message!r}')
 
124
  if expected_error in message:
 
125
  reject_ok += 1
 
126
  print()
 
127
  print(f'PASS: {ok}/{len(PROGRAMS)} valid, {reject_ok}/{len(REJECTED_PROGRAMS)} rejected')
 
128
  return ok == len(PROGRAMS) and reject_ok == len(REJECTED_PROGRAMS)
129
 
130
 
 
131
  if __name__ == '__main__':
 
132
  sys.exit(0 if run() else 1)
 
1
+ # AUTO: Imports a module used by this file.
2
  import sys, os
3
+ # AUTO: Calls `sys.path.insert`.
4
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
5
 
6
+ # AUTO: Imports names from another module.
7
  from lexer import lex
8
+ # AUTO: Imports names from another module.
9
  from parser import LL1Parser
10
+ # AUTO: Imports names from another module.
11
  from cfg import cfg, first_sets, predict_sets
12
+ # AUTO: Imports names from another module.
13
  from semantic import validate_ast
14
+ # AUTO: Imports names from another module.
15
  from interpreter import Interpreter
16
 
17
 
18
+ # AUTO: Defines class `_Collector`.
19
  class _Collector:
20
+ # AUTO: Defines function `__init__`.
21
  def __init__(self): self.outputs = []
22
+ # AUTO: Defines function `emit`.
23
  def emit(self, event, data=None, **kw):
24
+ # AUTO: Checks this condition.
25
  if event == 'output' and data:
26
+ # AUTO: Appends a value to a list.
27
  self.outputs.append(data.get('output', ''))
28
 
29
 
30
+ # AUTO: Sets `PROGRAMS`.
31
  PROGRAMS = [
32
+ # AUTO: Executes this statement.
33
  ('hello', 'root() { plant("hello"); reclaim; }',
34
+ # AUTO: Calls `function`.
35
  ['hello']),
36
+ # AUTO: Sets `('arith', 'root() { seed a`.
37
  ('arith', 'root() { seed a = ~5 + 3; plant("b={}", a); reclaim; }',
38
+ # AUTO: Sets `['b`.
39
  ['b=-2']),
40
+ # AUTO: Sets `('unary', 'root() { tree x`.
41
  ('unary', 'root() { tree x = 2.5; tree y = ~x; seed n = ~(2 + 3); plant("{} {}", y, n); reclaim; }',
42
+ # AUTO: Calls `function`.
43
  ['-2.5 -5']),
44
+ # AUTO: Executes this statement.
45
  ('strings', 'root() { vine hi = "Hi"; leaf mark = \'!\'; vine msg = hi ` mark; branch same = msg == "Hi!"; plant("{} {}", msg, same); reclaim; }',
46
+ # AUTO: Calls `function`.
47
  ['Hi! True']),
48
+ # AUTO: Sets `('float', 'root() { tree pi`.
49
  ('float', 'root() { tree pi = 3.14; tree r = 2.0; tree area = pi * r * r; plant("area={}", area); reclaim; }',
50
+ # AUTO: Sets `['area`.
51
  ['area=12.56']),
52
+ # AUTO: Adds into `('forloop', 'root() { seed s = 0; cultivate(seed i = 1; i <= 3; i++) { s`.
53
  ('forloop', 'root() { seed s = 0; cultivate(seed i = 1; i <= 3; i++) { s += i; } plant("sum={}", s); reclaim; }',
54
+ # AUTO: Sets `['sum`.
55
  ['sum=6']),
56
+ # AUTO: Sets `('assignexpr', 'root() { seed a`.
57
  ('assignexpr', 'root() { seed a = 0; seed b = 0; seed c = (a = b = 5) + 2; plant("a={} b={} c={}", a, b, c); reclaim; }',
58
+ # AUTO: Sets `['a`.
59
  ['a=5 b=5 c=7']),
60
+ # AUTO: Sets `('switch', 'root() { seed x`.
61
  ('switch', 'root() { seed x = 2; harvest(x) { variety 1: { plant("one"); prune; } variety 2: { plant("two"); prune; } soil: { plant("other"); } } reclaim; }',
62
+ # AUTO: Calls `function`.
63
  ['two']),
64
+ # AUTO: Adds into `('declfirst', 'root() { seed num = 5; plant("begin"); num`.
65
  ('declfirst', 'root() { seed num = 5; plant("begin"); num += 2; plant("num={}", num); reclaim; }',
66
+ # AUTO: Sets `['begin', 'num`.
67
  ['begin', 'num=7']),
68
+ # AUTO: Sets `('bundlevar', 'bundle Student { seed age; }; root() { bundle Student student; student.age`.
69
  ('bundlevar', 'bundle Student { seed age; }; root() { bundle Student student; student.age = 20; plant("age={}", student.age); reclaim; }',
70
+ # AUTO: Sets `['age`.
71
  ['age=20']),
72
+ # AUTO: Sets `('nestedreturn', 'root() { branch stop`.
73
  ('nestedreturn', 'root() { branch stop = frost; spring (stop) { reclaim; } plant("continued"); reclaim; }',
74
+ # AUTO: Calls `function`.
75
  ['continued']),
76
+ # AUTO: Sets `('arr_postfix', 'root() { seed arr[3]; arr[0]`.
77
  ('arr_postfix', 'root() { seed arr[3]; arr[0] = 1; arr[0]++; plant("v={}", arr[0]); reclaim; }',
78
+ # AUTO: Sets `['v`.
79
  ['v=2']),
80
+ # AUTO: Sets `('arr_prefix', 'root() { seed arr[3]; arr[0]`.
81
  ('arr_prefix', 'root() { seed arr[3]; arr[0] = 5; ++arr[0]; plant("v={}", arr[0]); reclaim; }',
82
+ # AUTO: Sets `['v`.
83
  ['v=6']),
84
+ # AUTO: Sets `('arr_postdec', 'root() { seed arr[3]; arr[0]`.
85
  ('arr_postdec', 'root() { seed arr[3]; arr[0] = 7; arr[0]--; plant("v={}", arr[0]); reclaim; }',
86
+ # AUTO: Sets `['v`.
87
  ['v=6']),
88
+ # AUTO: Sets `('struct_postinc', 'bundle P { seed a; }; root() { bundle P p; p.a`.
89
  ('struct_postinc', 'bundle P { seed a; }; root() { bundle P p; p.a = 1; p.a++; plant("v={}", p.a); reclaim; }',
90
+ # AUTO: Sets `['v`.
91
  ['v=2']),
92
+ # AUTO: Sets `('struct_preinc', 'bundle P { seed a; }; root() { bundle P p; p.a`.
93
  ('struct_preinc', 'bundle P { seed a; }; root() { bundle P p; p.a = 5; ++p.a; plant("v={}", p.a); reclaim; }',
94
+ # AUTO: Sets `['v`.
95
  ['v=6']),
96
+ # AUTO: Sets `('struct_postdec', 'bundle P { seed a; }; root() { bundle P p; p.a`.
97
  ('struct_postdec', 'bundle P { seed a; }; root() { bundle P p; p.a = 10; p.a--; plant("v={}", p.a); reclaim; }',
98
+ # AUTO: Sets `['v`.
99
  ['v=9']),
100
+ # AUTO: Sets `('arr_in_loop', 'root() { seed arr[3]; arr[0]`.
101
  ('arr_in_loop', 'root() { seed arr[3]; arr[0]=0; arr[1]=0; arr[2]=0; cultivate(seed i = 0; i < 3; i++) { arr[i]++; } plant("a={} b={} c={}", arr[0], arr[1], arr[2]); reclaim; }',
102
+ # AUTO: Sets `['a`.
103
  ['a=1 b=1 c=1']),
104
+ # AUTO: Multiplies into `('exp_assign_seed', 'root() { seed x = 2; x *`.
105
  ('exp_assign_seed', 'root() { seed x = 2; x **= 3; plant("{}", x); reclaim; }',
106
+ # AUTO: Calls `function`.
107
  ['8']),
108
+ # AUTO: Multiplies into `('exp_assign_chain', 'root() { seed x = 2; x *`.
109
  ('exp_assign_chain', 'root() { seed x = 2; x **= 3; x **= 2; plant("{}", x); reclaim; }',
110
+ # AUTO: Calls `function`.
111
  ['64']),
112
+ # AUTO: Multiplies into `('exp_assign_member', 'bundle P { seed v; }; root() { bundle P p; p.v = 5; p.v *`.
113
  ('exp_assign_member', 'bundle P { seed v; }; root() { bundle P p; p.v = 5; p.v **= 2; plant("{}", p.v); reclaim; }',
114
+ # AUTO: Calls `function`.
115
  ['25']),
116
+ # AUTO: Closes the current grouped code/data.
117
  ]
118
 
119
+ # AUTO: Sets `REJECTED_PROGRAMS`.
120
  REJECTED_PROGRAMS = [
121
+ # AUTO: Sets `('late_decl', 'root() { plant("begin"); seed num`.
122
  ('late_decl', 'root() { plant("begin"); seed num = 5; reclaim; }',
123
+ # AUTO: Calls `function`.
124
  'Local declarations must appear first in the block.'),
125
+ # AUTO: Sets `('late_nested', 'root() { branch ok`.
126
  ('late_nested', 'root() { branch ok = sunshine; spring (ok) { plant("begin"); seed num = 5; } reclaim; }',
127
+ # AUTO: Calls `function`.
128
  'Local declarations must appear first in the block.'),
129
+ # AUTO: Sets `('late_case', 'root() { seed n`.
130
  ('late_case', 'root() { seed n = 1; harvest (n) { variety 1: plant("begin"); seed late = 5; prune; } reclaim; }',
131
+ # AUTO: Calls `function`.
132
  'Local declarations must appear first in the block.'),
133
+ # AUTO: Executes this statement.
134
  ('bundle_no_var', 'bundle Student { seed age; }; root() { bundle Student; reclaim; }',
135
+ # AUTO: Calls `function`.
136
  'Expected: id'),
137
+ # AUTO: Executes this statement.
138
  ('bundle_leading_comma', 'bundle Student { seed age; }; root() { bundle Student, student; reclaim; }',
139
+ # AUTO: Calls `function`.
140
  'Expected: id'),
141
+ # AUTO: Executes this statement.
142
  ('bundle_multiple', 'bundle Student { seed age; }; root() { bundle Student first, second; reclaim; }',
143
+ # AUTO: Calls `function`.
144
  "Expected: ';'"),
145
+ # AUTO: Executes this statement.
146
  ('missing_root_reclaim', 'root() { }',
147
+ # AUTO: Calls `function`.
148
  "expected 'reclaim;' before '}'"),
149
+ # AUTO: Sets `('missing_final_reclaim', 'root() { branch stop`.
150
  ('missing_final_reclaim', 'root() { branch stop = sunshine; spring (stop) { reclaim; } }',
151
+ # AUTO: Calls `function`.
152
  "expected 'reclaim;' before '}'"),
153
+ # AUTO: Executes this statement.
154
  ('missing_function_reclaim', 'pollinate empty greet() { plant("hi"); } root() { reclaim; }',
155
+ # AUTO: Calls `function`.
156
  "expected 'reclaim;' before '}'"),
157
+ # AUTO: Executes this statement.
158
  ('array_decimal_size', 'root() { seed arr[2.5]; reclaim; }',
159
+ # AUTO: Calls `function`.
160
  "Expected: ']', intlit"),
161
+ # AUTO: Sets `('empty_initializer', 'root() { seed x`.
162
  ('empty_initializer', 'root() { seed x = ; reclaim; }',
163
+ # AUTO: Sets `"Missing value after '`.
164
  "Missing value after '=' operator."),
165
+ # AUTO: Sets `('string_case_literal', 'root() { seed x`.
166
  ('string_case_literal', 'root() { seed x = 1; harvest (x) { variety "a": { prune; } } reclaim; }',
167
+ # AUTO: Calls `function`.
168
  "Expected: chrlit, 'frost', intlit, 'sunshine'"),
169
+ # AUTO: Closes the current grouped code/data.
170
  ]
171
 
172
 
173
+ # AUTO: Defines function `run`.
174
  def run():
175
+ # AUTO: Sets `parser`.
176
  parser = LL1Parser(cfg=cfg, predict_sets=predict_sets, first_sets=first_sets,
177
+ # AUTO: Sets `start_symbol`.
178
  start_symbol='<program>', end_marker='EOF',
179
+ # AUTO: Sets `skip_token_types`.
180
  skip_token_types={'\n', 'comment', 'mcommentlit'})
181
+ # AUTO: Sets `ok`.
182
  ok = 0
183
+ # AUTO: Starts a loop over these values.
184
  for name, src, expected in PROGRAMS:
185
+ # AUTO: Sets `tokens, lex_errs`.
186
  tokens, lex_errs = lex(src)
187
+ # AUTO: Checks this condition.
188
  if lex_errs:
189
+ # AUTO: Executes this statement.
190
  print(f'{name:10s} LEX FAIL {lex_errs[:1]}'); continue
191
+ # AUTO: Sets `pr`.
192
  pr = parser.parse_and_build(tokens)
193
+ # AUTO: Checks this condition.
194
  if not pr['success']:
195
+ # AUTO: Executes this statement.
196
  print(f'{name:10s} PARSE FAIL {pr["errors"][:1]}'); continue
197
+ # AUTO: Sets `sr`.
198
  sr = validate_ast(pr['ast'], pr['symbol_table'])
199
+ # AUTO: Checks this condition.
200
  if not sr.get('success', True):
201
+ # AUTO: Executes this statement.
202
  print(f'{name:10s} SEM FAIL {sr.get("errors", [])[:1]}'); continue
203
+ # AUTO: Sets `c`.
204
  c = _Collector()
205
+ # AUTO: Starts protected code that can catch errors.
206
  try:
207
+ # AUTO: Sets `Interpreter(socketio`.
208
  Interpreter(socketio=c).interpret(sr['ast'])
209
+ # AUTO: Executes this statement.
210
  status = 'OK' if c.outputs == expected else f'WRONG (expected {expected})'
211
+ # AUTO: Calls `print`.
212
  print(f'{name:10s} {status:30s} got {c.outputs}')
213
+ # AUTO: Checks this condition.
214
  if c.outputs == expected: ok += 1
215
+ # AUTO: Handles the matching error case.
216
  except Exception as e:
217
+ # AUTO: Calls `print`.
218
  print(f'{name:10s} RUN FAIL {e}')
219
 
220
+ # AUTO: Sets `reject_ok`.
221
  reject_ok = 0
222
+ # AUTO: Starts a loop over these values.
223
  for name, src, expected_error in REJECTED_PROGRAMS:
224
+ # AUTO: Sets `tokens, lex_errs`.
225
  tokens, lex_errs = lex(src)
226
+ # AUTO: Checks this condition.
227
  if lex_errs:
228
+ # AUTO: Executes this statement.
229
  print(f'{name:10s} LEX FAIL {lex_errs[:1]}'); continue
230
+ # AUTO: Sets `pr`.
231
  pr = parser.parse_and_build(tokens)
232
+ # AUTO: Sets `message`.
233
  message = pr.get('errors', [''])[0] if not pr['success'] else ''
234
+ # AUTO: Sets `status`.
235
  status = 'OK' if expected_error in message else f'WRONG (expected rejection containing {expected_error!r})'
236
+ # AUTO: Calls `print`.
237
  print(f'{name:10s} {status:30s} got {message!r}')
238
+ # AUTO: Checks this condition.
239
  if expected_error in message:
240
+ # AUTO: Adds into `reject_ok`.
241
  reject_ok += 1
242
+ # AUTO: Calls `print`.
243
  print()
244
+ # AUTO: Calls `print`.
245
  print(f'PASS: {ok}/{len(PROGRAMS)} valid, {reject_ok}/{len(REJECTED_PROGRAMS)} rejected')
246
+ # AUTO: Returns this result to the caller.
247
  return ok == len(PROGRAMS) and reject_ok == len(REJECTED_PROGRAMS)
248
 
249
 
250
+ # AUTO: Checks this condition.
251
  if __name__ == '__main__':
252
+ # AUTO: Calls `sys.exit`.
253
  sys.exit(0 if run() else 1)
Backend/ai/__init__.py CHANGED
@@ -1,2 +1,3 @@
1
 
 
2
  from .fallback import fallback_reply # noqa: F401
 
1
 
2
+ # AUTO: Imports names from another module.
3
  from .fallback import fallback_reply # noqa: F401
Backend/ai/fallback.py CHANGED
The diff for this file is too large to render. See raw diff
 
Backend/cfg/__init__.py CHANGED
@@ -1,9 +1,17 @@
1
 
 
2
  from .grammar import ( # noqa: F401
 
3
  cfg,
 
4
  first_sets,
 
5
  predict_sets,
 
6
  compute_first,
 
7
  compute_follow,
 
8
  compute_predict,
 
9
  )
 
1
 
2
+ # AUTO: Imports names from another module.
3
  from .grammar import ( # noqa: F401
4
+ # AUTO: Executes this statement.
5
  cfg,
6
+ # AUTO: Executes this statement.
7
  first_sets,
8
+ # AUTO: Executes this statement.
9
  predict_sets,
10
+ # AUTO: Executes this statement.
11
  compute_first,
12
+ # AUTO: Executes this statement.
13
  compute_follow,
14
+ # AUTO: Executes this statement.
15
  compute_predict,
16
+ # AUTO: Closes the current grouped code/data.
17
  )
Backend/cfg/grammar.py CHANGED
@@ -1,665 +1,1225 @@
 
 
 
 
 
 
 
1
  import sys
 
2
  from collections import defaultdict
3
 
 
4
  if sys.platform == 'win32':
 
5
  try:
 
6
  sys.stdout.reconfigure(encoding='utf-8')
 
7
  except:
 
8
  pass
9
 
 
10
  EPSILON = "λ"
11
 
12
 
 
13
  def compute_first(cfg):
 
 
14
  first = defaultdict(set)
 
15
  epsilon = EPSILON
16
 
 
17
  for lhs, productions in cfg.items():
 
18
  for prod in productions:
 
19
  if not prod:
 
20
  continue
 
21
  if prod[0] == epsilon:
 
22
  first[lhs].add(epsilon)
 
23
  elif prod[0] not in cfg:
 
24
  first[lhs].add(prod[0])
25
 
 
26
  changed = True
 
27
  while changed:
 
28
  changed = False
 
29
  for lhs, productions in cfg.items():
 
30
  for prod in productions:
 
31
  before = len(first[lhs])
32
 
 
33
  for symbol in prod:
 
34
  if symbol in cfg:
 
35
  first[lhs] |= (first[symbol] - {epsilon})
 
36
  if epsilon not in first[symbol]:
 
37
  break
 
38
  else:
 
39
  if symbol != epsilon:
 
40
  first[lhs].add(symbol)
 
41
  break
 
42
  else:
 
43
  first[lhs].add(epsilon)
44
 
 
45
  if len(first[lhs]) > before:
 
46
  changed = True
47
 
 
48
  return first
49
 
50
 
 
51
  def compute_follow(cfg, first):
 
 
52
  follow = defaultdict(set)
 
53
  epsilon = EPSILON
54
 
 
55
  start_symbol = next(iter(cfg))
 
56
  follow[start_symbol].add("EOF")
57
 
 
58
  changed = True
 
59
  while changed:
 
60
  changed = False
 
61
  for lhs, productions in cfg.items():
 
62
  for prod in productions:
 
63
  for i, symbol in enumerate(prod):
 
64
  if symbol in cfg:
 
65
  before = len(follow[symbol])
66
 
 
67
  j = i + 1
 
68
  while j < len(prod):
 
69
  next_symbol = prod[j]
 
70
  if next_symbol in cfg:
 
71
  follow[symbol] |= (first[next_symbol] - {epsilon})
 
72
  if epsilon not in first[next_symbol]:
 
73
  break
 
74
  else:
 
75
  if next_symbol != epsilon:
 
76
  follow[symbol].add(next_symbol)
 
77
  break
 
78
  j += 1
 
79
  else:
 
80
  follow[symbol] |= follow[lhs]
81
 
 
82
  if len(follow[symbol]) > before:
 
83
  changed = True
84
 
 
85
  return follow
86
 
87
 
 
88
  def compute_predict(cfg, first, follow):
 
 
89
  predict = {}
 
90
  epsilon = EPSILON
91
 
 
92
  for lhs, productions in cfg.items():
 
93
  for prod in productions:
 
94
  key = (lhs, tuple(prod))
 
95
  predict[key] = set()
96
 
 
97
  if not prod or (len(prod) == 1 and prod[0] == epsilon):
 
98
  predict[key] = follow[lhs].copy()
 
99
  continue
100
 
 
101
  first_set = set()
 
102
  for symbol in prod:
 
103
  if symbol in cfg:
 
104
  first_set |= (first[symbol] - {epsilon})
 
105
  if epsilon not in first[symbol]:
 
106
  break
 
107
  else:
 
108
  if symbol != epsilon:
 
109
  first_set.add(symbol)
 
110
  break
 
111
  else:
 
112
  first_set.add(epsilon)
113
 
 
114
  if epsilon in first_set:
 
115
  predict[key] = (first_set - {epsilon}) | follow[lhs]
 
116
  else:
 
117
  predict[key] = first_set
118
 
 
119
  return predict
120
 
121
 
 
 
122
  cfg = {
 
123
  "<program>": [
 
124
  [
 
125
  "<global_declaration>",
 
126
  "<function_definition>",
 
127
  "root",
 
128
  "(",
 
129
  ")",
 
130
  "{",
 
131
  "<local_declaration>",
 
132
  "<body_statement>",
 
133
  "reclaim",
 
134
  ";",
 
135
  "}",
 
136
  ]
 
137
  ],
138
 
 
139
  "<global_declaration>": [
 
140
  ["bundle", "id", "<bundle_or_var>", "<global_declaration>"],
 
141
  ["<data_type>", "id", "<array_dec>", "<var_value>", ";", "<global_declaration>"],
 
142
  ["fertile", "<data_type>", "id", "=", "<init_val>", "<const_next>", ";", "<global_declaration>"],
 
143
  [EPSILON],
 
144
  ],
145
 
 
146
  "<bundle_or_var>": [
 
147
  ["{", "<bundle_members>", "}", ";"],
 
148
  ["<bundle_mem_dec>", ";"],
 
149
  ],
150
 
 
151
  "<local_declaration>": [
 
152
  ["<var_dec>", ";", "<local_declaration>"],
 
153
  ["<const_dec>", ";", "<local_declaration>"],
 
154
  [EPSILON],
 
155
  ],
156
 
 
157
  "<data_type>": [
 
158
  ["seed"],
 
159
  ["tree"],
 
160
  ["leaf"],
 
161
  ["branch"],
 
162
  ["vine"],
 
163
  ],
164
 
 
165
  "<const_dec>": [
 
166
  ["fertile", "<data_type>", "id", "=", "<init_val>", "<const_next>"],
 
167
  ],
168
 
 
169
  "<const_next>": [
 
170
  [",", "id", "=", "<init_val>", "<const_next>"],
 
171
  [EPSILON],
 
172
  ],
173
 
 
174
  "<var_dec>": [
 
175
  ["<data_type>", "id", "<array_dec>", "<var_value>"],
 
176
  ["bundle", "id", "<bundle_mem_dec>"],
 
177
  ],
178
 
 
179
  "<bundle_mem_dec>": [
 
180
  ["id", "<array_dec>"],
 
181
  ],
182
 
 
183
  "<var_value>": [
 
184
  ["=", "<init_val>", "<var_value_next>"],
 
185
  ["<var_value_next>"],
 
186
  ],
187
 
 
188
  "<var_value_next>": [
 
189
  [",", "id", "<array_dec>", "<var_value>"],
 
190
  [EPSILON],
 
191
  ],
192
 
 
193
  "<init_val>": [
 
194
  ["<array_init_opt>"],
 
195
  ["water", "(", "<water_arg>", ")"],
 
196
  ["<expression>"],
 
197
  ],
198
 
 
199
  "<array_dec>": [
 
200
  ["[", "<array_dim_opt>", "]", "<array_dec>"],
 
201
  [EPSILON],
 
202
  ],
203
 
 
204
  "<array_dim_opt>": [
 
205
  ["intlit"],
 
206
  [EPSILON],
 
207
  ],
208
 
 
209
  "<array_init_opt>": [
 
210
  ["{", "<init_vals>", "}"],
 
211
  ],
212
 
 
213
  "<init_vals>": [
 
214
  ["<init_val_item>", "<init_vals_next>"],
 
215
  [EPSILON],
 
216
  ],
217
 
 
218
  "<init_vals_next>": [
 
219
  [",", "<init_val_item>", "<init_vals_next>"],
 
220
  [EPSILON],
 
221
  ],
222
 
 
223
  "<init_val_item>": [
 
224
  ["{", "<init_vals>", "}"],
 
225
  ["<expression>"],
 
226
  ],
227
 
 
228
  "<bundle_members>": [
 
229
  ["<data_type>", "id", ";", "<bundle_members>"],
 
230
  ["id", "id", ";", "<bundle_members>"],
 
231
  [EPSILON],
 
232
  ],
233
 
 
234
  "<function_definition>": [
 
235
  [
 
236
  "pollinate",
 
237
  "<return_type>",
 
238
  "id",
 
239
  "(",
 
240
  "<parameters>",
 
241
  ")",
 
242
  "{",
 
243
  "<local_declaration>",
 
244
  "<body_statement>",
 
245
  "reclaim",
 
246
  "<reclaim_value>",
 
247
  "}",
 
248
  "<function_definition>",
 
249
  ],
 
250
  [EPSILON],
 
251
  ],
252
 
 
253
  "<return_type>": [
 
254
  ["<data_type>"],
 
255
  ["empty"],
 
256
  ["id"],
 
257
  ],
258
 
 
259
  "<parameters>": [
 
260
  [EPSILON],
 
261
  ["<param>", "<param_next>"],
 
262
  ],
263
 
 
264
  "<param>": [
 
265
  ["<data_type>", "id", "<param_array>"],
 
266
  ["id", "id"],
 
267
  ],
268
 
 
269
  "<param_array>": [
 
270
  [EPSILON],
 
271
  ["[", "]"],
 
272
  ],
273
 
 
274
  "<param_next>": [
 
275
  [EPSILON],
 
276
  [",", "<param>", "<param_next>"],
 
277
  ],
278
 
 
279
  "<reclaim_value>": [
 
280
  ["<expression>", ";"],
 
281
  [";"],
 
282
  ],
283
 
 
284
  "<body_statement>": [
 
285
  ["<non_reclaim_stmt>", "<body_statement>"],
 
286
  [EPSILON],
 
287
  ],
288
 
 
289
  "<non_reclaim_stmt>": [
 
290
  ["id", "<id_stmt>"],
 
291
  ["<inc_dec_op>", "id", "<id_next>", ";"],
 
292
  ["<io_stmt>"],
 
293
  ["<conditional_stmt>"],
 
294
  ["<loop_stmt>"],
 
295
  ["<switch_stmt>"],
 
296
  ["<control_stmt>"],
 
297
  ],
298
 
 
299
  "<statement>": [
 
300
  ["<simple_stmt>", "<statement>"],
 
301
  [EPSILON],
 
302
  ],
303
 
 
304
  "<simple_stmt>": [
 
305
  ["<non_reclaim_stmt>"],
 
306
  ["reclaim", "<reclaim_value>"],
 
307
  ],
308
 
 
309
  "<id_stmt>": [
 
310
  ["<id_next>", "<id_stmt_tail>"],
 
311
  ["(", "<arguments>", ")", ";"],
 
312
  ],
313
 
 
314
  "<id_stmt_tail>": [
 
315
  ["<assign_op>", "<assign_rhs>", ";"],
 
316
  ["<inc_dec_op>", ";"],
 
317
  ],
318
 
 
319
  "<assign_rhs>": [
 
320
  ["water", "(", "<water_arg>", ")"],
 
321
  ["<expression>"],
 
322
  ],
323
 
 
324
  "<assign_op>": [
 
325
  ["="],
 
326
  ["+="],
 
327
  ["-="],
 
328
  ["*="],
 
329
  ["/="],
 
330
  ["%="],
 
331
  ["**="],
 
332
  ],
333
 
 
334
  "<id_next>": [
 
335
  ["<array_access>", "<post_array_access>"],
 
336
  ["<struct_access>"],
 
337
  [EPSILON],
 
338
  ],
339
 
 
340
  "<array_access>": [
 
341
  ["[", "<expression>", "]", "<array_access_more>"],
 
342
  ],
343
 
 
344
  "<array_access_more>": [
 
345
  ["[", "<expression>", "]", "<array_access_more>"],
 
346
  [EPSILON],
 
347
  ],
348
 
 
349
  "<struct_access>": [
 
350
  [".", "id", "<struct_access_more>"],
 
351
  ],
352
 
 
353
  "<struct_access_more>": [
 
354
  [".", "id", "<struct_access_more>"],
 
355
  [EPSILON],
 
356
  ],
357
 
 
358
  "<post_array_access>": [
 
359
  [".", "id", "<post_array_access>"],
 
360
  [EPSILON],
 
361
  ],
362
 
 
363
  "<io_stmt>": [
 
364
  ["plant", "(", "<arguments>", ")", ";"],
 
365
  ["water", "(", "<water_arg>", ")", ";"],
 
366
  ],
367
 
 
368
  "<water_arg>": [
 
369
  ["<data_type>"],
 
370
  ["id", "<water_id_tail>"],
 
371
  [EPSILON],
 
372
  ],
373
 
 
374
  "<water_id_tail>": [
 
375
  ["[", "<expression>", "]", "<water_id_tail>"],
 
376
  [EPSILON],
 
377
  ],
378
 
 
379
  "<arguments>": [
 
380
  ["<expression>", "<arg_next>"],
 
381
  [EPSILON],
 
382
  ],
383
 
 
384
  "<arg_next>": [
 
385
  [",", "<expression>", "<arg_next>"],
 
386
  [EPSILON],
 
387
  ],
388
 
 
389
  "<conditional_stmt>": [
 
390
  [
 
391
  "spring",
 
392
  "(",
 
393
  "<expression>",
 
394
  ")",
 
395
  "{",
 
396
  "<local_declaration>",
 
397
  "<statement>",
 
398
  "}",
 
399
  "<elseif_chain>",
 
400
  "<else_opt>",
 
401
  ]
 
402
  ],
403
 
 
404
  "<elseif_chain>": [
 
405
  [
 
406
  "bud",
 
407
  "(",
 
408
  "<expression>",
 
409
  ")",
 
410
  "{",
 
411
  "<local_declaration>",
 
412
  "<statement>",
 
413
  "}",
 
414
  "<elseif_chain>",
 
415
  ],
 
416
  [EPSILON],
 
417
  ],
418
 
 
419
  "<else_opt>": [
 
420
  ["wither", "{", "<local_declaration>", "<statement>", "}"],
 
421
  [EPSILON],
 
422
  ],
423
 
 
424
  "<loop_stmt>": [
 
425
  ["grow", "(", "<expression>", ")", "{", "<local_declaration>", "<statement>", "}"],
426
 
 
427
  [
 
428
  "cultivate",
 
429
  "(",
 
430
  "<for_init>",
 
431
  ";",
 
432
  "<expression>",
 
433
  ";",
 
434
  "<for_update>",
 
435
  ")",
 
436
  "{",
 
437
  "<local_declaration>",
 
438
  "<statement>",
 
439
  "}",
 
440
  ],
441
 
 
442
  ["tend", "{", "<local_declaration>", "<statement>", "}", "grow", "(", "<expression>", ")", ";"],
 
443
  ],
444
 
 
445
  "<for_init>": [
 
446
  ["<data_type>", "id", "<array_dec>", "<var_value>"],
 
447
  ["id", "<id_next>", "<assign_op>", "<expression>"],
 
448
  [EPSILON],
 
449
  ],
450
 
 
451
  "<for_update>": [
 
452
  ["id", "<id_next>", "<for_update_tail>"],
 
453
  [EPSILON],
 
454
  ],
455
 
 
456
  "<for_update_tail>": [
 
457
  ["<inc_dec_op>"],
 
458
  ["<assign_op>", "<expression>"],
 
459
  ],
460
 
 
461
  "<inc_dec_op>": [
 
462
  ["++"],
 
463
  ["--"],
 
464
  ],
465
 
 
466
  "<switch_stmt>": [
 
467
  ["harvest", "(", "<expression>", ")", "{", "<case_list>", "<default_opt>", "}"],
 
468
  ],
469
 
 
470
  "<case_list>": [
 
471
  [
 
472
  "variety",
 
473
  "<case_literal>",
 
474
  ":",
 
475
  "<local_declaration>",
 
476
  "<case_statements>",
 
477
  "<case_list>",
 
478
  ],
 
479
  [EPSILON],
 
480
  ],
481
 
 
482
  "<case_literal>": [
 
483
  ["intlit"],
 
484
  ["chrlit"],
 
485
  ["sunshine"],
 
486
  ["frost"],
 
487
  ],
488
 
 
489
  "<case_statements>": [
 
490
  ["<case_statement>", "<case_statements>"],
 
491
  [EPSILON],
 
492
  ],
493
 
 
494
  "<case_statement>": [
 
495
  ["id", "<id_stmt>"],
 
496
  ["<inc_dec_op>", "id", "<id_next>", ";"],
 
497
  ["<io_stmt>"],
 
498
  ["<conditional_stmt>"],
 
499
  ["<loop_stmt>"],
 
500
  ["<switch_stmt>"],
 
501
  ["{", "<local_declaration>", "<statement>", "}"],
 
502
  ["prune", ";"],
 
503
  ["skip", ";"],
 
504
  ["reclaim", "<reclaim_value>"],
 
505
  ],
506
 
 
507
  "<default_opt>": [
 
508
  ["soil", ":", "<local_declaration>", "<case_statements>"],
 
509
  [EPSILON],
 
510
  ],
511
 
 
512
  "<control_stmt>": [
 
513
  ["prune", ";"],
 
514
  ["skip", ";"],
 
515
  ],
516
 
517
 
 
518
  "<expression>": [
 
519
  ["<assignment_expression>"],
 
520
  ],
521
 
 
522
  "<assignment_expression>": [
 
523
  ["<logic_or>", "<assignment_expression_next>"],
 
524
  ],
525
 
 
526
  "<assignment_expression_next>": [
 
527
  ["<assign_op>", "<assignment_expression>"],
 
528
  [EPSILON],
 
529
  ],
530
 
 
531
  "<logic_or>": [
 
532
  ["<logic_and>", "<logic_or_next>"],
 
533
  ],
534
 
 
535
  "<logic_or_next>": [
 
536
  ["||", "<logic_and>", "<logic_or_next>"],
 
537
  [EPSILON],
 
538
  ],
539
 
 
540
  "<logic_and>": [
 
541
  ["<relational>", "<logic_and_next>"],
 
542
  ],
543
 
 
544
  "<logic_and_next>": [
 
545
  ["&&", "<relational>", "<logic_and_next>"],
 
546
  [EPSILON],
 
547
  ],
548
 
 
549
  "<relational>": [
 
550
  ["<arithmetic>", "<relational_next>"],
 
551
  ],
552
 
 
553
  "<relational_next>": [
 
554
  ["<relational_op>", "<arithmetic>"],
 
555
  [EPSILON],
 
556
  ],
557
 
 
558
  "<relational_op>": [
 
559
  [">"],
 
560
  ["<"],
 
561
  [">="],
 
562
  ["<="],
 
563
  ["=="],
 
564
  ["!="],
 
565
  ],
566
 
 
567
  "<arithmetic>": [
 
568
  ["<term>", "<arithmetic_next>"],
 
569
  ],
570
 
 
571
  "<arithmetic_next>": [
 
572
  ["+", "<term>", "<arithmetic_next>"],
 
573
  ["-", "<term>", "<arithmetic_next>"],
 
574
  ["`", "<term>", "<arithmetic_next>"],
 
575
  [EPSILON],
 
576
  ],
577
 
578
 
 
579
  "<term>": [
 
580
  ["<power>", "<term_next>"],
 
581
  ],
582
 
 
583
  "<term_next>": [
 
584
  ["*", "<power>", "<term_next>"],
 
585
  ["/", "<power>", "<term_next>"],
 
586
  ["%", "<power>", "<term_next>"],
 
587
  [EPSILON],
 
588
  ],
589
 
 
590
  "<power>": [
 
591
  ["<factor>", "<power_next>"],
 
592
  ],
593
 
 
594
  "<power_next>": [
 
595
  ["**", "<power>"],
 
596
  [EPSILON],
 
597
  ],
598
 
 
599
  "<factor>": [
 
600
  ["(", "<paren_expr>"],
 
601
  ["<unary_op>", "<factor>"],
 
602
  ["id", "<factor_id_next>"],
 
603
  ["<literal>"],
 
604
  ],
605
 
 
606
  "<literal>": [
 
607
  ["intlit"],
 
608
  ["dblit"],
 
609
  ["chrlit"],
 
610
  ["stringlit"],
 
611
  ["sunshine"],
 
612
  ["frost"],
 
613
  ],
614
 
 
615
  "<paren_expr>": [
 
616
  ["<data_type>", ")", "<factor>"],
 
617
  ["<expression>", ")"],
 
618
  ],
619
 
 
620
  "<unary_op>": [
 
621
  ["~"],
 
622
  ["!"],
 
623
  ],
624
 
 
625
  "<factor_id_next>": [
 
626
  ["<array_access>", "<post_array_access>"],
 
627
  ["<struct_access>"],
 
628
  ["(", "<arguments>", ")"],
 
629
  [EPSILON],
 
630
  ],
 
631
  }
632
 
633
 
 
 
634
  first_sets = compute_first(cfg)
 
635
  follow_sets = compute_follow(cfg, first_sets)
 
636
  predict_sets = compute_predict(cfg, first_sets, follow_sets)
637
 
638
 
 
639
  if __name__ == '__main__':
 
640
  print("=" * 80)
 
641
  print("FIRST SETS")
 
642
  print("=" * 80)
 
643
  print("Shows which terminals can appear first in each non-terminal")
 
644
  print()
 
645
  for nt in first_sets:
 
646
  print(f"First({nt}) = {{ {', '.join(sorted(first_sets[nt]))} }}")
647
 
 
648
  print("\n" + "=" * 80)
 
649
  print("FOLLOW SETS")
 
650
  print("=" * 80)
 
651
  print("Shows which terminals can appear after each non-terminal")
 
652
  print()
 
653
  for nt in follow_sets:
 
654
  print(f"Follow({nt}) = {{ {', '.join(sorted(follow_sets[nt]))} }}")
655
 
 
656
  print("\n" + "=" * 80)
 
657
  print("PREDICT SETS")
 
658
  print("=" * 80)
 
659
  print("Shows which terminal triggers each production rule")
 
660
  print()
 
661
  for (lhs, prod), pset in predict_sets.items():
 
662
  prod_str = " ".join(prod)
 
663
  print(f"Predict({lhs} -> {prod_str}) = {{ {', '.join(sorted(pset))} }}")
664
 
 
665
  print("\n" + "=" * 80)
 
1
+ """System CFG and LL(1) helper-set generation for GrowALanguage.
2
+
3
+ The parser imports cfg, first_sets, and predict_sets from this file. The grammar
4
+ is written as productions, then FIRST/FOLLOW/PREDICT sets are computed from it.
5
+ """
6
+
7
+ # AUTO: Imports a module used by this file.
8
  import sys
9
+ # AUTO: Imports names from another module.
10
  from collections import defaultdict
11
 
12
+ # AUTO: Checks this condition.
13
  if sys.platform == 'win32':
14
+ # AUTO: Starts protected code that can catch errors.
15
  try:
16
+ # AUTO: Sets `sys.stdout.reconfigure(encoding`.
17
  sys.stdout.reconfigure(encoding='utf-8')
18
+ # AUTO: Executes this statement.
19
  except:
20
+ # AUTO: Does nothing for this required block.
21
  pass
22
 
23
+ # AUTO: Sets `EPSILON`.
24
  EPSILON = "λ"
25
 
26
 
27
+ # AUTO: Defines function `compute_first`.
28
  def compute_first(cfg):
29
+ # GUIDE: FIRST(A) answers what token can begin strings derived from A.
30
+ # AUTO: Sets `first`.
31
  first = defaultdict(set)
32
+ # AUTO: Sets `epsilon`.
33
  epsilon = EPSILON
34
 
35
+ # AUTO: Starts a loop over these values.
36
  for lhs, productions in cfg.items():
37
+ # AUTO: Starts a loop over these values.
38
  for prod in productions:
39
+ # AUTO: Checks this condition.
40
  if not prod:
41
+ # AUTO: Skips to the next loop iteration.
42
  continue
43
+ # AUTO: Checks this condition.
44
  if prod[0] == epsilon:
45
+ # AUTO: Calls `first[lhs].add`.
46
  first[lhs].add(epsilon)
47
+ # AUTO: Checks the next alternate condition.
48
  elif prod[0] not in cfg:
49
+ # AUTO: Calls `first[lhs].add`.
50
  first[lhs].add(prod[0])
51
 
52
+ # AUTO: Sets `changed`.
53
  changed = True
54
+ # AUTO: Repeats while this condition is true.
55
  while changed:
56
+ # AUTO: Sets `changed`.
57
  changed = False
58
+ # AUTO: Starts a loop over these values.
59
  for lhs, productions in cfg.items():
60
+ # AUTO: Starts a loop over these values.
61
  for prod in productions:
62
+ # AUTO: Sets `before`.
63
  before = len(first[lhs])
64
 
65
+ # AUTO: Starts a loop over these values.
66
  for symbol in prod:
67
+ # AUTO: Checks this condition.
68
  if symbol in cfg:
69
+ # AUTO: Sets `first[lhs] |`.
70
  first[lhs] |= (first[symbol] - {epsilon})
71
+ # AUTO: Checks this condition.
72
  if epsilon not in first[symbol]:
73
+ # AUTO: Stops the nearest loop.
74
  break
75
+ # AUTO: Runs when previous condition did not pass.
76
  else:
77
+ # AUTO: Checks this condition.
78
  if symbol != epsilon:
79
+ # AUTO: Calls `first[lhs].add`.
80
  first[lhs].add(symbol)
81
+ # AUTO: Stops the nearest loop.
82
  break
83
+ # AUTO: Runs when previous condition did not pass.
84
  else:
85
+ # AUTO: Calls `first[lhs].add`.
86
  first[lhs].add(epsilon)
87
 
88
+ # AUTO: Checks this condition.
89
  if len(first[lhs]) > before:
90
+ # AUTO: Sets `changed`.
91
  changed = True
92
 
93
+ # AUTO: Returns this result to the caller.
94
  return first
95
 
96
 
97
+ # AUTO: Defines function `compute_follow`.
98
  def compute_follow(cfg, first):
99
+ # GUIDE: FOLLOW(A) answers what token can appear immediately after A.
100
+ # AUTO: Sets `follow`.
101
  follow = defaultdict(set)
102
+ # AUTO: Sets `epsilon`.
103
  epsilon = EPSILON
104
 
105
+ # AUTO: Sets `start_symbol`.
106
  start_symbol = next(iter(cfg))
107
+ # AUTO: Calls `follow[start_symbol].add`.
108
  follow[start_symbol].add("EOF")
109
 
110
+ # AUTO: Sets `changed`.
111
  changed = True
112
+ # AUTO: Repeats while this condition is true.
113
  while changed:
114
+ # AUTO: Sets `changed`.
115
  changed = False
116
+ # AUTO: Starts a loop over these values.
117
  for lhs, productions in cfg.items():
118
+ # AUTO: Starts a loop over these values.
119
  for prod in productions:
120
+ # AUTO: Starts a loop over these values.
121
  for i, symbol in enumerate(prod):
122
+ # AUTO: Checks this condition.
123
  if symbol in cfg:
124
+ # AUTO: Sets `before`.
125
  before = len(follow[symbol])
126
 
127
+ # AUTO: Sets `j`.
128
  j = i + 1
129
+ # AUTO: Repeats while this condition is true.
130
  while j < len(prod):
131
+ # AUTO: Sets `next_symbol`.
132
  next_symbol = prod[j]
133
+ # AUTO: Checks this condition.
134
  if next_symbol in cfg:
135
+ # AUTO: Sets `follow[symbol] |`.
136
  follow[symbol] |= (first[next_symbol] - {epsilon})
137
+ # AUTO: Checks this condition.
138
  if epsilon not in first[next_symbol]:
139
+ # AUTO: Stops the nearest loop.
140
  break
141
+ # AUTO: Runs when previous condition did not pass.
142
  else:
143
+ # AUTO: Checks this condition.
144
  if next_symbol != epsilon:
145
+ # AUTO: Calls `follow[symbol].add`.
146
  follow[symbol].add(next_symbol)
147
+ # AUTO: Stops the nearest loop.
148
  break
149
+ # AUTO: Adds into `j`.
150
  j += 1
151
+ # AUTO: Runs when previous condition did not pass.
152
  else:
153
+ # AUTO: Sets `follow[symbol] |`.
154
  follow[symbol] |= follow[lhs]
155
 
156
+ # AUTO: Checks this condition.
157
  if len(follow[symbol]) > before:
158
+ # AUTO: Sets `changed`.
159
  changed = True
160
 
161
+ # AUTO: Returns this result to the caller.
162
  return follow
163
 
164
 
165
+ # AUTO: Defines function `compute_predict`.
166
  def compute_predict(cfg, first, follow):
167
+ # GUIDE: PREDICT chooses a production using one lookahead token.
168
+ # AUTO: Sets `predict`.
169
  predict = {}
170
+ # AUTO: Sets `epsilon`.
171
  epsilon = EPSILON
172
 
173
+ # AUTO: Starts a loop over these values.
174
  for lhs, productions in cfg.items():
175
+ # AUTO: Starts a loop over these values.
176
  for prod in productions:
177
+ # AUTO: Sets `key`.
178
  key = (lhs, tuple(prod))
179
+ # AUTO: Sets `predict[key]`.
180
  predict[key] = set()
181
 
182
+ # AUTO: Checks this condition.
183
  if not prod or (len(prod) == 1 and prod[0] == epsilon):
184
+ # AUTO: Sets `predict[key]`.
185
  predict[key] = follow[lhs].copy()
186
+ # AUTO: Skips to the next loop iteration.
187
  continue
188
 
189
+ # AUTO: Sets `first_set`.
190
  first_set = set()
191
+ # AUTO: Starts a loop over these values.
192
  for symbol in prod:
193
+ # AUTO: Checks this condition.
194
  if symbol in cfg:
195
+ # AUTO: Sets `first_set |`.
196
  first_set |= (first[symbol] - {epsilon})
197
+ # AUTO: Checks this condition.
198
  if epsilon not in first[symbol]:
199
+ # AUTO: Stops the nearest loop.
200
  break
201
+ # AUTO: Runs when previous condition did not pass.
202
  else:
203
+ # AUTO: Checks this condition.
204
  if symbol != epsilon:
205
+ # AUTO: Calls `first_set.add`.
206
  first_set.add(symbol)
207
+ # AUTO: Stops the nearest loop.
208
  break
209
+ # AUTO: Runs when previous condition did not pass.
210
  else:
211
+ # AUTO: Calls `first_set.add`.
212
  first_set.add(epsilon)
213
 
214
+ # AUTO: Checks this condition.
215
  if epsilon in first_set:
216
+ # AUTO: Sets `predict[key]`.
217
  predict[key] = (first_set - {epsilon}) | follow[lhs]
218
+ # AUTO: Runs when previous condition did not pass.
219
  else:
220
+ # AUTO: Sets `predict[key]`.
221
  predict[key] = first_set
222
 
223
+ # AUTO: Returns this result to the caller.
224
  return predict
225
 
226
 
227
+ # GUIDE: Actual grammar used by parser.py; terminals match lexer token.type values.
228
+ # AUTO: Sets `cfg`.
229
  cfg = {
230
+ # AUTO: Executes this statement.
231
  "<program>": [
232
+ # AUTO: Executes this statement.
233
  [
234
+ # AUTO: Executes this statement.
235
  "<global_declaration>",
236
+ # AUTO: Executes this statement.
237
  "<function_definition>",
238
+ # AUTO: Executes this statement.
239
  "root",
240
+ # AUTO: Executes this statement.
241
  "(",
242
+ # AUTO: Executes this statement.
243
  ")",
244
+ # AUTO: Executes this statement.
245
  "{",
246
+ # AUTO: Executes this statement.
247
  "<local_declaration>",
248
+ # AUTO: Executes this statement.
249
  "<body_statement>",
250
+ # AUTO: Executes this statement.
251
  "reclaim",
252
+ # AUTO: Executes this statement.
253
  ";",
254
+ # AUTO: Executes this statement.
255
  "}",
256
+ # AUTO: Closes the current grouped code/data.
257
  ]
258
+ # AUTO: Closes the current grouped code/data.
259
  ],
260
 
261
+ # AUTO: Executes this statement.
262
  "<global_declaration>": [
263
+ # AUTO: Executes this statement.
264
  ["bundle", "id", "<bundle_or_var>", "<global_declaration>"],
265
+ # AUTO: Executes this statement.
266
  ["<data_type>", "id", "<array_dec>", "<var_value>", ";", "<global_declaration>"],
267
+ # AUTO: Sets `["fertile", "<data_type>", "id", "`.
268
  ["fertile", "<data_type>", "id", "=", "<init_val>", "<const_next>", ";", "<global_declaration>"],
269
+ # AUTO: Executes this statement.
270
  [EPSILON],
271
+ # AUTO: Closes the current grouped code/data.
272
  ],
273
 
274
+ # AUTO: Executes this statement.
275
  "<bundle_or_var>": [
276
+ # AUTO: Executes this statement.
277
  ["{", "<bundle_members>", "}", ";"],
278
+ # AUTO: Executes this statement.
279
  ["<bundle_mem_dec>", ";"],
280
+ # AUTO: Closes the current grouped code/data.
281
  ],
282
 
283
+ # AUTO: Executes this statement.
284
  "<local_declaration>": [
285
+ # AUTO: Executes this statement.
286
  ["<var_dec>", ";", "<local_declaration>"],
287
+ # AUTO: Executes this statement.
288
  ["<const_dec>", ";", "<local_declaration>"],
289
+ # AUTO: Executes this statement.
290
  [EPSILON],
291
+ # AUTO: Closes the current grouped code/data.
292
  ],
293
 
294
+ # AUTO: Executes this statement.
295
  "<data_type>": [
296
+ # AUTO: Executes this statement.
297
  ["seed"],
298
+ # AUTO: Executes this statement.
299
  ["tree"],
300
+ # AUTO: Executes this statement.
301
  ["leaf"],
302
+ # AUTO: Executes this statement.
303
  ["branch"],
304
+ # AUTO: Executes this statement.
305
  ["vine"],
306
+ # AUTO: Closes the current grouped code/data.
307
  ],
308
 
309
+ # AUTO: Executes this statement.
310
  "<const_dec>": [
311
+ # AUTO: Sets `["fertile", "<data_type>", "id", "`.
312
  ["fertile", "<data_type>", "id", "=", "<init_val>", "<const_next>"],
313
+ # AUTO: Closes the current grouped code/data.
314
  ],
315
 
316
+ # AUTO: Executes this statement.
317
  "<const_next>": [
318
+ # AUTO: Sets `[",", "id", "`.
319
  [",", "id", "=", "<init_val>", "<const_next>"],
320
+ # AUTO: Executes this statement.
321
  [EPSILON],
322
+ # AUTO: Closes the current grouped code/data.
323
  ],
324
 
325
+ # AUTO: Executes this statement.
326
  "<var_dec>": [
327
+ # AUTO: Executes this statement.
328
  ["<data_type>", "id", "<array_dec>", "<var_value>"],
329
+ # AUTO: Executes this statement.
330
  ["bundle", "id", "<bundle_mem_dec>"],
331
+ # AUTO: Closes the current grouped code/data.
332
  ],
333
 
334
+ # AUTO: Executes this statement.
335
  "<bundle_mem_dec>": [
336
+ # AUTO: Executes this statement.
337
  ["id", "<array_dec>"],
338
+ # AUTO: Closes the current grouped code/data.
339
  ],
340
 
341
+ # AUTO: Executes this statement.
342
  "<var_value>": [
343
+ # AUTO: Sets `["`.
344
  ["=", "<init_val>", "<var_value_next>"],
345
+ # AUTO: Executes this statement.
346
  ["<var_value_next>"],
347
+ # AUTO: Closes the current grouped code/data.
348
  ],
349
 
350
+ # AUTO: Executes this statement.
351
  "<var_value_next>": [
352
+ # AUTO: Executes this statement.
353
  [",", "id", "<array_dec>", "<var_value>"],
354
+ # AUTO: Executes this statement.
355
  [EPSILON],
356
+ # AUTO: Closes the current grouped code/data.
357
  ],
358
 
359
+ # AUTO: Executes this statement.
360
  "<init_val>": [
361
+ # AUTO: Executes this statement.
362
  ["<array_init_opt>"],
363
+ # AUTO: Executes this statement.
364
  ["water", "(", "<water_arg>", ")"],
365
+ # AUTO: Executes this statement.
366
  ["<expression>"],
367
+ # AUTO: Closes the current grouped code/data.
368
  ],
369
 
370
+ # AUTO: Executes this statement.
371
  "<array_dec>": [
372
+ # AUTO: Executes this statement.
373
  ["[", "<array_dim_opt>", "]", "<array_dec>"],
374
+ # AUTO: Executes this statement.
375
  [EPSILON],
376
+ # AUTO: Closes the current grouped code/data.
377
  ],
378
 
379
+ # AUTO: Executes this statement.
380
  "<array_dim_opt>": [
381
+ # AUTO: Executes this statement.
382
  ["intlit"],
383
+ # AUTO: Executes this statement.
384
  [EPSILON],
385
+ # AUTO: Closes the current grouped code/data.
386
  ],
387
 
388
+ # AUTO: Executes this statement.
389
  "<array_init_opt>": [
390
+ # AUTO: Executes this statement.
391
  ["{", "<init_vals>", "}"],
392
+ # AUTO: Closes the current grouped code/data.
393
  ],
394
 
395
+ # AUTO: Executes this statement.
396
  "<init_vals>": [
397
+ # AUTO: Executes this statement.
398
  ["<init_val_item>", "<init_vals_next>"],
399
+ # AUTO: Executes this statement.
400
  [EPSILON],
401
+ # AUTO: Closes the current grouped code/data.
402
  ],
403
 
404
+ # AUTO: Executes this statement.
405
  "<init_vals_next>": [
406
+ # AUTO: Executes this statement.
407
  [",", "<init_val_item>", "<init_vals_next>"],
408
+ # AUTO: Executes this statement.
409
  [EPSILON],
410
+ # AUTO: Closes the current grouped code/data.
411
  ],
412
 
413
+ # AUTO: Executes this statement.
414
  "<init_val_item>": [
415
+ # AUTO: Executes this statement.
416
  ["{", "<init_vals>", "}"],
417
+ # AUTO: Executes this statement.
418
  ["<expression>"],
419
+ # AUTO: Closes the current grouped code/data.
420
  ],
421
 
422
+ # AUTO: Executes this statement.
423
  "<bundle_members>": [
424
+ # AUTO: Executes this statement.
425
  ["<data_type>", "id", ";", "<bundle_members>"],
426
+ # AUTO: Executes this statement.
427
  ["id", "id", ";", "<bundle_members>"],
428
+ # AUTO: Executes this statement.
429
  [EPSILON],
430
+ # AUTO: Closes the current grouped code/data.
431
  ],
432
 
433
+ # AUTO: Executes this statement.
434
  "<function_definition>": [
435
+ # AUTO: Executes this statement.
436
  [
437
+ # AUTO: Executes this statement.
438
  "pollinate",
439
+ # AUTO: Executes this statement.
440
  "<return_type>",
441
+ # AUTO: Executes this statement.
442
  "id",
443
+ # AUTO: Executes this statement.
444
  "(",
445
+ # AUTO: Executes this statement.
446
  "<parameters>",
447
+ # AUTO: Executes this statement.
448
  ")",
449
+ # AUTO: Executes this statement.
450
  "{",
451
+ # AUTO: Executes this statement.
452
  "<local_declaration>",
453
+ # AUTO: Executes this statement.
454
  "<body_statement>",
455
+ # AUTO: Executes this statement.
456
  "reclaim",
457
+ # AUTO: Executes this statement.
458
  "<reclaim_value>",
459
+ # AUTO: Executes this statement.
460
  "}",
461
+ # AUTO: Executes this statement.
462
  "<function_definition>",
463
+ # AUTO: Closes the current grouped code/data.
464
  ],
465
+ # AUTO: Executes this statement.
466
  [EPSILON],
467
+ # AUTO: Closes the current grouped code/data.
468
  ],
469
 
470
+ # AUTO: Executes this statement.
471
  "<return_type>": [
472
+ # AUTO: Executes this statement.
473
  ["<data_type>"],
474
+ # AUTO: Executes this statement.
475
  ["empty"],
476
+ # AUTO: Executes this statement.
477
  ["id"],
478
+ # AUTO: Closes the current grouped code/data.
479
  ],
480
 
481
+ # AUTO: Executes this statement.
482
  "<parameters>": [
483
+ # AUTO: Executes this statement.
484
  [EPSILON],
485
+ # AUTO: Executes this statement.
486
  ["<param>", "<param_next>"],
487
+ # AUTO: Closes the current grouped code/data.
488
  ],
489
 
490
+ # AUTO: Executes this statement.
491
  "<param>": [
492
+ # AUTO: Executes this statement.
493
  ["<data_type>", "id", "<param_array>"],
494
+ # AUTO: Executes this statement.
495
  ["id", "id"],
496
+ # AUTO: Closes the current grouped code/data.
497
  ],
498
 
499
+ # AUTO: Executes this statement.
500
  "<param_array>": [
501
+ # AUTO: Executes this statement.
502
  [EPSILON],
503
+ # AUTO: Executes this statement.
504
  ["[", "]"],
505
+ # AUTO: Closes the current grouped code/data.
506
  ],
507
 
508
+ # AUTO: Executes this statement.
509
  "<param_next>": [
510
+ # AUTO: Executes this statement.
511
  [EPSILON],
512
+ # AUTO: Executes this statement.
513
  [",", "<param>", "<param_next>"],
514
+ # AUTO: Closes the current grouped code/data.
515
  ],
516
 
517
+ # AUTO: Executes this statement.
518
  "<reclaim_value>": [
519
+ # AUTO: Executes this statement.
520
  ["<expression>", ";"],
521
+ # AUTO: Executes this statement.
522
  [";"],
523
+ # AUTO: Closes the current grouped code/data.
524
  ],
525
 
526
+ # AUTO: Executes this statement.
527
  "<body_statement>": [
528
+ # AUTO: Executes this statement.
529
  ["<non_reclaim_stmt>", "<body_statement>"],
530
+ # AUTO: Executes this statement.
531
  [EPSILON],
532
+ # AUTO: Closes the current grouped code/data.
533
  ],
534
 
535
+ # AUTO: Executes this statement.
536
  "<non_reclaim_stmt>": [
537
+ # AUTO: Executes this statement.
538
  ["id", "<id_stmt>"],
539
+ # AUTO: Executes this statement.
540
  ["<inc_dec_op>", "id", "<id_next>", ";"],
541
+ # AUTO: Executes this statement.
542
  ["<io_stmt>"],
543
+ # AUTO: Executes this statement.
544
  ["<conditional_stmt>"],
545
+ # AUTO: Executes this statement.
546
  ["<loop_stmt>"],
547
+ # AUTO: Executes this statement.
548
  ["<switch_stmt>"],
549
+ # AUTO: Executes this statement.
550
  ["<control_stmt>"],
551
+ # AUTO: Closes the current grouped code/data.
552
  ],
553
 
554
+ # AUTO: Executes this statement.
555
  "<statement>": [
556
+ # AUTO: Executes this statement.
557
  ["<simple_stmt>", "<statement>"],
558
+ # AUTO: Executes this statement.
559
  [EPSILON],
560
+ # AUTO: Closes the current grouped code/data.
561
  ],
562
 
563
+ # AUTO: Executes this statement.
564
  "<simple_stmt>": [
565
+ # AUTO: Executes this statement.
566
  ["<non_reclaim_stmt>"],
567
+ # AUTO: Executes this statement.
568
  ["reclaim", "<reclaim_value>"],
569
+ # AUTO: Closes the current grouped code/data.
570
  ],
571
 
572
+ # AUTO: Executes this statement.
573
  "<id_stmt>": [
574
+ # AUTO: Executes this statement.
575
  ["<id_next>", "<id_stmt_tail>"],
576
+ # AUTO: Executes this statement.
577
  ["(", "<arguments>", ")", ";"],
578
+ # AUTO: Closes the current grouped code/data.
579
  ],
580
 
581
+ # AUTO: Executes this statement.
582
  "<id_stmt_tail>": [
583
+ # AUTO: Executes this statement.
584
  ["<assign_op>", "<assign_rhs>", ";"],
585
+ # AUTO: Executes this statement.
586
  ["<inc_dec_op>", ";"],
587
+ # AUTO: Closes the current grouped code/data.
588
  ],
589
 
590
+ # AUTO: Executes this statement.
591
  "<assign_rhs>": [
592
+ # AUTO: Executes this statement.
593
  ["water", "(", "<water_arg>", ")"],
594
+ # AUTO: Executes this statement.
595
  ["<expression>"],
596
+ # AUTO: Closes the current grouped code/data.
597
  ],
598
 
599
+ # AUTO: Executes this statement.
600
  "<assign_op>": [
601
+ # AUTO: Sets `["`.
602
  ["="],
603
+ # AUTO: Adds into `["`.
604
  ["+="],
605
+ # AUTO: Subtracts from `["`.
606
  ["-="],
607
+ # AUTO: Multiplies into `["`.
608
  ["*="],
609
+ # AUTO: Divides into `["`.
610
  ["/="],
611
+ # AUTO: Sets `["%`.
612
  ["%="],
613
+ # AUTO: Multiplies into `["*`.
614
  ["**="],
615
+ # AUTO: Closes the current grouped code/data.
616
  ],
617
 
618
+ # AUTO: Executes this statement.
619
  "<id_next>": [
620
+ # AUTO: Executes this statement.
621
  ["<array_access>", "<post_array_access>"],
622
+ # AUTO: Executes this statement.
623
  ["<struct_access>"],
624
+ # AUTO: Executes this statement.
625
  [EPSILON],
626
+ # AUTO: Closes the current grouped code/data.
627
  ],
628
 
629
+ # AUTO: Executes this statement.
630
  "<array_access>": [
631
+ # AUTO: Executes this statement.
632
  ["[", "<expression>", "]", "<array_access_more>"],
633
+ # AUTO: Closes the current grouped code/data.
634
  ],
635
 
636
+ # AUTO: Executes this statement.
637
  "<array_access_more>": [
638
+ # AUTO: Executes this statement.
639
  ["[", "<expression>", "]", "<array_access_more>"],
640
+ # AUTO: Executes this statement.
641
  [EPSILON],
642
+ # AUTO: Closes the current grouped code/data.
643
  ],
644
 
645
+ # AUTO: Executes this statement.
646
  "<struct_access>": [
647
+ # AUTO: Executes this statement.
648
  [".", "id", "<struct_access_more>"],
649
+ # AUTO: Closes the current grouped code/data.
650
  ],
651
 
652
+ # AUTO: Executes this statement.
653
  "<struct_access_more>": [
654
+ # AUTO: Executes this statement.
655
  [".", "id", "<struct_access_more>"],
656
+ # AUTO: Executes this statement.
657
  [EPSILON],
658
+ # AUTO: Closes the current grouped code/data.
659
  ],
660
 
661
+ # AUTO: Executes this statement.
662
  "<post_array_access>": [
663
+ # AUTO: Executes this statement.
664
  [".", "id", "<post_array_access>"],
665
+ # AUTO: Executes this statement.
666
  [EPSILON],
667
+ # AUTO: Closes the current grouped code/data.
668
  ],
669
 
670
+ # AUTO: Executes this statement.
671
  "<io_stmt>": [
672
+ # AUTO: Executes this statement.
673
  ["plant", "(", "<arguments>", ")", ";"],
674
+ # AUTO: Executes this statement.
675
  ["water", "(", "<water_arg>", ")", ";"],
676
+ # AUTO: Closes the current grouped code/data.
677
  ],
678
 
679
+ # AUTO: Executes this statement.
680
  "<water_arg>": [
681
+ # AUTO: Executes this statement.
682
  ["<data_type>"],
683
+ # AUTO: Executes this statement.
684
  ["id", "<water_id_tail>"],
685
+ # AUTO: Executes this statement.
686
  [EPSILON],
687
+ # AUTO: Closes the current grouped code/data.
688
  ],
689
 
690
+ # AUTO: Executes this statement.
691
  "<water_id_tail>": [
692
+ # AUTO: Executes this statement.
693
  ["[", "<expression>", "]", "<water_id_tail>"],
694
+ # AUTO: Executes this statement.
695
  [EPSILON],
696
+ # AUTO: Closes the current grouped code/data.
697
  ],
698
 
699
+ # AUTO: Executes this statement.
700
  "<arguments>": [
701
+ # AUTO: Executes this statement.
702
  ["<expression>", "<arg_next>"],
703
+ # AUTO: Executes this statement.
704
  [EPSILON],
705
+ # AUTO: Closes the current grouped code/data.
706
  ],
707
 
708
+ # AUTO: Executes this statement.
709
  "<arg_next>": [
710
+ # AUTO: Executes this statement.
711
  [",", "<expression>", "<arg_next>"],
712
+ # AUTO: Executes this statement.
713
  [EPSILON],
714
+ # AUTO: Closes the current grouped code/data.
715
  ],
716
 
717
+ # AUTO: Executes this statement.
718
  "<conditional_stmt>": [
719
+ # AUTO: Executes this statement.
720
  [
721
+ # AUTO: Executes this statement.
722
  "spring",
723
+ # AUTO: Executes this statement.
724
  "(",
725
+ # AUTO: Executes this statement.
726
  "<expression>",
727
+ # AUTO: Executes this statement.
728
  ")",
729
+ # AUTO: Executes this statement.
730
  "{",
731
+ # AUTO: Executes this statement.
732
  "<local_declaration>",
733
+ # AUTO: Executes this statement.
734
  "<statement>",
735
+ # AUTO: Executes this statement.
736
  "}",
737
+ # AUTO: Executes this statement.
738
  "<elseif_chain>",
739
+ # AUTO: Executes this statement.
740
  "<else_opt>",
741
+ # AUTO: Closes the current grouped code/data.
742
  ]
743
+ # AUTO: Closes the current grouped code/data.
744
  ],
745
 
746
+ # AUTO: Executes this statement.
747
  "<elseif_chain>": [
748
+ # AUTO: Executes this statement.
749
  [
750
+ # AUTO: Executes this statement.
751
  "bud",
752
+ # AUTO: Executes this statement.
753
  "(",
754
+ # AUTO: Executes this statement.
755
  "<expression>",
756
+ # AUTO: Executes this statement.
757
  ")",
758
+ # AUTO: Executes this statement.
759
  "{",
760
+ # AUTO: Executes this statement.
761
  "<local_declaration>",
762
+ # AUTO: Executes this statement.
763
  "<statement>",
764
+ # AUTO: Executes this statement.
765
  "}",
766
+ # AUTO: Executes this statement.
767
  "<elseif_chain>",
768
+ # AUTO: Closes the current grouped code/data.
769
  ],
770
+ # AUTO: Executes this statement.
771
  [EPSILON],
772
+ # AUTO: Closes the current grouped code/data.
773
  ],
774
 
775
+ # AUTO: Executes this statement.
776
  "<else_opt>": [
777
+ # AUTO: Executes this statement.
778
  ["wither", "{", "<local_declaration>", "<statement>", "}"],
779
+ # AUTO: Executes this statement.
780
  [EPSILON],
781
+ # AUTO: Closes the current grouped code/data.
782
  ],
783
 
784
+ # AUTO: Executes this statement.
785
  "<loop_stmt>": [
786
+ # AUTO: Executes this statement.
787
  ["grow", "(", "<expression>", ")", "{", "<local_declaration>", "<statement>", "}"],
788
 
789
+ # AUTO: Executes this statement.
790
  [
791
+ # AUTO: Executes this statement.
792
  "cultivate",
793
+ # AUTO: Executes this statement.
794
  "(",
795
+ # AUTO: Executes this statement.
796
  "<for_init>",
797
+ # AUTO: Executes this statement.
798
  ";",
799
+ # AUTO: Executes this statement.
800
  "<expression>",
801
+ # AUTO: Executes this statement.
802
  ";",
803
+ # AUTO: Executes this statement.
804
  "<for_update>",
805
+ # AUTO: Executes this statement.
806
  ")",
807
+ # AUTO: Executes this statement.
808
  "{",
809
+ # AUTO: Executes this statement.
810
  "<local_declaration>",
811
+ # AUTO: Executes this statement.
812
  "<statement>",
813
+ # AUTO: Executes this statement.
814
  "}",
815
+ # AUTO: Closes the current grouped code/data.
816
  ],
817
 
818
+ # AUTO: Executes this statement.
819
  ["tend", "{", "<local_declaration>", "<statement>", "}", "grow", "(", "<expression>", ")", ";"],
820
+ # AUTO: Closes the current grouped code/data.
821
  ],
822
 
823
+ # AUTO: Executes this statement.
824
  "<for_init>": [
825
+ # AUTO: Executes this statement.
826
  ["<data_type>", "id", "<array_dec>", "<var_value>"],
827
+ # AUTO: Executes this statement.
828
  ["id", "<id_next>", "<assign_op>", "<expression>"],
829
+ # AUTO: Executes this statement.
830
  [EPSILON],
831
+ # AUTO: Closes the current grouped code/data.
832
  ],
833
 
834
+ # AUTO: Executes this statement.
835
  "<for_update>": [
836
+ # AUTO: Executes this statement.
837
  ["id", "<id_next>", "<for_update_tail>"],
838
+ # AUTO: Executes this statement.
839
  [EPSILON],
840
+ # AUTO: Closes the current grouped code/data.
841
  ],
842
 
843
+ # AUTO: Executes this statement.
844
  "<for_update_tail>": [
845
+ # AUTO: Executes this statement.
846
  ["<inc_dec_op>"],
847
+ # AUTO: Executes this statement.
848
  ["<assign_op>", "<expression>"],
849
+ # AUTO: Closes the current grouped code/data.
850
  ],
851
 
852
+ # AUTO: Executes this statement.
853
  "<inc_dec_op>": [
854
+ # AUTO: Executes this statement.
855
  ["++"],
856
+ # AUTO: Executes this statement.
857
  ["--"],
858
+ # AUTO: Closes the current grouped code/data.
859
  ],
860
 
861
+ # AUTO: Executes this statement.
862
  "<switch_stmt>": [
863
+ # AUTO: Executes this statement.
864
  ["harvest", "(", "<expression>", ")", "{", "<case_list>", "<default_opt>", "}"],
865
+ # AUTO: Closes the current grouped code/data.
866
  ],
867
 
868
+ # AUTO: Executes this statement.
869
  "<case_list>": [
870
+ # AUTO: Executes this statement.
871
  [
872
+ # AUTO: Executes this statement.
873
  "variety",
874
+ # AUTO: Executes this statement.
875
  "<case_literal>",
876
+ # AUTO: Executes this statement.
877
  ":",
878
+ # AUTO: Executes this statement.
879
  "<local_declaration>",
880
+ # AUTO: Executes this statement.
881
  "<case_statements>",
882
+ # AUTO: Executes this statement.
883
  "<case_list>",
884
+ # AUTO: Closes the current grouped code/data.
885
  ],
886
+ # AUTO: Executes this statement.
887
  [EPSILON],
888
+ # AUTO: Closes the current grouped code/data.
889
  ],
890
 
891
+ # AUTO: Executes this statement.
892
  "<case_literal>": [
893
+ # AUTO: Executes this statement.
894
  ["intlit"],
895
+ # AUTO: Executes this statement.
896
  ["chrlit"],
897
+ # AUTO: Executes this statement.
898
  ["sunshine"],
899
+ # AUTO: Executes this statement.
900
  ["frost"],
901
+ # AUTO: Closes the current grouped code/data.
902
  ],
903
 
904
+ # AUTO: Executes this statement.
905
  "<case_statements>": [
906
+ # AUTO: Executes this statement.
907
  ["<case_statement>", "<case_statements>"],
908
+ # AUTO: Executes this statement.
909
  [EPSILON],
910
+ # AUTO: Closes the current grouped code/data.
911
  ],
912
 
913
+ # AUTO: Executes this statement.
914
  "<case_statement>": [
915
+ # AUTO: Executes this statement.
916
  ["id", "<id_stmt>"],
917
+ # AUTO: Executes this statement.
918
  ["<inc_dec_op>", "id", "<id_next>", ";"],
919
+ # AUTO: Executes this statement.
920
  ["<io_stmt>"],
921
+ # AUTO: Executes this statement.
922
  ["<conditional_stmt>"],
923
+ # AUTO: Executes this statement.
924
  ["<loop_stmt>"],
925
+ # AUTO: Executes this statement.
926
  ["<switch_stmt>"],
927
+ # AUTO: Executes this statement.
928
  ["{", "<local_declaration>", "<statement>", "}"],
929
+ # AUTO: Executes this statement.
930
  ["prune", ";"],
931
+ # AUTO: Executes this statement.
932
  ["skip", ";"],
933
+ # AUTO: Executes this statement.
934
  ["reclaim", "<reclaim_value>"],
935
+ # AUTO: Closes the current grouped code/data.
936
  ],
937
 
938
+ # AUTO: Executes this statement.
939
  "<default_opt>": [
940
+ # AUTO: Executes this statement.
941
  ["soil", ":", "<local_declaration>", "<case_statements>"],
942
+ # AUTO: Executes this statement.
943
  [EPSILON],
944
+ # AUTO: Closes the current grouped code/data.
945
  ],
946
 
947
+ # AUTO: Executes this statement.
948
  "<control_stmt>": [
949
+ # AUTO: Executes this statement.
950
  ["prune", ";"],
951
+ # AUTO: Executes this statement.
952
  ["skip", ";"],
953
+ # AUTO: Closes the current grouped code/data.
954
  ],
955
 
956
 
957
+ # AUTO: Executes this statement.
958
  "<expression>": [
959
+ # AUTO: Executes this statement.
960
  ["<assignment_expression>"],
961
+ # AUTO: Closes the current grouped code/data.
962
  ],
963
 
964
+ # AUTO: Executes this statement.
965
  "<assignment_expression>": [
966
+ # AUTO: Executes this statement.
967
  ["<logic_or>", "<assignment_expression_next>"],
968
+ # AUTO: Closes the current grouped code/data.
969
  ],
970
 
971
+ # AUTO: Executes this statement.
972
  "<assignment_expression_next>": [
973
+ # AUTO: Executes this statement.
974
  ["<assign_op>", "<assignment_expression>"],
975
+ # AUTO: Executes this statement.
976
  [EPSILON],
977
+ # AUTO: Closes the current grouped code/data.
978
  ],
979
 
980
+ # AUTO: Executes this statement.
981
  "<logic_or>": [
982
+ # AUTO: Executes this statement.
983
  ["<logic_and>", "<logic_or_next>"],
984
+ # AUTO: Closes the current grouped code/data.
985
  ],
986
 
987
+ # AUTO: Executes this statement.
988
  "<logic_or_next>": [
989
+ # AUTO: Executes this statement.
990
  ["||", "<logic_and>", "<logic_or_next>"],
991
+ # AUTO: Executes this statement.
992
  [EPSILON],
993
+ # AUTO: Closes the current grouped code/data.
994
  ],
995
 
996
+ # AUTO: Executes this statement.
997
  "<logic_and>": [
998
+ # AUTO: Executes this statement.
999
  ["<relational>", "<logic_and_next>"],
1000
+ # AUTO: Closes the current grouped code/data.
1001
  ],
1002
 
1003
+ # AUTO: Executes this statement.
1004
  "<logic_and_next>": [
1005
+ # AUTO: Executes this statement.
1006
  ["&&", "<relational>", "<logic_and_next>"],
1007
+ # AUTO: Executes this statement.
1008
  [EPSILON],
1009
+ # AUTO: Closes the current grouped code/data.
1010
  ],
1011
 
1012
+ # AUTO: Executes this statement.
1013
  "<relational>": [
1014
+ # AUTO: Executes this statement.
1015
  ["<arithmetic>", "<relational_next>"],
1016
+ # AUTO: Closes the current grouped code/data.
1017
  ],
1018
 
1019
+ # AUTO: Executes this statement.
1020
  "<relational_next>": [
1021
+ # AUTO: Executes this statement.
1022
  ["<relational_op>", "<arithmetic>"],
1023
+ # AUTO: Executes this statement.
1024
  [EPSILON],
1025
+ # AUTO: Closes the current grouped code/data.
1026
  ],
1027
 
1028
+ # AUTO: Executes this statement.
1029
  "<relational_op>": [
1030
+ # AUTO: Executes this statement.
1031
  [">"],
1032
+ # AUTO: Executes this statement.
1033
  ["<"],
1034
+ # AUTO: Executes this statement.
1035
  [">="],
1036
+ # AUTO: Executes this statement.
1037
  ["<="],
1038
+ # AUTO: Executes this statement.
1039
  ["=="],
1040
+ # AUTO: Executes this statement.
1041
  ["!="],
1042
+ # AUTO: Closes the current grouped code/data.
1043
  ],
1044
 
1045
+ # AUTO: Executes this statement.
1046
  "<arithmetic>": [
1047
+ # AUTO: Executes this statement.
1048
  ["<term>", "<arithmetic_next>"],
1049
+ # AUTO: Closes the current grouped code/data.
1050
  ],
1051
 
1052
+ # AUTO: Executes this statement.
1053
  "<arithmetic_next>": [
1054
+ # AUTO: Executes this statement.
1055
  ["+", "<term>", "<arithmetic_next>"],
1056
+ # AUTO: Executes this statement.
1057
  ["-", "<term>", "<arithmetic_next>"],
1058
+ # AUTO: Executes this statement.
1059
  ["`", "<term>", "<arithmetic_next>"],
1060
+ # AUTO: Executes this statement.
1061
  [EPSILON],
1062
+ # AUTO: Closes the current grouped code/data.
1063
  ],
1064
 
1065
 
1066
+ # AUTO: Executes this statement.
1067
  "<term>": [
1068
+ # AUTO: Executes this statement.
1069
  ["<power>", "<term_next>"],
1070
+ # AUTO: Closes the current grouped code/data.
1071
  ],
1072
 
1073
+ # AUTO: Executes this statement.
1074
  "<term_next>": [
1075
+ # AUTO: Executes this statement.
1076
  ["*", "<power>", "<term_next>"],
1077
+ # AUTO: Executes this statement.
1078
  ["/", "<power>", "<term_next>"],
1079
+ # AUTO: Executes this statement.
1080
  ["%", "<power>", "<term_next>"],
1081
+ # AUTO: Executes this statement.
1082
  [EPSILON],
1083
+ # AUTO: Closes the current grouped code/data.
1084
  ],
1085
 
1086
+ # AUTO: Executes this statement.
1087
  "<power>": [
1088
+ # AUTO: Executes this statement.
1089
  ["<factor>", "<power_next>"],
1090
+ # AUTO: Closes the current grouped code/data.
1091
  ],
1092
 
1093
+ # AUTO: Executes this statement.
1094
  "<power_next>": [
1095
+ # AUTO: Executes this statement.
1096
  ["**", "<power>"],
1097
+ # AUTO: Executes this statement.
1098
  [EPSILON],
1099
+ # AUTO: Closes the current grouped code/data.
1100
  ],
1101
 
1102
+ # AUTO: Executes this statement.
1103
  "<factor>": [
1104
+ # AUTO: Executes this statement.
1105
  ["(", "<paren_expr>"],
1106
+ # AUTO: Executes this statement.
1107
  ["<unary_op>", "<factor>"],
1108
+ # AUTO: Executes this statement.
1109
  ["id", "<factor_id_next>"],
1110
+ # AUTO: Executes this statement.
1111
  ["<literal>"],
1112
+ # AUTO: Closes the current grouped code/data.
1113
  ],
1114
 
1115
+ # AUTO: Executes this statement.
1116
  "<literal>": [
1117
+ # AUTO: Executes this statement.
1118
  ["intlit"],
1119
+ # AUTO: Executes this statement.
1120
  ["dblit"],
1121
+ # AUTO: Executes this statement.
1122
  ["chrlit"],
1123
+ # AUTO: Executes this statement.
1124
  ["stringlit"],
1125
+ # AUTO: Executes this statement.
1126
  ["sunshine"],
1127
+ # AUTO: Executes this statement.
1128
  ["frost"],
1129
+ # AUTO: Closes the current grouped code/data.
1130
  ],
1131
 
1132
+ # AUTO: Executes this statement.
1133
  "<paren_expr>": [
1134
+ # AUTO: Executes this statement.
1135
  ["<data_type>", ")", "<factor>"],
1136
+ # AUTO: Executes this statement.
1137
  ["<expression>", ")"],
1138
+ # AUTO: Closes the current grouped code/data.
1139
  ],
1140
 
1141
+ # AUTO: Executes this statement.
1142
  "<unary_op>": [
1143
+ # AUTO: Executes this statement.
1144
  ["~"],
1145
+ # AUTO: Executes this statement.
1146
  ["!"],
1147
+ # AUTO: Closes the current grouped code/data.
1148
  ],
1149
 
1150
+ # AUTO: Executes this statement.
1151
  "<factor_id_next>": [
1152
+ # AUTO: Executes this statement.
1153
  ["<array_access>", "<post_array_access>"],
1154
+ # AUTO: Executes this statement.
1155
  ["<struct_access>"],
1156
+ # AUTO: Executes this statement.
1157
  ["(", "<arguments>", ")"],
1158
+ # AUTO: Executes this statement.
1159
  [EPSILON],
1160
+ # AUTO: Closes the current grouped code/data.
1161
  ],
1162
+ # AUTO: Closes the current grouped code/data.
1163
  }
1164
 
1165
 
1166
+ # GUIDE: Build the LL(1) helper sets once at import time.
1167
+ # AUTO: Sets `first_sets`.
1168
  first_sets = compute_first(cfg)
1169
+ # AUTO: Sets `follow_sets`.
1170
  follow_sets = compute_follow(cfg, first_sets)
1171
+ # AUTO: Sets `predict_sets`.
1172
  predict_sets = compute_predict(cfg, first_sets, follow_sets)
1173
 
1174
 
1175
+ # AUTO: Checks this condition.
1176
  if __name__ == '__main__':
1177
+ # AUTO: Sets `print("`.
1178
  print("=" * 80)
1179
+ # AUTO: Calls `print`.
1180
  print("FIRST SETS")
1181
+ # AUTO: Sets `print("`.
1182
  print("=" * 80)
1183
+ # AUTO: Calls `print`.
1184
  print("Shows which terminals can appear first in each non-terminal")
1185
+ # AUTO: Calls `print`.
1186
  print()
1187
+ # AUTO: Starts a loop over these values.
1188
  for nt in first_sets:
1189
+ # AUTO: Sets `print(f"First({nt})`.
1190
  print(f"First({nt}) = {{ {', '.join(sorted(first_sets[nt]))} }}")
1191
 
1192
+ # AUTO: Sets `print("\n" + "`.
1193
  print("\n" + "=" * 80)
1194
+ # AUTO: Calls `print`.
1195
  print("FOLLOW SETS")
1196
+ # AUTO: Sets `print("`.
1197
  print("=" * 80)
1198
+ # AUTO: Calls `print`.
1199
  print("Shows which terminals can appear after each non-terminal")
1200
+ # AUTO: Calls `print`.
1201
  print()
1202
+ # AUTO: Starts a loop over these values.
1203
  for nt in follow_sets:
1204
+ # AUTO: Sets `print(f"Follow({nt})`.
1205
  print(f"Follow({nt}) = {{ {', '.join(sorted(follow_sets[nt]))} }}")
1206
 
1207
+ # AUTO: Sets `print("\n" + "`.
1208
  print("\n" + "=" * 80)
1209
+ # AUTO: Calls `print`.
1210
  print("PREDICT SETS")
1211
+ # AUTO: Sets `print("`.
1212
  print("=" * 80)
1213
+ # AUTO: Calls `print`.
1214
  print("Shows which terminal triggers each production rule")
1215
+ # AUTO: Calls `print`.
1216
  print()
1217
+ # AUTO: Starts a loop over these values.
1218
  for (lhs, prod), pset in predict_sets.items():
1219
+ # AUTO: Sets `prod_str`.
1220
  prod_str = " ".join(prod)
1221
+ # AUTO: Sets `print(f"Predict({lhs} -> {prod_str})`.
1222
  print(f"Predict({lhs} -> {prod_str}) = {{ {', '.join(sorted(pset))} }}")
1223
 
1224
+ # AUTO: Sets `print("\n" + "`.
1225
  print("\n" + "=" * 80)
Backend/icg/__init__.py CHANGED
@@ -1,2 +1,3 @@
1
 
 
2
  from .generator import generate_icg, ICGenerator, TACInstruction # noqa: F401
 
1
 
2
+ # AUTO: Imports names from another module.
3
  from .generator import generate_icg, ICGenerator, TACInstruction # noqa: F401
Backend/icg/generator.py CHANGED
@@ -1,989 +1,1822 @@
 
1
  from __future__ import annotations
 
2
  from dataclasses import dataclass
 
3
  from typing import Any, Dict, List, Optional, Tuple, Union
4
 
5
 
 
6
  @dataclass
 
7
  class _Tok:
 
8
  type: str
 
9
  value: str
 
10
  line: int
 
11
  col: int = 0
12
 
13
 
 
14
  def _as_tok(raw: Any) -> _Tok:
 
15
  if isinstance(raw, dict):
 
16
  return _Tok(
 
17
  type=str(raw.get("type", "")),
 
18
  value=str(raw.get("value", "")),
 
19
  line=int(raw.get("line", 0) or 0),
 
20
  col=int(raw.get("col", 0) or 0),
 
21
  )
 
22
  return _Tok(
 
23
  type=str(getattr(raw, "type", "")),
 
24
  value=str(getattr(raw, "value", "")),
 
25
  line=int(getattr(raw, "line", 0) or 0),
 
26
  col=int(getattr(raw, "col", 0) or 0),
 
27
  )
28
 
29
 
 
30
  @dataclass
 
31
  class TACInstruction:
 
32
  op: str
 
33
  arg1: Optional[str] = None
 
34
  arg2: Optional[str] = None
 
35
  result: Optional[str] = None
36
 
 
37
  def __str__(self) -> str:
 
38
  if self.op == "LABEL":
 
39
  return f"{self.result}:"
 
40
  if self.op == "GOTO":
 
41
  return f"goto {self.result}"
 
42
  if self.op == "IF":
 
43
  return f"if {self.arg1} goto {self.result}"
 
44
  if self.op == "IFFALSE":
 
45
  return f"ifFalse {self.arg1} goto {self.result}"
 
46
  if self.op == "PARAM":
 
47
  return f"param {self.arg1}"
 
48
  if self.op == "CALL":
 
49
  return f"{self.result} = call {self.arg1}, {self.arg2}"
 
50
  if self.op == "RETURN":
 
51
  return f"return {self.arg1 or ''}"
 
52
  if self.op == "PRINT":
 
53
  return f"print {self.arg1}"
 
54
  if self.op == "READ":
 
55
  return f"read {self.result}"
 
56
  if self.op == "FUNC":
 
57
  return f"func {self.arg1}:"
 
58
  if self.op == "ENDFUNC":
 
59
  return f"endfunc"
 
60
  if self.op == "DECLARE":
 
61
  return f"declare {self.result} : {self.arg1}"
 
62
  if self.op == "ARRAY_DECLARE":
 
63
  return f"declare {self.result}[{self.arg2}] : {self.arg1}"
 
64
  if self.op == "ARRAY_STORE":
 
65
  return f"{self.result}[{self.arg2}] = {self.arg1}"
 
66
  if self.op == "ARRAY_LOAD":
 
67
  return f"{self.result} = {self.arg1}[{self.arg2}]"
 
68
  if self.op == "STRUCT_STORE":
 
69
  return f"{self.arg1}.{self.arg2} = {self.result}"
 
70
  if self.op == "STRUCT_LOAD":
 
71
  return f"{self.result} = {self.arg1}.{self.arg2}"
 
72
  if self.op == "CONST":
 
73
  return f"const {self.result} : {self.arg1} = {self.arg2}"
 
74
  if self.op == "INC":
 
75
  return f"{self.arg1} = {self.arg1} + 1"
 
76
  if self.op == "DEC":
 
77
  return f"{self.arg1} = {self.arg1} - 1"
 
78
  if self.arg2 is not None:
 
79
  return f"{self.result} = {self.arg1} {self.op} {self.arg2}"
 
80
  if self.op == "=":
 
81
  return f"{self.result} = {self.arg1}"
 
82
  if self.op == "UNARY_MINUS":
 
83
  return f"{self.result} = -{self.arg1}"
 
84
  if self.op == "NOT":
 
85
  return f"{self.result} = !{self.arg1}"
 
86
  return f"{self.op} {self.arg1 or ''} {self.arg2 or ''} {self.result or ''}".strip()
87
 
 
88
  def to_dict(self) -> dict:
 
89
  return {
 
90
  "op": self.op,
 
91
  "arg1": self.arg1,
 
92
  "arg2": self.arg2,
 
93
  "result": self.result,
 
94
  "text": str(self),
 
95
  }
96
 
97
 
 
98
  GAL_TYPE_MAP = {
 
99
  "seed": "int",
 
100
  "tree": "float",
 
101
  "leaf": "char",
 
102
  "branch": "bool",
 
103
  "vine": "string",
 
104
  "empty": "void",
 
105
  }
106
 
 
107
  DATA_TYPE_TOKENS = set(GAL_TYPE_MAP.keys())
108
 
 
109
  ASSIGN_OPS = {"=", "+=", "-=", "*=", "/=", "%="}
110
 
111
 
 
112
  class ICGenerator:
113
 
 
114
  def __init__(self, tokens: List[Any]):
 
115
  self.tokens: List[_Tok] = self._prepare(tokens)
 
116
  self.pos: int = 0
 
117
  self.code: List[TACInstruction] = []
 
118
  self.errors: List[str] = []
 
119
  self._temp_counter: int = 0
 
120
  self._label_counter: int = 0
121
 
122
 
 
123
  def _prepare(self, raw_tokens: List[Any]) -> List[_Tok]:
 
124
  toks: List[_Tok] = []
 
125
  for t in raw_tokens:
 
126
  tv = _as_tok(t)
 
127
  if tv.type in ("\n", "comment", "mcommentlit"):
 
128
  continue
 
129
  toks.append(tv)
 
130
  if not toks or toks[-1].type != "EOF":
 
131
  last_line = toks[-1].line if toks else 1
 
132
  toks.append(_Tok("EOF", "EOF", last_line))
 
133
  return toks
134
 
 
135
  def _peek(self) -> _Tok:
 
136
  if self.pos < len(self.tokens):
 
137
  return self.tokens[self.pos]
 
138
  return _Tok("EOF", "EOF", 0)
139
 
 
140
  def _advance(self) -> _Tok:
 
141
  tok = self._peek()
 
142
  if self.pos < len(self.tokens):
 
143
  self.pos += 1
 
144
  return tok
145
 
 
146
  def _expect(self, token_type: str) -> _Tok:
 
147
  tok = self._peek()
 
148
  if tok.type != token_type:
 
149
  self.errors.append(
 
150
  f"ICG Line {tok.line}: expected '{token_type}', got '{tok.type}'"
 
151
  )
 
152
  return tok
 
153
  return self._advance()
154
 
 
155
  def _match(self, token_type: str) -> bool:
 
156
  if self._peek().type == token_type:
 
157
  self._advance()
 
158
  return True
 
159
  return False
160
 
 
161
  def _new_temp(self) -> str:
 
162
  name = f"t{self._temp_counter}"
 
163
  self._temp_counter += 1
 
164
  return name
165
 
 
166
  def _new_label(self) -> str:
 
167
  name = f"L{self._label_counter}"
 
168
  self._label_counter += 1
 
169
  return name
170
 
 
171
  def _emit(self, op: str, arg1: Optional[str] = None,
 
172
  arg2: Optional[str] = None, result: Optional[str] = None):
 
173
  self.code.append(TACInstruction(op, arg1, arg2, result))
174
 
 
175
  def _is_data_type(self, tok: _Tok) -> bool:
 
176
  return tok.type in DATA_TYPE_TOKENS
177
 
178
 
 
179
  def generate(self) -> Tuple[List[TACInstruction], List[str]]:
 
180
  try:
 
181
  self._program()
 
182
  except Exception as exc:
 
183
  self.errors.append(f"ICG internal error: {exc}")
 
184
  return self.code, self.errors
185
 
 
186
  def _program(self):
 
187
  self._global_declaration()
 
188
  self._function_definition()
189
 
 
190
  self._expect("root")
 
191
  self._expect("(")
 
192
  self._expect(")")
 
193
  self._expect("{")
 
194
  self._emit("FUNC", "root")
195
 
 
196
  self._declaration()
 
197
  self._statement()
198
 
 
199
  self._expect("reclaim")
 
200
  self._expect(";")
 
201
  self._emit("RETURN")
202
 
 
203
  self._expect("}")
 
204
  self._emit("ENDFUNC")
205
 
206
 
 
207
  def _global_declaration(self):
 
208
  while True:
 
209
  tok = self._peek()
 
210
  if tok.type == "bundle":
 
211
  self._advance()
 
212
  name_tok = self._expect("id")
 
213
  nxt = self._peek()
 
214
  if nxt.type == "{":
 
215
  self._advance()
 
216
  self._bundle_members()
 
217
  self._expect("}")
 
218
  self._expect(";")
 
219
  else:
 
220
  self._bundle_mem_dec()
 
221
  self._expect(";")
 
222
  self._global_declaration()
 
223
  return
224
 
 
225
  elif self._is_data_type(tok):
 
226
  dtype = self._advance()
 
227
  id_tok = self._expect("id")
 
228
  arr_dims = self._array_dec()
 
229
  if arr_dims:
 
230
  for dim in arr_dims:
 
231
  self._emit("ARRAY_DECLARE", GAL_TYPE_MAP.get(dtype.type, dtype.type),
 
232
  dim, id_tok.value)
 
233
  else:
 
234
  self._emit("DECLARE", GAL_TYPE_MAP.get(dtype.type, dtype.type),
 
235
  None, id_tok.value)
 
236
  self._var_value(id_tok.value)
 
237
  self._expect(";")
 
238
  self._global_declaration()
 
239
  return
240
 
 
241
  elif tok.type == "fertile":
 
242
  self._const_dec()
 
243
  self._expect(";")
 
244
  self._global_declaration()
 
245
  return
246
 
 
247
  else:
 
248
  return
249
 
250
 
 
251
  def _declaration(self):
 
252
  while True:
 
253
  tok = self._peek()
 
254
  if self._is_data_type(tok) or tok.type == "bundle":
 
255
  self._var_dec()
 
256
  self._expect(";")
 
257
  elif tok.type == "fertile":
 
258
  self._const_dec()
 
259
  self._expect(";")
 
260
  else:
 
261
  break
262
 
 
263
  def _var_dec(self):
 
264
  tok = self._peek()
 
265
  if tok.type == "bundle":
 
266
  self._advance()
 
267
  id_tok = self._expect("id")
 
268
  self._bundle_mem_dec()
 
269
  return
270
 
 
271
  dtype = self._advance()
 
272
  id_tok = self._expect("id")
 
273
  arr_dims = self._array_dec()
 
274
  if arr_dims:
 
275
  for dim in arr_dims:
 
276
  self._emit("ARRAY_DECLARE", GAL_TYPE_MAP.get(dtype.type, dtype.type),
 
277
  dim, id_tok.value)
 
278
  else:
 
279
  self._emit("DECLARE", GAL_TYPE_MAP.get(dtype.type, dtype.type), None, id_tok.value)
 
280
  self._var_value(id_tok.value)
281
 
 
282
  def _const_dec(self):
 
283
  self._expect("fertile")
 
284
  dtype = self._advance()
 
285
  id_tok = self._expect("id")
 
286
  self._expect("=")
 
287
  val = self._init_val()
 
288
  self._emit("CONST", GAL_TYPE_MAP.get(dtype.type, dtype.type), val, id_tok.value)
 
289
  self._const_next(dtype)
290
 
 
291
  def _const_next(self, dtype_tok: _Tok):
 
292
  while self._match(","):
 
293
  id_tok = self._expect("id")
 
294
  self._expect("=")
 
295
  val = self._init_val()
 
296
  self._emit("CONST", GAL_TYPE_MAP.get(dtype_tok.type, dtype_tok.type), val, id_tok.value)
297
 
 
298
  def _var_value(self, var_name: str):
 
299
  if self._peek().type == "=":
 
300
  self._advance()
 
301
  val = self._init_val()
 
302
  self._emit("=", val, None, var_name)
 
303
  self._var_value_next()
 
304
  else:
 
305
  self._var_value_next()
306
 
 
307
  def _var_value_next(self):
 
308
  if self._match(","):
 
309
  id_tok = self._expect("id")
 
310
  arr_dims = self._array_dec()
 
311
  if arr_dims:
 
312
  for dim in arr_dims:
 
313
  self._emit("ARRAY_DECLARE", "int", dim, id_tok.value)
 
314
  else:
 
315
  self._emit("DECLARE", "int", None, id_tok.value)
 
316
  self._var_value(id_tok.value)
317
 
 
318
  def _init_val(self) -> str:
 
319
  if self._peek().type == "{":
 
320
  return self._array_init()
 
321
  return self._expression()
322
 
 
323
  def _array_init(self) -> str:
 
324
  self._expect("{")
 
325
  tmp = self._new_temp()
 
326
  idx = 0
 
327
  if self._peek().type != "}":
 
328
  val = self._init_val_item()
 
329
  self._emit("ARRAY_STORE", val, str(idx), tmp)
 
330
  idx += 1
 
331
  while self._match(","):
 
332
  val = self._init_val_item()
 
333
  self._emit("ARRAY_STORE", val, str(idx), tmp)
 
334
  idx += 1
 
335
  self._expect("}")
 
336
  return tmp
337
 
 
338
  def _init_val_item(self) -> str:
 
339
  if self._peek().type == "{":
 
340
  return self._array_init()
 
341
  return self._expression()
342
 
343
 
 
344
  def _array_dec(self) -> List[str]:
 
345
  dims: List[str] = []
 
346
  while self._peek().type == "[":
 
347
  self._advance()
 
348
  if self._peek().type == "intlit":
 
349
  dims.append(self._advance().value)
 
350
  else:
 
351
  dims.append("0")
 
352
  self._expect("]")
 
353
  return dims
354
 
 
355
  def _bundle_members(self):
 
356
  while self._is_data_type(self._peek()):
 
357
  dtype = self._advance()
 
358
  id_tok = self._expect("id")
 
359
  self._expect(";")
360
 
 
361
  def _bundle_mem_dec(self):
 
362
  id_tok = self._expect("id")
 
363
  arr_dims = self._array_dec()
 
364
  if arr_dims:
 
365
  for dim in arr_dims:
 
366
  self._emit("ARRAY_DECLARE", "bundle", dim, id_tok.value)
 
367
  else:
 
368
  self._emit("DECLARE", "bundle", None, id_tok.value)
369
 
370
 
 
371
  def _function_definition(self):
 
372
  while self._peek().type == "pollinate":
 
373
  self._advance()
374
 
 
375
  if self._peek().type == "empty":
 
376
  ret_type = self._advance().type
 
377
  elif self._is_data_type(self._peek()):
 
378
  ret_type = self._advance().type
 
379
  elif self._peek().type == "id":
 
380
  ret_type = self._advance().value
 
381
  else:
 
382
  ret_type = "void"
383
 
 
384
  func_name = self._expect("id")
 
385
  self._expect("(")
386
 
 
387
  params = self._parameters()
388
 
 
389
  self._expect(")")
 
390
  self._expect("{")
391
 
 
392
  self._emit("FUNC", func_name.value)
 
393
  for ptype, pname, *rest in params:
 
394
  is_array = rest[0] if rest else False
 
395
  if is_array:
 
396
  self._emit("ARRAY_DECLARE", GAL_TYPE_MAP.get(ptype, ptype), "param", pname)
 
397
  else:
 
398
  self._emit("DECLARE", GAL_TYPE_MAP.get(ptype, ptype), None, pname)
399
 
 
400
  self._declaration()
 
401
  self._statement()
402
 
 
403
  self._expect("reclaim")
 
404
  if self._peek().type == ";":
 
405
  self._emit("RETURN")
 
406
  else:
 
407
  val = self._expression()
 
408
  self._emit("RETURN", val)
 
409
  self._expect(";")
410
 
 
411
  self._expect("}")
 
412
  self._emit("ENDFUNC")
413
 
 
414
  def _parameters(self):
 
415
  params = []
 
416
  if self._is_data_type(self._peek()) or self._peek().type == "id":
 
417
  p = self._param()
 
418
  params.append(p)
 
419
  while self._match(","):
 
420
  p = self._param()
 
421
  params.append(p)
 
422
  return params
423
 
 
424
  def _param(self) -> Tuple[str, str]:
 
425
  dtype = self._advance()
 
426
  id_tok = self._expect("id")
 
427
  type_name = dtype.value if dtype.type == "id" else dtype.type
 
428
  is_array = False
 
429
  if self._peek().type == "[":
 
430
  self._advance()
 
431
  self._expect("]")
 
432
  is_array = True
 
433
  return (type_name, id_tok.value, is_array)
434
 
435
 
 
436
  def _statement(self, allow_reclaim: bool = False):
 
437
  stopping_tokens = {"}", "EOF", "variety", "soil", "prune"}
 
438
  while self._peek().type not in stopping_tokens:
 
439
  tok = self._peek()
 
440
  if tok.type == "reclaim":
 
441
  if not allow_reclaim:
 
442
  return
 
443
  self._return_stmt()
 
444
  continue
 
445
  if self._is_data_type(tok) or tok.type == "bundle" or tok.type == "fertile":
 
446
  if tok.type == "fertile":
 
447
  self._const_dec()
 
448
  self._expect(";")
 
449
  else:
 
450
  self._var_dec()
 
451
  self._expect(";")
 
452
  continue
 
453
  self._simple_stmt()
454
 
 
455
  def _return_stmt(self):
 
456
  self._expect("reclaim")
 
457
  if self._peek().type == ";":
 
458
  self._emit("RETURN")
 
459
  else:
 
460
  self._emit("RETURN", self._expression())
 
461
  self._expect(";")
462
 
 
463
  def _simple_stmt(self):
 
464
  tok = self._peek()
465
 
 
466
  if tok.type == "id":
 
467
  self._id_stmt()
 
468
  elif tok.type in ("water", "plant"):
 
469
  self._io_stmt()
 
470
  elif tok.type == "spring":
 
471
  self._conditional_stmt()
 
472
  elif tok.type in ("grow", "cultivate", "tend"):
 
473
  self._loop_stmt()
 
474
  elif tok.type == "harvest":
 
475
  self._switch_stmt()
 
476
  elif tok.type in ("prune", "skip"):
 
477
  self._control_stmt()
 
478
  else:
 
479
  self.errors.append(f"ICG Line {tok.line}: unexpected token '{tok.type}'")
 
480
  self._advance()
481
 
482
 
 
483
  def _id_stmt(self):
 
484
  id_tok = self._advance()
 
485
  tok = self._peek()
486
 
 
487
  if tok.type == "(":
 
488
  self._advance()
 
489
  args = self._arguments()
 
490
  self._expect(")")
 
491
  self._expect(";")
 
492
  for a in args:
 
493
  self._emit("PARAM", a)
 
494
  tmp = self._new_temp()
 
495
  self._emit("CALL", id_tok.value, str(len(args)), tmp)
 
496
  return
497
 
 
498
  if tok.type in ("++", "--"):
 
499
  op_tok = self._advance()
 
500
  self._expect(";")
 
501
  self._emit("INC" if op_tok.type == "++" else "DEC", id_tok.value)
 
502
  return
503
 
 
504
  lhs = id_tok.value
 
505
  lhs = self._resolve_lhs(lhs)
506
 
 
507
  if self._peek().type in ASSIGN_OPS:
 
508
  op_tok = self._advance()
 
509
  rhs = self._expression()
 
510
  self._expect(";")
511
 
 
512
  if op_tok.type == "=":
 
513
  self._emit("=", rhs, None, lhs)
 
514
  else:
 
515
  base_op = op_tok.type[0]
 
516
  tmp = self._new_temp()
 
517
  self._emit(base_op, lhs, rhs, tmp)
 
518
  self._emit("=", tmp, None, lhs)
 
519
  else:
 
520
  self.errors.append(f"ICG Line {tok.line}: unexpected token after id '{id_tok.value}'")
 
521
  while self._peek().type not in (";", "}", "EOF"):
 
522
  self._advance()
 
523
  self._match(";")
524
 
 
525
  def _resolve_lhs(self, base: str) -> str:
 
526
  tok = self._peek()
 
527
  if tok.type == "[":
 
528
  self._advance()
 
529
  idx = self._expression()
 
530
  self._expect("]")
 
531
  while self._peek().type == "[":
 
532
  self._advance()
 
533
  idx2 = self._expression()
 
534
  self._expect("]")
 
535
  tmp = self._new_temp()
 
536
  self._emit("*", idx, idx2, tmp)
 
537
  idx = tmp
 
538
  return f"{base}[{idx}]"
 
539
  elif tok.type == ".":
 
540
  self._advance()
 
541
  member = self._expect("id")
 
542
  chain = f"{base}.{member.value}"
 
543
  while self._peek().type == ".":
 
544
  self._advance()
 
545
  m2 = self._expect("id")
 
546
  chain = f"{chain}.{m2.value}"
 
547
  return chain
 
548
  return base
549
 
550
 
 
551
  def _io_stmt(self):
 
552
  io_tok = self._advance()
 
553
  self._expect("(")
 
554
  args = self._arguments()
 
555
  self._expect(")")
 
556
  self._expect(";")
557
 
 
558
  if io_tok.type == "plant":
 
559
  for a in args:
 
560
  self._emit("PRINT", a)
 
561
  else:
 
562
  for a in args:
 
563
  self._emit("READ", None, None, a)
564
 
565
 
 
566
  def _conditional_stmt(self):
 
567
  self._expect("spring")
 
568
  self._expect("(")
 
569
  cond = self._expression()
 
570
  self._expect(")")
571
 
 
572
  false_label = self._new_label()
 
573
  end_label = self._new_label()
574
 
 
575
  self._emit("IFFALSE", cond, None, false_label)
576
 
 
577
  self._expect("{")
 
578
  self._statement(allow_reclaim=True)
 
579
  self._expect("}")
580
 
 
581
  self._emit("GOTO", None, None, end_label)
 
582
  self._emit("LABEL", None, None, false_label)
583
 
 
584
  self._elseif_chain(end_label)
585
 
 
586
  if self._peek().type == "wither":
 
587
  self._advance()
 
588
  self._expect("{")
 
589
  self._statement(allow_reclaim=True)
 
590
  self._expect("}")
591
 
 
592
  self._emit("LABEL", None, None, end_label)
593
 
 
594
  def _elseif_chain(self, end_label: str):
 
595
  while self._peek().type == "bud":
 
596
  self._advance()
 
597
  self._expect("(")
 
598
  cond = self._expression()
 
599
  self._expect(")")
600
 
 
601
  next_label = self._new_label()
 
602
  self._emit("IFFALSE", cond, None, next_label)
603
 
 
604
  self._expect("{")
 
605
  self._statement(allow_reclaim=True)
 
606
  self._expect("}")
607
 
 
608
  self._emit("GOTO", None, None, end_label)
 
609
  self._emit("LABEL", None, None, next_label)
610
 
611
 
 
612
  def _loop_stmt(self):
 
613
  tok = self._peek()
 
614
  if tok.type == "grow":
 
615
  self._while_loop()
 
616
  elif tok.type == "cultivate":
 
617
  self._for_loop()
 
618
  elif tok.type == "tend":
 
619
  self._do_while_loop()
620
 
 
621
  def _while_loop(self):
 
622
  self._expect("grow")
 
623
  self._expect("(")
624
 
 
625
  start_label = self._new_label()
 
626
  end_label = self._new_label()
627
 
 
628
  self._emit("LABEL", None, None, start_label)
 
629
  cond = self._expression()
 
630
  self._expect(")")
631
 
 
632
  self._emit("IFFALSE", cond, None, end_label)
633
 
 
634
  self._expect("{")
 
635
  self._statement(allow_reclaim=True)
 
636
  self._expect("}")
637
 
 
638
  self._emit("GOTO", None, None, start_label)
 
639
  self._emit("LABEL", None, None, end_label)
640
 
 
641
  def _for_loop(self):
 
642
  self._expect("cultivate")
 
643
  self._expect("(")
644
 
 
645
  self._for_init()
 
646
  self._expect(";")
647
 
 
648
  start_label = self._new_label()
 
649
  end_label = self._new_label()
 
650
  update_label = self._new_label()
651
 
 
652
  self._emit("LABEL", None, None, start_label)
653
 
 
654
  cond = self._expression()
 
655
  self._emit("IFFALSE", cond, None, end_label)
656
 
 
657
  self._expect(";")
658
 
 
659
  update_instrs: List[TACInstruction] = []
 
660
  saved_code = self.code
 
661
  self.code = update_instrs
 
662
  self._for_update()
 
663
  self.code = saved_code
664
 
 
665
  self._expect(")")
 
666
  self._expect("{")
 
667
  self._statement(allow_reclaim=True)
 
668
  self._expect("}")
669
 
 
670
  self._emit("LABEL", None, None, update_label)
 
671
  self.code.extend(update_instrs)
 
672
  self._emit("GOTO", None, None, start_label)
 
673
  self._emit("LABEL", None, None, end_label)
674
 
 
675
  def _for_init(self):
 
676
  tok = self._peek()
 
677
  if self._is_data_type(tok):
 
678
  self._var_dec()
 
679
  elif tok.type == "id":
 
680
  id_tok = self._advance()
 
681
  lhs = self._resolve_lhs(id_tok.value)
 
682
  if self._peek().type in ASSIGN_OPS:
 
683
  op_tok = self._advance()
 
684
  rhs = self._expression()
 
685
  if op_tok.type == "=":
 
686
  self._emit("=", rhs, None, lhs)
 
687
  else:
 
688
  base_op = op_tok.type[0]
 
689
  tmp = self._new_temp()
 
690
  self._emit(base_op, lhs, rhs, tmp)
 
691
  self._emit("=", tmp, None, lhs)
692
 
 
693
  def _for_update(self):
 
694
  if self._peek().type == "id":
 
695
  id_tok = self._advance()
 
696
  tok = self._peek()
 
697
  if tok.type in ("++", "--"):
 
698
  op = self._advance()
 
699
  self._emit("INC" if op.type == "++" else "DEC", id_tok.value)
 
700
  else:
 
701
  lhs = self._resolve_lhs(id_tok.value)
 
702
  if self._peek().type in ASSIGN_OPS:
 
703
  op_tok = self._advance()
 
704
  rhs = self._expression()
 
705
  if op_tok.type == "=":
 
706
  self._emit("=", rhs, None, lhs)
 
707
  else:
 
708
  base_op = op_tok.type[0]
 
709
  tmp = self._new_temp()
 
710
  self._emit(base_op, lhs, rhs, tmp)
 
711
  self._emit("=", tmp, None, lhs)
712
 
 
713
  def _do_while_loop(self):
 
714
  self._expect("tend")
 
715
  self._expect("{")
716
 
 
717
  start_label = self._new_label()
 
718
  self._emit("LABEL", None, None, start_label)
719
 
 
720
  self._statement(allow_reclaim=True)
 
721
  self._expect("}")
722
 
 
723
  self._expect("grow")
 
724
  self._expect("(")
 
725
  cond = self._expression()
 
726
  self._expect(")")
 
727
  self._expect(";")
728
 
 
729
  self._emit("IF", cond, None, start_label)
730
 
731
 
 
732
  def _switch_stmt(self):
 
733
  self._expect("harvest")
 
734
  self._expect("(")
 
735
  expr = self._expression()
 
736
  self._expect(")")
 
737
  self._expect("{")
738
 
 
739
  end_label = self._new_label()
 
740
  self._case_list(expr, end_label)
 
741
  self._default_opt(end_label)
742
 
 
743
  self._expect("}")
 
744
  self._emit("LABEL", None, None, end_label)
745
 
 
746
  def _case_list(self, switch_expr: str, end_label: str):
 
747
  while self._peek().type == "variety":
 
748
  self._advance()
 
749
  case_val = self._expression()
 
750
  self._expect(":")
751
 
 
752
  next_label = self._new_label()
 
753
  body_label = self._new_label()
 
754
  cmp_tmp = self._new_temp()
755
 
 
756
  self._emit("==", switch_expr, case_val, cmp_tmp)
 
757
  self._emit("IFFALSE", cmp_tmp, None, next_label)
 
758
  self._emit("LABEL", None, None, body_label)
759
 
 
760
  self._declaration()
 
761
  self._case_statements()
762
 
 
763
  if self._peek().type == "prune":
 
764
  self._advance()
 
765
  self._expect(";")
766
 
 
767
  self._emit("GOTO", None, None, end_label)
 
768
  self._emit("LABEL", None, None, next_label)
769
 
 
770
  def _case_statements(self):
 
771
  while self._peek().type not in ("variety", "soil", "}", "prune", "EOF"):
 
772
  tok = self._peek()
 
773
  if tok.type == "id":
 
774
  self._id_stmt()
 
775
  elif tok.type == "water":
 
776
  self._io_stmt()
 
777
  elif tok.type == "plant":
 
778
  self._io_stmt()
 
779
  elif tok.type == "skip":
 
780
  self._advance()
 
781
  self._expect(";")
 
782
  elif tok.type == "reclaim":
 
783
  self._return_stmt()
 
784
  else:
 
785
  break
786
 
 
787
  def _default_opt(self, end_label: str):
 
788
  if self._peek().type == "soil":
 
789
  self._advance()
 
790
  self._expect(":")
 
791
  self._declaration()
 
792
  self._case_statements()
793
 
794
 
 
795
  def _control_stmt(self):
 
796
  tok = self._advance()
 
797
  self._expect(";")
 
798
  if tok.type == "prune":
 
799
  self._emit("GOTO", None, None, "BREAK")
 
800
  else:
 
801
  self._emit("GOTO", None, None, "CONTINUE")
802
 
803
 
 
804
  def _expression(self) -> str:
 
805
  left = self._logic_or()
 
806
  if self._peek().type not in ASSIGN_OPS:
 
807
  return left
808
 
 
809
  op_tok = self._advance()
 
810
  right = self._expression()
 
811
  if op_tok.type == "=":
 
812
  self._emit("=", right, None, left)
 
813
  else:
 
814
  tmp = self._new_temp()
 
815
  self._emit(op_tok.type[0], left, right, tmp)
 
816
  self._emit("=", tmp, None, left)
 
817
  return left
818
 
 
819
  def _logic_or(self) -> str:
 
820
  left = self._logic_and()
 
821
  while self._peek().type == "||":
 
822
  self._advance()
 
823
  right = self._logic_and()
 
824
  tmp = self._new_temp()
 
825
  self._emit("||", left, right, tmp)
 
826
  left = tmp
 
827
  return left
828
 
 
829
  def _logic_and(self) -> str:
 
830
  left = self._relational()
 
831
  while self._peek().type == "&&":
 
832
  self._advance()
 
833
  right = self._relational()
 
834
  tmp = self._new_temp()
 
835
  self._emit("&&", left, right, tmp)
 
836
  left = tmp
 
837
  return left
838
 
 
839
  def _relational(self) -> str:
 
840
  left = self._arithmetic()
 
841
  if self._peek().type in (">", "<", ">=", "<=", "==", "!="):
 
842
  op = self._advance().type
 
843
  right = self._arithmetic()
 
844
  tmp = self._new_temp()
 
845
  self._emit(op, left, right, tmp)
 
846
  return tmp
 
847
  return left
848
 
 
849
  def _arithmetic(self) -> str:
 
850
  left = self._term()
 
851
  while self._peek().type in ("+", "-"):
 
852
  op = self._advance().type
 
853
  right = self._term()
 
854
  tmp = self._new_temp()
 
855
  self._emit(op, left, right, tmp)
 
856
  left = tmp
 
857
  return left
858
 
 
859
  def _term(self) -> str:
 
860
  left = self._power()
 
861
  while self._peek().type in ("*", "/", "%"):
 
862
  op = self._advance().type
 
863
  right = self._power()
 
864
  tmp = self._new_temp()
 
865
  self._emit(op, left, right, tmp)
 
866
  left = tmp
 
867
  return left
868
 
 
869
  def _power(self) -> str:
 
870
  left = self._factor()
 
871
  if self._peek().type == "**":
 
872
  op = self._advance().type
 
873
  right = self._power()
 
874
  tmp = self._new_temp()
 
875
  self._emit(op, left, right, tmp)
 
876
  return tmp
 
877
  return left
878
 
 
879
  def _factor(self) -> str:
 
880
  tok = self._peek()
881
 
 
882
  if tok.type == "(":
 
883
  self._advance()
 
884
  val = self._expression()
 
885
  self._expect(")")
 
886
  return val
887
 
 
888
  if tok.type in ("~", "!"):
 
889
  op = self._advance()
 
890
  inner = self._factor()
 
891
  tmp = self._new_temp()
 
892
  if op.type == "~":
 
893
  self._emit("UNARY_MINUS", inner, None, tmp)
 
894
  else:
 
895
  self._emit("NOT", inner, None, tmp)
 
896
  return tmp
897
 
 
898
  if tok.type == "id":
 
899
  id_tok = self._advance()
 
900
  nxt = self._peek()
901
 
 
902
  if nxt.type == "(":
 
903
  self._advance()
 
904
  args = self._arguments()
 
905
  self._expect(")")
 
906
  for a in args:
 
907
  self._emit("PARAM", a)
 
908
  tmp = self._new_temp()
 
909
  self._emit("CALL", id_tok.value, str(len(args)), tmp)
 
910
  return tmp
911
 
 
912
  if nxt.type == "[":
 
913
  self._advance()
 
914
  idx = self._expression()
 
915
  self._expect("]")
 
916
  while self._peek().type == "[":
 
917
  self._advance()
 
918
  idx2 = self._expression()
 
919
  self._expect("]")
 
920
  tmp = self._new_temp()
 
921
  self._emit("*", idx, idx2, tmp)
 
922
  idx = tmp
 
923
  location = f"{id_tok.value}[{idx}]"
 
924
  if self._peek().type in ASSIGN_OPS:
 
925
  return location
 
926
  tmp = self._new_temp()
 
927
  self._emit("ARRAY_LOAD", id_tok.value, idx, tmp)
 
928
  return tmp
929
 
 
930
  if nxt.type == ".":
 
931
  self._advance()
 
932
  member = self._expect("id")
 
933
  chain = member.value
 
934
  while self._peek().type == ".":
 
935
  self._advance()
 
936
  m2 = self._expect("id")
 
937
  chain = f"{chain}.{m2.value}"
 
938
  location = f"{id_tok.value}.{chain}"
 
939
  if self._peek().type in ASSIGN_OPS:
 
940
  return location
 
941
  tmp = self._new_temp()
 
942
  self._emit("STRUCT_LOAD", id_tok.value, chain, tmp)
 
943
  return tmp
944
 
 
945
  return id_tok.value
946
 
 
947
  if tok.type == "intlit":
 
948
  return self._advance().value
 
949
  if tok.type == "dblit":
 
950
  return self._advance().value
 
951
  if tok.type == "chrlit":
 
952
  return self._advance().value
 
953
  if tok.type == "stringlit":
 
954
  return self._advance().value
 
955
  if tok.type == "sunshine":
 
956
  self._advance()
 
957
  return "true"
 
958
  if tok.type == "frost":
 
959
  self._advance()
 
960
  return "false"
961
 
 
962
  self.errors.append(f"ICG Line {tok.line}: unexpected token in expression: '{tok.type}'")
 
963
  self._advance()
 
964
  return "???"
965
 
966
 
 
967
  def _arguments(self) -> List[str]:
 
968
  args: List[str] = []
 
969
  if self._peek().type in (")", "EOF"):
 
970
  return args
 
971
  args.append(self._expression())
 
972
  while self._match(","):
 
973
  args.append(self._expression())
 
974
  return args
975
 
976
 
 
977
  def generate_icg(tokens: List[Any]) -> Dict[str, Any]:
 
978
  gen = ICGenerator(tokens)
 
979
  code, errors = gen.generate()
980
 
 
981
  tac_dicts = [instr.to_dict() for instr in code]
 
982
  tac_text = "\n".join(str(instr) for instr in code)
983
 
 
984
  return {
 
985
  "success": len(errors) == 0,
 
986
  "tac": tac_dicts,
 
987
  "tac_text": tac_text,
 
988
  "errors": errors,
 
989
  }
 
1
+ # AUTO: Imports names from another module.
2
  from __future__ import annotations
3
+ # AUTO: Imports names from another module.
4
  from dataclasses import dataclass
5
+ # AUTO: Imports names from another module.
6
  from typing import Any, Dict, List, Optional, Tuple, Union
7
 
8
 
9
+ # AUTO: Attaches this decorator to the next function/class.
10
  @dataclass
11
+ # AUTO: Defines class `_Tok`.
12
  class _Tok:
13
+ # AUTO: Executes this statement.
14
  type: str
15
+ # AUTO: Executes this statement.
16
  value: str
17
+ # AUTO: Executes this statement.
18
  line: int
19
+ # AUTO: Sets `col: int`.
20
  col: int = 0
21
 
22
 
23
+ # AUTO: Defines function `_as_tok`.
24
  def _as_tok(raw: Any) -> _Tok:
25
+ # AUTO: Checks this condition.
26
  if isinstance(raw, dict):
27
+ # AUTO: Returns this result to the caller.
28
  return _Tok(
29
+ # AUTO: Sets `type`.
30
  type=str(raw.get("type", "")),
31
+ # AUTO: Sets `value`.
32
  value=str(raw.get("value", "")),
33
+ # AUTO: Sets `line`.
34
  line=int(raw.get("line", 0) or 0),
35
+ # AUTO: Sets `col`.
36
  col=int(raw.get("col", 0) or 0),
37
+ # AUTO: Closes the current grouped code/data.
38
  )
39
+ # AUTO: Returns this result to the caller.
40
  return _Tok(
41
+ # AUTO: Sets `type`.
42
  type=str(getattr(raw, "type", "")),
43
+ # AUTO: Sets `value`.
44
  value=str(getattr(raw, "value", "")),
45
+ # AUTO: Sets `line`.
46
  line=int(getattr(raw, "line", 0) or 0),
47
+ # AUTO: Sets `col`.
48
  col=int(getattr(raw, "col", 0) or 0),
49
+ # AUTO: Closes the current grouped code/data.
50
  )
51
 
52
 
53
+ # AUTO: Attaches this decorator to the next function/class.
54
  @dataclass
55
+ # AUTO: Defines class `TACInstruction`.
56
  class TACInstruction:
57
+ # AUTO: Executes this statement.
58
  op: str
59
+ # AUTO: Sets `arg1: Optional[str]`.
60
  arg1: Optional[str] = None
61
+ # AUTO: Sets `arg2: Optional[str]`.
62
  arg2: Optional[str] = None
63
+ # AUTO: Sets `result: Optional[str]`.
64
  result: Optional[str] = None
65
 
66
+ # AUTO: Defines function `__str__`.
67
  def __str__(self) -> str:
68
+ # AUTO: Checks this condition.
69
  if self.op == "LABEL":
70
+ # AUTO: Returns this result to the caller.
71
  return f"{self.result}:"
72
+ # AUTO: Checks this condition.
73
  if self.op == "GOTO":
74
+ # AUTO: Returns this result to the caller.
75
  return f"goto {self.result}"
76
+ # AUTO: Checks this condition.
77
  if self.op == "IF":
78
+ # AUTO: Returns this result to the caller.
79
  return f"if {self.arg1} goto {self.result}"
80
+ # AUTO: Checks this condition.
81
  if self.op == "IFFALSE":
82
+ # AUTO: Returns this result to the caller.
83
  return f"ifFalse {self.arg1} goto {self.result}"
84
+ # AUTO: Checks this condition.
85
  if self.op == "PARAM":
86
+ # AUTO: Returns this result to the caller.
87
  return f"param {self.arg1}"
88
+ # AUTO: Checks this condition.
89
  if self.op == "CALL":
90
+ # AUTO: Returns this result to the caller.
91
  return f"{self.result} = call {self.arg1}, {self.arg2}"
92
+ # AUTO: Checks this condition.
93
  if self.op == "RETURN":
94
+ # AUTO: Returns this result to the caller.
95
  return f"return {self.arg1 or ''}"
96
+ # AUTO: Checks this condition.
97
  if self.op == "PRINT":
98
+ # AUTO: Returns this result to the caller.
99
  return f"print {self.arg1}"
100
+ # AUTO: Checks this condition.
101
  if self.op == "READ":
102
+ # AUTO: Returns this result to the caller.
103
  return f"read {self.result}"
104
+ # AUTO: Checks this condition.
105
  if self.op == "FUNC":
106
+ # AUTO: Returns this result to the caller.
107
  return f"func {self.arg1}:"
108
+ # AUTO: Checks this condition.
109
  if self.op == "ENDFUNC":
110
+ # AUTO: Returns this result to the caller.
111
  return f"endfunc"
112
+ # AUTO: Checks this condition.
113
  if self.op == "DECLARE":
114
+ # AUTO: Returns this result to the caller.
115
  return f"declare {self.result} : {self.arg1}"
116
+ # AUTO: Checks this condition.
117
  if self.op == "ARRAY_DECLARE":
118
+ # AUTO: Returns this result to the caller.
119
  return f"declare {self.result}[{self.arg2}] : {self.arg1}"
120
+ # AUTO: Checks this condition.
121
  if self.op == "ARRAY_STORE":
122
+ # AUTO: Returns this result to the caller.
123
  return f"{self.result}[{self.arg2}] = {self.arg1}"
124
+ # AUTO: Checks this condition.
125
  if self.op == "ARRAY_LOAD":
126
+ # AUTO: Returns this result to the caller.
127
  return f"{self.result} = {self.arg1}[{self.arg2}]"
128
+ # AUTO: Checks this condition.
129
  if self.op == "STRUCT_STORE":
130
+ # AUTO: Returns this result to the caller.
131
  return f"{self.arg1}.{self.arg2} = {self.result}"
132
+ # AUTO: Checks this condition.
133
  if self.op == "STRUCT_LOAD":
134
+ # AUTO: Returns this result to the caller.
135
  return f"{self.result} = {self.arg1}.{self.arg2}"
136
+ # AUTO: Checks this condition.
137
  if self.op == "CONST":
138
+ # AUTO: Returns this result to the caller.
139
  return f"const {self.result} : {self.arg1} = {self.arg2}"
140
+ # AUTO: Checks this condition.
141
  if self.op == "INC":
142
+ # AUTO: Returns this result to the caller.
143
  return f"{self.arg1} = {self.arg1} + 1"
144
+ # AUTO: Checks this condition.
145
  if self.op == "DEC":
146
+ # AUTO: Returns this result to the caller.
147
  return f"{self.arg1} = {self.arg1} - 1"
148
+ # AUTO: Checks this condition.
149
  if self.arg2 is not None:
150
+ # AUTO: Returns this result to the caller.
151
  return f"{self.result} = {self.arg1} {self.op} {self.arg2}"
152
+ # AUTO: Checks this condition.
153
  if self.op == "=":
154
+ # AUTO: Returns this result to the caller.
155
  return f"{self.result} = {self.arg1}"
156
+ # AUTO: Checks this condition.
157
  if self.op == "UNARY_MINUS":
158
+ # AUTO: Returns this result to the caller.
159
  return f"{self.result} = -{self.arg1}"
160
+ # AUTO: Checks this condition.
161
  if self.op == "NOT":
162
+ # AUTO: Returns this result to the caller.
163
  return f"{self.result} = !{self.arg1}"
164
+ # AUTO: Returns this result to the caller.
165
  return f"{self.op} {self.arg1 or ''} {self.arg2 or ''} {self.result or ''}".strip()
166
 
167
+ # AUTO: Defines function `to_dict`.
168
  def to_dict(self) -> dict:
169
+ # AUTO: Returns this result to the caller.
170
  return {
171
+ # AUTO: Executes this statement.
172
  "op": self.op,
173
+ # AUTO: Executes this statement.
174
  "arg1": self.arg1,
175
+ # AUTO: Executes this statement.
176
  "arg2": self.arg2,
177
+ # AUTO: Executes this statement.
178
  "result": self.result,
179
+ # AUTO: Calls `str`.
180
  "text": str(self),
181
+ # AUTO: Closes the current grouped code/data.
182
  }
183
 
184
 
185
+ # AUTO: Sets `GAL_TYPE_MAP`.
186
  GAL_TYPE_MAP = {
187
+ # AUTO: Executes this statement.
188
  "seed": "int",
189
+ # AUTO: Executes this statement.
190
  "tree": "float",
191
+ # AUTO: Executes this statement.
192
  "leaf": "char",
193
+ # AUTO: Executes this statement.
194
  "branch": "bool",
195
+ # AUTO: Executes this statement.
196
  "vine": "string",
197
+ # AUTO: Executes this statement.
198
  "empty": "void",
199
+ # AUTO: Closes the current grouped code/data.
200
  }
201
 
202
+ # AUTO: Sets `DATA_TYPE_TOKENS`.
203
  DATA_TYPE_TOKENS = set(GAL_TYPE_MAP.keys())
204
 
205
+ # AUTO: Adds into `ASSIGN_OPS = {"=", "`.
206
  ASSIGN_OPS = {"=", "+=", "-=", "*=", "/=", "%="}
207
 
208
 
209
+ # AUTO: Defines class `ICGenerator`.
210
  class ICGenerator:
211
 
212
+ # AUTO: Defines function `__init__`.
213
  def __init__(self, tokens: List[Any]):
214
+ # AUTO: Sets `self.tokens: List[_Tok]`.
215
  self.tokens: List[_Tok] = self._prepare(tokens)
216
+ # AUTO: Sets `self.pos: int`.
217
  self.pos: int = 0
218
+ # AUTO: Sets `self.code: List[TACInstruction]`.
219
  self.code: List[TACInstruction] = []
220
+ # AUTO: Sets `self.errors: List[str]`.
221
  self.errors: List[str] = []
222
+ # AUTO: Sets `self._temp_counter: int`.
223
  self._temp_counter: int = 0
224
+ # AUTO: Sets `self._label_counter: int`.
225
  self._label_counter: int = 0
226
 
227
 
228
+ # AUTO: Defines function `_prepare`.
229
  def _prepare(self, raw_tokens: List[Any]) -> List[_Tok]:
230
+ # AUTO: Sets `toks: List[_Tok]`.
231
  toks: List[_Tok] = []
232
+ # AUTO: Starts a loop over these values.
233
  for t in raw_tokens:
234
+ # AUTO: Sets `tv`.
235
  tv = _as_tok(t)
236
+ # AUTO: Checks this condition.
237
  if tv.type in ("\n", "comment", "mcommentlit"):
238
+ # AUTO: Skips to the next loop iteration.
239
  continue
240
+ # AUTO: Appends a value to a list.
241
  toks.append(tv)
242
+ # AUTO: Checks this condition.
243
  if not toks or toks[-1].type != "EOF":
244
+ # AUTO: Sets `last_line`.
245
  last_line = toks[-1].line if toks else 1
246
+ # AUTO: Appends a value to a list.
247
  toks.append(_Tok("EOF", "EOF", last_line))
248
+ # AUTO: Returns this result to the caller.
249
  return toks
250
 
251
+ # AUTO: Defines function `_peek`.
252
  def _peek(self) -> _Tok:
253
+ # AUTO: Checks this condition.
254
  if self.pos < len(self.tokens):
255
+ # AUTO: Returns this result to the caller.
256
  return self.tokens[self.pos]
257
+ # AUTO: Returns this result to the caller.
258
  return _Tok("EOF", "EOF", 0)
259
 
260
+ # AUTO: Defines function `_advance`.
261
  def _advance(self) -> _Tok:
262
+ # AUTO: Sets `tok`.
263
  tok = self._peek()
264
+ # AUTO: Checks this condition.
265
  if self.pos < len(self.tokens):
266
+ # AUTO: Adds into `self.pos`.
267
  self.pos += 1
268
+ # AUTO: Returns this result to the caller.
269
  return tok
270
 
271
+ # AUTO: Defines function `_expect`.
272
  def _expect(self, token_type: str) -> _Tok:
273
+ # AUTO: Sets `tok`.
274
  tok = self._peek()
275
+ # AUTO: Checks this condition.
276
  if tok.type != token_type:
277
+ # AUTO: Appends a value to a list.
278
  self.errors.append(
279
+ # AUTO: Executes this statement.
280
  f"ICG Line {tok.line}: expected '{token_type}', got '{tok.type}'"
281
+ # AUTO: Closes the current grouped code/data.
282
  )
283
+ # AUTO: Returns this result to the caller.
284
  return tok
285
+ # AUTO: Returns this result to the caller.
286
  return self._advance()
287
 
288
+ # AUTO: Defines function `_match`.
289
  def _match(self, token_type: str) -> bool:
290
+ # AUTO: Checks this condition.
291
  if self._peek().type == token_type:
292
+ # AUTO: Calls `self._advance`.
293
  self._advance()
294
+ # AUTO: Returns this result to the caller.
295
  return True
296
+ # AUTO: Returns this result to the caller.
297
  return False
298
 
299
+ # AUTO: Defines function `_new_temp`.
300
  def _new_temp(self) -> str:
301
+ # AUTO: Sets `name`.
302
  name = f"t{self._temp_counter}"
303
+ # AUTO: Adds into `self._temp_counter`.
304
  self._temp_counter += 1
305
+ # AUTO: Returns this result to the caller.
306
  return name
307
 
308
+ # AUTO: Defines function `_new_label`.
309
  def _new_label(self) -> str:
310
+ # AUTO: Sets `name`.
311
  name = f"L{self._label_counter}"
312
+ # AUTO: Adds into `self._label_counter`.
313
  self._label_counter += 1
314
+ # AUTO: Returns this result to the caller.
315
  return name
316
 
317
+ # AUTO: Defines function `_emit`.
318
  def _emit(self, op: str, arg1: Optional[str] = None,
319
+ # AUTO: Sets `arg2: Optional[str]`.
320
  arg2: Optional[str] = None, result: Optional[str] = None):
321
+ # AUTO: Appends a value to a list.
322
  self.code.append(TACInstruction(op, arg1, arg2, result))
323
 
324
+ # AUTO: Defines function `_is_data_type`.
325
  def _is_data_type(self, tok: _Tok) -> bool:
326
+ # AUTO: Returns this result to the caller.
327
  return tok.type in DATA_TYPE_TOKENS
328
 
329
 
330
+ # AUTO: Defines function `generate`.
331
  def generate(self) -> Tuple[List[TACInstruction], List[str]]:
332
+ # AUTO: Starts protected code that can catch errors.
333
  try:
334
+ # AUTO: Calls `self._program`.
335
  self._program()
336
+ # AUTO: Handles the matching error case.
337
  except Exception as exc:
338
+ # AUTO: Appends a value to a list.
339
  self.errors.append(f"ICG internal error: {exc}")
340
+ # AUTO: Returns this result to the caller.
341
  return self.code, self.errors
342
 
343
+ # AUTO: Defines function `_program`.
344
  def _program(self):
345
+ # AUTO: Calls `self._global_declaration`.
346
  self._global_declaration()
347
+ # AUTO: Calls `self._function_definition`.
348
  self._function_definition()
349
 
350
+ # AUTO: Calls `self._expect`.
351
  self._expect("root")
352
+ # AUTO: Calls `self._expect`.
353
  self._expect("(")
354
+ # AUTO: Calls `self._expect`.
355
  self._expect(")")
356
+ # AUTO: Calls `self._expect`.
357
  self._expect("{")
358
+ # AUTO: Calls `self._emit`.
359
  self._emit("FUNC", "root")
360
 
361
+ # AUTO: Calls `self._declaration`.
362
  self._declaration()
363
+ # AUTO: Calls `self._statement`.
364
  self._statement()
365
 
366
+ # AUTO: Calls `self._expect`.
367
  self._expect("reclaim")
368
+ # AUTO: Calls `self._expect`.
369
  self._expect(";")
370
+ # AUTO: Calls `self._emit`.
371
  self._emit("RETURN")
372
 
373
+ # AUTO: Calls `self._expect`.
374
  self._expect("}")
375
+ # AUTO: Calls `self._emit`.
376
  self._emit("ENDFUNC")
377
 
378
 
379
+ # AUTO: Defines function `_global_declaration`.
380
  def _global_declaration(self):
381
+ # AUTO: Repeats while this condition is true.
382
  while True:
383
+ # AUTO: Sets `tok`.
384
  tok = self._peek()
385
+ # AUTO: Checks this condition.
386
  if tok.type == "bundle":
387
+ # AUTO: Calls `self._advance`.
388
  self._advance()
389
+ # AUTO: Sets `name_tok`.
390
  name_tok = self._expect("id")
391
+ # AUTO: Sets `nxt`.
392
  nxt = self._peek()
393
+ # AUTO: Checks this condition.
394
  if nxt.type == "{":
395
+ # AUTO: Calls `self._advance`.
396
  self._advance()
397
+ # AUTO: Calls `self._bundle_members`.
398
  self._bundle_members()
399
+ # AUTO: Calls `self._expect`.
400
  self._expect("}")
401
+ # AUTO: Calls `self._expect`.
402
  self._expect(";")
403
+ # AUTO: Runs when previous condition did not pass.
404
  else:
405
+ # AUTO: Calls `self._bundle_mem_dec`.
406
  self._bundle_mem_dec()
407
+ # AUTO: Calls `self._expect`.
408
  self._expect(";")
409
+ # AUTO: Calls `self._global_declaration`.
410
  self._global_declaration()
411
+ # AUTO: Returns this result to the caller.
412
  return
413
 
414
+ # AUTO: Checks the next alternate condition.
415
  elif self._is_data_type(tok):
416
+ # AUTO: Sets `dtype`.
417
  dtype = self._advance()
418
+ # AUTO: Sets `id_tok`.
419
  id_tok = self._expect("id")
420
+ # AUTO: Sets `arr_dims`.
421
  arr_dims = self._array_dec()
422
+ # AUTO: Checks this condition.
423
  if arr_dims:
424
+ # AUTO: Starts a loop over these values.
425
  for dim in arr_dims:
426
+ # AUTO: Calls `self._emit`.
427
  self._emit("ARRAY_DECLARE", GAL_TYPE_MAP.get(dtype.type, dtype.type),
428
+ # AUTO: Executes this statement.
429
  dim, id_tok.value)
430
+ # AUTO: Runs when previous condition did not pass.
431
  else:
432
+ # AUTO: Calls `self._emit`.
433
  self._emit("DECLARE", GAL_TYPE_MAP.get(dtype.type, dtype.type),
434
+ # AUTO: Executes this statement.
435
  None, id_tok.value)
436
+ # AUTO: Calls `self._var_value`.
437
  self._var_value(id_tok.value)
438
+ # AUTO: Calls `self._expect`.
439
  self._expect(";")
440
+ # AUTO: Calls `self._global_declaration`.
441
  self._global_declaration()
442
+ # AUTO: Returns this result to the caller.
443
  return
444
 
445
+ # AUTO: Checks the next alternate condition.
446
  elif tok.type == "fertile":
447
+ # AUTO: Calls `self._const_dec`.
448
  self._const_dec()
449
+ # AUTO: Calls `self._expect`.
450
  self._expect(";")
451
+ # AUTO: Calls `self._global_declaration`.
452
  self._global_declaration()
453
+ # AUTO: Returns this result to the caller.
454
  return
455
 
456
+ # AUTO: Runs when previous condition did not pass.
457
  else:
458
+ # AUTO: Returns this result to the caller.
459
  return
460
 
461
 
462
+ # AUTO: Defines function `_declaration`.
463
  def _declaration(self):
464
+ # AUTO: Repeats while this condition is true.
465
  while True:
466
+ # AUTO: Sets `tok`.
467
  tok = self._peek()
468
+ # AUTO: Checks this condition.
469
  if self._is_data_type(tok) or tok.type == "bundle":
470
+ # AUTO: Calls `self._var_dec`.
471
  self._var_dec()
472
+ # AUTO: Calls `self._expect`.
473
  self._expect(";")
474
+ # AUTO: Checks the next alternate condition.
475
  elif tok.type == "fertile":
476
+ # AUTO: Calls `self._const_dec`.
477
  self._const_dec()
478
+ # AUTO: Calls `self._expect`.
479
  self._expect(";")
480
+ # AUTO: Runs when previous condition did not pass.
481
  else:
482
+ # AUTO: Stops the nearest loop.
483
  break
484
 
485
+ # AUTO: Defines function `_var_dec`.
486
  def _var_dec(self):
487
+ # AUTO: Sets `tok`.
488
  tok = self._peek()
489
+ # AUTO: Checks this condition.
490
  if tok.type == "bundle":
491
+ # AUTO: Calls `self._advance`.
492
  self._advance()
493
+ # AUTO: Sets `id_tok`.
494
  id_tok = self._expect("id")
495
+ # AUTO: Calls `self._bundle_mem_dec`.
496
  self._bundle_mem_dec()
497
+ # AUTO: Returns this result to the caller.
498
  return
499
 
500
+ # AUTO: Sets `dtype`.
501
  dtype = self._advance()
502
+ # AUTO: Sets `id_tok`.
503
  id_tok = self._expect("id")
504
+ # AUTO: Sets `arr_dims`.
505
  arr_dims = self._array_dec()
506
+ # AUTO: Checks this condition.
507
  if arr_dims:
508
+ # AUTO: Starts a loop over these values.
509
  for dim in arr_dims:
510
+ # AUTO: Calls `self._emit`.
511
  self._emit("ARRAY_DECLARE", GAL_TYPE_MAP.get(dtype.type, dtype.type),
512
+ # AUTO: Executes this statement.
513
  dim, id_tok.value)
514
+ # AUTO: Runs when previous condition did not pass.
515
  else:
516
+ # AUTO: Calls `self._emit`.
517
  self._emit("DECLARE", GAL_TYPE_MAP.get(dtype.type, dtype.type), None, id_tok.value)
518
+ # AUTO: Calls `self._var_value`.
519
  self._var_value(id_tok.value)
520
 
521
+ # AUTO: Defines function `_const_dec`.
522
  def _const_dec(self):
523
+ # AUTO: Calls `self._expect`.
524
  self._expect("fertile")
525
+ # AUTO: Sets `dtype`.
526
  dtype = self._advance()
527
+ # AUTO: Sets `id_tok`.
528
  id_tok = self._expect("id")
529
+ # AUTO: Sets `self._expect("`.
530
  self._expect("=")
531
+ # AUTO: Sets `val`.
532
  val = self._init_val()
533
+ # AUTO: Calls `self._emit`.
534
  self._emit("CONST", GAL_TYPE_MAP.get(dtype.type, dtype.type), val, id_tok.value)
535
+ # AUTO: Calls `self._const_next`.
536
  self._const_next(dtype)
537
 
538
+ # AUTO: Defines function `_const_next`.
539
  def _const_next(self, dtype_tok: _Tok):
540
+ # AUTO: Repeats while this condition is true.
541
  while self._match(","):
542
+ # AUTO: Sets `id_tok`.
543
  id_tok = self._expect("id")
544
+ # AUTO: Sets `self._expect("`.
545
  self._expect("=")
546
+ # AUTO: Sets `val`.
547
  val = self._init_val()
548
+ # AUTO: Calls `self._emit`.
549
  self._emit("CONST", GAL_TYPE_MAP.get(dtype_tok.type, dtype_tok.type), val, id_tok.value)
550
 
551
+ # AUTO: Defines function `_var_value`.
552
  def _var_value(self, var_name: str):
553
+ # AUTO: Checks this condition.
554
  if self._peek().type == "=":
555
+ # AUTO: Calls `self._advance`.
556
  self._advance()
557
+ # AUTO: Sets `val`.
558
  val = self._init_val()
559
+ # AUTO: Sets `self._emit("`.
560
  self._emit("=", val, None, var_name)
561
+ # AUTO: Calls `self._var_value_next`.
562
  self._var_value_next()
563
+ # AUTO: Runs when previous condition did not pass.
564
  else:
565
+ # AUTO: Calls `self._var_value_next`.
566
  self._var_value_next()
567
 
568
+ # AUTO: Defines function `_var_value_next`.
569
  def _var_value_next(self):
570
+ # AUTO: Checks this condition.
571
  if self._match(","):
572
+ # AUTO: Sets `id_tok`.
573
  id_tok = self._expect("id")
574
+ # AUTO: Sets `arr_dims`.
575
  arr_dims = self._array_dec()
576
+ # AUTO: Checks this condition.
577
  if arr_dims:
578
+ # AUTO: Starts a loop over these values.
579
  for dim in arr_dims:
580
+ # AUTO: Calls `self._emit`.
581
  self._emit("ARRAY_DECLARE", "int", dim, id_tok.value)
582
+ # AUTO: Runs when previous condition did not pass.
583
  else:
584
+ # AUTO: Calls `self._emit`.
585
  self._emit("DECLARE", "int", None, id_tok.value)
586
+ # AUTO: Calls `self._var_value`.
587
  self._var_value(id_tok.value)
588
 
589
+ # AUTO: Defines function `_init_val`.
590
  def _init_val(self) -> str:
591
+ # AUTO: Checks this condition.
592
  if self._peek().type == "{":
593
+ # AUTO: Returns this result to the caller.
594
  return self._array_init()
595
+ # AUTO: Returns this result to the caller.
596
  return self._expression()
597
 
598
+ # AUTO: Defines function `_array_init`.
599
  def _array_init(self) -> str:
600
+ # AUTO: Calls `self._expect`.
601
  self._expect("{")
602
+ # AUTO: Sets `tmp`.
603
  tmp = self._new_temp()
604
+ # AUTO: Sets `idx`.
605
  idx = 0
606
+ # AUTO: Checks this condition.
607
  if self._peek().type != "}":
608
+ # AUTO: Sets `val`.
609
  val = self._init_val_item()
610
+ # AUTO: Calls `self._emit`.
611
  self._emit("ARRAY_STORE", val, str(idx), tmp)
612
+ # AUTO: Adds into `idx`.
613
  idx += 1
614
+ # AUTO: Repeats while this condition is true.
615
  while self._match(","):
616
+ # AUTO: Sets `val`.
617
  val = self._init_val_item()
618
+ # AUTO: Calls `self._emit`.
619
  self._emit("ARRAY_STORE", val, str(idx), tmp)
620
+ # AUTO: Adds into `idx`.
621
  idx += 1
622
+ # AUTO: Calls `self._expect`.
623
  self._expect("}")
624
+ # AUTO: Returns this result to the caller.
625
  return tmp
626
 
627
+ # AUTO: Defines function `_init_val_item`.
628
  def _init_val_item(self) -> str:
629
+ # AUTO: Checks this condition.
630
  if self._peek().type == "{":
631
+ # AUTO: Returns this result to the caller.
632
  return self._array_init()
633
+ # AUTO: Returns this result to the caller.
634
  return self._expression()
635
 
636
 
637
+ # AUTO: Defines function `_array_dec`.
638
  def _array_dec(self) -> List[str]:
639
+ # AUTO: Sets `dims: List[str]`.
640
  dims: List[str] = []
641
+ # AUTO: Repeats while this condition is true.
642
  while self._peek().type == "[":
643
+ # AUTO: Calls `self._advance`.
644
  self._advance()
645
+ # AUTO: Checks this condition.
646
  if self._peek().type == "intlit":
647
+ # AUTO: Appends a value to a list.
648
  dims.append(self._advance().value)
649
+ # AUTO: Runs when previous condition did not pass.
650
  else:
651
+ # AUTO: Appends a value to a list.
652
  dims.append("0")
653
+ # AUTO: Calls `self._expect`.
654
  self._expect("]")
655
+ # AUTO: Returns this result to the caller.
656
  return dims
657
 
658
+ # AUTO: Defines function `_bundle_members`.
659
  def _bundle_members(self):
660
+ # AUTO: Repeats while this condition is true.
661
  while self._is_data_type(self._peek()):
662
+ # AUTO: Sets `dtype`.
663
  dtype = self._advance()
664
+ # AUTO: Sets `id_tok`.
665
  id_tok = self._expect("id")
666
+ # AUTO: Calls `self._expect`.
667
  self._expect(";")
668
 
669
+ # AUTO: Defines function `_bundle_mem_dec`.
670
  def _bundle_mem_dec(self):
671
+ # AUTO: Sets `id_tok`.
672
  id_tok = self._expect("id")
673
+ # AUTO: Sets `arr_dims`.
674
  arr_dims = self._array_dec()
675
+ # AUTO: Checks this condition.
676
  if arr_dims:
677
+ # AUTO: Starts a loop over these values.
678
  for dim in arr_dims:
679
+ # AUTO: Calls `self._emit`.
680
  self._emit("ARRAY_DECLARE", "bundle", dim, id_tok.value)
681
+ # AUTO: Runs when previous condition did not pass.
682
  else:
683
+ # AUTO: Calls `self._emit`.
684
  self._emit("DECLARE", "bundle", None, id_tok.value)
685
 
686
 
687
+ # AUTO: Defines function `_function_definition`.
688
  def _function_definition(self):
689
+ # AUTO: Repeats while this condition is true.
690
  while self._peek().type == "pollinate":
691
+ # AUTO: Calls `self._advance`.
692
  self._advance()
693
 
694
+ # AUTO: Checks this condition.
695
  if self._peek().type == "empty":
696
+ # AUTO: Sets `ret_type`.
697
  ret_type = self._advance().type
698
+ # AUTO: Checks the next alternate condition.
699
  elif self._is_data_type(self._peek()):
700
+ # AUTO: Sets `ret_type`.
701
  ret_type = self._advance().type
702
+ # AUTO: Checks the next alternate condition.
703
  elif self._peek().type == "id":
704
+ # AUTO: Sets `ret_type`.
705
  ret_type = self._advance().value
706
+ # AUTO: Runs when previous condition did not pass.
707
  else:
708
+ # AUTO: Sets `ret_type`.
709
  ret_type = "void"
710
 
711
+ # AUTO: Sets `func_name`.
712
  func_name = self._expect("id")
713
+ # AUTO: Calls `self._expect`.
714
  self._expect("(")
715
 
716
+ # AUTO: Sets `params`.
717
  params = self._parameters()
718
 
719
+ # AUTO: Calls `self._expect`.
720
  self._expect(")")
721
+ # AUTO: Calls `self._expect`.
722
  self._expect("{")
723
 
724
+ # AUTO: Calls `self._emit`.
725
  self._emit("FUNC", func_name.value)
726
+ # AUTO: Starts a loop over these values.
727
  for ptype, pname, *rest in params:
728
+ # AUTO: Sets `is_array`.
729
  is_array = rest[0] if rest else False
730
+ # AUTO: Checks this condition.
731
  if is_array:
732
+ # AUTO: Calls `self._emit`.
733
  self._emit("ARRAY_DECLARE", GAL_TYPE_MAP.get(ptype, ptype), "param", pname)
734
+ # AUTO: Runs when previous condition did not pass.
735
  else:
736
+ # AUTO: Calls `self._emit`.
737
  self._emit("DECLARE", GAL_TYPE_MAP.get(ptype, ptype), None, pname)
738
 
739
+ # AUTO: Calls `self._declaration`.
740
  self._declaration()
741
+ # AUTO: Calls `self._statement`.
742
  self._statement()
743
 
744
+ # AUTO: Calls `self._expect`.
745
  self._expect("reclaim")
746
+ # AUTO: Checks this condition.
747
  if self._peek().type == ";":
748
+ # AUTO: Calls `self._emit`.
749
  self._emit("RETURN")
750
+ # AUTO: Runs when previous condition did not pass.
751
  else:
752
+ # AUTO: Sets `val`.
753
  val = self._expression()
754
+ # AUTO: Calls `self._emit`.
755
  self._emit("RETURN", val)
756
+ # AUTO: Calls `self._expect`.
757
  self._expect(";")
758
 
759
+ # AUTO: Calls `self._expect`.
760
  self._expect("}")
761
+ # AUTO: Calls `self._emit`.
762
  self._emit("ENDFUNC")
763
 
764
+ # AUTO: Defines function `_parameters`.
765
  def _parameters(self):
766
+ # AUTO: Sets `params`.
767
  params = []
768
+ # AUTO: Checks this condition.
769
  if self._is_data_type(self._peek()) or self._peek().type == "id":
770
+ # AUTO: Sets `p`.
771
  p = self._param()
772
+ # AUTO: Appends a value to a list.
773
  params.append(p)
774
+ # AUTO: Repeats while this condition is true.
775
  while self._match(","):
776
+ # AUTO: Sets `p`.
777
  p = self._param()
778
+ # AUTO: Appends a value to a list.
779
  params.append(p)
780
+ # AUTO: Returns this result to the caller.
781
  return params
782
 
783
+ # AUTO: Defines function `_param`.
784
  def _param(self) -> Tuple[str, str]:
785
+ # AUTO: Sets `dtype`.
786
  dtype = self._advance()
787
+ # AUTO: Sets `id_tok`.
788
  id_tok = self._expect("id")
789
+ # AUTO: Executes this statement.
790
  type_name = dtype.value if dtype.type == "id" else dtype.type
791
+ # AUTO: Sets `is_array`.
792
  is_array = False
793
+ # AUTO: Checks this condition.
794
  if self._peek().type == "[":
795
+ # AUTO: Calls `self._advance`.
796
  self._advance()
797
+ # AUTO: Calls `self._expect`.
798
  self._expect("]")
799
+ # AUTO: Sets `is_array`.
800
  is_array = True
801
+ # AUTO: Returns this result to the caller.
802
  return (type_name, id_tok.value, is_array)
803
 
804
 
805
+ # AUTO: Defines function `_statement`.
806
  def _statement(self, allow_reclaim: bool = False):
807
+ # AUTO: Sets `stopping_tokens`.
808
  stopping_tokens = {"}", "EOF", "variety", "soil", "prune"}
809
+ # AUTO: Repeats while this condition is true.
810
  while self._peek().type not in stopping_tokens:
811
+ # AUTO: Sets `tok`.
812
  tok = self._peek()
813
+ # AUTO: Checks this condition.
814
  if tok.type == "reclaim":
815
+ # AUTO: Checks this condition.
816
  if not allow_reclaim:
817
+ # AUTO: Returns this result to the caller.
818
  return
819
+ # AUTO: Calls `self._return_stmt`.
820
  self._return_stmt()
821
+ # AUTO: Skips to the next loop iteration.
822
  continue
823
+ # AUTO: Checks this condition.
824
  if self._is_data_type(tok) or tok.type == "bundle" or tok.type == "fertile":
825
+ # AUTO: Checks this condition.
826
  if tok.type == "fertile":
827
+ # AUTO: Calls `self._const_dec`.
828
  self._const_dec()
829
+ # AUTO: Calls `self._expect`.
830
  self._expect(";")
831
+ # AUTO: Runs when previous condition did not pass.
832
  else:
833
+ # AUTO: Calls `self._var_dec`.
834
  self._var_dec()
835
+ # AUTO: Calls `self._expect`.
836
  self._expect(";")
837
+ # AUTO: Skips to the next loop iteration.
838
  continue
839
+ # AUTO: Calls `self._simple_stmt`.
840
  self._simple_stmt()
841
 
842
+ # AUTO: Defines function `_return_stmt`.
843
  def _return_stmt(self):
844
+ # AUTO: Calls `self._expect`.
845
  self._expect("reclaim")
846
+ # AUTO: Checks this condition.
847
  if self._peek().type == ";":
848
+ # AUTO: Calls `self._emit`.
849
  self._emit("RETURN")
850
+ # AUTO: Runs when previous condition did not pass.
851
  else:
852
+ # AUTO: Calls `self._emit`.
853
  self._emit("RETURN", self._expression())
854
+ # AUTO: Calls `self._expect`.
855
  self._expect(";")
856
 
857
+ # AUTO: Defines function `_simple_stmt`.
858
  def _simple_stmt(self):
859
+ # AUTO: Sets `tok`.
860
  tok = self._peek()
861
 
862
+ # AUTO: Checks this condition.
863
  if tok.type == "id":
864
+ # AUTO: Calls `self._id_stmt`.
865
  self._id_stmt()
866
+ # AUTO: Checks the next alternate condition.
867
  elif tok.type in ("water", "plant"):
868
+ # AUTO: Calls `self._io_stmt`.
869
  self._io_stmt()
870
+ # AUTO: Checks the next alternate condition.
871
  elif tok.type == "spring":
872
+ # AUTO: Calls `self._conditional_stmt`.
873
  self._conditional_stmt()
874
+ # AUTO: Checks the next alternate condition.
875
  elif tok.type in ("grow", "cultivate", "tend"):
876
+ # AUTO: Calls `self._loop_stmt`.
877
  self._loop_stmt()
878
+ # AUTO: Checks the next alternate condition.
879
  elif tok.type == "harvest":
880
+ # AUTO: Calls `self._switch_stmt`.
881
  self._switch_stmt()
882
+ # AUTO: Checks the next alternate condition.
883
  elif tok.type in ("prune", "skip"):
884
+ # AUTO: Calls `self._control_stmt`.
885
  self._control_stmt()
886
+ # AUTO: Runs when previous condition did not pass.
887
  else:
888
+ # AUTO: Appends a value to a list.
889
  self.errors.append(f"ICG Line {tok.line}: unexpected token '{tok.type}'")
890
+ # AUTO: Calls `self._advance`.
891
  self._advance()
892
 
893
 
894
+ # AUTO: Defines function `_id_stmt`.
895
  def _id_stmt(self):
896
+ # AUTO: Sets `id_tok`.
897
  id_tok = self._advance()
898
+ # AUTO: Sets `tok`.
899
  tok = self._peek()
900
 
901
+ # AUTO: Checks this condition.
902
  if tok.type == "(":
903
+ # AUTO: Calls `self._advance`.
904
  self._advance()
905
+ # AUTO: Sets `args`.
906
  args = self._arguments()
907
+ # AUTO: Calls `self._expect`.
908
  self._expect(")")
909
+ # AUTO: Calls `self._expect`.
910
  self._expect(";")
911
+ # AUTO: Starts a loop over these values.
912
  for a in args:
913
+ # AUTO: Calls `self._emit`.
914
  self._emit("PARAM", a)
915
+ # AUTO: Sets `tmp`.
916
  tmp = self._new_temp()
917
+ # AUTO: Calls `self._emit`.
918
  self._emit("CALL", id_tok.value, str(len(args)), tmp)
919
+ # AUTO: Returns this result to the caller.
920
  return
921
 
922
+ # AUTO: Checks this condition.
923
  if tok.type in ("++", "--"):
924
+ # AUTO: Sets `op_tok`.
925
  op_tok = self._advance()
926
+ # AUTO: Calls `self._expect`.
927
  self._expect(";")
928
+ # AUTO: Calls `self._emit`.
929
  self._emit("INC" if op_tok.type == "++" else "DEC", id_tok.value)
930
+ # AUTO: Returns this result to the caller.
931
  return
932
 
933
+ # AUTO: Sets `lhs`.
934
  lhs = id_tok.value
935
+ # AUTO: Sets `lhs`.
936
  lhs = self._resolve_lhs(lhs)
937
 
938
+ # AUTO: Checks this condition.
939
  if self._peek().type in ASSIGN_OPS:
940
+ # AUTO: Sets `op_tok`.
941
  op_tok = self._advance()
942
+ # AUTO: Sets `rhs`.
943
  rhs = self._expression()
944
+ # AUTO: Calls `self._expect`.
945
  self._expect(";")
946
 
947
+ # AUTO: Checks this condition.
948
  if op_tok.type == "=":
949
+ # AUTO: Sets `self._emit("`.
950
  self._emit("=", rhs, None, lhs)
951
+ # AUTO: Runs when previous condition did not pass.
952
  else:
953
+ # AUTO: Sets `base_op`.
954
  base_op = op_tok.type[0]
955
+ # AUTO: Sets `tmp`.
956
  tmp = self._new_temp()
957
+ # AUTO: Calls `self._emit`.
958
  self._emit(base_op, lhs, rhs, tmp)
959
+ # AUTO: Sets `self._emit("`.
960
  self._emit("=", tmp, None, lhs)
961
+ # AUTO: Runs when previous condition did not pass.
962
  else:
963
+ # AUTO: Appends a value to a list.
964
  self.errors.append(f"ICG Line {tok.line}: unexpected token after id '{id_tok.value}'")
965
+ # AUTO: Repeats while this condition is true.
966
  while self._peek().type not in (";", "}", "EOF"):
967
+ # AUTO: Calls `self._advance`.
968
  self._advance()
969
+ # AUTO: Calls `self._match`.
970
  self._match(";")
971
 
972
+ # AUTO: Defines function `_resolve_lhs`.
973
  def _resolve_lhs(self, base: str) -> str:
974
+ # AUTO: Sets `tok`.
975
  tok = self._peek()
976
+ # AUTO: Checks this condition.
977
  if tok.type == "[":
978
+ # AUTO: Calls `self._advance`.
979
  self._advance()
980
+ # AUTO: Sets `idx`.
981
  idx = self._expression()
982
+ # AUTO: Calls `self._expect`.
983
  self._expect("]")
984
+ # AUTO: Repeats while this condition is true.
985
  while self._peek().type == "[":
986
+ # AUTO: Calls `self._advance`.
987
  self._advance()
988
+ # AUTO: Sets `idx2`.
989
  idx2 = self._expression()
990
+ # AUTO: Calls `self._expect`.
991
  self._expect("]")
992
+ # AUTO: Sets `tmp`.
993
  tmp = self._new_temp()
994
+ # AUTO: Calls `self._emit`.
995
  self._emit("*", idx, idx2, tmp)
996
+ # AUTO: Sets `idx`.
997
  idx = tmp
998
+ # AUTO: Returns this result to the caller.
999
  return f"{base}[{idx}]"
1000
+ # AUTO: Checks the next alternate condition.
1001
  elif tok.type == ".":
1002
+ # AUTO: Calls `self._advance`.
1003
  self._advance()
1004
+ # AUTO: Sets `member`.
1005
  member = self._expect("id")
1006
+ # AUTO: Sets `chain`.
1007
  chain = f"{base}.{member.value}"
1008
+ # AUTO: Repeats while this condition is true.
1009
  while self._peek().type == ".":
1010
+ # AUTO: Calls `self._advance`.
1011
  self._advance()
1012
+ # AUTO: Sets `m2`.
1013
  m2 = self._expect("id")
1014
+ # AUTO: Sets `chain`.
1015
  chain = f"{chain}.{m2.value}"
1016
+ # AUTO: Returns this result to the caller.
1017
  return chain
1018
+ # AUTO: Returns this result to the caller.
1019
  return base
1020
 
1021
 
1022
+ # AUTO: Defines function `_io_stmt`.
1023
  def _io_stmt(self):
1024
+ # AUTO: Sets `io_tok`.
1025
  io_tok = self._advance()
1026
+ # AUTO: Calls `self._expect`.
1027
  self._expect("(")
1028
+ # AUTO: Sets `args`.
1029
  args = self._arguments()
1030
+ # AUTO: Calls `self._expect`.
1031
  self._expect(")")
1032
+ # AUTO: Calls `self._expect`.
1033
  self._expect(";")
1034
 
1035
+ # AUTO: Checks this condition.
1036
  if io_tok.type == "plant":
1037
+ # AUTO: Starts a loop over these values.
1038
  for a in args:
1039
+ # AUTO: Calls `self._emit`.
1040
  self._emit("PRINT", a)
1041
+ # AUTO: Runs when previous condition did not pass.
1042
  else:
1043
+ # AUTO: Starts a loop over these values.
1044
  for a in args:
1045
+ # AUTO: Calls `self._emit`.
1046
  self._emit("READ", None, None, a)
1047
 
1048
 
1049
+ # AUTO: Defines function `_conditional_stmt`.
1050
  def _conditional_stmt(self):
1051
+ # AUTO: Calls `self._expect`.
1052
  self._expect("spring")
1053
+ # AUTO: Calls `self._expect`.
1054
  self._expect("(")
1055
+ # AUTO: Sets `cond`.
1056
  cond = self._expression()
1057
+ # AUTO: Calls `self._expect`.
1058
  self._expect(")")
1059
 
1060
+ # AUTO: Sets `false_label`.
1061
  false_label = self._new_label()
1062
+ # AUTO: Sets `end_label`.
1063
  end_label = self._new_label()
1064
 
1065
+ # AUTO: Calls `self._emit`.
1066
  self._emit("IFFALSE", cond, None, false_label)
1067
 
1068
+ # AUTO: Calls `self._expect`.
1069
  self._expect("{")
1070
+ # AUTO: Sets `self._statement(allow_reclaim`.
1071
  self._statement(allow_reclaim=True)
1072
+ # AUTO: Calls `self._expect`.
1073
  self._expect("}")
1074
 
1075
+ # AUTO: Calls `self._emit`.
1076
  self._emit("GOTO", None, None, end_label)
1077
+ # AUTO: Calls `self._emit`.
1078
  self._emit("LABEL", None, None, false_label)
1079
 
1080
+ # AUTO: Calls `self._elseif_chain`.
1081
  self._elseif_chain(end_label)
1082
 
1083
+ # AUTO: Checks this condition.
1084
  if self._peek().type == "wither":
1085
+ # AUTO: Calls `self._advance`.
1086
  self._advance()
1087
+ # AUTO: Calls `self._expect`.
1088
  self._expect("{")
1089
+ # AUTO: Sets `self._statement(allow_reclaim`.
1090
  self._statement(allow_reclaim=True)
1091
+ # AUTO: Calls `self._expect`.
1092
  self._expect("}")
1093
 
1094
+ # AUTO: Calls `self._emit`.
1095
  self._emit("LABEL", None, None, end_label)
1096
 
1097
+ # AUTO: Defines function `_elseif_chain`.
1098
  def _elseif_chain(self, end_label: str):
1099
+ # AUTO: Repeats while this condition is true.
1100
  while self._peek().type == "bud":
1101
+ # AUTO: Calls `self._advance`.
1102
  self._advance()
1103
+ # AUTO: Calls `self._expect`.
1104
  self._expect("(")
1105
+ # AUTO: Sets `cond`.
1106
  cond = self._expression()
1107
+ # AUTO: Calls `self._expect`.
1108
  self._expect(")")
1109
 
1110
+ # AUTO: Sets `next_label`.
1111
  next_label = self._new_label()
1112
+ # AUTO: Calls `self._emit`.
1113
  self._emit("IFFALSE", cond, None, next_label)
1114
 
1115
+ # AUTO: Calls `self._expect`.
1116
  self._expect("{")
1117
+ # AUTO: Sets `self._statement(allow_reclaim`.
1118
  self._statement(allow_reclaim=True)
1119
+ # AUTO: Calls `self._expect`.
1120
  self._expect("}")
1121
 
1122
+ # AUTO: Calls `self._emit`.
1123
  self._emit("GOTO", None, None, end_label)
1124
+ # AUTO: Calls `self._emit`.
1125
  self._emit("LABEL", None, None, next_label)
1126
 
1127
 
1128
+ # AUTO: Defines function `_loop_stmt`.
1129
  def _loop_stmt(self):
1130
+ # AUTO: Sets `tok`.
1131
  tok = self._peek()
1132
+ # AUTO: Checks this condition.
1133
  if tok.type == "grow":
1134
+ # AUTO: Calls `self._while_loop`.
1135
  self._while_loop()
1136
+ # AUTO: Checks the next alternate condition.
1137
  elif tok.type == "cultivate":
1138
+ # AUTO: Calls `self._for_loop`.
1139
  self._for_loop()
1140
+ # AUTO: Checks the next alternate condition.
1141
  elif tok.type == "tend":
1142
+ # AUTO: Calls `self._do_while_loop`.
1143
  self._do_while_loop()
1144
 
1145
+ # AUTO: Defines function `_while_loop`.
1146
  def _while_loop(self):
1147
+ # AUTO: Calls `self._expect`.
1148
  self._expect("grow")
1149
+ # AUTO: Calls `self._expect`.
1150
  self._expect("(")
1151
 
1152
+ # AUTO: Sets `start_label`.
1153
  start_label = self._new_label()
1154
+ # AUTO: Sets `end_label`.
1155
  end_label = self._new_label()
1156
 
1157
+ # AUTO: Calls `self._emit`.
1158
  self._emit("LABEL", None, None, start_label)
1159
+ # AUTO: Sets `cond`.
1160
  cond = self._expression()
1161
+ # AUTO: Calls `self._expect`.
1162
  self._expect(")")
1163
 
1164
+ # AUTO: Calls `self._emit`.
1165
  self._emit("IFFALSE", cond, None, end_label)
1166
 
1167
+ # AUTO: Calls `self._expect`.
1168
  self._expect("{")
1169
+ # AUTO: Sets `self._statement(allow_reclaim`.
1170
  self._statement(allow_reclaim=True)
1171
+ # AUTO: Calls `self._expect`.
1172
  self._expect("}")
1173
 
1174
+ # AUTO: Calls `self._emit`.
1175
  self._emit("GOTO", None, None, start_label)
1176
+ # AUTO: Calls `self._emit`.
1177
  self._emit("LABEL", None, None, end_label)
1178
 
1179
+ # AUTO: Defines function `_for_loop`.
1180
  def _for_loop(self):
1181
+ # AUTO: Calls `self._expect`.
1182
  self._expect("cultivate")
1183
+ # AUTO: Calls `self._expect`.
1184
  self._expect("(")
1185
 
1186
+ # AUTO: Calls `self._for_init`.
1187
  self._for_init()
1188
+ # AUTO: Calls `self._expect`.
1189
  self._expect(";")
1190
 
1191
+ # AUTO: Sets `start_label`.
1192
  start_label = self._new_label()
1193
+ # AUTO: Sets `end_label`.
1194
  end_label = self._new_label()
1195
+ # AUTO: Sets `update_label`.
1196
  update_label = self._new_label()
1197
 
1198
+ # AUTO: Calls `self._emit`.
1199
  self._emit("LABEL", None, None, start_label)
1200
 
1201
+ # AUTO: Sets `cond`.
1202
  cond = self._expression()
1203
+ # AUTO: Calls `self._emit`.
1204
  self._emit("IFFALSE", cond, None, end_label)
1205
 
1206
+ # AUTO: Calls `self._expect`.
1207
  self._expect(";")
1208
 
1209
+ # AUTO: Sets `update_instrs: List[TACInstruction]`.
1210
  update_instrs: List[TACInstruction] = []
1211
+ # AUTO: Sets `saved_code`.
1212
  saved_code = self.code
1213
+ # AUTO: Sets `self.code`.
1214
  self.code = update_instrs
1215
+ # AUTO: Calls `self._for_update`.
1216
  self._for_update()
1217
+ # AUTO: Sets `self.code`.
1218
  self.code = saved_code
1219
 
1220
+ # AUTO: Calls `self._expect`.
1221
  self._expect(")")
1222
+ # AUTO: Calls `self._expect`.
1223
  self._expect("{")
1224
+ # AUTO: Sets `self._statement(allow_reclaim`.
1225
  self._statement(allow_reclaim=True)
1226
+ # AUTO: Calls `self._expect`.
1227
  self._expect("}")
1228
 
1229
+ # AUTO: Calls `self._emit`.
1230
  self._emit("LABEL", None, None, update_label)
1231
+ # AUTO: Calls `self.code.extend`.
1232
  self.code.extend(update_instrs)
1233
+ # AUTO: Calls `self._emit`.
1234
  self._emit("GOTO", None, None, start_label)
1235
+ # AUTO: Calls `self._emit`.
1236
  self._emit("LABEL", None, None, end_label)
1237
 
1238
+ # AUTO: Defines function `_for_init`.
1239
  def _for_init(self):
1240
+ # AUTO: Sets `tok`.
1241
  tok = self._peek()
1242
+ # AUTO: Checks this condition.
1243
  if self._is_data_type(tok):
1244
+ # AUTO: Calls `self._var_dec`.
1245
  self._var_dec()
1246
+ # AUTO: Checks the next alternate condition.
1247
  elif tok.type == "id":
1248
+ # AUTO: Sets `id_tok`.
1249
  id_tok = self._advance()
1250
+ # AUTO: Sets `lhs`.
1251
  lhs = self._resolve_lhs(id_tok.value)
1252
+ # AUTO: Checks this condition.
1253
  if self._peek().type in ASSIGN_OPS:
1254
+ # AUTO: Sets `op_tok`.
1255
  op_tok = self._advance()
1256
+ # AUTO: Sets `rhs`.
1257
  rhs = self._expression()
1258
+ # AUTO: Checks this condition.
1259
  if op_tok.type == "=":
1260
+ # AUTO: Sets `self._emit("`.
1261
  self._emit("=", rhs, None, lhs)
1262
+ # AUTO: Runs when previous condition did not pass.
1263
  else:
1264
+ # AUTO: Sets `base_op`.
1265
  base_op = op_tok.type[0]
1266
+ # AUTO: Sets `tmp`.
1267
  tmp = self._new_temp()
1268
+ # AUTO: Calls `self._emit`.
1269
  self._emit(base_op, lhs, rhs, tmp)
1270
+ # AUTO: Sets `self._emit("`.
1271
  self._emit("=", tmp, None, lhs)
1272
 
1273
+ # AUTO: Defines function `_for_update`.
1274
  def _for_update(self):
1275
+ # AUTO: Checks this condition.
1276
  if self._peek().type == "id":
1277
+ # AUTO: Sets `id_tok`.
1278
  id_tok = self._advance()
1279
+ # AUTO: Sets `tok`.
1280
  tok = self._peek()
1281
+ # AUTO: Checks this condition.
1282
  if tok.type in ("++", "--"):
1283
+ # AUTO: Sets `op`.
1284
  op = self._advance()
1285
+ # AUTO: Calls `self._emit`.
1286
  self._emit("INC" if op.type == "++" else "DEC", id_tok.value)
1287
+ # AUTO: Runs when previous condition did not pass.
1288
  else:
1289
+ # AUTO: Sets `lhs`.
1290
  lhs = self._resolve_lhs(id_tok.value)
1291
+ # AUTO: Checks this condition.
1292
  if self._peek().type in ASSIGN_OPS:
1293
+ # AUTO: Sets `op_tok`.
1294
  op_tok = self._advance()
1295
+ # AUTO: Sets `rhs`.
1296
  rhs = self._expression()
1297
+ # AUTO: Checks this condition.
1298
  if op_tok.type == "=":
1299
+ # AUTO: Sets `self._emit("`.
1300
  self._emit("=", rhs, None, lhs)
1301
+ # AUTO: Runs when previous condition did not pass.
1302
  else:
1303
+ # AUTO: Sets `base_op`.
1304
  base_op = op_tok.type[0]
1305
+ # AUTO: Sets `tmp`.
1306
  tmp = self._new_temp()
1307
+ # AUTO: Calls `self._emit`.
1308
  self._emit(base_op, lhs, rhs, tmp)
1309
+ # AUTO: Sets `self._emit("`.
1310
  self._emit("=", tmp, None, lhs)
1311
 
1312
+ # AUTO: Defines function `_do_while_loop`.
1313
  def _do_while_loop(self):
1314
+ # AUTO: Calls `self._expect`.
1315
  self._expect("tend")
1316
+ # AUTO: Calls `self._expect`.
1317
  self._expect("{")
1318
 
1319
+ # AUTO: Sets `start_label`.
1320
  start_label = self._new_label()
1321
+ # AUTO: Calls `self._emit`.
1322
  self._emit("LABEL", None, None, start_label)
1323
 
1324
+ # AUTO: Sets `self._statement(allow_reclaim`.
1325
  self._statement(allow_reclaim=True)
1326
+ # AUTO: Calls `self._expect`.
1327
  self._expect("}")
1328
 
1329
+ # AUTO: Calls `self._expect`.
1330
  self._expect("grow")
1331
+ # AUTO: Calls `self._expect`.
1332
  self._expect("(")
1333
+ # AUTO: Sets `cond`.
1334
  cond = self._expression()
1335
+ # AUTO: Calls `self._expect`.
1336
  self._expect(")")
1337
+ # AUTO: Calls `self._expect`.
1338
  self._expect(";")
1339
 
1340
+ # AUTO: Calls `self._emit`.
1341
  self._emit("IF", cond, None, start_label)
1342
 
1343
 
1344
+ # AUTO: Defines function `_switch_stmt`.
1345
  def _switch_stmt(self):
1346
+ # AUTO: Calls `self._expect`.
1347
  self._expect("harvest")
1348
+ # AUTO: Calls `self._expect`.
1349
  self._expect("(")
1350
+ # AUTO: Sets `expr`.
1351
  expr = self._expression()
1352
+ # AUTO: Calls `self._expect`.
1353
  self._expect(")")
1354
+ # AUTO: Calls `self._expect`.
1355
  self._expect("{")
1356
 
1357
+ # AUTO: Sets `end_label`.
1358
  end_label = self._new_label()
1359
+ # AUTO: Calls `self._case_list`.
1360
  self._case_list(expr, end_label)
1361
+ # AUTO: Calls `self._default_opt`.
1362
  self._default_opt(end_label)
1363
 
1364
+ # AUTO: Calls `self._expect`.
1365
  self._expect("}")
1366
+ # AUTO: Calls `self._emit`.
1367
  self._emit("LABEL", None, None, end_label)
1368
 
1369
+ # AUTO: Defines function `_case_list`.
1370
  def _case_list(self, switch_expr: str, end_label: str):
1371
+ # AUTO: Repeats while this condition is true.
1372
  while self._peek().type == "variety":
1373
+ # AUTO: Calls `self._advance`.
1374
  self._advance()
1375
+ # AUTO: Sets `case_val`.
1376
  case_val = self._expression()
1377
+ # AUTO: Calls `self._expect`.
1378
  self._expect(":")
1379
 
1380
+ # AUTO: Sets `next_label`.
1381
  next_label = self._new_label()
1382
+ # AUTO: Sets `body_label`.
1383
  body_label = self._new_label()
1384
+ # AUTO: Sets `cmp_tmp`.
1385
  cmp_tmp = self._new_temp()
1386
 
1387
+ # AUTO: Calls `self._emit`.
1388
  self._emit("==", switch_expr, case_val, cmp_tmp)
1389
+ # AUTO: Calls `self._emit`.
1390
  self._emit("IFFALSE", cmp_tmp, None, next_label)
1391
+ # AUTO: Calls `self._emit`.
1392
  self._emit("LABEL", None, None, body_label)
1393
 
1394
+ # AUTO: Calls `self._declaration`.
1395
  self._declaration()
1396
+ # AUTO: Calls `self._case_statements`.
1397
  self._case_statements()
1398
 
1399
+ # AUTO: Checks this condition.
1400
  if self._peek().type == "prune":
1401
+ # AUTO: Calls `self._advance`.
1402
  self._advance()
1403
+ # AUTO: Calls `self._expect`.
1404
  self._expect(";")
1405
 
1406
+ # AUTO: Calls `self._emit`.
1407
  self._emit("GOTO", None, None, end_label)
1408
+ # AUTO: Calls `self._emit`.
1409
  self._emit("LABEL", None, None, next_label)
1410
 
1411
+ # AUTO: Defines function `_case_statements`.
1412
  def _case_statements(self):
1413
+ # AUTO: Repeats while this condition is true.
1414
  while self._peek().type not in ("variety", "soil", "}", "prune", "EOF"):
1415
+ # AUTO: Sets `tok`.
1416
  tok = self._peek()
1417
+ # AUTO: Checks this condition.
1418
  if tok.type == "id":
1419
+ # AUTO: Calls `self._id_stmt`.
1420
  self._id_stmt()
1421
+ # AUTO: Checks the next alternate condition.
1422
  elif tok.type == "water":
1423
+ # AUTO: Calls `self._io_stmt`.
1424
  self._io_stmt()
1425
+ # AUTO: Checks the next alternate condition.
1426
  elif tok.type == "plant":
1427
+ # AUTO: Calls `self._io_stmt`.
1428
  self._io_stmt()
1429
+ # AUTO: Checks the next alternate condition.
1430
  elif tok.type == "skip":
1431
+ # AUTO: Calls `self._advance`.
1432
  self._advance()
1433
+ # AUTO: Calls `self._expect`.
1434
  self._expect(";")
1435
+ # AUTO: Checks the next alternate condition.
1436
  elif tok.type == "reclaim":
1437
+ # AUTO: Calls `self._return_stmt`.
1438
  self._return_stmt()
1439
+ # AUTO: Runs when previous condition did not pass.
1440
  else:
1441
+ # AUTO: Stops the nearest loop.
1442
  break
1443
 
1444
+ # AUTO: Defines function `_default_opt`.
1445
  def _default_opt(self, end_label: str):
1446
+ # AUTO: Checks this condition.
1447
  if self._peek().type == "soil":
1448
+ # AUTO: Calls `self._advance`.
1449
  self._advance()
1450
+ # AUTO: Calls `self._expect`.
1451
  self._expect(":")
1452
+ # AUTO: Calls `self._declaration`.
1453
  self._declaration()
1454
+ # AUTO: Calls `self._case_statements`.
1455
  self._case_statements()
1456
 
1457
 
1458
+ # AUTO: Defines function `_control_stmt`.
1459
  def _control_stmt(self):
1460
+ # AUTO: Sets `tok`.
1461
  tok = self._advance()
1462
+ # AUTO: Calls `self._expect`.
1463
  self._expect(";")
1464
+ # AUTO: Checks this condition.
1465
  if tok.type == "prune":
1466
+ # AUTO: Calls `self._emit`.
1467
  self._emit("GOTO", None, None, "BREAK")
1468
+ # AUTO: Runs when previous condition did not pass.
1469
  else:
1470
+ # AUTO: Calls `self._emit`.
1471
  self._emit("GOTO", None, None, "CONTINUE")
1472
 
1473
 
1474
+ # AUTO: Defines function `_expression`.
1475
  def _expression(self) -> str:
1476
+ # AUTO: Sets `left`.
1477
  left = self._logic_or()
1478
+ # AUTO: Checks this condition.
1479
  if self._peek().type not in ASSIGN_OPS:
1480
+ # AUTO: Returns this result to the caller.
1481
  return left
1482
 
1483
+ # AUTO: Sets `op_tok`.
1484
  op_tok = self._advance()
1485
+ # AUTO: Sets `right`.
1486
  right = self._expression()
1487
+ # AUTO: Checks this condition.
1488
  if op_tok.type == "=":
1489
+ # AUTO: Sets `self._emit("`.
1490
  self._emit("=", right, None, left)
1491
+ # AUTO: Runs when previous condition did not pass.
1492
  else:
1493
+ # AUTO: Sets `tmp`.
1494
  tmp = self._new_temp()
1495
+ # AUTO: Calls `self._emit`.
1496
  self._emit(op_tok.type[0], left, right, tmp)
1497
+ # AUTO: Sets `self._emit("`.
1498
  self._emit("=", tmp, None, left)
1499
+ # AUTO: Returns this result to the caller.
1500
  return left
1501
 
1502
+ # AUTO: Defines function `_logic_or`.
1503
  def _logic_or(self) -> str:
1504
+ # AUTO: Sets `left`.
1505
  left = self._logic_and()
1506
+ # AUTO: Repeats while this condition is true.
1507
  while self._peek().type == "||":
1508
+ # AUTO: Calls `self._advance`.
1509
  self._advance()
1510
+ # AUTO: Sets `right`.
1511
  right = self._logic_and()
1512
+ # AUTO: Sets `tmp`.
1513
  tmp = self._new_temp()
1514
+ # AUTO: Calls `self._emit`.
1515
  self._emit("||", left, right, tmp)
1516
+ # AUTO: Sets `left`.
1517
  left = tmp
1518
+ # AUTO: Returns this result to the caller.
1519
  return left
1520
 
1521
+ # AUTO: Defines function `_logic_and`.
1522
  def _logic_and(self) -> str:
1523
+ # AUTO: Sets `left`.
1524
  left = self._relational()
1525
+ # AUTO: Repeats while this condition is true.
1526
  while self._peek().type == "&&":
1527
+ # AUTO: Calls `self._advance`.
1528
  self._advance()
1529
+ # AUTO: Sets `right`.
1530
  right = self._relational()
1531
+ # AUTO: Sets `tmp`.
1532
  tmp = self._new_temp()
1533
+ # AUTO: Calls `self._emit`.
1534
  self._emit("&&", left, right, tmp)
1535
+ # AUTO: Sets `left`.
1536
  left = tmp
1537
+ # AUTO: Returns this result to the caller.
1538
  return left
1539
 
1540
+ # AUTO: Defines function `_relational`.
1541
  def _relational(self) -> str:
1542
+ # AUTO: Sets `left`.
1543
  left = self._arithmetic()
1544
+ # AUTO: Checks this condition.
1545
  if self._peek().type in (">", "<", ">=", "<=", "==", "!="):
1546
+ # AUTO: Sets `op`.
1547
  op = self._advance().type
1548
+ # AUTO: Sets `right`.
1549
  right = self._arithmetic()
1550
+ # AUTO: Sets `tmp`.
1551
  tmp = self._new_temp()
1552
+ # AUTO: Calls `self._emit`.
1553
  self._emit(op, left, right, tmp)
1554
+ # AUTO: Returns this result to the caller.
1555
  return tmp
1556
+ # AUTO: Returns this result to the caller.
1557
  return left
1558
 
1559
+ # AUTO: Defines function `_arithmetic`.
1560
  def _arithmetic(self) -> str:
1561
+ # AUTO: Sets `left`.
1562
  left = self._term()
1563
+ # AUTO: Repeats while this condition is true.
1564
  while self._peek().type in ("+", "-"):
1565
+ # AUTO: Sets `op`.
1566
  op = self._advance().type
1567
+ # AUTO: Sets `right`.
1568
  right = self._term()
1569
+ # AUTO: Sets `tmp`.
1570
  tmp = self._new_temp()
1571
+ # AUTO: Calls `self._emit`.
1572
  self._emit(op, left, right, tmp)
1573
+ # AUTO: Sets `left`.
1574
  left = tmp
1575
+ # AUTO: Returns this result to the caller.
1576
  return left
1577
 
1578
+ # AUTO: Defines function `_term`.
1579
  def _term(self) -> str:
1580
+ # AUTO: Sets `left`.
1581
  left = self._power()
1582
+ # AUTO: Repeats while this condition is true.
1583
  while self._peek().type in ("*", "/", "%"):
1584
+ # AUTO: Sets `op`.
1585
  op = self._advance().type
1586
+ # AUTO: Sets `right`.
1587
  right = self._power()
1588
+ # AUTO: Sets `tmp`.
1589
  tmp = self._new_temp()
1590
+ # AUTO: Calls `self._emit`.
1591
  self._emit(op, left, right, tmp)
1592
+ # AUTO: Sets `left`.
1593
  left = tmp
1594
+ # AUTO: Returns this result to the caller.
1595
  return left
1596
 
1597
+ # AUTO: Defines function `_power`.
1598
  def _power(self) -> str:
1599
+ # AUTO: Sets `left`.
1600
  left = self._factor()
1601
+ # AUTO: Checks this condition.
1602
  if self._peek().type == "**":
1603
+ # AUTO: Sets `op`.
1604
  op = self._advance().type
1605
+ # AUTO: Sets `right`.
1606
  right = self._power()
1607
+ # AUTO: Sets `tmp`.
1608
  tmp = self._new_temp()
1609
+ # AUTO: Calls `self._emit`.
1610
  self._emit(op, left, right, tmp)
1611
+ # AUTO: Returns this result to the caller.
1612
  return tmp
1613
+ # AUTO: Returns this result to the caller.
1614
  return left
1615
 
1616
+ # AUTO: Defines function `_factor`.
1617
  def _factor(self) -> str:
1618
+ # AUTO: Sets `tok`.
1619
  tok = self._peek()
1620
 
1621
+ # AUTO: Checks this condition.
1622
  if tok.type == "(":
1623
+ # AUTO: Calls `self._advance`.
1624
  self._advance()
1625
+ # AUTO: Sets `val`.
1626
  val = self._expression()
1627
+ # AUTO: Calls `self._expect`.
1628
  self._expect(")")
1629
+ # AUTO: Returns this result to the caller.
1630
  return val
1631
 
1632
+ # AUTO: Checks this condition.
1633
  if tok.type in ("~", "!"):
1634
+ # AUTO: Sets `op`.
1635
  op = self._advance()
1636
+ # AUTO: Sets `inner`.
1637
  inner = self._factor()
1638
+ # AUTO: Sets `tmp`.
1639
  tmp = self._new_temp()
1640
+ # AUTO: Checks this condition.
1641
  if op.type == "~":
1642
+ # AUTO: Calls `self._emit`.
1643
  self._emit("UNARY_MINUS", inner, None, tmp)
1644
+ # AUTO: Runs when previous condition did not pass.
1645
  else:
1646
+ # AUTO: Calls `self._emit`.
1647
  self._emit("NOT", inner, None, tmp)
1648
+ # AUTO: Returns this result to the caller.
1649
  return tmp
1650
 
1651
+ # AUTO: Checks this condition.
1652
  if tok.type == "id":
1653
+ # AUTO: Sets `id_tok`.
1654
  id_tok = self._advance()
1655
+ # AUTO: Sets `nxt`.
1656
  nxt = self._peek()
1657
 
1658
+ # AUTO: Checks this condition.
1659
  if nxt.type == "(":
1660
+ # AUTO: Calls `self._advance`.
1661
  self._advance()
1662
+ # AUTO: Sets `args`.
1663
  args = self._arguments()
1664
+ # AUTO: Calls `self._expect`.
1665
  self._expect(")")
1666
+ # AUTO: Starts a loop over these values.
1667
  for a in args:
1668
+ # AUTO: Calls `self._emit`.
1669
  self._emit("PARAM", a)
1670
+ # AUTO: Sets `tmp`.
1671
  tmp = self._new_temp()
1672
+ # AUTO: Calls `self._emit`.
1673
  self._emit("CALL", id_tok.value, str(len(args)), tmp)
1674
+ # AUTO: Returns this result to the caller.
1675
  return tmp
1676
 
1677
+ # AUTO: Checks this condition.
1678
  if nxt.type == "[":
1679
+ # AUTO: Calls `self._advance`.
1680
  self._advance()
1681
+ # AUTO: Sets `idx`.
1682
  idx = self._expression()
1683
+ # AUTO: Calls `self._expect`.
1684
  self._expect("]")
1685
+ # AUTO: Repeats while this condition is true.
1686
  while self._peek().type == "[":
1687
+ # AUTO: Calls `self._advance`.
1688
  self._advance()
1689
+ # AUTO: Sets `idx2`.
1690
  idx2 = self._expression()
1691
+ # AUTO: Calls `self._expect`.
1692
  self._expect("]")
1693
+ # AUTO: Sets `tmp`.
1694
  tmp = self._new_temp()
1695
+ # AUTO: Calls `self._emit`.
1696
  self._emit("*", idx, idx2, tmp)
1697
+ # AUTO: Sets `idx`.
1698
  idx = tmp
1699
+ # AUTO: Sets `location`.
1700
  location = f"{id_tok.value}[{idx}]"
1701
+ # AUTO: Checks this condition.
1702
  if self._peek().type in ASSIGN_OPS:
1703
+ # AUTO: Returns this result to the caller.
1704
  return location
1705
+ # AUTO: Sets `tmp`.
1706
  tmp = self._new_temp()
1707
+ # AUTO: Calls `self._emit`.
1708
  self._emit("ARRAY_LOAD", id_tok.value, idx, tmp)
1709
+ # AUTO: Returns this result to the caller.
1710
  return tmp
1711
 
1712
+ # AUTO: Checks this condition.
1713
  if nxt.type == ".":
1714
+ # AUTO: Calls `self._advance`.
1715
  self._advance()
1716
+ # AUTO: Sets `member`.
1717
  member = self._expect("id")
1718
+ # AUTO: Sets `chain`.
1719
  chain = member.value
1720
+ # AUTO: Repeats while this condition is true.
1721
  while self._peek().type == ".":
1722
+ # AUTO: Calls `self._advance`.
1723
  self._advance()
1724
+ # AUTO: Sets `m2`.
1725
  m2 = self._expect("id")
1726
+ # AUTO: Sets `chain`.
1727
  chain = f"{chain}.{m2.value}"
1728
+ # AUTO: Sets `location`.
1729
  location = f"{id_tok.value}.{chain}"
1730
+ # AUTO: Checks this condition.
1731
  if self._peek().type in ASSIGN_OPS:
1732
+ # AUTO: Returns this result to the caller.
1733
  return location
1734
+ # AUTO: Sets `tmp`.
1735
  tmp = self._new_temp()
1736
+ # AUTO: Calls `self._emit`.
1737
  self._emit("STRUCT_LOAD", id_tok.value, chain, tmp)
1738
+ # AUTO: Returns this result to the caller.
1739
  return tmp
1740
 
1741
+ # AUTO: Returns this result to the caller.
1742
  return id_tok.value
1743
 
1744
+ # AUTO: Checks this condition.
1745
  if tok.type == "intlit":
1746
+ # AUTO: Returns this result to the caller.
1747
  return self._advance().value
1748
+ # AUTO: Checks this condition.
1749
  if tok.type == "dblit":
1750
+ # AUTO: Returns this result to the caller.
1751
  return self._advance().value
1752
+ # AUTO: Checks this condition.
1753
  if tok.type == "chrlit":
1754
+ # AUTO: Returns this result to the caller.
1755
  return self._advance().value
1756
+ # AUTO: Checks this condition.
1757
  if tok.type == "stringlit":
1758
+ # AUTO: Returns this result to the caller.
1759
  return self._advance().value
1760
+ # AUTO: Checks this condition.
1761
  if tok.type == "sunshine":
1762
+ # AUTO: Calls `self._advance`.
1763
  self._advance()
1764
+ # AUTO: Returns this result to the caller.
1765
  return "true"
1766
+ # AUTO: Checks this condition.
1767
  if tok.type == "frost":
1768
+ # AUTO: Calls `self._advance`.
1769
  self._advance()
1770
+ # AUTO: Returns this result to the caller.
1771
  return "false"
1772
 
1773
+ # AUTO: Appends a value to a list.
1774
  self.errors.append(f"ICG Line {tok.line}: unexpected token in expression: '{tok.type}'")
1775
+ # AUTO: Calls `self._advance`.
1776
  self._advance()
1777
+ # AUTO: Returns this result to the caller.
1778
  return "???"
1779
 
1780
 
1781
+ # AUTO: Defines function `_arguments`.
1782
  def _arguments(self) -> List[str]:
1783
+ # AUTO: Sets `args: List[str]`.
1784
  args: List[str] = []
1785
+ # AUTO: Checks this condition.
1786
  if self._peek().type in (")", "EOF"):
1787
+ # AUTO: Returns this result to the caller.
1788
  return args
1789
+ # AUTO: Appends a value to a list.
1790
  args.append(self._expression())
1791
+ # AUTO: Repeats while this condition is true.
1792
  while self._match(","):
1793
+ # AUTO: Appends a value to a list.
1794
  args.append(self._expression())
1795
+ # AUTO: Returns this result to the caller.
1796
  return args
1797
 
1798
 
1799
+ # AUTO: Defines function `generate_icg`.
1800
  def generate_icg(tokens: List[Any]) -> Dict[str, Any]:
1801
+ # AUTO: Sets `gen`.
1802
  gen = ICGenerator(tokens)
1803
+ # AUTO: Sets `code, errors`.
1804
  code, errors = gen.generate()
1805
 
1806
+ # AUTO: Sets `tac_dicts`.
1807
  tac_dicts = [instr.to_dict() for instr in code]
1808
+ # AUTO: Sets `tac_text`.
1809
  tac_text = "\n".join(str(instr) for instr in code)
1810
 
1811
+ # AUTO: Returns this result to the caller.
1812
  return {
1813
+ # AUTO: Executes this statement.
1814
  "success": len(errors) == 0,
1815
+ # AUTO: Executes this statement.
1816
  "tac": tac_dicts,
1817
+ # AUTO: Executes this statement.
1818
  "tac_text": tac_text,
1819
+ # AUTO: Executes this statement.
1820
  "errors": errors,
1821
+ # AUTO: Closes the current grouped code/data.
1822
  }
Backend/interpreter/__init__.py CHANGED
@@ -1,8 +1,15 @@
1
 
 
2
  from .interpreter import Interpreter # noqa: F401
 
3
  from .errors import ( # noqa: F401 - convenience re-exports
 
4
  InterpreterError,
 
5
  _CancelledError,
 
6
  InterpreterInputRequest,
 
7
  ReturnValue,
 
8
  )
 
1
 
2
+ # AUTO: Imports names from another module.
3
  from .interpreter import Interpreter # noqa: F401
4
+ # AUTO: Imports names from another module.
5
  from .errors import ( # noqa: F401 - convenience re-exports
6
+ # AUTO: Executes this statement.
7
  InterpreterError,
8
+ # AUTO: Executes this statement.
9
  _CancelledError,
10
+ # AUTO: Executes this statement.
11
  InterpreterInputRequest,
12
+ # AUTO: Executes this statement.
13
  ReturnValue,
14
+ # AUTO: Closes the current grouped code/data.
15
  )
Backend/interpreter/errors.py CHANGED
@@ -1,34 +1,55 @@
 
1
  import re
2
 
 
3
  _REDUNDANT_PREFIX = re.compile(r'^(Runtime Error|Semantic Error|Type Mismatch|Syntax Error)\s*:?\s*', re.IGNORECASE)
4
 
5
 
 
6
  class ReturnValue(Exception):
7
 
 
8
  def __init__(self, value):
 
9
  self.value = value
10
 
11
 
 
12
  class _CancelledError(Exception):
 
13
  pass
14
 
15
 
 
16
  class InterpreterError(Exception):
17
 
 
18
  def __init__(self, message, line):
 
19
  super().__init__(message)
 
20
  clean = _REDUNDANT_PREFIX.sub('', str(message)).strip()
 
21
  if line is not None and str(line) != "":
 
22
  self.message = f"RUNTIME error line {line}: {clean}"
 
23
  else:
 
24
  self.message = clean
25
 
 
26
  def __str__(self):
 
27
  return self.message
28
 
29
 
 
30
  class InterpreterInputRequest(Exception):
31
 
 
32
  def __init__(self, prompt, line):
 
33
  self.prompt = prompt
 
34
  self.line = line
 
1
+ # AUTO: Imports a module used by this file.
2
  import re
3
 
4
+ # AUTO: Sets `_REDUNDANT_PREFIX`.
5
  _REDUNDANT_PREFIX = re.compile(r'^(Runtime Error|Semantic Error|Type Mismatch|Syntax Error)\s*:?\s*', re.IGNORECASE)
6
 
7
 
8
+ # AUTO: Defines class `ReturnValue`.
9
  class ReturnValue(Exception):
10
 
11
+ # AUTO: Defines function `__init__`.
12
  def __init__(self, value):
13
+ # AUTO: Sets `self.value`.
14
  self.value = value
15
 
16
 
17
+ # AUTO: Defines class `_CancelledError`.
18
  class _CancelledError(Exception):
19
+ # AUTO: Does nothing for this required block.
20
  pass
21
 
22
 
23
+ # AUTO: Defines class `InterpreterError`.
24
  class InterpreterError(Exception):
25
 
26
+ # AUTO: Defines function `__init__`.
27
  def __init__(self, message, line):
28
+ # AUTO: Calls `super`.
29
  super().__init__(message)
30
+ # AUTO: Sets `clean`.
31
  clean = _REDUNDANT_PREFIX.sub('', str(message)).strip()
32
+ # AUTO: Checks this condition.
33
  if line is not None and str(line) != "":
34
+ # AUTO: Sets `self.message`.
35
  self.message = f"RUNTIME error line {line}: {clean}"
36
+ # AUTO: Runs when previous condition did not pass.
37
  else:
38
+ # AUTO: Sets `self.message`.
39
  self.message = clean
40
 
41
+ # AUTO: Defines function `__str__`.
42
  def __str__(self):
43
+ # AUTO: Returns this result to the caller.
44
  return self.message
45
 
46
 
47
+ # AUTO: Defines class `InterpreterInputRequest`.
48
  class InterpreterInputRequest(Exception):
49
 
50
+ # AUTO: Defines function `__init__`.
51
  def __init__(self, prompt, line):
52
+ # AUTO: Sets `self.prompt`.
53
  self.prompt = prompt
54
+ # AUTO: Sets `self.line`.
55
  self.line = line
Backend/interpreter/interpreter.py CHANGED
The diff for this file is too large to render. See raw diff
 
Backend/lexer/__init__.py CHANGED
@@ -1,7 +1,12 @@
1
 
 
2
  from .scanner import Lexer, lex
3
 
 
4
  from shared.tokens import * # noqa: F401,F403 - TT_* constants
 
5
  from shared.tokens import Token, get_token_description # noqa: F401 - explicit
 
6
  from lexer.positions import Position # noqa: F401
 
7
  from lexer.errors import LexicalError # noqa: F401
 
1
 
2
+ # AUTO: Imports names from another module.
3
  from .scanner import Lexer, lex
4
 
5
+ # AUTO: Imports names from another module.
6
  from shared.tokens import * # noqa: F401,F403 - TT_* constants
7
+ # AUTO: Imports names from another module.
8
  from shared.tokens import Token, get_token_description # noqa: F401 - explicit
9
+ # AUTO: Imports names from another module.
10
  from lexer.positions import Position # noqa: F401
11
+ # AUTO: Imports names from another module.
12
  from lexer.errors import LexicalError # noqa: F401
Backend/lexer/delimiters.py CHANGED
@@ -1,60 +1,127 @@
 
 
 
 
 
 
 
 
1
  ZERO = '0'
 
2
  DIGIT = '123456789'
 
3
  ZERODIGIT = ZERO + DIGIT
4
 
 
5
  LOW_ALPHA = 'abcdefghijklmnopqrstuvwxyz'
 
6
  UPPER_ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 
7
  ALPHA = LOW_ALPHA + UPPER_ALPHA
 
8
  ALPHANUM = ALPHA + ZERODIGIT + '_'
9
 
 
10
  WHITESPACE = ' \t\n'
 
11
  EOF = None
12
 
 
 
13
  statement_end_delim = set(ALPHA + WHITESPACE + '}') | {EOF}
 
14
  open_paren_delim = set(ALPHA + ZERODIGIT + WHITESPACE + '"' + "'" + '~' + '!' + '(' + ')')
 
15
  close_paren_delim = set(WHITESPACE) | {';', '{', ')', ',', '+', '-', '*', '/', '%', '=', '>', '<', '!', '&', '|', ']'}
 
16
  open_bracket_delim = set(ALPHA + ZERODIGIT + WHITESPACE + '~' + '!' + '(' + '"' + "'")
 
17
  close_bracket_delim = set(WHITESPACE) | {';', ',', ')', ']', '[', '.', '=', '+', '-', '*', '/', '%', '>', '<', '!', '&', '|'}
 
18
  block_start_delim = set(ALPHA + ZERODIGIT + WHITESPACE + '}/{"\'~!(')
 
19
  block_end_delim = set(ALPHA + ZERODIGIT + WHITESPACE + '};,)]')
 
20
  case_colon_delim = set(ALPHA + WHITESPACE + '}/')
 
21
  after_comma_delim = set(ALPHA + ZERODIGIT + WHITESPACE + '"' + "'" + '~' + '!' + '(' + '{')
 
22
  space_delim = {' ', '\t', '\n'}
 
23
  period_delim = {'.'}
 
24
  underscore_delim = {'_'}
 
25
  open_brack_delim = {'['}
 
26
  close_brack_delim = {']'}
 
27
  comma_delim = {','}
 
 
 
28
  delim1 = {'}'}
 
29
  delim2 = {':'}
 
30
  delim3 = {'{'}
 
31
  delim4 = {':', '('}
 
32
  delim5 = {'('}
 
33
  delim6 = {';', ',', '=', '>', '<', '!', '}', ')', '('}
 
34
  delim7 = {'('}
 
35
  delim8 = {';'}
 
36
  delim9 = set(ALPHA + '(' + ',' + ';' + ')')
 
37
  delim10 = {';', ')'}
 
38
  delim11 = set(LOW_ALPHA + ZERODIGIT + ']~')
 
39
  delim12 = set(ALPHA + ZERODIGIT + ']' + '~')
 
40
  delim13 = {';', ')', '['}
 
41
  delim14 = set(ALPHA + ZERODIGIT + '"' + "'" + '{')
 
42
  delim15 = {'\n', ';', '}', ','}
 
43
  delim16 = set(ALPHANUM + ')' + '"' + '!' + '(' + '[' + '\'')
 
44
  delim17 = {'}', ';', ',', '+', '-', '*', '/', '%', '=', '>', '<', '!', '&', '|'}
 
45
  delim18 = {';', '{', ')', '&', '|', '+', '-', '*', '/', '%'}
 
46
  delim19 = {';', ',', '}', ')', '=', '>', '<', '!'}
 
47
  delim20 = set(ALPHA + ZERODIGIT + '"' + "'" + '{')
 
48
  delim21 = set(ALPHA + ZERODIGIT + WHITESPACE + '~!("\'')
 
49
  delim22 = {',', ';', '(', ')', '{', '[', ']'}
 
50
  delim23 = {';', ',', '}', ']', ')', ':', '+', '-', '*', '/', '%', '=', '>', '<', '!', '&', '|', '`'}
 
51
  delim24 = set(ZERODIGIT + ALPHA + '~!("\'' + WHITESPACE)
 
52
  delim25 = set(ALPHANUM + ';) \t\n')
 
53
  delim26 = set(ZERODIGIT + ALPHA + '~(' + '"\'' + ' \t\n')
 
54
  idf_delim = {' ', ',', ';', '(', ')', '{', '}', '[', ']', ':', '+', '-', '*', '/', '%',
 
55
  '>', '<', '=', '\t', '\n', '.', '"', "'", '&', '|', '`'}
 
56
  whlnum_delim = {';', ' ', ',', '}', ']', ')', ':', '+', '-', '*', '/', '%', '=', '>', '<',
 
57
  '!', '&', '|', '\t', '\n'}
 
58
  decim_delim = {'}', ';', ',', '+', '-', '*', '/', '%', '=', '>', '<', '!', '&', '|', ' ',
 
59
  '\t', '\n', ')', ']'}
 
60
  comment_delim = set(ALPHANUM + ';+-*/%}{()' + '\n')
 
1
+ """Character groups used by the scanner to validate token boundaries.
2
+
3
+ A delimiter set is not a token by itself. It means "after reading this token,
4
+ the next character must be one of these characters, otherwise report a lexical
5
+ delimiter error."
6
+ """
7
+
8
+ # LINE: ZERO is separated because some numeric rules treat 0 specially.
9
  ZERO = '0'
10
+ # LINE: DIGIT contains non-zero digits.
11
  DIGIT = '123456789'
12
+ # LINE: ZERODIGIT means any numeric digit 0-9.
13
  ZERODIGIT = ZERO + DIGIT
14
 
15
+ # LINE: Lowercase letters accepted by identifiers/reserved words.
16
  LOW_ALPHA = 'abcdefghijklmnopqrstuvwxyz'
17
+ # LINE: Uppercase letters accepted by identifiers/reserved words.
18
  UPPER_ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
19
+ # LINE: ALPHA combines all letters.
20
  ALPHA = LOW_ALPHA + UPPER_ALPHA
21
+ # LINE: ALPHANUM is what identifiers can continue with.
22
  ALPHANUM = ALPHA + ZERODIGIT + '_'
23
 
24
+ # LINE: Common whitespace characters.
25
  WHITESPACE = ' \t\n'
26
+ # LINE: EOF marker used inside delimiter sets.
27
  EOF = None
28
 
29
+ # GUIDE: Named delimiter sets describe common token-boundary situations.
30
+ # LINE: Characters allowed after a statement-ending context.
31
  statement_end_delim = set(ALPHA + WHITESPACE + '}') | {EOF}
32
+ # LINE: Characters allowed after an opening parenthesis.
33
  open_paren_delim = set(ALPHA + ZERODIGIT + WHITESPACE + '"' + "'" + '~' + '!' + '(' + ')')
34
+ # LINE: Characters allowed after a closing parenthesis.
35
  close_paren_delim = set(WHITESPACE) | {';', '{', ')', ',', '+', '-', '*', '/', '%', '=', '>', '<', '!', '&', '|', ']'}
36
+ # AUTO: Sets `open_bracket_delim`.
37
  open_bracket_delim = set(ALPHA + ZERODIGIT + WHITESPACE + '~' + '!' + '(' + '"' + "'")
38
+ # AUTO: Sets `close_bracket_delim`.
39
  close_bracket_delim = set(WHITESPACE) | {';', ',', ')', ']', '[', '.', '=', '+', '-', '*', '/', '%', '>', '<', '!', '&', '|'}
40
+ # AUTO: Sets `block_start_delim`.
41
  block_start_delim = set(ALPHA + ZERODIGIT + WHITESPACE + '}/{"\'~!(')
42
+ # AUTO: Sets `block_end_delim`.
43
  block_end_delim = set(ALPHA + ZERODIGIT + WHITESPACE + '};,)]')
44
+ # AUTO: Sets `case_colon_delim`.
45
  case_colon_delim = set(ALPHA + WHITESPACE + '}/')
46
+ # AUTO: Sets `after_comma_delim`.
47
  after_comma_delim = set(ALPHA + ZERODIGIT + WHITESPACE + '"' + "'" + '~' + '!' + '(' + '{')
48
+ # AUTO: Sets `space_delim`.
49
  space_delim = {' ', '\t', '\n'}
50
+ # AUTO: Sets `period_delim`.
51
  period_delim = {'.'}
52
+ # AUTO: Sets `underscore_delim`.
53
  underscore_delim = {'_'}
54
+ # AUTO: Sets `open_brack_delim`.
55
  open_brack_delim = {'['}
56
+ # AUTO: Sets `close_brack_delim`.
57
  close_brack_delim = {']'}
58
+ # AUTO: Sets `comma_delim`.
59
  comma_delim = {','}
60
+
61
+ # GUIDE: Numbered delimiter sets are legacy FSM groups used by scanner.py branches.
62
+ # LINE: Each delimN is a valid-next-character set, not a token.
63
  delim1 = {'}'}
64
+ # AUTO: Sets `delim2`.
65
  delim2 = {':'}
66
+ # AUTO: Sets `delim3`.
67
  delim3 = {'{'}
68
+ # AUTO: Sets `delim4`.
69
  delim4 = {':', '('}
70
+ # AUTO: Sets `delim5`.
71
  delim5 = {'('}
72
+ # AUTO: Sets `delim6`.
73
  delim6 = {';', ',', '=', '>', '<', '!', '}', ')', '('}
74
+ # AUTO: Sets `delim7`.
75
  delim7 = {'('}
76
+ # AUTO: Sets `delim8`.
77
  delim8 = {';'}
78
+ # AUTO: Sets `delim9`.
79
  delim9 = set(ALPHA + '(' + ',' + ';' + ')')
80
+ # AUTO: Sets `delim10`.
81
  delim10 = {';', ')'}
82
+ # AUTO: Sets `delim11`.
83
  delim11 = set(LOW_ALPHA + ZERODIGIT + ']~')
84
+ # AUTO: Sets `delim12`.
85
  delim12 = set(ALPHA + ZERODIGIT + ']' + '~')
86
+ # AUTO: Sets `delim13`.
87
  delim13 = {';', ')', '['}
88
+ # AUTO: Sets `delim14`.
89
  delim14 = set(ALPHA + ZERODIGIT + '"' + "'" + '{')
90
+ # AUTO: Sets `delim15`.
91
  delim15 = {'\n', ';', '}', ','}
92
+ # AUTO: Sets `delim16`.
93
  delim16 = set(ALPHANUM + ')' + '"' + '!' + '(' + '[' + '\'')
94
+ # AUTO: Sets `delim17`.
95
  delim17 = {'}', ';', ',', '+', '-', '*', '/', '%', '=', '>', '<', '!', '&', '|'}
96
+ # AUTO: Sets `delim18`.
97
  delim18 = {';', '{', ')', '&', '|', '+', '-', '*', '/', '%'}
98
+ # AUTO: Sets `delim19`.
99
  delim19 = {';', ',', '}', ')', '=', '>', '<', '!'}
100
+ # AUTO: Sets `delim20`.
101
  delim20 = set(ALPHA + ZERODIGIT + '"' + "'" + '{')
102
+ # AUTO: Sets `delim21`.
103
  delim21 = set(ALPHA + ZERODIGIT + WHITESPACE + '~!("\'')
104
+ # AUTO: Sets `delim22`.
105
  delim22 = {',', ';', '(', ')', '{', '[', ']'}
106
+ # AUTO: Sets `delim23`.
107
  delim23 = {';', ',', '}', ']', ')', ':', '+', '-', '*', '/', '%', '=', '>', '<', '!', '&', '|', '`'}
108
+ # LINE: delim24 is used after binary/assignment operators like +, +=, ==.
109
  delim24 = set(ZERODIGIT + ALPHA + '~!("\'' + WHITESPACE)
110
+ # LINE: delim25 is used after ++/-- so postfix/prefix boundaries are accepted.
111
  delim25 = set(ALPHANUM + ';) \t\n')
112
+ # AUTO: Sets `delim26`.
113
  delim26 = set(ZERODIGIT + ALPHA + '~(' + '"\'' + ' \t\n')
114
+ # LINE: idf_delim tells lexer what can legally appear after an identifier.
115
  idf_delim = {' ', ',', ';', '(', ')', '{', '}', '[', ']', ':', '+', '-', '*', '/', '%',
116
+ # AUTO: Sets `'>', '<', '`.
117
  '>', '<', '=', '\t', '\n', '.', '"', "'", '&', '|', '`'}
118
+ # LINE: whlnum_delim tells lexer what can legally appear after an integer.
119
  whlnum_delim = {';', ' ', ',', '}', ']', ')', ':', '+', '-', '*', '/', '%', '=', '>', '<',
120
+ # AUTO: Executes this statement.
121
  '!', '&', '|', '\t', '\n'}
122
+ # LINE: decim_delim tells lexer what can legally appear after a double literal.
123
  decim_delim = {'}', ';', ',', '+', '-', '*', '/', '%', '=', '>', '<', '!', '&', '|', ' ',
124
+ # AUTO: Executes this statement.
125
  '\t', '\n', ')', ']'}
126
+ # LINE: comment_delim is used around comment scanning boundaries.
127
  comment_delim = set(ALPHANUM + ';+-*/%}{()' + '\n')
Backend/lexer/errors.py CHANGED
@@ -1,11 +1,18 @@
1
 
2
 
 
3
  class LexicalError:
4
 
 
5
  def __init__(self, pos, details):
 
6
  self.pos = pos
 
7
  self.details = details
8
 
 
9
  def as_string(self):
 
10
  self.details = self.details.replace('\n', '\\n')
 
11
  return f"LEXICAL error line {self.pos.ln} col {self.pos.col} {self.details}"
 
1
 
2
 
3
+ # AUTO: Defines class `LexicalError`.
4
  class LexicalError:
5
 
6
+ # AUTO: Defines function `__init__`.
7
  def __init__(self, pos, details):
8
+ # AUTO: Sets `self.pos`.
9
  self.pos = pos
10
+ # AUTO: Sets `self.details`.
11
  self.details = details
12
 
13
+ # AUTO: Defines function `as_string`.
14
  def as_string(self):
15
+ # AUTO: Sets `self.details`.
16
  self.details = self.details.replace('\n', '\\n')
17
+ # AUTO: Returns this result to the caller.
18
  return f"LEXICAL error line {self.pos.ln} col {self.pos.col} {self.details}"
Backend/lexer/positions.py CHANGED
@@ -1,21 +1,35 @@
1
 
2
 
 
3
  class Position:
4
 
 
5
  def __init__(self, index, ln, col=0):
 
6
  self.index = index
 
7
  self.ln = ln
 
8
  self.col = col
9
 
 
10
  def advance(self, current_char):
 
11
  self.index += 1
 
12
  self.col += 1
13
 
 
14
  if current_char == '\n':
 
15
  self.ln += 1
 
16
  self.col = 0
17
 
 
18
  return self
19
 
 
20
  def copy(self):
 
21
  return Position(self.index, self.ln, self.col)
 
1
 
2
 
3
+ # AUTO: Defines class `Position`.
4
  class Position:
5
 
6
+ # AUTO: Defines function `__init__`.
7
  def __init__(self, index, ln, col=0):
8
+ # AUTO: Sets `self.index`.
9
  self.index = index
10
+ # AUTO: Sets `self.ln`.
11
  self.ln = ln
12
+ # AUTO: Sets `self.col`.
13
  self.col = col
14
 
15
+ # AUTO: Defines function `advance`.
16
  def advance(self, current_char):
17
+ # AUTO: Adds into `self.index`.
18
  self.index += 1
19
+ # AUTO: Adds into `self.col`.
20
  self.col += 1
21
 
22
+ # AUTO: Checks this condition.
23
  if current_char == '\n':
24
+ # AUTO: Adds into `self.ln`.
25
  self.ln += 1
26
+ # AUTO: Sets `self.col`.
27
  self.col = 0
28
 
29
+ # AUTO: Returns this result to the caller.
30
  return self
31
 
32
+ # AUTO: Defines function `copy`.
33
  def copy(self):
34
+ # AUTO: Returns this result to the caller.
35
  return Position(self.index, self.ln, self.col)
Backend/lexer/scanner.py CHANGED
The diff for this file is too large to render. See raw diff
 
Backend/parser/__init__.py CHANGED
@@ -1,2 +1,3 @@
1
 
 
2
  from .parser import LL1Parser # noqa: F401 - main public class
 
1
 
2
+ # AUTO: Imports names from another module.
3
  from .parser import LL1Parser # noqa: F401 - main public class
Backend/parser/builder.py CHANGED
The diff for this file is too large to render. See raw diff
 
Backend/parser/parser.py CHANGED
The diff for this file is too large to render. See raw diff
 
Backend/semantic/__init__.py CHANGED
@@ -1,3 +1,5 @@
1
 
 
2
  from .symbol_table import SymbolTable # noqa: F401
 
3
  from .analyzer import ASTValidator, validate_ast # noqa: F401
 
1
 
2
+ # AUTO: Imports names from another module.
3
  from .symbol_table import SymbolTable # noqa: F401
4
+ # AUTO: Imports names from another module.
5
  from .analyzer import ASTValidator, validate_ast # noqa: F401
Backend/semantic/analyzer.py CHANGED
@@ -1,194 +1,365 @@
 
 
 
 
 
 
 
 
1
  from semantic.errors import SemanticError
2
 
3
 
 
4
  class ASTValidator:
5
 
 
6
  def __init__(self):
 
 
 
7
  self.errors = []
 
8
  self.warnings = []
 
9
  self._in_loop = 0
 
10
  self._in_switch = 0
 
11
  self._in_function = False
 
12
  self._current_func_type = None
13
 
14
 
 
15
  def validate(self, ast, symbol_table_data):
 
 
 
16
  self._walk(ast)
 
17
  return {
 
18
  "success": len(self.errors) == 0,
 
19
  "errors": self.errors,
 
20
  "warnings": self.warnings,
 
21
  "symbol_table": symbol_table_data,
 
22
  "ast": ast,
 
23
  }
24
 
25
 
 
26
  def _walk(self, node):
 
 
 
27
  if node is None:
 
28
  return
 
29
  handler = getattr(self, f'_check_{node.node_type}', None)
 
30
  if handler:
 
31
  handler(node)
 
32
  else:
 
33
  for child in getattr(node, 'children', []):
 
34
  self._walk(child)
35
 
36
 
 
37
  def _check_Program(self, node):
 
38
  for child in node.children:
 
39
  self._walk(child)
40
 
 
41
  def _check_VariableDeclaration(self, node):
 
42
  if len(node.children) < 2:
 
43
  self.errors.append(
 
44
  f"SEMANTIC error line {node.line}: Malformed variable declaration.")
 
45
  for child in node.children:
 
46
  self._walk(child)
47
 
 
48
  def _check_VariableDeclarationList(self, node):
 
49
  for child in node.children:
 
50
  self._walk(child)
51
 
 
52
  def _check_SturdyDeclaration(self, node):
 
53
  if len(node.children) < 3:
 
54
  self.errors.append(
 
55
  f"SEMANTIC error line {node.line}: Fertile declaration must have type, name, and value.")
 
56
  for child in node.children:
 
57
  self._walk(child)
58
 
 
59
  def _check_FunctionDeclaration(self, node):
 
60
  if not node.value:
 
61
  self.errors.append(
 
62
  f"SEMANTIC error line {node.line}: Function declaration missing name.")
 
63
  prev_in_func = self._in_function
 
64
  prev_func_type = self._current_func_type
 
65
  self._in_function = True
 
66
  if node.children:
 
67
  self._current_func_type = node.children[0].value
 
68
  for child in node.children:
 
69
  self._walk(child)
 
70
  self._in_function = prev_in_func
 
71
  self._current_func_type = prev_func_type
72
 
 
73
  def _check_FunctionCall(self, node):
 
74
  for child in node.children:
 
75
  self._walk(child)
76
 
 
77
  def _check_Assignment(self, node):
 
78
  for child in node.children:
 
79
  self._walk(child)
80
 
 
81
  def _check_AssignmentList(self, node):
 
82
  for child in node.children:
 
83
  self._walk(child)
84
 
 
85
  def _check_BinaryOp(self, node):
 
86
  for child in node.children:
 
87
  self._walk(child)
88
 
 
89
  def _check_UnaryOp(self, node):
 
90
  for child in node.children:
 
91
  self._walk(child)
92
 
 
93
  def _check_Block(self, node):
 
94
  for child in node.children:
 
95
  self._walk(child)
96
 
 
97
  def _check_IfStatement(self, node):
 
98
  for child in node.children:
 
99
  self._walk(child)
100
 
 
101
  def _check_ElseIfStatement(self, node):
 
102
  for child in node.children:
 
103
  self._walk(child)
104
 
 
105
  def _check_ElseStatement(self, node):
 
106
  for child in node.children:
 
107
  self._walk(child)
108
 
 
109
  def _check_ForLoop(self, node):
 
 
110
  self._in_loop += 1
 
111
  for child in node.children:
 
112
  self._walk(child)
 
 
113
  self._in_loop -= 1
114
 
 
115
  def _check_WhileLoop(self, node):
 
 
116
  self._in_loop += 1
 
117
  for child in node.children:
 
118
  self._walk(child)
 
119
  self._in_loop -= 1
120
 
 
121
  def _check_DoWhileLoop(self, node):
 
122
  self._in_loop += 1
 
123
  for child in node.children:
 
124
  self._walk(child)
 
125
  self._in_loop -= 1
126
 
 
127
  def _check_Switch(self, node):
 
128
  self._in_switch += 1
 
129
  for child in node.children:
 
130
  self._walk(child)
 
131
  self._in_switch -= 1
132
 
 
133
  def _check_Break(self, node):
 
 
134
  if self._in_loop == 0 and self._in_switch == 0:
 
135
  self.errors.append(
 
136
  f"SEMANTIC error line {node.line}: 'prune' used outside a loop or switch.")
137
 
 
138
  def _check_Continue(self, node):
 
 
139
  if self._in_loop == 0:
 
140
  self.errors.append(
 
141
  f"SEMANTIC error line {node.line}: 'skip' used outside a loop.")
142
 
 
143
  def _check_Return(self, node):
 
144
  for child in node.children:
 
145
  self._walk(child)
146
 
 
147
  def _check_PrintStatement(self, node):
 
148
  for child in node.children:
 
149
  self._walk(child)
150
 
 
151
  def _check_Condition(self, node):
 
152
  for child in node.children:
 
153
  self._walk(child)
154
 
 
155
  def _check_Update(self, node):
 
156
  for child in node.children:
 
157
  self._walk(child)
158
 
 
159
  def _check_List(self, node):
 
160
  for child in node.children:
 
161
  self._walk(child)
162
 
 
163
  def _check_ListAccess(self, node):
 
164
  for child in node.children:
 
165
  self._walk(child)
166
 
 
167
  def _check_TypeCast(self, node):
 
168
  for child in node.children:
 
169
  self._walk(child)
170
 
 
171
  def _check_MemberAccess(self, node):
 
172
  for child in node.children:
 
173
  self._walk(child)
174
 
 
175
  def _check_BundleDefinition(self, node):
 
176
  if not node.bundle_name:
 
177
  self.errors.append(
 
178
  f"SEMANTIC error line {node.line}: Bundle definition missing name.")
179
 
 
180
  def _check_Append(self, node):
 
181
  for child in node.children:
 
182
  self._walk(child)
183
 
 
184
  def _check_Insert(self, node):
 
185
  for child in node.children:
 
186
  self._walk(child)
187
 
 
188
  def _check_Remove(self, node):
 
189
  for child in node.children:
 
190
  self._walk(child)
191
 
 
192
  def validate_ast(ast, symbol_table_data):
 
193
  validator = ASTValidator()
 
194
  return validator.validate(ast, symbol_table_data)
 
1
+ """Final AST validation pass for GAL.
2
+
3
+ builder.py already catches many declaration/type issues while creating the AST.
4
+ This validator walks the finished AST and checks rules that are easier to see
5
+ from the tree structure, such as prune/skip placement.
6
+ """
7
+
8
+ # AUTO: Imports names from another module.
9
  from semantic.errors import SemanticError
10
 
11
 
12
+ # AUTO: Defines class `ASTValidator`.
13
  class ASTValidator:
14
 
15
+ # AUTO: Defines function `__init__`.
16
  def __init__(self):
17
+ # GUIDE: Context counters tell checks if they are inside a loop,
18
+ # switch, or function while walking nested AST nodes.
19
+ # AUTO: Sets `self.errors`.
20
  self.errors = []
21
+ # AUTO: Sets `self.warnings`.
22
  self.warnings = []
23
+ # AUTO: Sets `self._in_loop`.
24
  self._in_loop = 0
25
+ # AUTO: Sets `self._in_switch`.
26
  self._in_switch = 0
27
+ # AUTO: Sets `self._in_function`.
28
  self._in_function = False
29
+ # AUTO: Sets `self._current_func_type`.
30
  self._current_func_type = None
31
 
32
 
33
+ # AUTO: Defines function `validate`.
34
  def validate(self, ast, symbol_table_data):
35
+ # Start walking from the ProgramNode. Every checker can add messages
36
+ # into self.errors; no errors means semantic validation succeeds.
37
+ # LINE: Begin recursive semantic walk from the AST root.
38
  self._walk(ast)
39
+ # LINE: Return validation result plus errors/warnings/symbol table.
40
  return {
41
+ # LINE: success is true only when no semantic errors were collected.
42
  "success": len(self.errors) == 0,
43
+ # AUTO: Executes this statement.
44
  "errors": self.errors,
45
+ # AUTO: Executes this statement.
46
  "warnings": self.warnings,
47
+ # AUTO: Executes this statement.
48
  "symbol_table": symbol_table_data,
49
+ # AUTO: Executes this statement.
50
  "ast": ast,
51
+ # AUTO: Closes the current grouped code/data.
52
  }
53
 
54
 
55
+ # AUTO: Defines function `_walk`.
56
  def _walk(self, node):
57
+ # GUIDE: Dynamic dispatch; Program calls _check_Program, Break calls
58
+ # _check_Break, and unknown node types simply recurse into children.
59
+ # LINE: Nothing to check for missing/empty AST node.
60
  if node is None:
61
+ # AUTO: Returns this result to the caller.
62
  return
63
+ # LINE: Find checker method based on node type, like _check_Break.
64
  handler = getattr(self, f'_check_{node.node_type}', None)
65
+ # LINE: If checker exists, run that specific semantic rule.
66
  if handler:
67
+ # AUTO: Calls `handler`.
68
  handler(node)
69
+ # AUTO: Runs when previous condition did not pass.
70
  else:
71
+ # LINE: Otherwise keep walking through this node's children.
72
  for child in getattr(node, 'children', []):
73
+ # AUTO: Calls `self._walk`.
74
  self._walk(child)
75
 
76
 
77
+ # AUTO: Defines function `_check_Program`.
78
  def _check_Program(self, node):
79
+ # LINE: ProgramNode validates by checking each top-level child.
80
  for child in node.children:
81
+ # AUTO: Calls `self._walk`.
82
  self._walk(child)
83
 
84
+ # AUTO: Defines function `_check_VariableDeclaration`.
85
  def _check_VariableDeclaration(self, node):
86
+ # LINE: VariableDeclaration must at least contain type and name.
87
  if len(node.children) < 2:
88
+ # AUTO: Appends a value to a list.
89
  self.errors.append(
90
+ # AUTO: Executes this statement.
91
  f"SEMANTIC error line {node.line}: Malformed variable declaration.")
92
+ # LINE: Continue validating initializer/children.
93
  for child in node.children:
94
+ # AUTO: Calls `self._walk`.
95
  self._walk(child)
96
 
97
+ # AUTO: Defines function `_check_VariableDeclarationList`.
98
  def _check_VariableDeclarationList(self, node):
99
+ # AUTO: Starts a loop over these values.
100
  for child in node.children:
101
+ # AUTO: Calls `self._walk`.
102
  self._walk(child)
103
 
104
+ # AUTO: Defines function `_check_SturdyDeclaration`.
105
  def _check_SturdyDeclaration(self, node):
106
+ # AUTO: Checks this condition.
107
  if len(node.children) < 3:
108
+ # AUTO: Appends a value to a list.
109
  self.errors.append(
110
+ # AUTO: Executes this statement.
111
  f"SEMANTIC error line {node.line}: Fertile declaration must have type, name, and value.")
112
+ # AUTO: Starts a loop over these values.
113
  for child in node.children:
114
+ # AUTO: Calls `self._walk`.
115
  self._walk(child)
116
 
117
+ # AUTO: Defines function `_check_FunctionDeclaration`.
118
  def _check_FunctionDeclaration(self, node):
119
+ # LINE: FunctionDeclaration must have a function name in node.value.
120
  if not node.value:
121
+ # AUTO: Appends a value to a list.
122
  self.errors.append(
123
+ # AUTO: Executes this statement.
124
  f"SEMANTIC error line {node.line}: Function declaration missing name.")
125
+ # LINE: Save previous function context before entering this function.
126
  prev_in_func = self._in_function
127
+ # AUTO: Sets `prev_func_type`.
128
  prev_func_type = self._current_func_type
129
+ # LINE: Mark validator as currently inside a function.
130
  self._in_function = True
131
+ # AUTO: Checks this condition.
132
  if node.children:
133
+ # LINE: First child stores return type.
134
  self._current_func_type = node.children[0].value
135
+ # LINE: Validate parameters/body/children inside the function.
136
  for child in node.children:
137
+ # AUTO: Calls `self._walk`.
138
  self._walk(child)
139
+ # LINE: Restore previous context after leaving function.
140
  self._in_function = prev_in_func
141
+ # AUTO: Sets `self._current_func_type`.
142
  self._current_func_type = prev_func_type
143
 
144
+ # AUTO: Defines function `_check_FunctionCall`.
145
  def _check_FunctionCall(self, node):
146
+ # AUTO: Starts a loop over these values.
147
  for child in node.children:
148
+ # AUTO: Calls `self._walk`.
149
  self._walk(child)
150
 
151
+ # AUTO: Defines function `_check_Assignment`.
152
  def _check_Assignment(self, node):
153
+ # AUTO: Starts a loop over these values.
154
  for child in node.children:
155
+ # AUTO: Calls `self._walk`.
156
  self._walk(child)
157
 
158
+ # AUTO: Defines function `_check_AssignmentList`.
159
  def _check_AssignmentList(self, node):
160
+ # AUTO: Starts a loop over these values.
161
  for child in node.children:
162
+ # AUTO: Calls `self._walk`.
163
  self._walk(child)
164
 
165
+ # AUTO: Defines function `_check_BinaryOp`.
166
  def _check_BinaryOp(self, node):
167
+ # AUTO: Starts a loop over these values.
168
  for child in node.children:
169
+ # AUTO: Calls `self._walk`.
170
  self._walk(child)
171
 
172
+ # AUTO: Defines function `_check_UnaryOp`.
173
  def _check_UnaryOp(self, node):
174
+ # AUTO: Starts a loop over these values.
175
  for child in node.children:
176
+ # AUTO: Calls `self._walk`.
177
  self._walk(child)
178
 
179
+ # AUTO: Defines function `_check_Block`.
180
  def _check_Block(self, node):
181
+ # AUTO: Starts a loop over these values.
182
  for child in node.children:
183
+ # AUTO: Calls `self._walk`.
184
  self._walk(child)
185
 
186
+ # AUTO: Defines function `_check_IfStatement`.
187
  def _check_IfStatement(self, node):
188
+ # AUTO: Starts a loop over these values.
189
  for child in node.children:
190
+ # AUTO: Calls `self._walk`.
191
  self._walk(child)
192
 
193
+ # AUTO: Defines function `_check_ElseIfStatement`.
194
  def _check_ElseIfStatement(self, node):
195
+ # AUTO: Starts a loop over these values.
196
  for child in node.children:
197
+ # AUTO: Calls `self._walk`.
198
  self._walk(child)
199
 
200
+ # AUTO: Defines function `_check_ElseStatement`.
201
  def _check_ElseStatement(self, node):
202
+ # AUTO: Starts a loop over these values.
203
  for child in node.children:
204
+ # AUTO: Calls `self._walk`.
205
  self._walk(child)
206
 
207
+ # AUTO: Defines function `_check_ForLoop`.
208
  def _check_ForLoop(self, node):
209
+ # Enter loop context so prune/skip inside this block are legal.
210
+ # LINE: Increase loop depth before checking cultivate body.
211
  self._in_loop += 1
212
+ # AUTO: Starts a loop over these values.
213
  for child in node.children:
214
+ # AUTO: Calls `self._walk`.
215
  self._walk(child)
216
+ # Leave loop context after all nested children are checked.
217
+ # LINE: Decrease loop depth after cultivate body is checked.
218
  self._in_loop -= 1
219
 
220
+ # AUTO: Defines function `_check_WhileLoop`.
221
  def _check_WhileLoop(self, node):
222
+ # Same context rule as cultivate: grow allows prune/skip inside.
223
+ # LINE: Increase loop depth before checking grow body.
224
  self._in_loop += 1
225
+ # AUTO: Starts a loop over these values.
226
  for child in node.children:
227
+ # AUTO: Calls `self._walk`.
228
  self._walk(child)
229
+ # LINE: Decrease loop depth after grow body.
230
  self._in_loop -= 1
231
 
232
+ # AUTO: Defines function `_check_DoWhileLoop`.
233
  def _check_DoWhileLoop(self, node):
234
+ # AUTO: Adds into `self._in_loop`.
235
  self._in_loop += 1
236
+ # AUTO: Starts a loop over these values.
237
  for child in node.children:
238
+ # AUTO: Calls `self._walk`.
239
  self._walk(child)
240
+ # AUTO: Subtracts from `self._in_loop`.
241
  self._in_loop -= 1
242
 
243
+ # AUTO: Defines function `_check_Switch`.
244
  def _check_Switch(self, node):
245
+ # LINE: harvest/variety context allows prune inside switch cases.
246
  self._in_switch += 1
247
+ # AUTO: Starts a loop over these values.
248
  for child in node.children:
249
+ # AUTO: Calls `self._walk`.
250
  self._walk(child)
251
+ # LINE: Leave switch context after all cases are checked.
252
  self._in_switch -= 1
253
 
254
+ # AUTO: Defines function `_check_Break`.
255
  def _check_Break(self, node):
256
+ # prune is valid only while the walker is inside a loop or harvest.
257
+ # LINE: If no loop/switch context, prune is illegal.
258
  if self._in_loop == 0 and self._in_switch == 0:
259
+ # AUTO: Appends a value to a list.
260
  self.errors.append(
261
+ # AUTO: Executes this statement.
262
  f"SEMANTIC error line {node.line}: 'prune' used outside a loop or switch.")
263
 
264
+ # AUTO: Defines function `_check_Continue`.
265
  def _check_Continue(self, node):
266
+ # skip is valid only while the walker is inside a loop.
267
+ # LINE: If no loop context, skip is illegal.
268
  if self._in_loop == 0:
269
+ # AUTO: Appends a value to a list.
270
  self.errors.append(
271
+ # AUTO: Executes this statement.
272
  f"SEMANTIC error line {node.line}: 'skip' used outside a loop.")
273
 
274
+ # AUTO: Defines function `_check_Return`.
275
  def _check_Return(self, node):
276
+ # AUTO: Starts a loop over these values.
277
  for child in node.children:
278
+ # AUTO: Calls `self._walk`.
279
  self._walk(child)
280
 
281
+ # AUTO: Defines function `_check_PrintStatement`.
282
  def _check_PrintStatement(self, node):
283
+ # AUTO: Starts a loop over these values.
284
  for child in node.children:
285
+ # AUTO: Calls `self._walk`.
286
  self._walk(child)
287
 
288
+ # AUTO: Defines function `_check_Condition`.
289
  def _check_Condition(self, node):
290
+ # AUTO: Starts a loop over these values.
291
  for child in node.children:
292
+ # AUTO: Calls `self._walk`.
293
  self._walk(child)
294
 
295
+ # AUTO: Defines function `_check_Update`.
296
  def _check_Update(self, node):
297
+ # AUTO: Starts a loop over these values.
298
  for child in node.children:
299
+ # AUTO: Calls `self._walk`.
300
  self._walk(child)
301
 
302
+ # AUTO: Defines function `_check_List`.
303
  def _check_List(self, node):
304
+ # AUTO: Starts a loop over these values.
305
  for child in node.children:
306
+ # AUTO: Calls `self._walk`.
307
  self._walk(child)
308
 
309
+ # AUTO: Defines function `_check_ListAccess`.
310
  def _check_ListAccess(self, node):
311
+ # AUTO: Starts a loop over these values.
312
  for child in node.children:
313
+ # AUTO: Calls `self._walk`.
314
  self._walk(child)
315
 
316
+ # AUTO: Defines function `_check_TypeCast`.
317
  def _check_TypeCast(self, node):
318
+ # AUTO: Starts a loop over these values.
319
  for child in node.children:
320
+ # AUTO: Calls `self._walk`.
321
  self._walk(child)
322
 
323
+ # AUTO: Defines function `_check_MemberAccess`.
324
  def _check_MemberAccess(self, node):
325
+ # AUTO: Starts a loop over these values.
326
  for child in node.children:
327
+ # AUTO: Calls `self._walk`.
328
  self._walk(child)
329
 
330
+ # AUTO: Defines function `_check_BundleDefinition`.
331
  def _check_BundleDefinition(self, node):
332
+ # AUTO: Checks this condition.
333
  if not node.bundle_name:
334
+ # AUTO: Appends a value to a list.
335
  self.errors.append(
336
+ # AUTO: Executes this statement.
337
  f"SEMANTIC error line {node.line}: Bundle definition missing name.")
338
 
339
+ # AUTO: Defines function `_check_Append`.
340
  def _check_Append(self, node):
341
+ # AUTO: Starts a loop over these values.
342
  for child in node.children:
343
+ # AUTO: Calls `self._walk`.
344
  self._walk(child)
345
 
346
+ # AUTO: Defines function `_check_Insert`.
347
  def _check_Insert(self, node):
348
+ # AUTO: Starts a loop over these values.
349
  for child in node.children:
350
+ # AUTO: Calls `self._walk`.
351
  self._walk(child)
352
 
353
+ # AUTO: Defines function `_check_Remove`.
354
  def _check_Remove(self, node):
355
+ # AUTO: Starts a loop over these values.
356
  for child in node.children:
357
+ # AUTO: Calls `self._walk`.
358
  self._walk(child)
359
 
360
+ # AUTO: Defines function `validate_ast`.
361
  def validate_ast(ast, symbol_table_data):
362
+ # LINE: Create a fresh validator for this compile/run.
363
  validator = ASTValidator()
364
+ # LINE: Run validator and return its result.
365
  return validator.validate(ast, symbol_table_data)
Backend/semantic/errors.py CHANGED
@@ -1,13 +1,22 @@
 
1
  import re
2
 
 
3
  _REDUNDANT_PREFIX = re.compile(r'^(Semantic Error|Syntax Error|Type Mismatch|Runtime Error)\s*:?\s*', re.IGNORECASE)
4
 
5
 
 
6
  class SemanticError(Exception):
 
7
  def __init__(self, message, line):
 
8
  super().__init__(message)
 
9
  clean = _REDUNDANT_PREFIX.sub('', str(message)).strip()
 
10
  self.message = f"SEMANTIC error line {line}: {clean}"
11
 
 
12
  def __str__(self):
 
13
  return self.message
 
1
+ # AUTO: Imports a module used by this file.
2
  import re
3
 
4
+ # AUTO: Sets `_REDUNDANT_PREFIX`.
5
  _REDUNDANT_PREFIX = re.compile(r'^(Semantic Error|Syntax Error|Type Mismatch|Runtime Error)\s*:?\s*', re.IGNORECASE)
6
 
7
 
8
+ # AUTO: Defines class `SemanticError`.
9
  class SemanticError(Exception):
10
+ # AUTO: Defines function `__init__`.
11
  def __init__(self, message, line):
12
+ # AUTO: Calls `super`.
13
  super().__init__(message)
14
+ # AUTO: Sets `clean`.
15
  clean = _REDUNDANT_PREFIX.sub('', str(message)).strip()
16
+ # AUTO: Sets `self.message`.
17
  self.message = f"SEMANTIC error line {line}: {clean}"
18
 
19
+ # AUTO: Defines function `__str__`.
20
  def __str__(self):
21
+ # AUTO: Returns this result to the caller.
22
  return self.message
Backend/semantic/symbol_table.py CHANGED
@@ -1,101 +1,173 @@
1
 
2
 
 
3
  class SymbolTable:
 
4
  def __init__(self):
 
5
  self.variables = {}
 
6
  self.global_variables = {}
 
7
  self.functions = {}
 
8
  self.scopes = [{}]
 
9
  self.current_func_name = None
 
10
  self.function_variables = {}
 
11
  self.bundle_types = {}
12
 
 
13
  def declare_variable(self, name, type_, value=None, is_list=False, is_fertile=False):
 
14
  scope = self.scopes[-1]
 
15
  current_func = self.current_func_name
16
 
17
 
 
18
  if name in self.functions:
 
19
  return f"Semantic Error: Variable '{name}' already declared as a function."
20
 
 
21
  if current_func:
 
22
  if current_func not in self.function_variables:
 
23
  self.function_variables[current_func] = set()
24
 
 
25
  if name in self.function_variables[current_func]:
 
26
  return f"Semantic Error: Variable '{name}' already declared in this function."
27
 
 
28
  self.function_variables[current_func].add(name)
29
 
 
30
  if self.current_func_name:
31
 
 
32
  scope[name] = {
 
33
  "type": type_,
 
34
  "value": value,
 
35
  "is_list": is_list,
 
36
  "is_fertile": is_fertile
 
37
  }
 
38
  else:
 
39
  if name in self.global_variables:
 
40
  return f"Semantic Error: Variable '{name}' already declared."
41
 
 
42
  self.variables[name] = {
 
43
  "type": type_,
 
44
  "value": value,
 
45
  "is_list": is_list,
 
46
  "is_fertile": is_fertile
 
47
  }
48
 
49
 
 
50
  def lookup_variable(self, name):
 
51
  for i, scope in enumerate(reversed(self.scopes)):
 
52
  if name in scope:
 
53
  return scope[name]
54
 
 
55
  if name in self.variables:
 
56
  return self.variables[name]
57
 
 
58
  return f"Semantic Error: Variable '{name}' used before declaration."
59
 
 
60
  def set_variable(self, name, value):
 
61
  current_scope = self.scopes[-1]
62
 
 
63
  if name in current_scope:
 
64
  current_scope[name]["value"] = value
 
65
  else:
 
66
  return f"Semantic Error: Variable '{name}' not declared in the current scope."
67
 
 
68
  def declare_function(self, name, return_type, params, node=None):
 
69
  if name in self.functions:
 
70
  return f"Semantic Error: Function '{name}' already declared."
 
71
  self.functions[name] = {"return_type": return_type, "params": params, "node": node}
72
 
 
73
  def lookup_function(self, name):
 
74
  if name in self.functions:
 
75
  return self.functions[name]
 
76
  return f"Semantic Error: Function '{name}' is not defined."
77
 
78
 
 
79
  def enter_scope(self):
 
80
  self.scopes.append({})
81
 
82
 
 
83
  def exit_scope(self):
 
84
  if len(self.scopes) > 1:
 
85
  self.scopes.pop()
86
 
 
87
  if self.current_func_name:
 
88
  current_func = self.current_func_name
89
 
 
90
  if current_func in self.function_variables:
 
91
  self.function_variables[current_func].clear()
92
 
93
 
 
94
  def debug_scopes(self):
 
95
  print("\n====== SYMBOL TABLE DEBUG ======")
 
96
  print("🔹 Local Scopes (Stacked from Global to Inner Scope):")
 
97
  for i, scope in enumerate(self.scopes):
 
98
  print(f" Scope {i}: {scope}")
 
99
  print("================================\n")
100
 
101
 
 
1
 
2
 
3
+ # AUTO: Defines class `SymbolTable`.
4
  class SymbolTable:
5
+ # AUTO: Defines function `__init__`.
6
  def __init__(self):
7
+ # AUTO: Sets `self.variables`.
8
  self.variables = {}
9
+ # AUTO: Sets `self.global_variables`.
10
  self.global_variables = {}
11
+ # AUTO: Sets `self.functions`.
12
  self.functions = {}
13
+ # AUTO: Sets `self.scopes`.
14
  self.scopes = [{}]
15
+ # AUTO: Sets `self.current_func_name`.
16
  self.current_func_name = None
17
+ # AUTO: Sets `self.function_variables`.
18
  self.function_variables = {}
19
+ # AUTO: Sets `self.bundle_types`.
20
  self.bundle_types = {}
21
 
22
+ # AUTO: Defines function `declare_variable`.
23
  def declare_variable(self, name, type_, value=None, is_list=False, is_fertile=False):
24
+ # AUTO: Sets `scope`.
25
  scope = self.scopes[-1]
26
+ # AUTO: Sets `current_func`.
27
  current_func = self.current_func_name
28
 
29
 
30
+ # AUTO: Checks this condition.
31
  if name in self.functions:
32
+ # AUTO: Returns this result to the caller.
33
  return f"Semantic Error: Variable '{name}' already declared as a function."
34
 
35
+ # AUTO: Checks this condition.
36
  if current_func:
37
+ # AUTO: Checks this condition.
38
  if current_func not in self.function_variables:
39
+ # AUTO: Sets `self.function_variables[current_func]`.
40
  self.function_variables[current_func] = set()
41
 
42
+ # AUTO: Checks this condition.
43
  if name in self.function_variables[current_func]:
44
+ # AUTO: Returns this result to the caller.
45
  return f"Semantic Error: Variable '{name}' already declared in this function."
46
 
47
+ # AUTO: Calls `self.function_variables[current_func].add`.
48
  self.function_variables[current_func].add(name)
49
 
50
+ # AUTO: Checks this condition.
51
  if self.current_func_name:
52
 
53
+ # AUTO: Sets `scope[name]`.
54
  scope[name] = {
55
+ # AUTO: Executes this statement.
56
  "type": type_,
57
+ # AUTO: Executes this statement.
58
  "value": value,
59
+ # AUTO: Executes this statement.
60
  "is_list": is_list,
61
+ # AUTO: Executes this statement.
62
  "is_fertile": is_fertile
63
+ # AUTO: Closes the current grouped code/data.
64
  }
65
+ # AUTO: Runs when previous condition did not pass.
66
  else:
67
+ # AUTO: Checks this condition.
68
  if name in self.global_variables:
69
+ # AUTO: Returns this result to the caller.
70
  return f"Semantic Error: Variable '{name}' already declared."
71
 
72
+ # AUTO: Sets `self.variables[name]`.
73
  self.variables[name] = {
74
+ # AUTO: Executes this statement.
75
  "type": type_,
76
+ # AUTO: Executes this statement.
77
  "value": value,
78
+ # AUTO: Executes this statement.
79
  "is_list": is_list,
80
+ # AUTO: Executes this statement.
81
  "is_fertile": is_fertile
82
+ # AUTO: Closes the current grouped code/data.
83
  }
84
 
85
 
86
+ # AUTO: Defines function `lookup_variable`.
87
  def lookup_variable(self, name):
88
+ # AUTO: Starts a loop over these values.
89
  for i, scope in enumerate(reversed(self.scopes)):
90
+ # AUTO: Checks this condition.
91
  if name in scope:
92
+ # AUTO: Returns this result to the caller.
93
  return scope[name]
94
 
95
+ # AUTO: Checks this condition.
96
  if name in self.variables:
97
+ # AUTO: Returns this result to the caller.
98
  return self.variables[name]
99
 
100
+ # AUTO: Returns this result to the caller.
101
  return f"Semantic Error: Variable '{name}' used before declaration."
102
 
103
+ # AUTO: Defines function `set_variable`.
104
  def set_variable(self, name, value):
105
+ # AUTO: Sets `current_scope`.
106
  current_scope = self.scopes[-1]
107
 
108
+ # AUTO: Checks this condition.
109
  if name in current_scope:
110
+ # AUTO: Sets `current_scope[name]["value"]`.
111
  current_scope[name]["value"] = value
112
+ # AUTO: Runs when previous condition did not pass.
113
  else:
114
+ # AUTO: Returns this result to the caller.
115
  return f"Semantic Error: Variable '{name}' not declared in the current scope."
116
 
117
+ # AUTO: Defines function `declare_function`.
118
  def declare_function(self, name, return_type, params, node=None):
119
+ # AUTO: Checks this condition.
120
  if name in self.functions:
121
+ # AUTO: Returns this result to the caller.
122
  return f"Semantic Error: Function '{name}' already declared."
123
+ # AUTO: Sets `self.functions[name]`.
124
  self.functions[name] = {"return_type": return_type, "params": params, "node": node}
125
 
126
+ # AUTO: Defines function `lookup_function`.
127
  def lookup_function(self, name):
128
+ # AUTO: Checks this condition.
129
  if name in self.functions:
130
+ # AUTO: Returns this result to the caller.
131
  return self.functions[name]
132
+ # AUTO: Returns this result to the caller.
133
  return f"Semantic Error: Function '{name}' is not defined."
134
 
135
 
136
+ # AUTO: Defines function `enter_scope`.
137
  def enter_scope(self):
138
+ # AUTO: Appends a value to a list.
139
  self.scopes.append({})
140
 
141
 
142
+ # AUTO: Defines function `exit_scope`.
143
  def exit_scope(self):
144
+ # AUTO: Checks this condition.
145
  if len(self.scopes) > 1:
146
+ # AUTO: Removes and returns an item.
147
  self.scopes.pop()
148
 
149
+ # AUTO: Checks this condition.
150
  if self.current_func_name:
151
+ # AUTO: Sets `current_func`.
152
  current_func = self.current_func_name
153
 
154
+ # AUTO: Checks this condition.
155
  if current_func in self.function_variables:
156
+ # AUTO: Calls `self.function_variables[current_func].clear`.
157
  self.function_variables[current_func].clear()
158
 
159
 
160
+ # AUTO: Defines function `debug_scopes`.
161
  def debug_scopes(self):
162
+ # AUTO: Calls `print`.
163
  print("\n====== SYMBOL TABLE DEBUG ======")
164
+ # AUTO: Calls `print`.
165
  print("🔹 Local Scopes (Stacked from Global to Inner Scope):")
166
+ # AUTO: Starts a loop over these values.
167
  for i, scope in enumerate(self.scopes):
168
+ # AUTO: Calls `print`.
169
  print(f" Scope {i}: {scope}")
170
+ # AUTO: Calls `print`.
171
  print("================================\n")
172
 
173
 
Backend/server.py CHANGED
@@ -1,597 +1,1095 @@
 
1
 
2
- # WARNING SUPPRESSION + EVENTLET BOOTSTRAP
 
 
 
 
 
3
  import warnings
 
4
  warnings.filterwarnings("ignore", message=".*RLock.*were not greened.*")
5
 
 
6
  import eventlet
 
7
  eventlet.monkey_patch()
8
 
 
9
  from flask import Flask, request, jsonify, send_from_directory
 
10
  from flask_cors import CORS
 
11
  from flask_socketio import SocketIO, emit
 
12
  import os
 
13
  from google import genai
 
14
  from lexer import lex, get_token_description
 
15
  from parser import LL1Parser
 
16
  from cfg import cfg, first_sets, predict_sets
 
17
  from parser.builder import analyze_semantics
 
18
  from semantic import validate_ast
 
19
  from icg import generate_icg
 
20
  from interpreter import Interpreter, InterpreterError, _CancelledError
 
21
  from ai import fallback_reply
22
 
23
 
 
24
  def _display_value(val):
 
25
  if val is None:
 
26
  return ''
 
27
  s = str(val)
 
28
  s = s.replace('\n', '\\n')
 
29
  s = s.replace('\t', '\\t')
 
30
  s = s.replace('\r', '\\r')
 
31
  return s
32
 
33
 
 
34
  app = Flask(__name__, static_folder='../UI', static_url_path='')
 
35
  CORS(app)
 
36
  socketio = SocketIO(app, cors_allowed_origins="*")
37
 
 
38
  interpreters = {}
39
 
40
 
 
41
  class SessionEmitter:
 
42
  def __init__(self, sio, sid):
 
43
  self._sio = sio
 
44
  self._sid = sid
45
 
 
46
  def emit(self, event, data=None, **kwargs):
 
47
  self._sio.emit(event, data, to=self._sid, **kwargs)
48
 
49
 
 
50
  parser = LL1Parser(
 
51
  cfg=cfg,
 
52
  predict_sets=predict_sets,
 
53
  first_sets=first_sets,
 
54
  start_symbol="<program>",
 
55
  end_marker="EOF",
 
56
  skip_token_types={'\n', 'comment', 'mcommentlit'}
 
57
  )
58
 
 
 
 
59
  @app.after_request
 
60
  def add_no_cache(response):
 
61
  response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
 
62
  response.headers['Pragma'] = 'no-cache'
 
63
  response.headers['Expires'] = '0'
 
64
  return response
65
 
 
66
  @app.route('/')
 
67
  def index():
 
68
  return send_from_directory('../UI', 'index.html')
69
 
 
70
  @app.route('/images/<path:filename>')
 
71
  def serve_images(filename):
 
72
  return send_from_directory('../images', filename)
73
 
 
74
  @app.route('/<path:path>')
 
75
  def serve_static(path):
 
76
  return send_from_directory('../UI', path)
77
 
78
 
 
 
79
  @app.route('/api/lex', methods=['POST'])
 
80
  def lexer_endpoint():
 
81
  try:
 
82
  data = request.get_json()
83
 
 
84
  if not data or 'source_code' not in data:
 
85
  return jsonify({
 
86
  'error': 'Missing source_code in request body'
 
87
  }), 400
88
 
 
89
  source_code = data['source_code']
90
 
 
91
  tokens, errors = lex(source_code)
92
 
 
93
  token_list = []
 
94
  for token in tokens:
 
95
  token_list.append({
 
96
  'type': token.type,
 
97
  'value': _display_value(token.value),
 
98
  'line': token.line,
 
99
  'col': getattr(token, 'col', 0),
 
100
  'description': get_token_description(token.type, token.value)
 
101
  })
102
 
 
103
  return jsonify({
 
104
  'tokens': token_list,
 
105
  'errors': errors
 
106
  })
107
 
 
108
  except Exception as e:
 
109
  return jsonify({
 
110
  'error': f'Server error: {str(e)}'
 
111
  }), 500
112
 
 
 
113
  @app.route('/api/parse', methods=['POST'])
 
114
  def parser_endpoint():
 
115
  try:
 
116
  data = request.get_json()
117
 
 
118
  if not data or 'source_code' not in data:
 
119
  return jsonify({
 
120
  'error': 'Missing source_code in request body'
 
121
  }), 400
122
 
 
123
  source_code = data['source_code']
124
 
 
125
  tokens, lex_errors = lex(source_code)
126
 
 
127
  token_list = []
 
128
  for token in tokens:
 
129
  token_list.append({
 
130
  'type': token.type,
 
131
  'value': _display_value(token.value),
 
132
  'line': token.line,
 
133
  'col': getattr(token, 'col', 0),
 
134
  'description': get_token_description(token.type, token.value)
 
135
  })
136
 
 
137
  if lex_errors:
 
138
  return jsonify({
 
139
  'success': False,
 
140
  'tokens': token_list,
 
141
  'errors': lex_errors,
 
142
  'stage': ['lexical'],
 
143
  'lexical_errors': True,
 
144
  'syntax_errors': False
 
145
  })
146
 
 
147
  parse_success, parse_errors = parser.parse(tokens)
148
 
 
149
  stages = []
 
150
  if parse_errors:
 
151
  stages.append('syntax')
152
 
 
153
  return jsonify({
 
154
  'success': parse_success,
 
155
  'tokens': token_list,
 
156
  'errors': parse_errors,
 
157
  'stage': stages if stages else ['success'],
 
158
  'lexical_errors': False,
 
159
  'syntax_errors': len(parse_errors) > 0
 
160
  })
161
 
 
162
  except Exception as e:
 
163
  return jsonify({
 
164
  'error': f'Server error: {str(e)}'
 
165
  }), 500
166
 
 
167
  @app.route('/api/health', methods=['GET'])
 
168
  def health_check():
 
169
  return jsonify({
 
170
  'status': 'healthy',
 
171
  'message': 'GAL Compiler Server is running'
 
172
  })
173
 
174
 
 
175
  @app.route('/api/semantic', methods=['POST'])
 
176
  def semantic_endpoint():
 
177
  try:
 
178
  data = request.get_json()
179
 
 
180
  if not data or 'source_code' not in data:
 
181
  return jsonify({
 
182
  'error': 'Missing source_code in request body'
 
183
  }), 400
184
 
 
185
  source_code = data['source_code']
186
 
 
187
  tokens, lex_errors = lex(source_code)
188
 
 
189
  token_list = []
 
190
  for token in tokens:
 
191
  token_list.append({
 
192
  'type': token.type,
 
193
  'value': _display_value(token.value),
 
194
  'line': token.line,
 
195
  'col': getattr(token, 'col', 0),
 
196
  'description': get_token_description(token.type, token.value)
 
197
  })
198
 
 
199
  if lex_errors:
 
200
  return jsonify({
 
201
  'success': False,
 
202
  'tokens': token_list,
 
203
  'errors': lex_errors,
 
204
  'warnings': [],
 
205
  'stage': 'lexical'
 
206
  })
207
 
 
208
  parse_result = parser.parse_and_build(tokens)
209
 
 
210
  if not parse_result['success']:
 
211
  error_stage = parse_result.get('error_stage', 'syntax')
 
212
  return jsonify({
 
213
  'success': False,
 
214
  'tokens': token_list,
 
215
  'errors': parse_result['errors'],
 
216
  'warnings': [],
 
217
  'stage': error_stage
 
218
  })
219
 
 
220
  semantic_result = validate_ast(parse_result['ast'], parse_result['symbol_table'])
221
 
 
222
  return jsonify({
 
223
  'success': semantic_result['success'],
 
224
  'tokens': token_list,
 
225
  'errors': semantic_result['errors'],
 
226
  'warnings': semantic_result['warnings'],
 
227
  'symbol_table': semantic_result['symbol_table'],
 
228
  'stage': 'semantic'
 
229
  })
230
 
 
231
  except Exception as e:
 
232
  return jsonify({
 
233
  'error': f'Server error: {str(e)}'
 
234
  }), 500
235
 
 
236
  @app.route('/api/icg', methods=['POST'])
 
237
  def icg_endpoint():
 
238
  try:
 
239
  data = request.get_json()
240
 
 
241
  if not data or 'source_code' not in data:
 
242
  return jsonify({
 
243
  'error': 'Missing source_code in request body'
 
244
  }), 400
245
 
 
246
  source_code = data['source_code']
247
 
 
248
  tokens, lex_errors = lex(source_code)
249
 
 
250
  token_list = []
 
251
  for token in tokens:
 
252
  token_list.append({
 
253
  'type': token.type,
 
254
  'value': _display_value(token.value),
 
255
  'line': token.line,
 
256
  'col': getattr(token, 'col', 0),
 
257
  'description': get_token_description(token.type, token.value)
 
258
  })
259
 
 
260
  if lex_errors:
 
261
  return jsonify({
 
262
  'success': False,
 
263
  'tokens': token_list,
 
264
  'errors': lex_errors,
 
265
  'stage': 'lexical'
 
266
  })
267
 
 
268
  parse_result = parser.parse_and_build(tokens)
 
269
  if not parse_result['success']:
 
270
  error_stage = parse_result.get('error_stage', 'syntax')
 
271
  return jsonify({
 
272
  'success': False,
 
273
  'tokens': token_list,
 
274
  'errors': parse_result['errors'],
 
275
  'stage': error_stage
 
276
  })
277
 
 
278
  semantic_result = validate_ast(parse_result['ast'], parse_result['symbol_table'])
 
279
  if not semantic_result['success']:
 
280
  return jsonify({
 
281
  'success': False,
 
282
  'tokens': token_list,
 
283
  'errors': semantic_result['errors'],
 
284
  'warnings': semantic_result['warnings'],
 
285
  'stage': 'semantic'
 
286
  })
287
 
 
288
  icg_result = generate_icg(tokens)
289
 
 
290
  return jsonify({
 
291
  'success': icg_result['success'],
 
292
  'tokens': token_list,
 
293
  'tac': icg_result['tac'],
 
294
  'tac_text': icg_result['tac_text'],
 
295
  'errors': icg_result['errors'],
 
296
  'warnings': semantic_result.get('warnings', []),
 
297
  'stage': 'icg'
 
298
  })
299
 
 
300
  except Exception as e:
 
301
  return jsonify({
 
302
  'error': f'Server error: {str(e)}'
 
303
  }), 500
304
 
305
 
 
306
  class OutputCollector:
 
307
  def __init__(self):
 
308
  self.outputs = []
 
309
  self.needs_input = False
310
 
 
311
  def emit(self, event, data=None, **kwargs):
 
312
  if event == 'output' and data:
 
313
  self.outputs.append(data.get('output', ''))
 
314
  elif event == 'input_required':
 
315
  self.needs_input = True
 
316
  raise _InputNeeded()
317
 
318
 
 
319
  class _InputNeeded(Exception):
 
320
  pass
321
 
322
 
 
 
323
  @app.route('/api/run', methods=['POST'])
 
324
  def run_endpoint():
 
325
  try:
 
326
  data = request.get_json()
 
327
  if not data or 'source_code' not in data:
 
328
  return jsonify({'error': 'Missing source_code in request body'}), 400
329
 
 
330
  source_code = data['source_code']
331
 
 
332
  tokens, lex_errors = lex(source_code)
 
333
  if lex_errors:
 
334
  return jsonify({
 
335
  'success': False,
 
336
  'stage': 'lexical',
 
337
  'output': [],
 
338
  'errors': lex_errors
 
339
  })
340
 
 
341
  parse_result = parser.parse_and_build(tokens)
 
342
  if not parse_result['success']:
 
343
  error_stage = parse_result.get('error_stage', 'syntax')
 
344
  return jsonify({
 
345
  'success': False,
 
346
  'stage': error_stage,
 
347
  'output': [],
 
348
  'errors': [str(e) for e in parse_result['errors']]
 
349
  })
350
 
 
351
  semantic_result = validate_ast(parse_result['ast'], parse_result['symbol_table'])
 
352
  if not semantic_result['success']:
 
353
  return jsonify({
 
354
  'success': False,
 
355
  'stage': 'semantic',
 
356
  'output': [],
 
357
  'errors': [str(e) for e in semantic_result['errors']]
 
358
  })
359
 
 
360
  ast = semantic_result['ast']
361
 
 
362
  collector = OutputCollector()
 
363
  interp = Interpreter(socketio=collector)
 
364
  try:
 
365
  interp.interpret(ast)
 
366
  return jsonify({
 
367
  'success': True,
 
368
  'stage': 'execution',
 
369
  'output': collector.outputs,
 
370
  'errors': []
 
371
  })
 
372
  except _InputNeeded:
 
373
  return jsonify({
 
374
  'success': False,
 
375
  'stage': 'execution',
 
376
  'output': collector.outputs,
 
377
  'errors': ['Program requires interactive input (water())'],
 
378
  'needs_input': True
 
379
  })
 
380
  except InterpreterError as e:
 
381
  collector.outputs.append(f'Runtime Error: {e}')
 
382
  return jsonify({
 
383
  'success': False,
 
384
  'stage': 'execution',
 
385
  'output': collector.outputs,
 
386
  'errors': [str(e)]
 
387
  })
 
388
  except Exception as e:
 
389
  collector.outputs.append(f'Internal Error: {e}')
 
390
  return jsonify({
 
391
  'success': False,
 
392
  'stage': 'execution',
 
393
  'output': collector.outputs,
 
394
  'errors': [str(e)]
 
395
  })
396
 
 
397
  except Exception as e:
 
398
  return jsonify({'error': f'Server error: {str(e)}'}), 500
399
 
400
 
 
401
  @socketio.on('connect')
 
402
  def handle_connect():
 
403
  pass
404
 
 
405
  @socketio.on('disconnect')
 
406
  def handle_disconnect():
 
407
  sid = request.sid
 
408
  interpreters.pop(sid, None)
409
 
 
410
  @socketio.on('run_code')
 
411
  def handle_run_code(data):
 
412
  sid = request.sid
 
413
  source_code = data.get('source_code', '')
414
 
 
415
  tokens, lex_errors = lex(source_code)
 
416
  if lex_errors:
 
417
  for err in lex_errors:
 
418
  emit('output', {'output': f'Lexical Error: {err}'})
 
419
  emit('execution_complete', {'success': False, 'stage': 'lexical'})
 
420
  return
421
 
 
422
  emit('stage_complete', {'stage': 'lexical', 'success': True})
423
 
 
424
  parse_result = parser.parse_and_build(tokens)
 
425
  if not parse_result['success']:
 
426
  error_stage = parse_result.get('error_stage', 'syntax')
 
427
  for err in parse_result['errors']:
 
428
  emit('output', {'output': f'{err}'})
 
429
  emit('execution_complete', {'success': False, 'stage': error_stage})
 
430
  return
431
 
 
432
  emit('stage_complete', {'stage': 'syntax', 'success': True})
433
 
 
434
  semantic_result = validate_ast(parse_result['ast'], parse_result['symbol_table'])
 
435
  if not semantic_result['success']:
 
436
  for err in semantic_result['errors']:
 
437
  emit('output', {'output': f'{err}'})
 
438
  emit('execution_complete', {'success': False, 'stage': 'semantic'})
 
439
  return
440
 
 
441
  emit('stage_complete', {'stage': 'semantic', 'success': True})
442
 
 
443
  ast = semantic_result['ast']
444
 
 
445
  old_interp = interpreters.get(sid)
 
446
  if old_interp and hasattr(old_interp, '_cancelled'):
 
447
  old_interp._cancelled = True
 
448
  for evt in list(old_interp.input_events.values()):
 
449
  try:
 
450
  evt.send(None)
 
451
  except (AttributeError, AssertionError):
 
452
  try:
 
453
  evt.set()
 
454
  except Exception:
 
455
  pass
456
 
 
 
457
  def run_interpreter():
 
458
  try:
 
459
  session_emitter = SessionEmitter(socketio, sid)
 
460
  interp = Interpreter(socketio=session_emitter)
 
461
  interp._cancelled = False
 
462
  interpreters[sid] = interp
 
463
  interp.interpret(ast)
 
464
  if not interp._cancelled:
 
465
  socketio.emit('execution_complete', {'success': True, 'stage': 'execution'}, to=sid)
 
466
  except _CancelledError:
 
467
  pass
 
468
  except InterpreterError as e:
 
469
  if not getattr(interp, '_cancelled', False):
 
470
  socketio.emit('output', {'output': f'Runtime Error: {e}'}, to=sid)
 
471
  socketio.emit('execution_complete', {'success': False, 'stage': 'execution'}, to=sid)
 
472
  except Exception as e:
 
473
  if not getattr(interp, '_cancelled', False):
 
474
  socketio.emit('output', {'output': f'Internal Error: {e}'}, to=sid)
 
475
  socketio.emit('execution_complete', {'success': False, 'stage': 'execution'}, to=sid)
 
476
  finally:
 
477
  if interpreters.get(sid) is interp:
 
478
  interpreters.pop(sid, None)
479
 
 
480
  socketio.start_background_task(run_interpreter)
481
 
 
482
  @socketio.on('capture_input')
 
483
  def handle_capture_input(data):
 
484
  sid = request.sid
 
485
  interp = interpreters.get(sid)
 
486
  if interp:
 
487
  var_name = data.get('var_name', '')
 
488
  input_value = data.get('input', '')
 
489
  interp.provide_input(var_name, input_value)
490
 
491
 
 
492
  _prompt_path = os.path.join(os.path.dirname(__file__), 'ai', 'prompt.txt')
 
493
  with open(_prompt_path, 'r', encoding='utf-8') as _f:
 
494
  GAL_SYSTEM_PROMPT = _f.read()
495
 
496
 
 
497
  _gemini_client = None
 
498
  _chat_sessions = {}
499
 
 
500
  def _get_gemini_client():
 
501
  global _gemini_client
 
502
  if _gemini_client is None:
 
503
  api_key = os.environ.get('GEMINI_API_KEY', '')
 
504
  if not api_key:
 
505
  return None
 
506
  _gemini_client = genai.Client(api_key=api_key)
 
507
  return _gemini_client
508
 
 
509
  @app.route('/api/chat', methods=['POST'])
 
510
  def chat_endpoint():
 
511
  try:
 
512
  data = request.get_json()
 
513
  if not data or 'message' not in data:
 
514
  return jsonify({'error': 'Missing message in request body'}), 400
515
 
 
516
  user_message = data['message']
 
517
  session_id = data.get('session_id', 'default')
 
518
  editor_code = data.get('editor_code', '')
519
 
 
520
  client = _get_gemini_client()
 
521
  if client is None:
 
522
  fb_reply = fallback_reply(user_message)
 
523
  return jsonify({'reply': fb_reply, 'mode': 'fallback'})
524
 
 
525
  if session_id not in _chat_sessions:
 
526
  _chat_sessions[session_id] = []
 
527
  history = _chat_sessions[session_id]
528
 
 
529
  full_message = user_message
 
530
  if editor_code.strip():
 
531
  full_message = f"[Current code in editor]:\n```\n{editor_code}\n```\n\nUser question: {user_message}"
532
 
 
533
  history.append({'role': 'user', 'parts': [{'text': full_message}]})
534
 
 
535
  if len(history) > 20:
 
536
  history[:] = history[-20:]
537
 
 
538
  models_to_try = ['gemini-2.5-flash', 'gemini-2.5-flash-lite', 'gemini-2.0-flash', 'gemini-2.0-flash-lite']
 
539
  last_error: Exception = RuntimeError("No Gemini models were available to try")
 
540
  for model_name in models_to_try:
 
541
  try:
 
542
  response = client.models.generate_content(
 
543
  model=model_name,
 
544
  contents=history,
 
545
  config=genai.types.GenerateContentConfig(
 
546
  system_instruction=GAL_SYSTEM_PROMPT,
 
547
  temperature=0.3,
 
548
  max_output_tokens=4096,
 
549
  ),
 
550
  )
 
551
  break
 
552
  except Exception as model_err:
 
553
  last_error = model_err
 
554
  err_msg = str(model_err)
 
555
  if '429' not in err_msg and 'RESOURCE_EXHAUSTED' not in err_msg and '503' not in err_msg and 'UNAVAILABLE' not in err_msg and '403' not in err_msg and 'PERMISSION_DENIED' not in err_msg:
 
556
  raise
 
557
  continue
 
558
  else:
 
559
  raise last_error
560
 
 
561
  reply = response.text or 'No response generated.'
562
 
 
563
  history.append({'role': 'model', 'parts': [{'text': reply}]})
564
 
 
565
  return jsonify({'reply': reply, 'mode': 'ai'})
566
 
 
567
  except Exception as e:
 
568
  try:
 
569
  fb_reply = fallback_reply(user_message)
 
570
  return jsonify({'reply': fb_reply, 'mode': 'fallback'})
 
571
  except Exception:
 
572
  pass
 
573
  err_str = str(e)
 
574
  return jsonify({'error': f'Chat error: {err_str}'}), 500
575
 
 
576
  @app.route('/api/chat/clear', methods=['POST'])
 
577
  def chat_clear_endpoint():
 
578
  data = request.get_json() or {}
 
579
  session_id = data.get('session_id', 'default')
 
580
  _chat_sessions.pop(session_id, None)
 
581
  return jsonify({'success': True})
582
 
583
 
 
584
  if __name__ == '__main__':
 
585
  port = int(os.environ.get('PORT', 5000))
 
586
  debug = os.environ.get('DEBUG', 'False') == 'True'
587
 
 
588
  print("Starting GAL Compiler Server...")
 
589
  print(f"Server running at http://0.0.0.0:{port}")
 
590
  print("API endpoints:")
 
591
  print(f" - POST http://localhost:{port}/api/lex (Lexical Analysis)")
 
592
  print(f" - POST http://localhost:{port}/api/parse (Syntax Analysis)")
 
593
  print(f" - POST http://localhost:{port}/api/semantic (Semantic Analysis)")
 
594
  print(f" - POST http://localhost:{port}/api/icg (Intermediate Code Generation)")
 
595
  print(f" - POST http://localhost:{port}/api/chat (AI Chat Helper)")
 
596
  print(f" - Socket.IO: run_code (Execute Program)")
 
597
  socketio.run(app, host='0.0.0.0', port=port, debug=debug, allow_unsafe_werkzeug=True)
 
1
+ """HTTP and Socket.IO entry point for the GAL compiler.
2
 
3
+ Pipeline from the UI:
4
+ source code -> lexer -> LL(1) parser/AST builder -> semantic validator -> interpreter.
5
+ """
6
+
7
+ # Eventlet must be patched before Flask-SocketIO starts using sockets.
8
+ # AUTO: Imports a module used by this file.
9
  import warnings
10
+ # AUTO: Sets `warnings.filterwarnings("ignore", message`.
11
  warnings.filterwarnings("ignore", message=".*RLock.*were not greened.*")
12
 
13
+ # AUTO: Imports a module used by this file.
14
  import eventlet
15
+ # AUTO: Calls `eventlet.monkey_patch`.
16
  eventlet.monkey_patch()
17
 
18
+ # AUTO: Imports names from another module.
19
  from flask import Flask, request, jsonify, send_from_directory
20
+ # AUTO: Imports names from another module.
21
  from flask_cors import CORS
22
+ # AUTO: Imports names from another module.
23
  from flask_socketio import SocketIO, emit
24
+ # AUTO: Imports a module used by this file.
25
  import os
26
+ # AUTO: Imports names from another module.
27
  from google import genai
28
+ # AUTO: Imports names from another module.
29
  from lexer import lex, get_token_description
30
+ # AUTO: Imports names from another module.
31
  from parser import LL1Parser
32
+ # AUTO: Imports names from another module.
33
  from cfg import cfg, first_sets, predict_sets
34
+ # AUTO: Imports names from another module.
35
  from parser.builder import analyze_semantics
36
+ # AUTO: Imports names from another module.
37
  from semantic import validate_ast
38
+ # AUTO: Imports names from another module.
39
  from icg import generate_icg
40
+ # AUTO: Imports names from another module.
41
  from interpreter import Interpreter, InterpreterError, _CancelledError
42
+ # AUTO: Imports names from another module.
43
  from ai import fallback_reply
44
 
45
 
46
+ # AUTO: Defines function `_display_value`.
47
  def _display_value(val):
48
+ # AUTO: Checks this condition.
49
  if val is None:
50
+ # AUTO: Returns this result to the caller.
51
  return ''
52
+ # AUTO: Sets `s`.
53
  s = str(val)
54
+ # AUTO: Sets `s`.
55
  s = s.replace('\n', '\\n')
56
+ # AUTO: Sets `s`.
57
  s = s.replace('\t', '\\t')
58
+ # AUTO: Sets `s`.
59
  s = s.replace('\r', '\\r')
60
+ # AUTO: Returns this result to the caller.
61
  return s
62
 
63
 
64
+ # AUTO: Sets `app`.
65
  app = Flask(__name__, static_folder='../UI', static_url_path='')
66
+ # AUTO: Calls `CORS`.
67
  CORS(app)
68
+ # AUTO: Sets `socketio`.
69
  socketio = SocketIO(app, cors_allowed_origins="*")
70
 
71
+ # AUTO: Sets `interpreters`.
72
  interpreters = {}
73
 
74
 
75
+ # AUTO: Defines class `SessionEmitter`.
76
  class SessionEmitter:
77
+ # AUTO: Defines function `__init__`.
78
  def __init__(self, sio, sid):
79
+ # AUTO: Sets `self._sio`.
80
  self._sio = sio
81
+ # AUTO: Sets `self._sid`.
82
  self._sid = sid
83
 
84
+ # AUTO: Defines function `emit`.
85
  def emit(self, event, data=None, **kwargs):
86
+ # AUTO: Sets `self._sio.emit(event, data, to`.
87
  self._sio.emit(event, data, to=self._sid, **kwargs)
88
 
89
 
90
+ # AUTO: Sets `parser`.
91
  parser = LL1Parser(
92
+ # AUTO: Sets `cfg`.
93
  cfg=cfg,
94
+ # AUTO: Sets `predict_sets`.
95
  predict_sets=predict_sets,
96
+ # AUTO: Sets `first_sets`.
97
  first_sets=first_sets,
98
+ # AUTO: Sets `start_symbol`.
99
  start_symbol="<program>",
100
+ # AUTO: Sets `end_marker`.
101
  end_marker="EOF",
102
+ # AUTO: Sets `skip_token_types`.
103
  skip_token_types={'\n', 'comment', 'mcommentlit'}
104
+ # AUTO: Closes the current grouped code/data.
105
  )
106
 
107
+
108
+ # GUIDE: Browser/cache setup for local testing.
109
+ # AUTO: Attaches this decorator to the next function/class.
110
  @app.after_request
111
+ # AUTO: Defines function `add_no_cache`.
112
  def add_no_cache(response):
113
+ # AUTO: Sets `response.headers['Cache-Control']`.
114
  response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
115
+ # AUTO: Sets `response.headers['Pragma']`.
116
  response.headers['Pragma'] = 'no-cache'
117
+ # AUTO: Sets `response.headers['Expires']`.
118
  response.headers['Expires'] = '0'
119
+ # AUTO: Returns this result to the caller.
120
  return response
121
 
122
+ # AUTO: Attaches this decorator to the next function/class.
123
  @app.route('/')
124
+ # AUTO: Defines function `index`.
125
  def index():
126
+ # AUTO: Returns this result to the caller.
127
  return send_from_directory('../UI', 'index.html')
128
 
129
+ # AUTO: Attaches this decorator to the next function/class.
130
  @app.route('/images/<path:filename>')
131
+ # AUTO: Defines function `serve_images`.
132
  def serve_images(filename):
133
+ # AUTO: Returns this result to the caller.
134
  return send_from_directory('../images', filename)
135
 
136
+ # AUTO: Attaches this decorator to the next function/class.
137
  @app.route('/<path:path>')
138
+ # AUTO: Defines function `serve_static`.
139
  def serve_static(path):
140
+ # AUTO: Returns this result to the caller.
141
  return send_from_directory('../UI', path)
142
 
143
 
144
+ # GUIDE: Lexer stage endpoint used by the lexeme table and Lexer run mode.
145
+ # AUTO: Attaches this decorator to the next function/class.
146
  @app.route('/api/lex', methods=['POST'])
147
+ # AUTO: Defines function `lexer_endpoint`.
148
  def lexer_endpoint():
149
+ # AUTO: Starts protected code that can catch errors.
150
  try:
151
+ # AUTO: Sets `data`.
152
  data = request.get_json()
153
 
154
+ # AUTO: Checks this condition.
155
  if not data or 'source_code' not in data:
156
+ # AUTO: Returns this result to the caller.
157
  return jsonify({
158
+ # AUTO: Executes this statement.
159
  'error': 'Missing source_code in request body'
160
+ # AUTO: Closes the current grouped code/data.
161
  }), 400
162
 
163
+ # AUTO: Sets `source_code`.
164
  source_code = data['source_code']
165
 
166
+ # AUTO: Sets `tokens, errors`.
167
  tokens, errors = lex(source_code)
168
 
169
+ # AUTO: Sets `token_list`.
170
  token_list = []
171
+ # AUTO: Starts a loop over these values.
172
  for token in tokens:
173
+ # AUTO: Appends a value to a list.
174
  token_list.append({
175
+ # AUTO: Executes this statement.
176
  'type': token.type,
177
+ # AUTO: Calls `_display_value`.
178
  'value': _display_value(token.value),
179
+ # AUTO: Executes this statement.
180
  'line': token.line,
181
+ # AUTO: Calls `getattr`.
182
  'col': getattr(token, 'col', 0),
183
+ # AUTO: Calls `get_token_description`.
184
  'description': get_token_description(token.type, token.value)
185
+ # AUTO: Closes the current grouped code/data.
186
  })
187
 
188
+ # AUTO: Returns this result to the caller.
189
  return jsonify({
190
+ # AUTO: Executes this statement.
191
  'tokens': token_list,
192
+ # AUTO: Executes this statement.
193
  'errors': errors
194
+ # AUTO: Closes the current grouped code/data.
195
  })
196
 
197
+ # AUTO: Handles the matching error case.
198
  except Exception as e:
199
+ # AUTO: Returns this result to the caller.
200
  return jsonify({
201
+ # AUTO: Executes this statement.
202
  'error': f'Server error: {str(e)}'
203
+ # AUTO: Closes the current grouped code/data.
204
  }), 500
205
 
206
+ # GUIDE: Syntax stage endpoint; tokenizes first, then checks tokens with CFG.
207
+ # AUTO: Attaches this decorator to the next function/class.
208
  @app.route('/api/parse', methods=['POST'])
209
+ # AUTO: Defines function `parser_endpoint`.
210
  def parser_endpoint():
211
+ # AUTO: Starts protected code that can catch errors.
212
  try:
213
+ # AUTO: Sets `data`.
214
  data = request.get_json()
215
 
216
+ # AUTO: Checks this condition.
217
  if not data or 'source_code' not in data:
218
+ # AUTO: Returns this result to the caller.
219
  return jsonify({
220
+ # AUTO: Executes this statement.
221
  'error': 'Missing source_code in request body'
222
+ # AUTO: Closes the current grouped code/data.
223
  }), 400
224
 
225
+ # AUTO: Sets `source_code`.
226
  source_code = data['source_code']
227
 
228
+ # AUTO: Sets `tokens, lex_errors`.
229
  tokens, lex_errors = lex(source_code)
230
 
231
+ # AUTO: Sets `token_list`.
232
  token_list = []
233
+ # AUTO: Starts a loop over these values.
234
  for token in tokens:
235
+ # AUTO: Appends a value to a list.
236
  token_list.append({
237
+ # AUTO: Executes this statement.
238
  'type': token.type,
239
+ # AUTO: Calls `_display_value`.
240
  'value': _display_value(token.value),
241
+ # AUTO: Executes this statement.
242
  'line': token.line,
243
+ # AUTO: Calls `getattr`.
244
  'col': getattr(token, 'col', 0),
245
+ # AUTO: Calls `get_token_description`.
246
  'description': get_token_description(token.type, token.value)
247
+ # AUTO: Closes the current grouped code/data.
248
  })
249
 
250
+ # AUTO: Checks this condition.
251
  if lex_errors:
252
+ # AUTO: Returns this result to the caller.
253
  return jsonify({
254
+ # AUTO: Executes this statement.
255
  'success': False,
256
+ # AUTO: Executes this statement.
257
  'tokens': token_list,
258
+ # AUTO: Executes this statement.
259
  'errors': lex_errors,
260
+ # AUTO: Executes this statement.
261
  'stage': ['lexical'],
262
+ # AUTO: Executes this statement.
263
  'lexical_errors': True,
264
+ # AUTO: Executes this statement.
265
  'syntax_errors': False
266
+ # AUTO: Closes the current grouped code/data.
267
  })
268
 
269
+ # AUTO: Sets `parse_success, parse_errors`.
270
  parse_success, parse_errors = parser.parse(tokens)
271
 
272
+ # AUTO: Sets `stages`.
273
  stages = []
274
+ # AUTO: Checks this condition.
275
  if parse_errors:
276
+ # AUTO: Appends a value to a list.
277
  stages.append('syntax')
278
 
279
+ # AUTO: Returns this result to the caller.
280
  return jsonify({
281
+ # AUTO: Executes this statement.
282
  'success': parse_success,
283
+ # AUTO: Executes this statement.
284
  'tokens': token_list,
285
+ # AUTO: Executes this statement.
286
  'errors': parse_errors,
287
+ # AUTO: Executes this statement.
288
  'stage': stages if stages else ['success'],
289
+ # AUTO: Executes this statement.
290
  'lexical_errors': False,
291
+ # AUTO: Executes this statement.
292
  'syntax_errors': len(parse_errors) > 0
293
+ # AUTO: Closes the current grouped code/data.
294
  })
295
 
296
+ # AUTO: Handles the matching error case.
297
  except Exception as e:
298
+ # AUTO: Returns this result to the caller.
299
  return jsonify({
300
+ # AUTO: Executes this statement.
301
  'error': f'Server error: {str(e)}'
302
+ # AUTO: Closes the current grouped code/data.
303
  }), 500
304
 
305
+ # AUTO: Attaches this decorator to the next function/class.
306
  @app.route('/api/health', methods=['GET'])
307
+ # AUTO: Defines function `health_check`.
308
  def health_check():
309
+ # AUTO: Returns this result to the caller.
310
  return jsonify({
311
+ # AUTO: Executes this statement.
312
  'status': 'healthy',
313
+ # AUTO: Executes this statement.
314
  'message': 'GAL Compiler Server is running'
315
+ # AUTO: Closes the current grouped code/data.
316
  })
317
 
318
 
319
+ # AUTO: Attaches this decorator to the next function/class.
320
  @app.route('/api/semantic', methods=['POST'])
321
+ # AUTO: Defines function `semantic_endpoint`.
322
  def semantic_endpoint():
323
+ # AUTO: Starts protected code that can catch errors.
324
  try:
325
+ # AUTO: Sets `data`.
326
  data = request.get_json()
327
 
328
+ # AUTO: Checks this condition.
329
  if not data or 'source_code' not in data:
330
+ # AUTO: Returns this result to the caller.
331
  return jsonify({
332
+ # AUTO: Executes this statement.
333
  'error': 'Missing source_code in request body'
334
+ # AUTO: Closes the current grouped code/data.
335
  }), 400
336
 
337
+ # AUTO: Sets `source_code`.
338
  source_code = data['source_code']
339
 
340
+ # AUTO: Sets `tokens, lex_errors`.
341
  tokens, lex_errors = lex(source_code)
342
 
343
+ # AUTO: Sets `token_list`.
344
  token_list = []
345
+ # AUTO: Starts a loop over these values.
346
  for token in tokens:
347
+ # AUTO: Appends a value to a list.
348
  token_list.append({
349
+ # AUTO: Executes this statement.
350
  'type': token.type,
351
+ # AUTO: Calls `_display_value`.
352
  'value': _display_value(token.value),
353
+ # AUTO: Executes this statement.
354
  'line': token.line,
355
+ # AUTO: Calls `getattr`.
356
  'col': getattr(token, 'col', 0),
357
+ # AUTO: Calls `get_token_description`.
358
  'description': get_token_description(token.type, token.value)
359
+ # AUTO: Closes the current grouped code/data.
360
  })
361
 
362
+ # AUTO: Checks this condition.
363
  if lex_errors:
364
+ # AUTO: Returns this result to the caller.
365
  return jsonify({
366
+ # AUTO: Executes this statement.
367
  'success': False,
368
+ # AUTO: Executes this statement.
369
  'tokens': token_list,
370
+ # AUTO: Executes this statement.
371
  'errors': lex_errors,
372
+ # AUTO: Executes this statement.
373
  'warnings': [],
374
+ # AUTO: Executes this statement.
375
  'stage': 'lexical'
376
+ # AUTO: Closes the current grouped code/data.
377
  })
378
 
379
+ # AUTO: Sets `parse_result`.
380
  parse_result = parser.parse_and_build(tokens)
381
 
382
+ # AUTO: Checks this condition.
383
  if not parse_result['success']:
384
+ # AUTO: Sets `error_stage`.
385
  error_stage = parse_result.get('error_stage', 'syntax')
386
+ # AUTO: Returns this result to the caller.
387
  return jsonify({
388
+ # AUTO: Executes this statement.
389
  'success': False,
390
+ # AUTO: Executes this statement.
391
  'tokens': token_list,
392
+ # AUTO: Executes this statement.
393
  'errors': parse_result['errors'],
394
+ # AUTO: Executes this statement.
395
  'warnings': [],
396
+ # AUTO: Executes this statement.
397
  'stage': error_stage
398
+ # AUTO: Closes the current grouped code/data.
399
  })
400
 
401
+ # AUTO: Sets `semantic_result`.
402
  semantic_result = validate_ast(parse_result['ast'], parse_result['symbol_table'])
403
 
404
+ # AUTO: Returns this result to the caller.
405
  return jsonify({
406
+ # AUTO: Executes this statement.
407
  'success': semantic_result['success'],
408
+ # AUTO: Executes this statement.
409
  'tokens': token_list,
410
+ # AUTO: Executes this statement.
411
  'errors': semantic_result['errors'],
412
+ # AUTO: Executes this statement.
413
  'warnings': semantic_result['warnings'],
414
+ # AUTO: Executes this statement.
415
  'symbol_table': semantic_result['symbol_table'],
416
+ # AUTO: Executes this statement.
417
  'stage': 'semantic'
418
+ # AUTO: Closes the current grouped code/data.
419
  })
420
 
421
+ # AUTO: Handles the matching error case.
422
  except Exception as e:
423
+ # AUTO: Returns this result to the caller.
424
  return jsonify({
425
+ # AUTO: Executes this statement.
426
  'error': f'Server error: {str(e)}'
427
+ # AUTO: Closes the current grouped code/data.
428
  }), 500
429
 
430
+ # AUTO: Attaches this decorator to the next function/class.
431
  @app.route('/api/icg', methods=['POST'])
432
+ # AUTO: Defines function `icg_endpoint`.
433
  def icg_endpoint():
434
+ # AUTO: Starts protected code that can catch errors.
435
  try:
436
+ # AUTO: Sets `data`.
437
  data = request.get_json()
438
 
439
+ # AUTO: Checks this condition.
440
  if not data or 'source_code' not in data:
441
+ # AUTO: Returns this result to the caller.
442
  return jsonify({
443
+ # AUTO: Executes this statement.
444
  'error': 'Missing source_code in request body'
445
+ # AUTO: Closes the current grouped code/data.
446
  }), 400
447
 
448
+ # AUTO: Sets `source_code`.
449
  source_code = data['source_code']
450
 
451
+ # AUTO: Sets `tokens, lex_errors`.
452
  tokens, lex_errors = lex(source_code)
453
 
454
+ # AUTO: Sets `token_list`.
455
  token_list = []
456
+ # AUTO: Starts a loop over these values.
457
  for token in tokens:
458
+ # AUTO: Appends a value to a list.
459
  token_list.append({
460
+ # AUTO: Executes this statement.
461
  'type': token.type,
462
+ # AUTO: Calls `_display_value`.
463
  'value': _display_value(token.value),
464
+ # AUTO: Executes this statement.
465
  'line': token.line,
466
+ # AUTO: Calls `getattr`.
467
  'col': getattr(token, 'col', 0),
468
+ # AUTO: Calls `get_token_description`.
469
  'description': get_token_description(token.type, token.value)
470
+ # AUTO: Closes the current grouped code/data.
471
  })
472
 
473
+ # AUTO: Checks this condition.
474
  if lex_errors:
475
+ # AUTO: Returns this result to the caller.
476
  return jsonify({
477
+ # AUTO: Executes this statement.
478
  'success': False,
479
+ # AUTO: Executes this statement.
480
  'tokens': token_list,
481
+ # AUTO: Executes this statement.
482
  'errors': lex_errors,
483
+ # AUTO: Executes this statement.
484
  'stage': 'lexical'
485
+ # AUTO: Closes the current grouped code/data.
486
  })
487
 
488
+ # AUTO: Sets `parse_result`.
489
  parse_result = parser.parse_and_build(tokens)
490
+ # AUTO: Checks this condition.
491
  if not parse_result['success']:
492
+ # AUTO: Sets `error_stage`.
493
  error_stage = parse_result.get('error_stage', 'syntax')
494
+ # AUTO: Returns this result to the caller.
495
  return jsonify({
496
+ # AUTO: Executes this statement.
497
  'success': False,
498
+ # AUTO: Executes this statement.
499
  'tokens': token_list,
500
+ # AUTO: Executes this statement.
501
  'errors': parse_result['errors'],
502
+ # AUTO: Executes this statement.
503
  'stage': error_stage
504
+ # AUTO: Closes the current grouped code/data.
505
  })
506
 
507
+ # AUTO: Sets `semantic_result`.
508
  semantic_result = validate_ast(parse_result['ast'], parse_result['symbol_table'])
509
+ # AUTO: Checks this condition.
510
  if not semantic_result['success']:
511
+ # AUTO: Returns this result to the caller.
512
  return jsonify({
513
+ # AUTO: Executes this statement.
514
  'success': False,
515
+ # AUTO: Executes this statement.
516
  'tokens': token_list,
517
+ # AUTO: Executes this statement.
518
  'errors': semantic_result['errors'],
519
+ # AUTO: Executes this statement.
520
  'warnings': semantic_result['warnings'],
521
+ # AUTO: Executes this statement.
522
  'stage': 'semantic'
523
+ # AUTO: Closes the current grouped code/data.
524
  })
525
 
526
+ # AUTO: Sets `icg_result`.
527
  icg_result = generate_icg(tokens)
528
 
529
+ # AUTO: Returns this result to the caller.
530
  return jsonify({
531
+ # AUTO: Executes this statement.
532
  'success': icg_result['success'],
533
+ # AUTO: Executes this statement.
534
  'tokens': token_list,
535
+ # AUTO: Executes this statement.
536
  'tac': icg_result['tac'],
537
+ # AUTO: Executes this statement.
538
  'tac_text': icg_result['tac_text'],
539
+ # AUTO: Executes this statement.
540
  'errors': icg_result['errors'],
541
+ # AUTO: Calls `semantic_result.get`.
542
  'warnings': semantic_result.get('warnings', []),
543
+ # AUTO: Executes this statement.
544
  'stage': 'icg'
545
+ # AUTO: Closes the current grouped code/data.
546
  })
547
 
548
+ # AUTO: Handles the matching error case.
549
  except Exception as e:
550
+ # AUTO: Returns this result to the caller.
551
  return jsonify({
552
+ # AUTO: Executes this statement.
553
  'error': f'Server error: {str(e)}'
554
+ # AUTO: Closes the current grouped code/data.
555
  }), 500
556
 
557
 
558
+ # AUTO: Defines class `OutputCollector`.
559
  class OutputCollector:
560
+ # AUTO: Defines function `__init__`.
561
  def __init__(self):
562
+ # AUTO: Sets `self.outputs`.
563
  self.outputs = []
564
+ # AUTO: Sets `self.needs_input`.
565
  self.needs_input = False
566
 
567
+ # AUTO: Defines function `emit`.
568
  def emit(self, event, data=None, **kwargs):
569
+ # AUTO: Checks this condition.
570
  if event == 'output' and data:
571
+ # AUTO: Appends a value to a list.
572
  self.outputs.append(data.get('output', ''))
573
+ # AUTO: Checks the next alternate condition.
574
  elif event == 'input_required':
575
+ # AUTO: Sets `self.needs_input`.
576
  self.needs_input = True
577
+ # AUTO: Stops this flow by raising an error.
578
  raise _InputNeeded()
579
 
580
 
581
+ # AUTO: Defines class `_InputNeeded`.
582
  class _InputNeeded(Exception):
583
+ # AUTO: Does nothing for this required block.
584
  pass
585
 
586
 
587
+ # GUIDE: Full non-interactive compiler pipeline from source code to output.
588
+ # AUTO: Attaches this decorator to the next function/class.
589
  @app.route('/api/run', methods=['POST'])
590
+ # AUTO: Defines function `run_endpoint`.
591
  def run_endpoint():
592
+ # AUTO: Starts protected code that can catch errors.
593
  try:
594
+ # LINE: Read JSON request body sent by the frontend.
595
  data = request.get_json()
596
+ # LINE: Reject request if editor source code is missing.
597
  if not data or 'source_code' not in data:
598
+ # AUTO: Returns this result to the caller.
599
  return jsonify({'error': 'Missing source_code in request body'}), 400
600
 
601
+ # LINE: Store the full editor text in source_code.
602
  source_code = data['source_code']
603
 
604
+ # LINE: Stage 1, scan source code into lexer tokens.
605
  tokens, lex_errors = lex(source_code)
606
+ # LINE: Stop pipeline if lexer found invalid characters/delimiters.
607
  if lex_errors:
608
+ # AUTO: Returns this result to the caller.
609
  return jsonify({
610
+ # AUTO: Executes this statement.
611
  'success': False,
612
+ # AUTO: Executes this statement.
613
  'stage': 'lexical',
614
+ # AUTO: Executes this statement.
615
  'output': [],
616
+ # AUTO: Executes this statement.
617
  'errors': lex_errors
618
+ # AUTO: Closes the current grouped code/data.
619
  })
620
 
621
+ # LINE: Stage 2, parse tokens and build AST if syntax is valid.
622
  parse_result = parser.parse_and_build(tokens)
623
+ # LINE: Stop pipeline if syntax or builder semantic checks failed.
624
  if not parse_result['success']:
625
+ # AUTO: Sets `error_stage`.
626
  error_stage = parse_result.get('error_stage', 'syntax')
627
+ # AUTO: Returns this result to the caller.
628
  return jsonify({
629
+ # AUTO: Executes this statement.
630
  'success': False,
631
+ # AUTO: Executes this statement.
632
  'stage': error_stage,
633
+ # AUTO: Executes this statement.
634
  'output': [],
635
+ # AUTO: Executes this statement.
636
  'errors': [str(e) for e in parse_result['errors']]
637
+ # AUTO: Closes the current grouped code/data.
638
  })
639
 
640
+ # LINE: Stage 3, validate AST semantic rules.
641
  semantic_result = validate_ast(parse_result['ast'], parse_result['symbol_table'])
642
+ # LINE: Stop pipeline if semantic analyzer found errors.
643
  if not semantic_result['success']:
644
+ # AUTO: Returns this result to the caller.
645
  return jsonify({
646
+ # AUTO: Executes this statement.
647
  'success': False,
648
+ # AUTO: Executes this statement.
649
  'stage': 'semantic',
650
+ # AUTO: Executes this statement.
651
  'output': [],
652
+ # AUTO: Executes this statement.
653
  'errors': [str(e) for e in semantic_result['errors']]
654
+ # AUTO: Closes the current grouped code/data.
655
  })
656
 
657
+ # LINE: Get the validated AST for execution.
658
  ast = semantic_result['ast']
659
 
660
+ # LINE: Collector captures plant() output for non-socket /api/run.
661
  collector = OutputCollector()
662
+ # LINE: Create interpreter and give it the collector as output target.
663
  interp = Interpreter(socketio=collector)
664
+ # AUTO: Starts protected code that can catch errors.
665
  try:
666
+ # LINE: Stage 4, execute ProgramNode through interpreter.
667
  interp.interpret(ast)
668
+ # LINE: Return successful runtime output to frontend.
669
  return jsonify({
670
+ # AUTO: Executes this statement.
671
  'success': True,
672
+ # AUTO: Executes this statement.
673
  'stage': 'execution',
674
+ # AUTO: Executes this statement.
675
  'output': collector.outputs,
676
+ # AUTO: Executes this statement.
677
  'errors': []
678
+ # AUTO: Closes the current grouped code/data.
679
  })
680
+ # AUTO: Handles the matching error case.
681
  except _InputNeeded:
682
+ # LINE: Non-interactive endpoint cannot continue if water() needs input.
683
  return jsonify({
684
+ # AUTO: Executes this statement.
685
  'success': False,
686
+ # AUTO: Executes this statement.
687
  'stage': 'execution',
688
+ # AUTO: Executes this statement.
689
  'output': collector.outputs,
690
+ # AUTO: Executes this statement.
691
  'errors': ['Program requires interactive input (water())'],
692
+ # AUTO: Executes this statement.
693
  'needs_input': True
694
+ # AUTO: Closes the current grouped code/data.
695
  })
696
+ # AUTO: Handles the matching error case.
697
  except InterpreterError as e:
698
+ # LINE: Runtime error from interpreter, such as division by zero.
699
  collector.outputs.append(f'Runtime Error: {e}')
700
+ # AUTO: Returns this result to the caller.
701
  return jsonify({
702
+ # AUTO: Executes this statement.
703
  'success': False,
704
+ # AUTO: Executes this statement.
705
  'stage': 'execution',
706
+ # AUTO: Executes this statement.
707
  'output': collector.outputs,
708
+ # AUTO: Executes this statement.
709
  'errors': [str(e)]
710
+ # AUTO: Closes the current grouped code/data.
711
  })
712
+ # AUTO: Handles the matching error case.
713
  except Exception as e:
714
+ # LINE: Unexpected server/interpreter exception.
715
  collector.outputs.append(f'Internal Error: {e}')
716
+ # AUTO: Returns this result to the caller.
717
  return jsonify({
718
+ # AUTO: Executes this statement.
719
  'success': False,
720
+ # AUTO: Executes this statement.
721
  'stage': 'execution',
722
+ # AUTO: Executes this statement.
723
  'output': collector.outputs,
724
+ # AUTO: Executes this statement.
725
  'errors': [str(e)]
726
+ # AUTO: Closes the current grouped code/data.
727
  })
728
 
729
+ # AUTO: Handles the matching error case.
730
  except Exception as e:
731
+ # LINE: Last-resort server error response.
732
  return jsonify({'error': f'Server error: {str(e)}'}), 500
733
 
734
 
735
+ # AUTO: Attaches this decorator to the next function/class.
736
  @socketio.on('connect')
737
+ # AUTO: Defines function `handle_connect`.
738
  def handle_connect():
739
+ # AUTO: Does nothing for this required block.
740
  pass
741
 
742
+ # AUTO: Attaches this decorator to the next function/class.
743
  @socketio.on('disconnect')
744
+ # AUTO: Defines function `handle_disconnect`.
745
  def handle_disconnect():
746
+ # AUTO: Sets `sid`.
747
  sid = request.sid
748
+ # AUTO: Removes and returns an item.
749
  interpreters.pop(sid, None)
750
 
751
+ # AUTO: Attaches this decorator to the next function/class.
752
  @socketio.on('run_code')
753
+ # AUTO: Defines function `handle_run_code`.
754
  def handle_run_code(data):
755
+ # LINE: sid identifies the browser session running this code.
756
  sid = request.sid
757
+ # LINE: Read current editor source from Socket.IO payload.
758
  source_code = data.get('source_code', '')
759
 
760
+ # LINE: Stage 1, lex source code.
761
  tokens, lex_errors = lex(source_code)
762
+ # LINE: Send lexical errors directly to the terminal output.
763
  if lex_errors:
764
+ # AUTO: Starts a loop over these values.
765
  for err in lex_errors:
766
+ # AUTO: Calls `emit`.
767
  emit('output', {'output': f'Lexical Error: {err}'})
768
+ # AUTO: Calls `emit`.
769
  emit('execution_complete', {'success': False, 'stage': 'lexical'})
770
+ # AUTO: Returns this result to the caller.
771
  return
772
 
773
+ # LINE: Notify frontend that lexical stage passed.
774
  emit('stage_complete', {'stage': 'lexical', 'success': True})
775
 
776
+ # LINE: Stage 2, parse and build AST.
777
  parse_result = parser.parse_and_build(tokens)
778
+ # LINE: Stop if syntax/builder failed.
779
  if not parse_result['success']:
780
+ # AUTO: Sets `error_stage`.
781
  error_stage = parse_result.get('error_stage', 'syntax')
782
+ # AUTO: Starts a loop over these values.
783
  for err in parse_result['errors']:
784
+ # AUTO: Calls `emit`.
785
  emit('output', {'output': f'{err}'})
786
+ # AUTO: Calls `emit`.
787
  emit('execution_complete', {'success': False, 'stage': error_stage})
788
+ # AUTO: Returns this result to the caller.
789
  return
790
 
791
+ # LINE: Notify frontend that syntax stage passed.
792
  emit('stage_complete', {'stage': 'syntax', 'success': True})
793
 
794
+ # LINE: Stage 3, semantic validation.
795
  semantic_result = validate_ast(parse_result['ast'], parse_result['symbol_table'])
796
+ # LINE: Stop if semantic validation failed.
797
  if not semantic_result['success']:
798
+ # AUTO: Starts a loop over these values.
799
  for err in semantic_result['errors']:
800
+ # AUTO: Calls `emit`.
801
  emit('output', {'output': f'{err}'})
802
+ # AUTO: Calls `emit`.
803
  emit('execution_complete', {'success': False, 'stage': 'semantic'})
804
+ # AUTO: Returns this result to the caller.
805
  return
806
 
807
+ # LINE: Notify frontend that semantic stage passed.
808
  emit('stage_complete', {'stage': 'semantic', 'success': True})
809
 
810
+ # LINE: Save validated AST before execution.
811
  ast = semantic_result['ast']
812
 
813
+ # LINE: Cancel any previous interpreter still waiting/running for this session.
814
  old_interp = interpreters.get(sid)
815
+ # AUTO: Checks this condition.
816
  if old_interp and hasattr(old_interp, '_cancelled'):
817
+ # AUTO: Sets `old_interp._cancelled`.
818
  old_interp._cancelled = True
819
+ # AUTO: Starts a loop over these values.
820
  for evt in list(old_interp.input_events.values()):
821
+ # AUTO: Starts protected code that can catch errors.
822
  try:
823
+ # AUTO: Calls `evt.send`.
824
  evt.send(None)
825
+ # AUTO: Handles the matching error case.
826
  except (AttributeError, AssertionError):
827
+ # AUTO: Starts protected code that can catch errors.
828
  try:
829
+ # AUTO: Calls `evt.set`.
830
  evt.set()
831
+ # AUTO: Handles the matching error case.
832
  except Exception:
833
+ # AUTO: Does nothing for this required block.
834
  pass
835
 
836
+ # Runs in the background so water() can pause and receive browser input.
837
+ # AUTO: Defines function `run_interpreter`.
838
  def run_interpreter():
839
+ # AUTO: Starts protected code that can catch errors.
840
  try:
841
+ # LINE: Emitter sends plant()/water() events to this browser session only.
842
  session_emitter = SessionEmitter(socketio, sid)
843
+ # LINE: Create one interpreter instance for this run.
844
  interp = Interpreter(socketio=session_emitter)
845
+ # LINE: Runtime cancellation flag used when rerunning code.
846
  interp._cancelled = False
847
+ # LINE: Store interpreter so capture_input can find it later.
848
  interpreters[sid] = interp
849
+ # LINE: Execute the validated AST.
850
  interp.interpret(ast)
851
+ # LINE: Tell UI execution completed if run was not cancelled.
852
  if not interp._cancelled:
853
+ # AUTO: Sets `socketio.emit('execution_complete', {'success': True, 'stage': 'execution'}, to`.
854
  socketio.emit('execution_complete', {'success': True, 'stage': 'execution'}, to=sid)
855
+ # AUTO: Handles the matching error case.
856
  except _CancelledError:
857
+ # AUTO: Does nothing for this required block.
858
  pass
859
+ # AUTO: Handles the matching error case.
860
  except InterpreterError as e:
861
+ # LINE: Send runtime errors to the output panel.
862
  if not getattr(interp, '_cancelled', False):
863
+ # AUTO: Sets `socketio.emit('output', {'output': f'Runtime Error: {e}'}, to`.
864
  socketio.emit('output', {'output': f'Runtime Error: {e}'}, to=sid)
865
+ # AUTO: Sets `socketio.emit('execution_complete', {'success': False, 'stage': 'execution'}, to`.
866
  socketio.emit('execution_complete', {'success': False, 'stage': 'execution'}, to=sid)
867
+ # AUTO: Handles the matching error case.
868
  except Exception as e:
869
+ # LINE: Send unexpected internal errors to the output panel.
870
  if not getattr(interp, '_cancelled', False):
871
+ # AUTO: Sets `socketio.emit('output', {'output': f'Internal Error: {e}'}, to`.
872
  socketio.emit('output', {'output': f'Internal Error: {e}'}, to=sid)
873
+ # AUTO: Sets `socketio.emit('execution_complete', {'success': False, 'stage': 'execution'}, to`.
874
  socketio.emit('execution_complete', {'success': False, 'stage': 'execution'}, to=sid)
875
+ # AUTO: Runs cleanup code no matter what happened.
876
  finally:
877
+ # LINE: Remove finished interpreter from active session map.
878
  if interpreters.get(sid) is interp:
879
+ # AUTO: Removes and returns an item.
880
  interpreters.pop(sid, None)
881
 
882
+ # LINE: Start interpreter in background so UI remains responsive.
883
  socketio.start_background_task(run_interpreter)
884
 
885
+ # AUTO: Attaches this decorator to the next function/class.
886
  @socketio.on('capture_input')
887
+ # AUTO: Defines function `handle_capture_input`.
888
  def handle_capture_input(data):
889
+ # LINE: Identify which browser session sent the water() answer.
890
  sid = request.sid
891
+ # LINE: Find that session's waiting interpreter.
892
  interp = interpreters.get(sid)
893
+ # AUTO: Checks this condition.
894
  if interp:
895
+ # LINE: Get variable name and input value from frontend payload.
896
  var_name = data.get('var_name', '')
897
+ # AUTO: Sets `input_value`.
898
  input_value = data.get('input', '')
899
+ # LINE: Resume the interpreter waiting inside eval_input().
900
  interp.provide_input(var_name, input_value)
901
 
902
 
903
+ # AUTO: Sets `_prompt_path`.
904
  _prompt_path = os.path.join(os.path.dirname(__file__), 'ai', 'prompt.txt')
905
+ # AUTO: Opens a managed resource/context.
906
  with open(_prompt_path, 'r', encoding='utf-8') as _f:
907
+ # AUTO: Sets `GAL_SYSTEM_PROMPT`.
908
  GAL_SYSTEM_PROMPT = _f.read()
909
 
910
 
911
+ # AUTO: Sets `_gemini_client`.
912
  _gemini_client = None
913
+ # AUTO: Sets `_chat_sessions`.
914
  _chat_sessions = {}
915
 
916
+ # AUTO: Defines function `_get_gemini_client`.
917
  def _get_gemini_client():
918
+ # AUTO: Uses a module-level variable inside this function.
919
  global _gemini_client
920
+ # AUTO: Checks this condition.
921
  if _gemini_client is None:
922
+ # AUTO: Sets `api_key`.
923
  api_key = os.environ.get('GEMINI_API_KEY', '')
924
+ # AUTO: Checks this condition.
925
  if not api_key:
926
+ # AUTO: Returns this result to the caller.
927
  return None
928
+ # AUTO: Sets `_gemini_client`.
929
  _gemini_client = genai.Client(api_key=api_key)
930
+ # AUTO: Returns this result to the caller.
931
  return _gemini_client
932
 
933
+ # AUTO: Attaches this decorator to the next function/class.
934
  @app.route('/api/chat', methods=['POST'])
935
+ # AUTO: Defines function `chat_endpoint`.
936
  def chat_endpoint():
937
+ # AUTO: Starts protected code that can catch errors.
938
  try:
939
+ # AUTO: Sets `data`.
940
  data = request.get_json()
941
+ # AUTO: Checks this condition.
942
  if not data or 'message' not in data:
943
+ # AUTO: Returns this result to the caller.
944
  return jsonify({'error': 'Missing message in request body'}), 400
945
 
946
+ # AUTO: Sets `user_message`.
947
  user_message = data['message']
948
+ # AUTO: Sets `session_id`.
949
  session_id = data.get('session_id', 'default')
950
+ # AUTO: Sets `editor_code`.
951
  editor_code = data.get('editor_code', '')
952
 
953
+ # AUTO: Sets `client`.
954
  client = _get_gemini_client()
955
+ # AUTO: Checks this condition.
956
  if client is None:
957
+ # AUTO: Sets `fb_reply`.
958
  fb_reply = fallback_reply(user_message)
959
+ # AUTO: Returns this result to the caller.
960
  return jsonify({'reply': fb_reply, 'mode': 'fallback'})
961
 
962
+ # AUTO: Checks this condition.
963
  if session_id not in _chat_sessions:
964
+ # AUTO: Sets `_chat_sessions[session_id]`.
965
  _chat_sessions[session_id] = []
966
+ # AUTO: Sets `history`.
967
  history = _chat_sessions[session_id]
968
 
969
+ # AUTO: Sets `full_message`.
970
  full_message = user_message
971
+ # AUTO: Checks this condition.
972
  if editor_code.strip():
973
+ # AUTO: Sets `full_message`.
974
  full_message = f"[Current code in editor]:\n```\n{editor_code}\n```\n\nUser question: {user_message}"
975
 
976
+ # AUTO: Appends a value to a list.
977
  history.append({'role': 'user', 'parts': [{'text': full_message}]})
978
 
979
+ # AUTO: Checks this condition.
980
  if len(history) > 20:
981
+ # AUTO: Sets `history[:]`.
982
  history[:] = history[-20:]
983
 
984
+ # AUTO: Sets `models_to_try`.
985
  models_to_try = ['gemini-2.5-flash', 'gemini-2.5-flash-lite', 'gemini-2.0-flash', 'gemini-2.0-flash-lite']
986
+ # AUTO: Sets `last_error: Exception`.
987
  last_error: Exception = RuntimeError("No Gemini models were available to try")
988
+ # AUTO: Starts a loop over these values.
989
  for model_name in models_to_try:
990
+ # AUTO: Starts protected code that can catch errors.
991
  try:
992
+ # AUTO: Sets `response`.
993
  response = client.models.generate_content(
994
+ # AUTO: Sets `model`.
995
  model=model_name,
996
+ # AUTO: Sets `contents`.
997
  contents=history,
998
+ # AUTO: Sets `config`.
999
  config=genai.types.GenerateContentConfig(
1000
+ # AUTO: Sets `system_instruction`.
1001
  system_instruction=GAL_SYSTEM_PROMPT,
1002
+ # AUTO: Sets `temperature`.
1003
  temperature=0.3,
1004
+ # AUTO: Sets `max_output_tokens`.
1005
  max_output_tokens=4096,
1006
+ # AUTO: Closes the current grouped code/data.
1007
  ),
1008
+ # AUTO: Closes the current grouped code/data.
1009
  )
1010
+ # AUTO: Stops the nearest loop.
1011
  break
1012
+ # AUTO: Handles the matching error case.
1013
  except Exception as model_err:
1014
+ # AUTO: Sets `last_error`.
1015
  last_error = model_err
1016
+ # AUTO: Sets `err_msg`.
1017
  err_msg = str(model_err)
1018
+ # AUTO: Checks this condition.
1019
  if '429' not in err_msg and 'RESOURCE_EXHAUSTED' not in err_msg and '503' not in err_msg and 'UNAVAILABLE' not in err_msg and '403' not in err_msg and 'PERMISSION_DENIED' not in err_msg:
1020
+ # AUTO: Executes this statement.
1021
  raise
1022
+ # AUTO: Skips to the next loop iteration.
1023
  continue
1024
+ # AUTO: Runs when previous condition did not pass.
1025
  else:
1026
+ # AUTO: Stops this flow by raising an error.
1027
  raise last_error
1028
 
1029
+ # AUTO: Sets `reply`.
1030
  reply = response.text or 'No response generated.'
1031
 
1032
+ # AUTO: Appends a value to a list.
1033
  history.append({'role': 'model', 'parts': [{'text': reply}]})
1034
 
1035
+ # AUTO: Returns this result to the caller.
1036
  return jsonify({'reply': reply, 'mode': 'ai'})
1037
 
1038
+ # AUTO: Handles the matching error case.
1039
  except Exception as e:
1040
+ # AUTO: Starts protected code that can catch errors.
1041
  try:
1042
+ # AUTO: Sets `fb_reply`.
1043
  fb_reply = fallback_reply(user_message)
1044
+ # AUTO: Returns this result to the caller.
1045
  return jsonify({'reply': fb_reply, 'mode': 'fallback'})
1046
+ # AUTO: Handles the matching error case.
1047
  except Exception:
1048
+ # AUTO: Does nothing for this required block.
1049
  pass
1050
+ # AUTO: Sets `err_str`.
1051
  err_str = str(e)
1052
+ # AUTO: Returns this result to the caller.
1053
  return jsonify({'error': f'Chat error: {err_str}'}), 500
1054
 
1055
+ # AUTO: Attaches this decorator to the next function/class.
1056
  @app.route('/api/chat/clear', methods=['POST'])
1057
+ # AUTO: Defines function `chat_clear_endpoint`.
1058
  def chat_clear_endpoint():
1059
+ # AUTO: Sets `data`.
1060
  data = request.get_json() or {}
1061
+ # AUTO: Sets `session_id`.
1062
  session_id = data.get('session_id', 'default')
1063
+ # AUTO: Removes and returns an item.
1064
  _chat_sessions.pop(session_id, None)
1065
+ # AUTO: Returns this result to the caller.
1066
  return jsonify({'success': True})
1067
 
1068
 
1069
+ # AUTO: Checks this condition.
1070
  if __name__ == '__main__':
1071
+ # AUTO: Sets `port`.
1072
  port = int(os.environ.get('PORT', 5000))
1073
+ # AUTO: Executes this statement.
1074
  debug = os.environ.get('DEBUG', 'False') == 'True'
1075
 
1076
+ # AUTO: Calls `print`.
1077
  print("Starting GAL Compiler Server...")
1078
+ # AUTO: Calls `print`.
1079
  print(f"Server running at http://0.0.0.0:{port}")
1080
+ # AUTO: Calls `print`.
1081
  print("API endpoints:")
1082
+ # AUTO: Calls `print`.
1083
  print(f" - POST http://localhost:{port}/api/lex (Lexical Analysis)")
1084
+ # AUTO: Calls `print`.
1085
  print(f" - POST http://localhost:{port}/api/parse (Syntax Analysis)")
1086
+ # AUTO: Calls `print`.
1087
  print(f" - POST http://localhost:{port}/api/semantic (Semantic Analysis)")
1088
+ # AUTO: Calls `print`.
1089
  print(f" - POST http://localhost:{port}/api/icg (Intermediate Code Generation)")
1090
+ # AUTO: Calls `print`.
1091
  print(f" - POST http://localhost:{port}/api/chat (AI Chat Helper)")
1092
+ # AUTO: Calls `print`.
1093
  print(f" - Socket.IO: run_code (Execute Program)")
1094
+ # AUTO: Sets `socketio.run(app, host`.
1095
  socketio.run(app, host='0.0.0.0', port=port, debug=debug, allow_unsafe_werkzeug=True)
Backend/shared/__init__.py CHANGED
@@ -1,4 +1,7 @@
1
 
 
2
  from .tokens import * # noqa: F401,F403 - TT_* constants
 
3
  from .tokens import Token, get_token_description # noqa: F401
 
4
  from .ast_nodes import * # noqa: F401,F403
 
1
 
2
+ # AUTO: Imports names from another module.
3
  from .tokens import * # noqa: F401,F403 - TT_* constants
4
+ # AUTO: Imports names from another module.
5
  from .tokens import Token, get_token_description # noqa: F401
6
+ # AUTO: Imports names from another module.
7
  from .ast_nodes import * # noqa: F401,F403
Backend/shared/ast_nodes.py CHANGED
@@ -1,204 +1,376 @@
1
 
 
2
 
 
 
 
 
 
 
3
  class ASTNode:
 
4
  def __init__(self, node_type, value=None, line=None):
 
5
  self.node_type = node_type
 
6
  self.value = value
 
7
  self.children = []
 
8
  self.parent = None
 
9
  self.line = line
10
 
 
11
  def add_child(self, child):
 
 
 
12
  child.parent = self
 
13
  self.children.append(child)
14
 
 
15
  def print_tree(self, level=0):
 
16
  indent = ' ' * (level * 3)
 
17
  print(f"{indent}╚═{self.node_type}: {self.value if self.value else ''}")
 
18
  for child in self.children:
 
19
  child.print_tree(level + 1)
20
 
21
 
 
22
  class ProgramNode(ASTNode):
 
23
  def __init__(self, line=None):
 
24
  super().__init__("Program", line=line)
25
 
 
26
  class VariableDeclarationNode(ASTNode):
 
27
  def __init__(self, var_type, var_name, value=None, line=None):
 
28
  super().__init__("VariableDeclaration", line=line)
 
29
  self.add_child(ASTNode("Type", var_type, line=line))
 
30
  self.add_child(ASTNode("Identifier", var_name, line=line))
 
31
  if value:
 
32
  self.add_child(value)
33
 
 
34
  class AssignmentNode(ASTNode):
 
35
  def __init__(self, target, value, line=None):
 
36
  super().__init__("Assignment", line=line)
 
37
  if isinstance(target, str):
 
38
  self.add_child(ASTNode("Identifier", target, line=line))
 
39
  else:
 
40
  self.add_child(target)
 
41
  self.add_child(value)
42
 
43
 
 
44
  class BinaryOpNode(ASTNode):
 
45
  def __init__(self, left, operator, right, line=None):
 
46
  super().__init__("BinaryOp", operator, line=line)
 
47
  self.add_child(left)
 
48
  self.add_child(right)
49
 
 
50
  class FunctionDeclarationNode(ASTNode):
 
51
  def __init__(self, return_type, name, params, line=None):
 
52
  super().__init__("FunctionDeclaration", name, line=line)
 
53
  self.add_child(ASTNode("ReturnType", return_type, line=line))
 
54
  self.add_child(params)
55
 
 
56
  class FunctionCallNode(ASTNode):
 
57
  def __init__(self, name, args, line=None):
 
58
  super().__init__("FunctionCall", name, line=line)
 
59
  for arg in args:
 
60
  self.add_child(arg)
61
 
 
62
  class IfStatementNode(ASTNode):
 
63
  def __init__(self, condition, line=None):
 
64
  super().__init__("IfStatement", line=line)
 
65
  self.add_child(condition)
66
 
 
67
  class ForLoopNode(ASTNode):
 
68
  def __init__(self, initialization, condition, update, line=None):
 
69
  super().__init__("ForLoop", line=line)
 
70
  self.add_child(initialization)
 
71
  self.add_child(condition)
 
72
  self.add_child(update)
73
 
 
74
  class WhileLoopNode(ASTNode):
 
75
  def __init__(self, condition, line=None):
 
76
  super().__init__("WhileLoop", line=line)
 
77
  self.add_child(condition)
78
 
 
79
  class DoWhileLoopNode(ASTNode):
 
80
  def __init__(self, condition, line=None):
 
81
  super().__init__("DoWhileLoop", line=line)
82
 
 
83
  class PrintNode(ASTNode):
 
84
  def __init__(self, args, line=None):
 
85
  super().__init__("PrintStatement", line=line)
 
86
  for arg in args:
 
87
  self.add_child(arg)
88
 
 
89
  class UnaryOpNode(ASTNode):
 
90
  def __init__(self, operator, operand, position="pre", line=None):
 
91
  super().__init__("UnaryOp", operator, line=line)
 
92
  self.position = position
 
93
  self.add_child(operand)
94
 
 
95
  class FertileDeclarationNode(ASTNode):
 
96
  def __init__(self, var_type, var_name, value, line=None):
 
97
  super().__init__("SturdyDeclaration", line=line)
 
98
  self.add_child(ASTNode("Type", var_type, line=line))
 
99
  self.add_child(ASTNode("Identifier", var_name, line=line))
 
100
  self.add_child(value)
101
 
 
102
  class ReturnNode(ASTNode):
 
103
  def __init__(self, return_value=None, line=None):
 
104
  super().__init__("Return", line=line)
 
105
  if return_value:
 
106
  self.add_child(return_value)
107
 
 
108
  class UpdateNode(ASTNode):
 
109
  def __init__(self, operator, operand, prefix = True, line=None):
 
110
  super().__init__("Update", line=line)
 
111
  self.prefix = prefix
 
112
  self.add_child(operand)
 
113
  self.add_child(operator)
114
 
 
115
  class SwitchNode(ASTNode):
 
116
  def __init__(self, expression, cases, default_case, line=None):
 
117
  super().__init__("Switch", line=line)
 
118
  self.add_child(expression)
 
119
  for case in cases:
 
120
  self.add_child(case)
 
121
  if default_case is not None:
 
122
  self.add_child(default_case)
123
 
 
124
  class ContinueNode(ASTNode):
 
125
  def __init__(self, line=None):
 
126
  super().__init__("Continue", line=line)
127
 
 
128
  class BreakNode(ASTNode):
 
129
  def __init__(self, line=None):
 
130
  super().__init__("Break", line=line)
131
 
 
132
  class ListNode(ASTNode):
 
133
  def __init__(self, line=None, elements=None):
 
134
  super().__init__("List", line=line)
 
135
  self.elements = elements or []
 
136
  for element in self.elements:
 
137
  self.add_child(element)
138
 
 
139
  class SoilNode(ASTNode):
 
140
  def __init__(self, variable_name, line=None):
 
141
  super().__init__("SoilFunction", line=line)
 
142
  self.add_child(ASTNode("Identifier", variable_name, line=line))
143
 
 
144
  class BloomNode(ASTNode):
 
145
  def __init__(self, variable_name, line=None):
 
146
  super().__init__("BloomFunction", line=line)
 
147
  self.add_child(ASTNode("Identifier", variable_name, line=line))
148
 
 
149
  class AppendNode(ASTNode):
 
150
  def __init__(self, elements, line=None):
 
151
  super().__init__("Append", line=line)
 
152
  for elem in elements:
 
153
  self.add_child(elem)
154
 
 
155
  class InsertNode(ASTNode):
 
156
  def __init__(self, index, elements, line=None):
 
157
  super().__init__("Insert", line=line)
 
158
  self.add_child(index)
 
159
  for elem in elements:
 
160
  self.add_child(elem)
161
 
 
162
  class RemoveNode(ASTNode):
 
163
  def __init__(self, value, index, line=None):
 
164
  super().__init__("Remove", line=line)
 
165
  self.add_child(ASTNode("Identifier", value, line=line))
 
166
  self.add_child(index)
167
 
 
168
  class CastNode(ASTNode):
 
169
  def __init__(self, target_type, expression, line=None):
 
170
  super().__init__("TypeCast", line=line)
 
171
  self.add_child(ASTNode("TargetType", target_type, line=line))
 
172
  self.add_child(expression)
173
 
174
 
 
175
  class ListAccessNode(ASTNode):
 
176
  def __init__(self, list_name, index_expr, line=None):
 
177
  super().__init__("ListAccess", line=line)
 
178
  self.add_child(ASTNode("ListName", list_name, line=line))
 
179
  self.add_child(index_expr)
180
 
181
 
 
182
  class MemberAccessNode(ASTNode):
 
183
  def __init__(self, object_name, member_name, line=None):
 
184
  super().__init__("MemberAccess", line=line)
 
185
  if isinstance(object_name, ASTNode):
 
186
  self.add_child(object_name)
 
187
  else:
 
188
  self.add_child(ASTNode("Object", object_name, line=line))
 
189
  self.add_child(ASTNode("Member", member_name, line=line))
190
 
191
 
 
192
  class ArrayMemberAccessNode(ASTNode):
 
193
  def __init__(self, list_access_node, member_name, line=None):
 
194
  super().__init__("ArrayMemberAccess", line=line)
 
195
  self.add_child(list_access_node)
 
196
  self.add_child(ASTNode("Member", member_name, line=line))
197
 
198
 
 
199
  class BundleDefinitionNode(ASTNode):
 
200
  def __init__(self, bundle_name, members, line=None):
 
201
  super().__init__("BundleDefinition", line=line)
 
202
  self.bundle_name = bundle_name
 
203
  self.members = members
204
 
 
1
 
2
+ """AST node classes shared by the parser, semantic validator, and interpreter.
3
 
4
+ Each class represents one language construct after parsing, for example a
5
+ VariableDeclarationNode, BinaryOpNode, FunctionCallNode, or WhileLoopNode.
6
+ """
7
+
8
+
9
+ # AUTO: Defines class `ASTNode`.
10
  class ASTNode:
11
+ # AUTO: Defines function `__init__`.
12
  def __init__(self, node_type, value=None, line=None):
13
+ # LINE: node_type tells semantic/interpreter what this AST node represents.
14
  self.node_type = node_type
15
+ # LINE: value stores the node's main text/value, like an operator or name.
16
  self.value = value
17
+ # LINE: children stores nested AST nodes under this node.
18
  self.children = []
19
+ # LINE: parent points back to the surrounding AST node.
20
  self.parent = None
21
+ # LINE: line stores the original source line for error messages.
22
  self.line = line
23
 
24
+ # AUTO: Defines function `add_child`.
25
  def add_child(self, child):
26
+ # GUIDE: Parent links let later stages know the surrounding construct,
27
+ # for example water() can see whether it belongs to an assignment.
28
+ # AUTO: Sets `child.parent`.
29
  child.parent = self
30
+ # AUTO: Appends a value to a list.
31
  self.children.append(child)
32
 
33
+ # AUTO: Defines function `print_tree`.
34
  def print_tree(self, level=0):
35
+ # AUTO: Sets `indent`.
36
  indent = ' ' * (level * 3)
37
+ # AUTO: Calls `print`.
38
  print(f"{indent}╚═{self.node_type}: {self.value if self.value else ''}")
39
+ # AUTO: Starts a loop over these values.
40
  for child in self.children:
41
+ # AUTO: Calls `child.print_tree`.
42
  child.print_tree(level + 1)
43
 
44
 
45
+ # AUTO: Defines class `ProgramNode`.
46
  class ProgramNode(ASTNode):
47
+ # AUTO: Defines function `__init__`.
48
  def __init__(self, line=None):
49
+ # LINE: ProgramNode is the root container for the whole AST.
50
  super().__init__("Program", line=line)
51
 
52
+ # AUTO: Defines class `VariableDeclarationNode`.
53
  class VariableDeclarationNode(ASTNode):
54
+ # AUTO: Defines function `__init__`.
55
  def __init__(self, var_type, var_name, value=None, line=None):
56
+ # LINE: VariableDeclaration represents code like seed x = 10;
57
  super().__init__("VariableDeclaration", line=line)
58
+ # LINE: First child stores declared type.
59
  self.add_child(ASTNode("Type", var_type, line=line))
60
+ # LINE: Second child stores variable name.
61
  self.add_child(ASTNode("Identifier", var_name, line=line))
62
+ # AUTO: Checks this condition.
63
  if value:
64
+ # LINE: Optional third child stores initializer expression.
65
  self.add_child(value)
66
 
67
+ # AUTO: Defines class `AssignmentNode`.
68
  class AssignmentNode(ASTNode):
69
+ # AUTO: Defines function `__init__`.
70
  def __init__(self, target, value, line=None):
71
+ # LINE: Assignment represents code like x = y + 1;
72
  super().__init__("Assignment", line=line)
73
+ # AUTO: Checks this condition.
74
  if isinstance(target, str):
75
+ # LINE: Simple target names are wrapped as Identifier nodes.
76
  self.add_child(ASTNode("Identifier", target, line=line))
77
+ # AUTO: Runs when previous condition did not pass.
78
  else:
79
+ # LINE: Complex targets are already AST nodes, like array/member access.
80
  self.add_child(target)
81
+ # LINE: Second child stores the RHS expression/value.
82
  self.add_child(value)
83
 
84
 
85
+ # AUTO: Defines class `BinaryOpNode`.
86
  class BinaryOpNode(ASTNode):
87
+ # AUTO: Defines function `__init__`.
88
  def __init__(self, left, operator, right, line=None):
89
+ # LINE: BinaryOp stores operator in value with left/right children.
90
  super().__init__("BinaryOp", operator, line=line)
91
+ # AUTO: Calls `self.add_child`.
92
  self.add_child(left)
93
+ # AUTO: Calls `self.add_child`.
94
  self.add_child(right)
95
 
96
+ # AUTO: Defines class `FunctionDeclarationNode`.
97
  class FunctionDeclarationNode(ASTNode):
98
+ # AUTO: Defines function `__init__`.
99
  def __init__(self, return_type, name, params, line=None):
100
+ # LINE: FunctionDeclaration stores function name in value.
101
  super().__init__("FunctionDeclaration", name, line=line)
102
+ # LINE: First child stores return type.
103
  self.add_child(ASTNode("ReturnType", return_type, line=line))
104
+ # LINE: Second child stores parameters.
105
  self.add_child(params)
106
 
107
+ # AUTO: Defines class `FunctionCallNode`.
108
  class FunctionCallNode(ASTNode):
109
+ # AUTO: Defines function `__init__`.
110
  def __init__(self, name, args, line=None):
111
+ # LINE: FunctionCall stores called function name in value.
112
  super().__init__("FunctionCall", name, line=line)
113
+ # LINE: Each argument becomes a child.
114
  for arg in args:
115
+ # AUTO: Calls `self.add_child`.
116
  self.add_child(arg)
117
 
118
+ # AUTO: Defines class `IfStatementNode`.
119
  class IfStatementNode(ASTNode):
120
+ # AUTO: Defines function `__init__`.
121
  def __init__(self, condition, line=None):
122
+ # AUTO: Sets `super().__init__("IfStatement", line`.
123
  super().__init__("IfStatement", line=line)
124
+ # AUTO: Calls `self.add_child`.
125
  self.add_child(condition)
126
 
127
+ # AUTO: Defines class `ForLoopNode`.
128
  class ForLoopNode(ASTNode):
129
+ # AUTO: Defines function `__init__`.
130
  def __init__(self, initialization, condition, update, line=None):
131
+ # AUTO: Sets `super().__init__("ForLoop", line`.
132
  super().__init__("ForLoop", line=line)
133
+ # AUTO: Calls `self.add_child`.
134
  self.add_child(initialization)
135
+ # AUTO: Calls `self.add_child`.
136
  self.add_child(condition)
137
+ # AUTO: Calls `self.add_child`.
138
  self.add_child(update)
139
 
140
+ # AUTO: Defines class `WhileLoopNode`.
141
  class WhileLoopNode(ASTNode):
142
+ # AUTO: Defines function `__init__`.
143
  def __init__(self, condition, line=None):
144
+ # AUTO: Sets `super().__init__("WhileLoop", line`.
145
  super().__init__("WhileLoop", line=line)
146
+ # AUTO: Calls `self.add_child`.
147
  self.add_child(condition)
148
 
149
+ # AUTO: Defines class `DoWhileLoopNode`.
150
  class DoWhileLoopNode(ASTNode):
151
+ # AUTO: Defines function `__init__`.
152
  def __init__(self, condition, line=None):
153
+ # AUTO: Sets `super().__init__("DoWhileLoop", line`.
154
  super().__init__("DoWhileLoop", line=line)
155
 
156
+ # AUTO: Defines class `PrintNode`.
157
  class PrintNode(ASTNode):
158
+ # AUTO: Defines function `__init__`.
159
  def __init__(self, args, line=None):
160
+ # AUTO: Sets `super().__init__("PrintStatement", line`.
161
  super().__init__("PrintStatement", line=line)
162
+ # AUTO: Starts a loop over these values.
163
  for arg in args:
164
+ # AUTO: Calls `self.add_child`.
165
  self.add_child(arg)
166
 
167
+ # AUTO: Defines class `UnaryOpNode`.
168
  class UnaryOpNode(ASTNode):
169
+ # AUTO: Defines function `__init__`.
170
  def __init__(self, operator, operand, position="pre", line=None):
171
+ # AUTO: Sets `super().__init__("UnaryOp", operator, line`.
172
  super().__init__("UnaryOp", operator, line=line)
173
+ # AUTO: Sets `self.position`.
174
  self.position = position
175
+ # AUTO: Calls `self.add_child`.
176
  self.add_child(operand)
177
 
178
+ # AUTO: Defines class `FertileDeclarationNode`.
179
  class FertileDeclarationNode(ASTNode):
180
+ # AUTO: Defines function `__init__`.
181
  def __init__(self, var_type, var_name, value, line=None):
182
+ # AUTO: Sets `super().__init__("SturdyDeclaration", line`.
183
  super().__init__("SturdyDeclaration", line=line)
184
+ # AUTO: Sets `self.add_child(ASTNode("Type", var_type, line`.
185
  self.add_child(ASTNode("Type", var_type, line=line))
186
+ # AUTO: Sets `self.add_child(ASTNode("Identifier", var_name, line`.
187
  self.add_child(ASTNode("Identifier", var_name, line=line))
188
+ # AUTO: Calls `self.add_child`.
189
  self.add_child(value)
190
 
191
+ # AUTO: Defines class `ReturnNode`.
192
  class ReturnNode(ASTNode):
193
+ # AUTO: Defines function `__init__`.
194
  def __init__(self, return_value=None, line=None):
195
+ # AUTO: Sets `super().__init__("Return", line`.
196
  super().__init__("Return", line=line)
197
+ # AUTO: Checks this condition.
198
  if return_value:
199
+ # AUTO: Calls `self.add_child`.
200
  self.add_child(return_value)
201
 
202
+ # AUTO: Defines class `UpdateNode`.
203
  class UpdateNode(ASTNode):
204
+ # AUTO: Defines function `__init__`.
205
  def __init__(self, operator, operand, prefix = True, line=None):
206
+ # AUTO: Sets `super().__init__("Update", line`.
207
  super().__init__("Update", line=line)
208
+ # AUTO: Sets `self.prefix`.
209
  self.prefix = prefix
210
+ # AUTO: Calls `self.add_child`.
211
  self.add_child(operand)
212
+ # AUTO: Calls `self.add_child`.
213
  self.add_child(operator)
214
 
215
+ # AUTO: Defines class `SwitchNode`.
216
  class SwitchNode(ASTNode):
217
+ # AUTO: Defines function `__init__`.
218
  def __init__(self, expression, cases, default_case, line=None):
219
+ # AUTO: Sets `super().__init__("Switch", line`.
220
  super().__init__("Switch", line=line)
221
+ # AUTO: Calls `self.add_child`.
222
  self.add_child(expression)
223
+ # AUTO: Starts a loop over these values.
224
  for case in cases:
225
+ # AUTO: Calls `self.add_child`.
226
  self.add_child(case)
227
+ # AUTO: Checks this condition.
228
  if default_case is not None:
229
+ # AUTO: Calls `self.add_child`.
230
  self.add_child(default_case)
231
 
232
+ # AUTO: Defines class `ContinueNode`.
233
  class ContinueNode(ASTNode):
234
+ # AUTO: Defines function `__init__`.
235
  def __init__(self, line=None):
236
+ # AUTO: Sets `super().__init__("Continue", line`.
237
  super().__init__("Continue", line=line)
238
 
239
+ # AUTO: Defines class `BreakNode`.
240
  class BreakNode(ASTNode):
241
+ # AUTO: Defines function `__init__`.
242
  def __init__(self, line=None):
243
+ # AUTO: Sets `super().__init__("Break", line`.
244
  super().__init__("Break", line=line)
245
 
246
+ # AUTO: Defines class `ListNode`.
247
  class ListNode(ASTNode):
248
+ # AUTO: Defines function `__init__`.
249
  def __init__(self, line=None, elements=None):
250
+ # AUTO: Sets `super().__init__("List", line`.
251
  super().__init__("List", line=line)
252
+ # AUTO: Sets `self.elements`.
253
  self.elements = elements or []
254
+ # AUTO: Starts a loop over these values.
255
  for element in self.elements:
256
+ # AUTO: Calls `self.add_child`.
257
  self.add_child(element)
258
 
259
+ # AUTO: Defines class `SoilNode`.
260
  class SoilNode(ASTNode):
261
+ # AUTO: Defines function `__init__`.
262
  def __init__(self, variable_name, line=None):
263
+ # AUTO: Sets `super().__init__("SoilFunction", line`.
264
  super().__init__("SoilFunction", line=line)
265
+ # AUTO: Sets `self.add_child(ASTNode("Identifier", variable_name, line`.
266
  self.add_child(ASTNode("Identifier", variable_name, line=line))
267
 
268
+ # AUTO: Defines class `BloomNode`.
269
  class BloomNode(ASTNode):
270
+ # AUTO: Defines function `__init__`.
271
  def __init__(self, variable_name, line=None):
272
+ # AUTO: Sets `super().__init__("BloomFunction", line`.
273
  super().__init__("BloomFunction", line=line)
274
+ # AUTO: Sets `self.add_child(ASTNode("Identifier", variable_name, line`.
275
  self.add_child(ASTNode("Identifier", variable_name, line=line))
276
 
277
+ # AUTO: Defines class `AppendNode`.
278
  class AppendNode(ASTNode):
279
+ # AUTO: Defines function `__init__`.
280
  def __init__(self, elements, line=None):
281
+ # AUTO: Sets `super().__init__("Append", line`.
282
  super().__init__("Append", line=line)
283
+ # AUTO: Starts a loop over these values.
284
  for elem in elements:
285
+ # AUTO: Calls `self.add_child`.
286
  self.add_child(elem)
287
 
288
+ # AUTO: Defines class `InsertNode`.
289
  class InsertNode(ASTNode):
290
+ # AUTO: Defines function `__init__`.
291
  def __init__(self, index, elements, line=None):
292
+ # AUTO: Sets `super().__init__("Insert", line`.
293
  super().__init__("Insert", line=line)
294
+ # AUTO: Calls `self.add_child`.
295
  self.add_child(index)
296
+ # AUTO: Starts a loop over these values.
297
  for elem in elements:
298
+ # AUTO: Calls `self.add_child`.
299
  self.add_child(elem)
300
 
301
+ # AUTO: Defines class `RemoveNode`.
302
  class RemoveNode(ASTNode):
303
+ # AUTO: Defines function `__init__`.
304
  def __init__(self, value, index, line=None):
305
+ # AUTO: Sets `super().__init__("Remove", line`.
306
  super().__init__("Remove", line=line)
307
+ # AUTO: Sets `self.add_child(ASTNode("Identifier", value, line`.
308
  self.add_child(ASTNode("Identifier", value, line=line))
309
+ # AUTO: Calls `self.add_child`.
310
  self.add_child(index)
311
 
312
+ # AUTO: Defines class `CastNode`.
313
  class CastNode(ASTNode):
314
+ # AUTO: Defines function `__init__`.
315
  def __init__(self, target_type, expression, line=None):
316
+ # AUTO: Sets `super().__init__("TypeCast", line`.
317
  super().__init__("TypeCast", line=line)
318
+ # AUTO: Sets `self.add_child(ASTNode("TargetType", target_type, line`.
319
  self.add_child(ASTNode("TargetType", target_type, line=line))
320
+ # AUTO: Calls `self.add_child`.
321
  self.add_child(expression)
322
 
323
 
324
+ # AUTO: Defines class `ListAccessNode`.
325
  class ListAccessNode(ASTNode):
326
+ # AUTO: Defines function `__init__`.
327
  def __init__(self, list_name, index_expr, line=None):
328
+ # AUTO: Sets `super().__init__("ListAccess", line`.
329
  super().__init__("ListAccess", line=line)
330
+ # AUTO: Sets `self.add_child(ASTNode("ListName", list_name, line`.
331
  self.add_child(ASTNode("ListName", list_name, line=line))
332
+ # AUTO: Calls `self.add_child`.
333
  self.add_child(index_expr)
334
 
335
 
336
+ # AUTO: Defines class `MemberAccessNode`.
337
  class MemberAccessNode(ASTNode):
338
+ # AUTO: Defines function `__init__`.
339
  def __init__(self, object_name, member_name, line=None):
340
+ # AUTO: Sets `super().__init__("MemberAccess", line`.
341
  super().__init__("MemberAccess", line=line)
342
+ # AUTO: Checks this condition.
343
  if isinstance(object_name, ASTNode):
344
+ # AUTO: Calls `self.add_child`.
345
  self.add_child(object_name)
346
+ # AUTO: Runs when previous condition did not pass.
347
  else:
348
+ # AUTO: Sets `self.add_child(ASTNode("Object", object_name, line`.
349
  self.add_child(ASTNode("Object", object_name, line=line))
350
+ # AUTO: Sets `self.add_child(ASTNode("Member", member_name, line`.
351
  self.add_child(ASTNode("Member", member_name, line=line))
352
 
353
 
354
+ # AUTO: Defines class `ArrayMemberAccessNode`.
355
  class ArrayMemberAccessNode(ASTNode):
356
+ # AUTO: Defines function `__init__`.
357
  def __init__(self, list_access_node, member_name, line=None):
358
+ # AUTO: Sets `super().__init__("ArrayMemberAccess", line`.
359
  super().__init__("ArrayMemberAccess", line=line)
360
+ # AUTO: Calls `self.add_child`.
361
  self.add_child(list_access_node)
362
+ # AUTO: Sets `self.add_child(ASTNode("Member", member_name, line`.
363
  self.add_child(ASTNode("Member", member_name, line=line))
364
 
365
 
366
+ # AUTO: Defines class `BundleDefinitionNode`.
367
  class BundleDefinitionNode(ASTNode):
368
+ # AUTO: Defines function `__init__`.
369
  def __init__(self, bundle_name, members, line=None):
370
+ # AUTO: Sets `super().__init__("BundleDefinition", line`.
371
  super().__init__("BundleDefinition", line=line)
372
+ # AUTO: Sets `self.bundle_name`.
373
  self.bundle_name = bundle_name
374
+ # AUTO: Sets `self.members`.
375
  self.members = members
376
 
Backend/shared/tokens.py CHANGED
@@ -1,208 +1,376 @@
1
 
 
 
 
 
 
 
2
 
3
  # --- Reserved Words (Keywords) ---
 
4
  TT_RW_WATER = 'water' # Input function - reads user input
 
5
  TT_RW_PLANT = 'plant' # Output function - prints to console
 
6
  TT_RW_SEED = 'seed' # Data Type - integer (int)
 
7
  TT_RW_LEAF = 'leaf' # Data Type - character (char)
 
8
  TT_RW_BRANCH = 'branch' # Data Type - boolean (true/false)
 
9
  TT_RW_TREE = 'tree' # Data Type - double/float
 
10
  TT_RW_SPRING = 'spring' # Conditional statement - if
 
11
  TT_RW_WITHER = 'wither' # Conditional statement - else
 
12
  TT_RW_BUD = 'bud' # Conditional statement - else-if
 
13
  TT_RW_HARVEST = 'harvest' # Switch statement
 
14
  TT_RW_GROW = 'grow' # Loop - while
 
15
  TT_RW_CULTIVATE = 'cultivate' # Loop - for
 
16
  TT_RW_TEND = 'tend' # Loop - do-while
 
17
  TT_RW_EMPTY = 'empty' # Void return type
 
18
  TT_RW_PRUNE = 'prune' # Break statement - exit loop
 
19
  TT_RW_SKIP = 'skip' # Continue statement - skip to next iteration
 
20
  TT_RW_RECLAIM = 'reclaim' # Return statement - return from function
 
21
  TT_RW_ROOT = 'root' # Main function entry point
 
22
  TT_RW_POLLINATE = 'pollinate' # Function declaration
 
23
  TT_RW_VARIETY = 'variety' # Case label in switch statement
 
24
  TT_RW_FERTILE = 'fertile' # Constant declaration
 
25
  TT_RW_SOIL = 'soil' # Default case in switch statement
 
26
  TT_RW_BUNDLE = 'bundle' # Struct definition
 
27
  TT_RW_VINE = 'vine' # String data type
28
 
29
  # --- Operators & Symbols ---
 
30
  TT_IDENTIFIER = 'id' # Variable/function names (e.g., myVar, calcTotal)
 
31
  TT_PLUS = '+' # Addition operator
 
32
  TT_MINUS = '-' # Subtraction operator
 
33
  TT_MUL = '*' # Multiplication operator
 
34
  TT_DIV = '/' # Division operator
 
35
  TT_MOD = '%' # Modulo operator (remainder)
 
36
  TT_EXP = '**' # Exponentiation operator (power)
 
37
  TT_EQ = '=' # Assignment operator
 
38
  TT_EQTO = '==' # Equality comparison operator
 
39
  TT_PLUSEQ = '+=' # Add and assign operator
 
40
  TT_MINUSEQ = '-=' # Subtract and assign operator
 
41
  TT_MULTIEQ = '*=' # Multiply and assign operator
 
42
  TT_DIVEQ = '/=' # Divide and assign operator
 
43
  TT_MODEQ = '%=' # Modulo and assign operator
 
44
  TT_EXPEQ = '**=' # Exponent and assign operator (x **= 2 → x = x ** 2)
 
45
  TT_CONCAT = '`' # String concatenation operator
 
46
  TT_LPAREN = '(' # Left parenthesis
 
47
  TT_RPAREN = ')' # Right parenthesis
 
48
  TT_SEMICOLON = ';' # Statement terminator
 
49
  TT_COMMA = ',' # Separator (function args, array elements)
 
50
  TT_COLON = ':' # Colon (used in switch cases)
 
51
  TT_BLOCK_START = '{' # Block start (scope begin)
 
52
  TT_BLOCK_END = '}' # Block end (scope close)
 
53
  TT_LT = '<' # Less than comparison
 
54
  TT_GT = '>' # Greater than comparison
 
55
  TT_LTEQ = '<=' # Less than or equal comparison
 
56
  TT_GTEQ = '>=' # Greater than or equal comparison
 
57
  TT_NOTEQ = '!=' # Not equal comparison
 
58
  TT_EOF = 'EOF' # End of file marker
 
59
  TT_AND = '&&' # Logical AND operator
 
60
  TT_OR = '||' # Logical OR operator
 
61
  TT_SINGLE_AND = '&' # Invalid single ampersand
 
62
  TT_SINGLE_OR = '|' # Invalid single pipe
 
63
  TT_NOT = '!' # Logical NOT operator
 
64
  TT_INCREMENT = '++' # Increment operator (e.g., x++)
 
65
  TT_DECREMENT = '--' # Decrement operator (e.g., x--)
 
66
  TT_LSQBR = '[' # Left square bracket (array indexing)
 
67
  TT_RSQBR = ']' # Right square bracket
 
68
  TT_NEGATIVE = '~' # Unary negation operator
 
69
  TT_MEMBER = 'member' # Member token for struct access
 
70
  TT_INTEGERLIT = 'intlit' # Integer literal token (e.g., 42, 100)
 
71
  TT_DOUBLELIT = 'dblit' # Double/float literal token (e.g., 3.14, 2.5)
 
72
  TT_STRINGLIT = 'stringlit' # String literal token (e.g., "hello")
 
73
  TT_CHARLIT = 'chrlit' # Character literal token (e.g., 'a')
 
74
  TT_BOOLLIT_TRUE = 'sunshine' # Boolean true literal
 
75
  TT_BOOLLIT_FALSE = 'frost' # Boolean false literal
 
76
  TT_STRCTACCESS = '.' # Struct member access operator
 
77
  TT_NL = '\n' # Newline token
 
78
  TT_DOT = '.' # Dot operator (struct access)
 
79
  TT_COMMENT = 'comment' # Single-line comment (//...)
 
80
  TT_MCOMMENT = 'mcommentlit' # Multi-line comment (/*...*/)
81
 
82
 
83
  # ============================================================================
84
  # TOKEN CLASS - Represents a single token (lexeme)
85
  # ============================================================================
 
86
  class Token:
87
  """Represents a token with type, value, line number, and column number"""
88
 
 
89
  def __init__(self, type_, value=None, line=1, col=0):
 
90
  self.type = type_ # Token type (e.g., TT_IDENTIFIER, TT_INTEGERLIT)
 
91
  self.value = value # Token text/value (e.g., "myVar", "42")
 
92
  self.line = line # Line number where token appears
 
93
  self.col = col # Column number where token starts (0-based)
94
 
95
 
96
  # ============================================================================
97
  # TOKEN TYPE DESCRIPTIONS - Maps token types to human-readable descriptions
98
  # ============================================================================
 
99
  def get_token_description(token_type: str, value: str = '') -> str:
100
  """Returns a descriptive name for each token type"""
101
  # Handle negative literals: value starts with ~ but token type is intlit/dbllit
 
102
  if token_type == 'intlit' and isinstance(value, str) and value.startswith('~'):
 
103
  return 'negative integer'
 
104
  if token_type == 'dblit' and isinstance(value, str) and value.startswith('~'):
 
105
  return 'negative float'
106
 
 
107
  descriptions = {
108
  # Reserved Words - I/O
 
109
  'water': 'Input Function',
 
110
  'plant': 'Output Function',
111
 
112
  # Reserved Words - Data Types
 
113
  'seed': 'Integer Type',
 
114
  'leaf': 'Character Type',
 
115
  'branch': 't/f',
 
116
  'tree': 'Float Type',
 
117
  'vine': 'String Type',
 
118
  'empty': 'Void Type',
119
 
120
  # Reserved Words - Control Flow
 
121
  'spring': 'If Statement',
 
122
  'wither': 'Else Statement',
 
123
  'bud': 'Else-If Statement',
 
124
  'harvest': 'Switch Statement',
 
125
  'variety': 'Case Label',
 
126
  'soil': 'Default Case',
127
 
128
  # Reserved Words - Loops
 
129
  'grow': 'While Loop',
 
130
  'cultivate': 'For Loop',
 
131
  'tend': 'Do-While Loop',
 
132
  'prune': 'Break Statement',
 
133
  'skip': 'Continue Statement',
134
 
135
  # Reserved Words - Functions
 
136
  'root': 'Main Function',
 
137
  'pollinate': 'Function Declaration',
 
138
  'reclaim': 'Return Statement',
139
 
140
  # Reserved Words - Other
 
141
  'fertile': 'Constant Declaration',
 
142
  'bundle': 'Struct Definition',
143
 
144
  # Identifiers and Literals
 
145
  'id': 'Identifier',
 
146
  'intlit': 'Integer Literal',
 
147
  'dblit': 'double Literal',
 
148
  'stringlit': 'string',
 
149
  'chrlit': 'Character',
 
150
  'sunshine': 'Boolean True',
 
151
  'frost': 'Boolean False',
152
 
153
  # Arithmetic Operators
 
154
  '+': 'Plus Operator',
 
155
  '-': 'Minus Operator',
 
156
  '*': 'Multiply Operator',
 
157
  '/': 'Divide Operator',
 
158
  '%': 'Modulo Operator',
 
159
  '**': 'Power Operator',
 
160
  '~': 'Negate Operator',
 
161
  '++': 'Increment Operator',
 
162
  '--': 'Decrement Operator',
163
 
164
  # Assignment Operators
 
165
  '=': 'Assign Operator',
 
166
  '+=': 'Add-Assign Operator',
 
167
  '-=': 'Sub-Assign Operator',
 
168
  '*=': 'Mul-Assign Operator',
 
169
  '/=': 'Div-Assign Operator',
 
170
  '%=': 'Mod-Assign Operator',
171
 
172
  # Comparison Operators
 
173
  '==': 'Equal Operator',
 
174
  '!=': 'Not-Equal Operator',
 
175
  '<': 'Less-Than Operator',
 
176
  '>': 'Greater-Than Operator',
 
177
  '<=': 'Less-Equal Operator',
 
178
  '>=': 'Greater-Equal Operator',
179
 
180
  # Logical Operators
 
181
  '&&': 'AND Operator',
 
182
  '&': 'Invalid Single-Ampersand',
 
183
  '||': 'OR Operator',
 
184
  '|': 'Invalid Single-Pipe',
 
185
  '!': 'NOT Operator',
186
 
187
  # Delimiters and Punctuation
 
188
  '(': 'Left Parenthesis',
 
189
  ')': 'Right Parenthesis',
 
190
  '{': 'Left Brace',
 
191
  '}': 'Right Brace',
 
192
  '[': 'Left Bracket',
 
193
  ']': 'Right Bracket',
 
194
  ';': 'Semicolon',
 
195
  ',': 'Comma',
 
196
  ':': 'Colon',
 
197
  '.': 'Dot Operator',
 
198
  '`': 'Concatenation Operator',
199
 
200
  # Special
 
201
  'member': 'Struct Member',
 
202
  'EOF': 'End of File',
 
203
  '\n': 'Newline',
 
204
  'comment': 'comment',
 
205
  'mcommentlit': 'multicomment',
 
206
  }
207
 
 
208
  return descriptions.get(token_type, 'Unknown Token')
 
1
 
2
+ """Token constants and Token class used by every compiler stage.
3
+
4
+ The lexer creates Token(type, value, line, col). The parser and later stages
5
+ mostly compare token.type values against these constants.
6
+ """
7
+
8
 
9
  # --- Reserved Words (Keywords) ---
10
+ # AUTO: Sets `TT_RW_WATER`.
11
  TT_RW_WATER = 'water' # Input function - reads user input
12
+ # AUTO: Sets `TT_RW_PLANT`.
13
  TT_RW_PLANT = 'plant' # Output function - prints to console
14
+ # AUTO: Sets `TT_RW_SEED`.
15
  TT_RW_SEED = 'seed' # Data Type - integer (int)
16
+ # AUTO: Sets `TT_RW_LEAF`.
17
  TT_RW_LEAF = 'leaf' # Data Type - character (char)
18
+ # AUTO: Sets `TT_RW_BRANCH`.
19
  TT_RW_BRANCH = 'branch' # Data Type - boolean (true/false)
20
+ # AUTO: Sets `TT_RW_TREE`.
21
  TT_RW_TREE = 'tree' # Data Type - double/float
22
+ # AUTO: Sets `TT_RW_SPRING`.
23
  TT_RW_SPRING = 'spring' # Conditional statement - if
24
+ # AUTO: Sets `TT_RW_WITHER`.
25
  TT_RW_WITHER = 'wither' # Conditional statement - else
26
+ # AUTO: Sets `TT_RW_BUD`.
27
  TT_RW_BUD = 'bud' # Conditional statement - else-if
28
+ # AUTO: Sets `TT_RW_HARVEST`.
29
  TT_RW_HARVEST = 'harvest' # Switch statement
30
+ # AUTO: Sets `TT_RW_GROW`.
31
  TT_RW_GROW = 'grow' # Loop - while
32
+ # AUTO: Sets `TT_RW_CULTIVATE`.
33
  TT_RW_CULTIVATE = 'cultivate' # Loop - for
34
+ # AUTO: Sets `TT_RW_TEND`.
35
  TT_RW_TEND = 'tend' # Loop - do-while
36
+ # AUTO: Sets `TT_RW_EMPTY`.
37
  TT_RW_EMPTY = 'empty' # Void return type
38
+ # AUTO: Sets `TT_RW_PRUNE`.
39
  TT_RW_PRUNE = 'prune' # Break statement - exit loop
40
+ # AUTO: Sets `TT_RW_SKIP`.
41
  TT_RW_SKIP = 'skip' # Continue statement - skip to next iteration
42
+ # AUTO: Sets `TT_RW_RECLAIM`.
43
  TT_RW_RECLAIM = 'reclaim' # Return statement - return from function
44
+ # AUTO: Sets `TT_RW_ROOT`.
45
  TT_RW_ROOT = 'root' # Main function entry point
46
+ # AUTO: Sets `TT_RW_POLLINATE`.
47
  TT_RW_POLLINATE = 'pollinate' # Function declaration
48
+ # AUTO: Sets `TT_RW_VARIETY`.
49
  TT_RW_VARIETY = 'variety' # Case label in switch statement
50
+ # AUTO: Sets `TT_RW_FERTILE`.
51
  TT_RW_FERTILE = 'fertile' # Constant declaration
52
+ # AUTO: Sets `TT_RW_SOIL`.
53
  TT_RW_SOIL = 'soil' # Default case in switch statement
54
+ # AUTO: Sets `TT_RW_BUNDLE`.
55
  TT_RW_BUNDLE = 'bundle' # Struct definition
56
+ # AUTO: Sets `TT_RW_VINE`.
57
  TT_RW_VINE = 'vine' # String data type
58
 
59
  # --- Operators & Symbols ---
60
+ # AUTO: Sets `TT_IDENTIFIER`.
61
  TT_IDENTIFIER = 'id' # Variable/function names (e.g., myVar, calcTotal)
62
+ # AUTO: Sets `TT_PLUS`.
63
  TT_PLUS = '+' # Addition operator
64
+ # AUTO: Sets `TT_MINUS`.
65
  TT_MINUS = '-' # Subtraction operator
66
+ # AUTO: Sets `TT_MUL`.
67
  TT_MUL = '*' # Multiplication operator
68
+ # AUTO: Sets `TT_DIV`.
69
  TT_DIV = '/' # Division operator
70
+ # AUTO: Sets `TT_MOD`.
71
  TT_MOD = '%' # Modulo operator (remainder)
72
+ # AUTO: Sets `TT_EXP`.
73
  TT_EXP = '**' # Exponentiation operator (power)
74
+ # AUTO: Sets `TT_EQ`.
75
  TT_EQ = '=' # Assignment operator
76
+ # AUTO: Executes this statement.
77
  TT_EQTO = '==' # Equality comparison operator
78
+ # AUTO: Adds into `TT_PLUSEQ = '`.
79
  TT_PLUSEQ = '+=' # Add and assign operator
80
+ # AUTO: Subtracts from `TT_MINUSEQ = '`.
81
  TT_MINUSEQ = '-=' # Subtract and assign operator
82
+ # AUTO: Multiplies into `TT_MULTIEQ = '`.
83
  TT_MULTIEQ = '*=' # Multiply and assign operator
84
+ # AUTO: Divides into `TT_DIVEQ = '`.
85
  TT_DIVEQ = '/=' # Divide and assign operator
86
+ # AUTO: Sets `TT_MODEQ`.
87
  TT_MODEQ = '%=' # Modulo and assign operator
88
+ # AUTO: Multiplies into `TT_EXPEQ = '*`.
89
  TT_EXPEQ = '**=' # Exponent and assign operator (x **= 2 → x = x ** 2)
90
+ # AUTO: Sets `TT_CONCAT`.
91
  TT_CONCAT = '`' # String concatenation operator
92
+ # AUTO: Sets `TT_LPAREN`.
93
  TT_LPAREN = '(' # Left parenthesis
94
+ # AUTO: Sets `TT_RPAREN`.
95
  TT_RPAREN = ')' # Right parenthesis
96
+ # AUTO: Sets `TT_SEMICOLON`.
97
  TT_SEMICOLON = ';' # Statement terminator
98
+ # AUTO: Sets `TT_COMMA`.
99
  TT_COMMA = ',' # Separator (function args, array elements)
100
+ # AUTO: Sets `TT_COLON`.
101
  TT_COLON = ':' # Colon (used in switch cases)
102
+ # AUTO: Sets `TT_BLOCK_START`.
103
  TT_BLOCK_START = '{' # Block start (scope begin)
104
+ # AUTO: Sets `TT_BLOCK_END`.
105
  TT_BLOCK_END = '}' # Block end (scope close)
106
+ # AUTO: Sets `TT_LT`.
107
  TT_LT = '<' # Less than comparison
108
+ # AUTO: Sets `TT_GT`.
109
  TT_GT = '>' # Greater than comparison
110
+ # AUTO: Executes this statement.
111
  TT_LTEQ = '<=' # Less than or equal comparison
112
+ # AUTO: Executes this statement.
113
  TT_GTEQ = '>=' # Greater than or equal comparison
114
+ # AUTO: Executes this statement.
115
  TT_NOTEQ = '!=' # Not equal comparison
116
+ # AUTO: Sets `TT_EOF`.
117
  TT_EOF = 'EOF' # End of file marker
118
+ # AUTO: Sets `TT_AND`.
119
  TT_AND = '&&' # Logical AND operator
120
+ # AUTO: Sets `TT_OR`.
121
  TT_OR = '||' # Logical OR operator
122
+ # AUTO: Sets `TT_SINGLE_AND`.
123
  TT_SINGLE_AND = '&' # Invalid single ampersand
124
+ # AUTO: Sets `TT_SINGLE_OR`.
125
  TT_SINGLE_OR = '|' # Invalid single pipe
126
+ # AUTO: Sets `TT_NOT`.
127
  TT_NOT = '!' # Logical NOT operator
128
+ # AUTO: Sets `TT_INCREMENT`.
129
  TT_INCREMENT = '++' # Increment operator (e.g., x++)
130
+ # AUTO: Sets `TT_DECREMENT`.
131
  TT_DECREMENT = '--' # Decrement operator (e.g., x--)
132
+ # AUTO: Sets `TT_LSQBR`.
133
  TT_LSQBR = '[' # Left square bracket (array indexing)
134
+ # AUTO: Sets `TT_RSQBR`.
135
  TT_RSQBR = ']' # Right square bracket
136
+ # AUTO: Sets `TT_NEGATIVE`.
137
  TT_NEGATIVE = '~' # Unary negation operator
138
+ # AUTO: Sets `TT_MEMBER`.
139
  TT_MEMBER = 'member' # Member token for struct access
140
+ # AUTO: Sets `TT_INTEGERLIT`.
141
  TT_INTEGERLIT = 'intlit' # Integer literal token (e.g., 42, 100)
142
+ # AUTO: Sets `TT_DOUBLELIT`.
143
  TT_DOUBLELIT = 'dblit' # Double/float literal token (e.g., 3.14, 2.5)
144
+ # AUTO: Sets `TT_STRINGLIT`.
145
  TT_STRINGLIT = 'stringlit' # String literal token (e.g., "hello")
146
+ # AUTO: Sets `TT_CHARLIT`.
147
  TT_CHARLIT = 'chrlit' # Character literal token (e.g., 'a')
148
+ # AUTO: Sets `TT_BOOLLIT_TRUE`.
149
  TT_BOOLLIT_TRUE = 'sunshine' # Boolean true literal
150
+ # AUTO: Sets `TT_BOOLLIT_FALSE`.
151
  TT_BOOLLIT_FALSE = 'frost' # Boolean false literal
152
+ # AUTO: Sets `TT_STRCTACCESS`.
153
  TT_STRCTACCESS = '.' # Struct member access operator
154
+ # AUTO: Sets `TT_NL`.
155
  TT_NL = '\n' # Newline token
156
+ # AUTO: Sets `TT_DOT`.
157
  TT_DOT = '.' # Dot operator (struct access)
158
+ # AUTO: Sets `TT_COMMENT`.
159
  TT_COMMENT = 'comment' # Single-line comment (//...)
160
+ # AUTO: Sets `TT_MCOMMENT`.
161
  TT_MCOMMENT = 'mcommentlit' # Multi-line comment (/*...*/)
162
 
163
 
164
  # ============================================================================
165
  # TOKEN CLASS - Represents a single token (lexeme)
166
  # ============================================================================
167
+ # AUTO: Defines class `Token`.
168
  class Token:
169
  """Represents a token with type, value, line number, and column number"""
170
 
171
+ # AUTO: Defines function `__init__`.
172
  def __init__(self, type_, value=None, line=1, col=0):
173
+ # LINE: token type is what parser compares, like id/intlit/seed.
174
  self.type = type_ # Token type (e.g., TT_IDENTIFIER, TT_INTEGERLIT)
175
+ # LINE: token value is the actual lexeme text, like x or 10.
176
  self.value = value # Token text/value (e.g., "myVar", "42")
177
+ # LINE: line records where the token starts in source code.
178
  self.line = line # Line number where token appears
179
+ # LINE: col records the starting column for error messages.
180
  self.col = col # Column number where token starts (0-based)
181
 
182
 
183
  # ============================================================================
184
  # TOKEN TYPE DESCRIPTIONS - Maps token types to human-readable descriptions
185
  # ============================================================================
186
+ # AUTO: Defines function `get_token_description`.
187
  def get_token_description(token_type: str, value: str = '') -> str:
188
  """Returns a descriptive name for each token type"""
189
  # Handle negative literals: value starts with ~ but token type is intlit/dbllit
190
+ # LINE: Special description for negative integer literals.
191
  if token_type == 'intlit' and isinstance(value, str) and value.startswith('~'):
192
+ # AUTO: Returns this result to the caller.
193
  return 'negative integer'
194
+ # LINE: Special description for negative double literals.
195
  if token_type == 'dblit' and isinstance(value, str) and value.startswith('~'):
196
+ # AUTO: Returns this result to the caller.
197
  return 'negative float'
198
 
199
+ # LINE: Map token types to human-readable lexeme table labels.
200
  descriptions = {
201
  # Reserved Words - I/O
202
+ # AUTO: Executes this statement.
203
  'water': 'Input Function',
204
+ # AUTO: Executes this statement.
205
  'plant': 'Output Function',
206
 
207
  # Reserved Words - Data Types
208
+ # AUTO: Executes this statement.
209
  'seed': 'Integer Type',
210
+ # AUTO: Executes this statement.
211
  'leaf': 'Character Type',
212
+ # AUTO: Executes this statement.
213
  'branch': 't/f',
214
+ # AUTO: Executes this statement.
215
  'tree': 'Float Type',
216
+ # AUTO: Executes this statement.
217
  'vine': 'String Type',
218
+ # AUTO: Executes this statement.
219
  'empty': 'Void Type',
220
 
221
  # Reserved Words - Control Flow
222
+ # AUTO: Executes this statement.
223
  'spring': 'If Statement',
224
+ # AUTO: Executes this statement.
225
  'wither': 'Else Statement',
226
+ # AUTO: Executes this statement.
227
  'bud': 'Else-If Statement',
228
+ # AUTO: Executes this statement.
229
  'harvest': 'Switch Statement',
230
+ # AUTO: Executes this statement.
231
  'variety': 'Case Label',
232
+ # AUTO: Executes this statement.
233
  'soil': 'Default Case',
234
 
235
  # Reserved Words - Loops
236
+ # AUTO: Executes this statement.
237
  'grow': 'While Loop',
238
+ # AUTO: Executes this statement.
239
  'cultivate': 'For Loop',
240
+ # AUTO: Executes this statement.
241
  'tend': 'Do-While Loop',
242
+ # AUTO: Executes this statement.
243
  'prune': 'Break Statement',
244
+ # AUTO: Executes this statement.
245
  'skip': 'Continue Statement',
246
 
247
  # Reserved Words - Functions
248
+ # AUTO: Executes this statement.
249
  'root': 'Main Function',
250
+ # AUTO: Executes this statement.
251
  'pollinate': 'Function Declaration',
252
+ # AUTO: Executes this statement.
253
  'reclaim': 'Return Statement',
254
 
255
  # Reserved Words - Other
256
+ # AUTO: Executes this statement.
257
  'fertile': 'Constant Declaration',
258
+ # AUTO: Executes this statement.
259
  'bundle': 'Struct Definition',
260
 
261
  # Identifiers and Literals
262
+ # AUTO: Executes this statement.
263
  'id': 'Identifier',
264
+ # AUTO: Executes this statement.
265
  'intlit': 'Integer Literal',
266
+ # AUTO: Executes this statement.
267
  'dblit': 'double Literal',
268
+ # AUTO: Executes this statement.
269
  'stringlit': 'string',
270
+ # AUTO: Executes this statement.
271
  'chrlit': 'Character',
272
+ # AUTO: Executes this statement.
273
  'sunshine': 'Boolean True',
274
+ # AUTO: Executes this statement.
275
  'frost': 'Boolean False',
276
 
277
  # Arithmetic Operators
278
+ # AUTO: Executes this statement.
279
  '+': 'Plus Operator',
280
+ # AUTO: Executes this statement.
281
  '-': 'Minus Operator',
282
+ # AUTO: Executes this statement.
283
  '*': 'Multiply Operator',
284
+ # AUTO: Executes this statement.
285
  '/': 'Divide Operator',
286
+ # AUTO: Executes this statement.
287
  '%': 'Modulo Operator',
288
+ # AUTO: Executes this statement.
289
  '**': 'Power Operator',
290
+ # AUTO: Executes this statement.
291
  '~': 'Negate Operator',
292
+ # AUTO: Executes this statement.
293
  '++': 'Increment Operator',
294
+ # AUTO: Executes this statement.
295
  '--': 'Decrement Operator',
296
 
297
  # Assignment Operators
298
+ # AUTO: Sets `'`.
299
  '=': 'Assign Operator',
300
+ # AUTO: Adds into `'`.
301
  '+=': 'Add-Assign Operator',
302
+ # AUTO: Subtracts from `'`.
303
  '-=': 'Sub-Assign Operator',
304
+ # AUTO: Multiplies into `'`.
305
  '*=': 'Mul-Assign Operator',
306
+ # AUTO: Divides into `'`.
307
  '/=': 'Div-Assign Operator',
308
+ # AUTO: Sets `'%`.
309
  '%=': 'Mod-Assign Operator',
310
 
311
  # Comparison Operators
312
+ # AUTO: Executes this statement.
313
  '==': 'Equal Operator',
314
+ # AUTO: Executes this statement.
315
  '!=': 'Not-Equal Operator',
316
+ # AUTO: Executes this statement.
317
  '<': 'Less-Than Operator',
318
+ # AUTO: Executes this statement.
319
  '>': 'Greater-Than Operator',
320
+ # AUTO: Executes this statement.
321
  '<=': 'Less-Equal Operator',
322
+ # AUTO: Executes this statement.
323
  '>=': 'Greater-Equal Operator',
324
 
325
  # Logical Operators
326
+ # AUTO: Executes this statement.
327
  '&&': 'AND Operator',
328
+ # AUTO: Executes this statement.
329
  '&': 'Invalid Single-Ampersand',
330
+ # AUTO: Executes this statement.
331
  '||': 'OR Operator',
332
+ # AUTO: Executes this statement.
333
  '|': 'Invalid Single-Pipe',
334
+ # AUTO: Executes this statement.
335
  '!': 'NOT Operator',
336
 
337
  # Delimiters and Punctuation
338
+ # AUTO: Executes this statement.
339
  '(': 'Left Parenthesis',
340
+ # AUTO: Executes this statement.
341
  ')': 'Right Parenthesis',
342
+ # AUTO: Executes this statement.
343
  '{': 'Left Brace',
344
+ # AUTO: Executes this statement.
345
  '}': 'Right Brace',
346
+ # AUTO: Executes this statement.
347
  '[': 'Left Bracket',
348
+ # AUTO: Executes this statement.
349
  ']': 'Right Bracket',
350
+ # AUTO: Executes this statement.
351
  ';': 'Semicolon',
352
+ # AUTO: Executes this statement.
353
  ',': 'Comma',
354
+ # AUTO: Executes this statement.
355
  ':': 'Colon',
356
+ # AUTO: Executes this statement.
357
  '.': 'Dot Operator',
358
+ # AUTO: Executes this statement.
359
  '`': 'Concatenation Operator',
360
 
361
  # Special
362
+ # AUTO: Executes this statement.
363
  'member': 'Struct Member',
364
+ # AUTO: Executes this statement.
365
  'EOF': 'End of File',
366
+ # AUTO: Executes this statement.
367
  '\n': 'Newline',
368
+ # AUTO: Executes this statement.
369
  'comment': 'comment',
370
+ # AUTO: Executes this statement.
371
  'mcommentlit': 'multicomment',
372
+ # AUTO: Closes the current grouped code/data.
373
  }
374
 
375
+ # AUTO: Returns this result to the caller.
376
  return descriptions.get(token_type, 'Unknown Token')