atlispcc 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: atlispcc
3
+ Version: 0.1.0
4
+ Summary: FAS4 bytecode decompiler and compiler toolchain for AutoLISP-compatible CAD platforms
5
+ Requires-Python: >=3.8
6
+ Requires-Dist: typing-extensions>=4.5.0
7
+ Provides-Extra: dev
8
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
9
+ Requires-Dist: black>=22.3.0; extra == "dev"
10
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
@@ -0,0 +1,25 @@
1
+ fas4_decompiler.py,sha256=Z1lguVxy1KuPJx9Rcz-MI6tyD2YC852oeFD9vz-MhD0,113187
2
+ fas_core.py,sha256=FhXKyO_HMmyZNuM-EVGCDqoXKQGycRkVHLyzFZFftXo,23976
3
+ validate.py,sha256=7G6Y4G8BranlICusBlmaNR5lyx3GofEK1QJT0pcvoa8,7669
4
+ compiler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ compiler/analyzer.py,sha256=ahxB5N3rU8t78LCBVp8kWq_QQGIK4Lu_dotv3ulRr-o,6508
6
+ compiler/assembler.py,sha256=IS0HL2F__ZNox40JcXO7Ori6PINlhz4cy5nxFe2rhXQ,9332
7
+ compiler/ast.py,sha256=isnMGZRSYBnumoHGFU-n00hmNb6fHhaFYx8Rx2FpivY,2217
8
+ compiler/cad_symbols.py,sha256=rqA6C6cBuXrIxV1w_cYjc5u-7-ELaKKvNyIfD34KYv8,5678
9
+ compiler/cli.py,sha256=DNRY-0jR9tQY8hZ5p2rQUhTF0Fl_3h2RbxmCjGydte0,17881
10
+ compiler/codegen.py,sha256=8z8PtoZcrZlXKt6d-AQICrxQXIQz7g6TNpHeC7V2rm0,29140
11
+ compiler/fas_writer.py,sha256=qQx6JwEBsOZ8owIpeoltmqePZjhQvwMIEGlSfYhIr1Q,4004
12
+ compiler/lexer.py,sha256=BsW4vafVCf9dog1nMVpfBxjgbC5P_PRaIbIN1WpNV-k,2704
13
+ compiler/linker.py,sha256=TlD7N0AuKy-HuaqSJJvr-Z2QkpZyV5Sm0BSSd0HzaJ0,20538
14
+ compiler/opcodes.py,sha256=_4xOnuuMQBIR5ghR6xTWLvjD0Wna21QqQkvGWDl82mU,5375
15
+ compiler/opt.py,sha256=TvN6J7Mw-GjANX2GbiGsePcut8iZDCb5hDFy_mpdBhY,50300
16
+ compiler/packages.py,sha256=TZKa2dxtzS-Q4t2elCGSdG_s8c4bem7HbMpK2g0xg-E,1440
17
+ compiler/parser.py,sha256=pfoII5WSlrGAW7Bu4L03fNsFrfgZEqDUHsZfrJc3Z2g,11452
18
+ compiler/resource_builder.py,sha256=VADirA_pRFcRQlPi4k7mufNv00UbCFtOOPO4Ig7EKY4,10873
19
+ compiler/symbol_table.py,sha256=tFPqLiLn6VRe-A3D8bS5-x4rh4JZKA37bqDwrEDFefE,8869
20
+ compiler/vlx_builder.py,sha256=LBH_bYRl4r4-9KPWpB2RkBIL2QMlvumshQvqE5DVHI0,2917
21
+ atlispcc-0.1.0.dist-info/METADATA,sha256=fvTsAPGnSAO0DXJpf4zvojjY-CIS9CanNyj0lML4BL4,363
22
+ atlispcc-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
23
+ atlispcc-0.1.0.dist-info/entry_points.txt,sha256=3VWSfOrIUal05XiRUYbqHvP5Lzb8tCMjEj2ngYnxv5E,47
24
+ atlispcc-0.1.0.dist-info/top_level.txt,sha256=tQ25b7vfMG8b2mIVPwdmSi9jAPbwfjwi-hvgPdJDyuI,43
25
+ atlispcc-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ atlispcc = compiler.cli:main
@@ -0,0 +1,4 @@
1
+ compiler
2
+ fas4_decompiler
3
+ fas_core
4
+ validate
compiler/__init__.py ADDED
File without changes
compiler/analyzer.py ADDED
@@ -0,0 +1,217 @@
1
+ from typing import Dict, List, Set, Tuple, Optional
2
+ from compiler.ast import (
3
+ Node,
4
+ Program,
5
+ Symbol,
6
+ Integer,
7
+ Real,
8
+ String,
9
+ ListExpr,
10
+ QuotedExpr,
11
+ Defun,
12
+ Setq,
13
+ IfNode,
14
+ WhileNode,
15
+ ForeachNode,
16
+ RepeatNode,
17
+ Call,
18
+ Progn,
19
+ LambdaNode,
20
+ Cond,
21
+ )
22
+ from compiler.codegen import CodeGenerator, BytecodeBuffer
23
+ from compiler.symbol_table import SymbolTable
24
+
25
+
26
+ def count_instructions(func: Defun, symtab: SymbolTable) -> int:
27
+ codegen = CodeGenerator(symtab)
28
+ bytecode, _ = codegen.generate_function(func)
29
+ return len(bytecode)
30
+
31
+
32
+ def _collect_call_names(node: Optional[Node], calls: Set[str]):
33
+ if node is None:
34
+ return
35
+ if isinstance(node, Call):
36
+ calls.add(node.name)
37
+ for a in node.args:
38
+ _collect_call_names(a, calls)
39
+ elif isinstance(node, Setq):
40
+ for _, v in node.pairs:
41
+ _collect_call_names(v, calls)
42
+ elif isinstance(node, IfNode):
43
+ _collect_call_names(node.cond, calls)
44
+ if node.then:
45
+ _collect_call_names(node.then, calls)
46
+ if node.else_expr:
47
+ _collect_call_names(node.else_expr, calls)
48
+ elif isinstance(node, WhileNode):
49
+ _collect_call_names(node.cond, calls)
50
+ for b in node.body:
51
+ _collect_call_names(b, calls)
52
+ elif isinstance(node, ForeachNode):
53
+ _collect_call_names(node.var, calls)
54
+ _collect_call_names(node.list_expr, calls)
55
+ for b in node.body:
56
+ _collect_call_names(b, calls)
57
+ elif isinstance(node, RepeatNode):
58
+ _collect_call_names(node.count, calls)
59
+ for b in node.body:
60
+ _collect_call_names(b, calls)
61
+ elif isinstance(node, Progn):
62
+ for f in node.forms:
63
+ _collect_call_names(f, calls)
64
+ elif isinstance(node, Cond):
65
+ for cl in node.clauses:
66
+ for c in cl:
67
+ _collect_call_names(c, calls)
68
+ elif isinstance(node, LambdaNode):
69
+ for b in node.body:
70
+ _collect_call_names(b, calls)
71
+
72
+
73
+ def _is_tail_call(node: Node, func_name: str) -> bool:
74
+ if isinstance(node, Call) and node.name == func_name:
75
+ return True
76
+ if isinstance(node, IfNode):
77
+ if node.then and _is_tail_call(node.then, func_name):
78
+ return True
79
+ if node.else_expr and _is_tail_call(node.else_expr, func_name):
80
+ return True
81
+ return False
82
+ if isinstance(node, Cond):
83
+ for clause in node.clauses:
84
+ if clause:
85
+ tail = clause[-1] if len(clause) > 1 else clause[0]
86
+ if _is_tail_call(tail, func_name):
87
+ return True
88
+ return False
89
+ if isinstance(node, Progn):
90
+ if node.forms:
91
+ return _is_tail_call(node.forms[-1], func_name)
92
+ return False
93
+ return False
94
+
95
+
96
+ def find_tail_recursive(func: Defun) -> bool:
97
+ if not func.body:
98
+ return False
99
+ return _is_tail_call(func.body[-1], func.name)
100
+
101
+
102
+ def estimate_constants(program: Program) -> int:
103
+ count = 0
104
+
105
+ def walk(node: Optional[Node]):
106
+ nonlocal count
107
+ if node is None:
108
+ return
109
+ if isinstance(node, (Integer, Real, String)):
110
+ count += 1
111
+ elif isinstance(node, Call):
112
+ for a in node.args:
113
+ walk(a)
114
+ elif isinstance(node, Setq):
115
+ for _, v in node.pairs:
116
+ walk(v)
117
+ elif isinstance(node, IfNode):
118
+ walk(node.cond)
119
+ if node.then:
120
+ walk(node.then)
121
+ if node.else_expr:
122
+ walk(node.else_expr)
123
+ elif isinstance(node, WhileNode):
124
+ walk(node.cond)
125
+ for b in node.body:
126
+ walk(b)
127
+ elif isinstance(node, ForeachNode):
128
+ walk(node.var)
129
+ walk(node.list_expr)
130
+ for b in node.body:
131
+ walk(b)
132
+ elif isinstance(node, RepeatNode):
133
+ walk(node.count)
134
+ for b in node.body:
135
+ walk(b)
136
+ elif isinstance(node, Progn):
137
+ for f in node.forms:
138
+ walk(f)
139
+ elif isinstance(node, Cond):
140
+ for cl in node.clauses:
141
+ for c in cl:
142
+ walk(c)
143
+ elif isinstance(node, ListExpr):
144
+ for i in node.items:
145
+ walk(i)
146
+ elif isinstance(node, QuotedExpr):
147
+ if node.expr:
148
+ walk(node.expr)
149
+ elif isinstance(node, LambdaNode):
150
+ for b in node.body:
151
+ walk(b)
152
+
153
+ for f in program.forms:
154
+ if isinstance(f, Defun):
155
+ for e in f.body:
156
+ walk(e)
157
+ else:
158
+ walk(f)
159
+ return count
160
+
161
+
162
+ def analyze(program: Program) -> Dict:
163
+ symtab = SymbolTable()
164
+ symtab.collect_symbols(program)
165
+
166
+ defuns = [f for f in program.forms if isinstance(f, Defun)]
167
+ other = [f for f in program.forms if not isinstance(f, Defun)]
168
+
169
+ func_stats = []
170
+ total_instructions = 0
171
+ tail_recursive = []
172
+
173
+ for f in defuns:
174
+ inst_count = count_instructions(f, symtab)
175
+ total_instructions += inst_count
176
+ calls: Set[str] = set()
177
+ for e in f.body:
178
+ _collect_call_names(e, calls)
179
+
180
+ is_tail_rec = find_tail_recursive(f)
181
+ if is_tail_rec:
182
+ tail_recursive.append(f.name)
183
+
184
+ func_stats.append(
185
+ {
186
+ "name": f.name,
187
+ "args": len(f.args),
188
+ "locals": len(f.locals),
189
+ "instructions": inst_count,
190
+ "calls": sorted(calls) if calls else [],
191
+ "tail_recursive": is_tail_rec,
192
+ }
193
+ )
194
+
195
+ all_calls: Set[str] = set()
196
+ for f in defuns:
197
+ for e in f.body:
198
+ _collect_call_names(e, all_calls)
199
+ for e in other:
200
+ _collect_call_names(e, all_calls)
201
+
202
+ builtin_calls = {c for c in all_calls if c[0].islower() and c != "setq"}
203
+ user_funcs = {f.name for f in defuns}
204
+ extern_calls = builtin_calls - user_funcs
205
+
206
+ return {
207
+ "functions": len(defuns),
208
+ "top_level_forms": len(other),
209
+ "total_instructions": total_instructions,
210
+ "constants_estimate": estimate_constants(program),
211
+ "symbol_table_entries": len(symtab.entries),
212
+ "function_offsets": len(symtab.function_offsets),
213
+ "locals": sum(len(f.locals) for f in defuns),
214
+ "tail_recursive_functions": tail_recursive,
215
+ "functions_detail": func_stats,
216
+ "builtin_calls": sorted(extern_calls),
217
+ }
compiler/assembler.py ADDED
@@ -0,0 +1,265 @@
1
+ """FAS4 assembler/disassembler: .fas ↔ .lasm (Lisp Assembly)."""
2
+
3
+ import os
4
+ import re
5
+ from typing import Any, List, Dict, Tuple, Optional
6
+
7
+ from compiler.opcodes import (
8
+ NAME_TO_OPCODE,
9
+ OPCODE_TO_NAME,
10
+ encode_instruction,
11
+ encode_op_u16,
12
+ )
13
+ from compiler.symbol_table import SymbolTable
14
+ from compiler.resource_builder import ResourceBuilder
15
+ from compiler.fas_writer import FasWriter
16
+
17
+ # ─── Disassembler (FAS → .lasm text) ─────────────────────────────────────────
18
+
19
+ # Opcodes that share a name with another opcode — disambiguate in output
20
+ _DUPLICATE_NAMES = {
21
+ 0x5C: "GET_LOCAL_WIDE", # shares name 'GET_LOCAL' with 0x05
22
+ }
23
+
24
+
25
+ def _disasm_op_name(opcode: int) -> str:
26
+ """Return the assembly mnemonic, disambiguating duplicates."""
27
+ if opcode in _DUPLICATE_NAMES:
28
+ return _DUPLICATE_NAMES[opcode]
29
+ return OPCODE_TO_NAME.get(opcode, f"UNK_{opcode:02x}")
30
+
31
+
32
+ def _resolve_sym(symbols: List[str], idx: int) -> str:
33
+ if 0 <= idx < len(symbols):
34
+ return symbols[idx]
35
+ return f"[{idx}]"
36
+
37
+
38
+ def _fmt_inst(inst, symbols: List[str]) -> str:
39
+ """Format one instruction as a .lasm line."""
40
+ prefix = f" 0x{inst.offset:04x}:"
41
+ name = _disasm_op_name(inst.opcode)
42
+ ops = list(inst.operands)
43
+
44
+ # DEFUN packs 4 bytes → gv(u16), offset(u16)
45
+ if name == "DEFUN" and len(ops) >= 4:
46
+ gv = ops[0] | (ops[1] << 8)
47
+ off = ops[2] | (ops[3] << 8)
48
+ sym = _resolve_sym(symbols, gv)
49
+ return f"{prefix} DEFUN {sym}, {off}"
50
+ # FUNC/USUBR: second operand is a symbol index
51
+ if name in ("FUNC", "USUBR") and len(ops) >= 2:
52
+ sym = _resolve_sym(symbols, ops[1])
53
+ rest = [str(ops[0]), sym]
54
+ if len(ops) > 2:
55
+ rest.extend(str(o) for o in ops[2:])
56
+ return f'{prefix} {name} {", ".join(rest)}'
57
+ # PUSH_VALUE/PUSH_G/SETQ_G/PUSH_GLOBAL: first operand is a symbol index
58
+ if name in ("PUSH_VALUE", "PUSH_G", "PUSH_GLOBAL", "SETQ_G") and ops:
59
+ sym_info = _resolve_sym(symbols, ops[0])
60
+ rest = [sym_info]
61
+ rest.extend(str(o) for o in ops[1:3])
62
+ return f'{prefix} {name} {", ".join(rest)}'
63
+ # GET_LOCAL/SET_LOCAL/CLEAR_LOCAL: operand is a local slot index (integer)
64
+ if name in ("GET_LOCAL", "GET_LOCAL_WIDE", "SET_LOCAL", "CLEAR_LOCAL") and ops:
65
+ return f"{prefix} {name} {ops[0]}"
66
+
67
+ if not ops:
68
+ return f"{prefix} {name}"
69
+ return f'{prefix} {name} {", ".join(str(o) for o in ops)}'
70
+
71
+
72
+ def disassemble_fas(fas_path: str) -> str:
73
+ """Disassemble a FAS4 file into .lasm text format."""
74
+ from fas4_decompiler import Fas4Parser, Disassembler, ControlFlowAnalyzer, is_compact_stub, extract_stub_function_names
75
+
76
+ parser = Fas4Parser()
77
+ fas = parser.parse(fas_path)
78
+ disasm = Disassembler(short_subr=is_compact_stub(fas.bytecode, fas.function_catalog, len(fas.symbol_slots)))
79
+ instructions = disasm.disassemble(fas.bytecode, fas.nsyms,
80
+ sym_count=len(fas.symbol_slots) if is_compact_stub(fas.bytecode, fas.function_catalog, len(fas.symbol_slots)) else None)
81
+ cfa = ControlFlowAnalyzer(instructions, fas.function_catalog,
82
+ function_names=extract_stub_function_names(fas))
83
+ functions = cfa.find_functions()
84
+
85
+ lines: List[str] = []
86
+ lines.append("; FAS4 assembly")
87
+ lines.append(f"; Source: {os.path.basename(fas_path)}")
88
+ if fas.crunch_date:
89
+ lines.append(f"; Crunch date: {fas.crunch_date}")
90
+ lines.append("")
91
+
92
+ # 紧凑 stub 格式按资源注册顺序(symbol_slots)编址
93
+ if getattr(fas, "symbol_slots", None):
94
+ symbols = [str(it[1]) for it in fas.symbol_slots]
95
+ else:
96
+ symbols = fas.symbols
97
+
98
+ lines.append(".symbols")
99
+ for idx, sym in enumerate(symbols):
100
+ lines.append(f" {idx}: SYMBOL {sym}")
101
+ lines.append(".end_symbols")
102
+ lines.append("")
103
+
104
+ for func in functions:
105
+ fname = func.name if func.name and func.name != "c:init" else "__init__"
106
+ lines.append(f".function {fname}, {func.sym_idx}")
107
+ for inst in func.instructions:
108
+ lines.append(_fmt_inst(inst, symbols))
109
+ lines.append(".end_function")
110
+ lines.append("")
111
+
112
+ return "\n".join(lines)
113
+
114
+
115
+ # ─── Assembler (.lasm text → FAS) ────────────────────────────────────────────
116
+
117
+
118
+ def assemble_lasm(lasm_text: str, output_path: str) -> str:
119
+ """Assemble .lasm text into a FAS4 binary file."""
120
+
121
+ # === Pass 1: parse structure ===
122
+ symbols_list: List[str] = []
123
+ functions: List[Tuple[str, int, List[str]]] = []
124
+ current_func_name = ""
125
+ current_func_idx = 0
126
+ current_func_body: List[str] = []
127
+ in_symbols = False
128
+ in_function = False
129
+
130
+ for raw in lasm_text.split("\n"):
131
+ line = raw.split(";", 1)[0].strip()
132
+ if not line:
133
+ continue
134
+
135
+ if line == ".symbols":
136
+ in_symbols = True
137
+ elif line == ".end_symbols":
138
+ in_symbols = False
139
+ elif line.startswith(".function"):
140
+ m = re.match(r"\.function\s+(\S+),\s*(\d+)", line)
141
+ if m:
142
+ if in_function:
143
+ functions.append((current_func_name, current_func_idx, current_func_body))
144
+ current_func_name = m.group(1)
145
+ current_func_idx = int(m.group(2))
146
+ current_func_body = []
147
+ in_function = True
148
+ elif line == ".end_function":
149
+ if in_function:
150
+ functions.append((current_func_name, current_func_idx, current_func_body))
151
+ current_func_name = ""
152
+ current_func_body = []
153
+ in_function = False
154
+ elif in_symbols:
155
+ m = re.match(r"\s*(\d+)\s*:\s*SYMBOL\s+(.+)", line)
156
+ if m:
157
+ idx = int(m.group(1))
158
+ name = m.group(2).strip()
159
+ while len(symbols_list) <= idx:
160
+ symbols_list.append("")
161
+ symbols_list[idx] = name
162
+ elif in_function:
163
+ current_func_body.append(line)
164
+
165
+ if in_function and current_func_name:
166
+ functions.append((current_func_name, current_func_idx, current_func_body))
167
+
168
+ # === Build SymbolTable with all symbols ===
169
+ symtab = SymbolTable()
170
+ for sym in symbols_list:
171
+ symtab.get_or_add(sym)
172
+
173
+ # === Pass 2: assemble bytecode ===
174
+ init_code = bytearray()
175
+ function_bytes: List[bytes] = []
176
+ function_names: List[str] = []
177
+
178
+ for fname, _fidx, body_lines in functions:
179
+ if fname == "__init__":
180
+ for line in body_lines:
181
+ bc = _asm_instr(line, symtab, symbols_list)
182
+ if bc:
183
+ init_code.extend(bc)
184
+ continue
185
+
186
+ fb = bytearray()
187
+ for line in body_lines:
188
+ bc = _asm_instr(line, symtab, symbols_list)
189
+ if bc:
190
+ fb.extend(bc)
191
+ function_bytes.append(bytes(fb))
192
+ function_names.append(fname)
193
+
194
+ # === Build function catalog and write FAS ===
195
+ func_offsets: Dict[str, int] = {}
196
+ all_bc = bytearray(init_code)
197
+
198
+ for fname, fbytes in zip(function_names, function_bytes):
199
+ offset = len(all_bc)
200
+ func_offsets[fname] = offset
201
+ all_bc.extend(fbytes)
202
+
203
+ for name in function_names:
204
+ symtab.add_func_ref(name, func_offsets[name])
205
+
206
+ seg0 = bytes(all_bc)
207
+ resource = ResourceBuilder(symtab)
208
+ seg1 = resource.build(func_offsets)
209
+ writer = FasWriter(symtab)
210
+ writer.write(output_path, seg0, seg1)
211
+ return output_path
212
+
213
+
214
+ _INSTR_RE = re.compile(r"(\w+)(.*)")
215
+ _ADDR_PREFIX = re.compile(r"^0x[0-9a-fA-F]+:")
216
+
217
+
218
+ def _asm_instr(line: str, symtab: SymbolTable, symbols: List[str]) -> Optional[bytes]:
219
+ """Parse one assembly instruction line and encode to bytes."""
220
+ content = _ADDR_PREFIX.sub("", line).strip()
221
+ if not content:
222
+ return None
223
+
224
+ m = _INSTR_RE.match(content)
225
+ if not m:
226
+ return None
227
+
228
+ mnemonic = m.group(1).upper()
229
+ rest = m.group(2).strip()
230
+ operands = _parse_ops(rest, symtab)
231
+
232
+ if mnemonic == "GET_LOCAL_WIDE":
233
+ # Use wide GET_LOCAL opcode (0x5c) instead of standard (0x05)
234
+ from compiler.opcodes import encode_op_u16
235
+
236
+ op = NAME_TO_OPCODE.get("GET_LOCAL", 0xFF)
237
+ # Force opcode 0x5c
238
+ return encode_op_u16(0x5C, int(operands[0]) if operands else 0)
239
+
240
+ return encode_instruction(mnemonic, operands)
241
+
242
+
243
+ def _parse_ops(text: str, symtab: SymbolTable) -> List:
244
+ """Parse comma-separated operands, resolving symbol names to indices."""
245
+ if not text.strip():
246
+ return []
247
+
248
+ parts = [p.strip() for p in text.split(",")]
249
+ result: List[Any] = []
250
+ for p in parts:
251
+ if not p:
252
+ continue
253
+ if p in symtab.name_to_idx:
254
+ result.append(symtab.name_to_idx[p])
255
+ elif p.startswith("[") and p.endswith("]") and p[1:-1].isdigit():
256
+ result.append(int(p[1:-1]))
257
+ else:
258
+ try:
259
+ result.append(int(p))
260
+ except ValueError:
261
+ try:
262
+ result.append(float(p))
263
+ except ValueError:
264
+ raise ValueError(f"Cannot resolve operand: {p!r}")
265
+ return result
compiler/ast.py ADDED
@@ -0,0 +1,120 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import List, Any, Optional
3
+
4
+
5
+ @dataclass
6
+ class Node:
7
+ pass
8
+
9
+
10
+ @dataclass
11
+ class Program(Node):
12
+ forms: List['Node'] = field(default_factory=list)
13
+
14
+
15
+ @dataclass
16
+ class Symbol(Node):
17
+ name: str = ''
18
+
19
+
20
+ @dataclass
21
+ class Integer(Node):
22
+ value: int = 0
23
+
24
+
25
+ @dataclass
26
+ class Real(Node):
27
+ value: float = 0.0
28
+
29
+
30
+ @dataclass
31
+ class String(Node):
32
+ value: str = ''
33
+
34
+
35
+ @dataclass
36
+ class ListExpr(Node):
37
+ items: List['Node'] = field(default_factory=list)
38
+ dotted: Optional['Node'] = None
39
+
40
+
41
+ @dataclass
42
+ class QuotedExpr(Node):
43
+ expr: Optional['Node'] = None
44
+
45
+
46
+ @dataclass
47
+ class Defun(Node):
48
+ name: str = ''
49
+ args: List[str] = field(default_factory=list)
50
+ optional: List[str] = field(default_factory=list)
51
+ rest: Optional[str] = None
52
+ locals: List[str] = field(default_factory=list)
53
+ body: List['Node'] = field(default_factory=list)
54
+ is_command: bool = False
55
+ quoted: bool = False
56
+
57
+
58
+ @dataclass
59
+ class Setq(Node):
60
+ pairs: List[tuple] = field(default_factory=list)
61
+
62
+
63
+ @dataclass
64
+ class IfNode(Node):
65
+ cond: Optional['Node'] = None
66
+ then: Optional['Node'] = None
67
+ else_expr: Optional['Node'] = None
68
+
69
+
70
+ @dataclass
71
+ class WhileNode(Node):
72
+ cond: Optional['Node'] = None
73
+ body: List['Node'] = field(default_factory=list)
74
+
75
+
76
+ @dataclass
77
+ class ForeachNode(Node):
78
+ var: Optional['Node'] = None
79
+ list_expr: Optional['Node'] = None
80
+ body: List['Node'] = field(default_factory=list)
81
+
82
+
83
+ @dataclass
84
+ class Call(Node):
85
+ name: str = ''
86
+ args: List['Node'] = field(default_factory=list)
87
+
88
+
89
+ @dataclass
90
+ class Progn(Node):
91
+ forms: List['Node'] = field(default_factory=list)
92
+
93
+
94
+ @dataclass
95
+ class LambdaNode(Node):
96
+ args: List[str] = field(default_factory=list)
97
+ body: List['Node'] = field(default_factory=list)
98
+
99
+
100
+ @dataclass
101
+ class RepeatNode(Node):
102
+ count: Optional['Node'] = None
103
+ body: List['Node'] = field(default_factory=list)
104
+
105
+
106
+ @dataclass
107
+ class Cond(Node):
108
+ clauses: List[tuple] = field(default_factory=list)
109
+
110
+
111
+ @dataclass
112
+ class Defpackage(Node):
113
+ name: str = ''
114
+ use: List[str] = field(default_factory=list)
115
+ export: List[str] = field(default_factory=list)
116
+
117
+
118
+ @dataclass
119
+ class InPackage(Node):
120
+ name: str = ''