add comments ehehe
Browse files- Backend/_smoke_test.py +121 -0
- Backend/ai/__init__.py +1 -0
- Backend/ai/fallback.py +0 -0
- Backend/cfg/__init__.py +8 -0
- Backend/cfg/grammar.py +560 -0
- Backend/icg/__init__.py +1 -0
- Backend/icg/generator.py +833 -0
- Backend/interpreter/__init__.py +7 -0
- Backend/interpreter/errors.py +21 -0
- Backend/interpreter/interpreter.py +0 -0
- Backend/lexer/__init__.py +5 -0
- Backend/lexer/delimiters.py +67 -0
- Backend/lexer/errors.py +7 -0
- Backend/lexer/positions.py +14 -0
- Backend/lexer/scanner.py +0 -0
- Backend/parser/__init__.py +1 -0
- Backend/parser/builder.py +0 -0
- Backend/parser/parser.py +0 -0
- Backend/semantic/__init__.py +2 -0
- Backend/semantic/analyzer.py +171 -0
- Backend/semantic/errors.py +9 -0
- Backend/semantic/symbol_table.py +72 -0
- Backend/server.py +499 -1
- Backend/shared/__init__.py +3 -0
- Backend/shared/ast_nodes.py +172 -0
- Backend/shared/tokens.py +168 -0
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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')
|