gelang 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/LICENSE +21 -0
- package/README.md +535 -0
- package/bin/ge.js +112 -0
- package/package.json +62 -0
- package/python/pyeffic/__init__.py +9 -0
- package/python/pyeffic/__main__.py +6 -0
- package/python/pyeffic/analyzer.py +464 -0
- package/python/pyeffic/apisurface.py +238 -0
- package/python/pyeffic/autoselect.py +327 -0
- package/python/pyeffic/backends.py +87 -0
- package/python/pyeffic/bench.py +233 -0
- package/python/pyeffic/cli.py +184 -0
- package/python/pyeffic/compiler.py +421 -0
- package/python/pyeffic/config.py +383 -0
- package/python/pyeffic/dartgen.py +441 -0
- package/python/pyeffic/deploy.py +586 -0
- package/python/pyeffic/diagnostics.py +194 -0
- package/python/pyeffic/difftest.py +424 -0
- package/python/pyeffic/downloader.py +307 -0
- package/python/pyeffic/emitters/__init__.py +11 -0
- package/python/pyeffic/emitters/base.py +2359 -0
- package/python/pyeffic/emitters/cpp.py +266 -0
- package/python/pyeffic/emitters/csharp.py +342 -0
- package/python/pyeffic/emitters/dart.py +349 -0
- package/python/pyeffic/emitters/go.py +388 -0
- package/python/pyeffic/emitters/kotlin.py +314 -0
- package/python/pyeffic/emitters/rust.py +314 -0
- package/python/pyeffic/emitters/zig.py +411 -0
- package/python/pyeffic/ffi.py +49 -0
- package/python/pyeffic/frontends/__init__.py +94 -0
- package/python/pyeffic/frontends/hybrid.py +709 -0
- package/python/pyeffic/frontends/typescript.py +965 -0
- package/python/pyeffic/ge_cli.py +1148 -0
- package/python/pyeffic/golden.py +348 -0
- package/python/pyeffic/idents.py +206 -0
- package/python/pyeffic/modules.py +220 -0
- package/python/pyeffic/packer.py +222 -0
- package/python/pyeffic/pipeline.py +797 -0
- package/python/pyeffic/reactgen.py +966 -0
- package/python/pyeffic/researcher.py +177 -0
- package/python/pyeffic/scaffold.py +397 -0
- package/python/pyeffic/stdlib.py +246 -0
- package/python/pyeffic/styling.py +220 -0
- package/python/pyeffic/templates/desktop_gui/README.md +106 -0
- package/python/pyeffic/templates/desktop_gui/app/__init__.py +0 -0
- package/python/pyeffic/templates/desktop_gui/app/core/__init__.py +0 -0
- package/python/pyeffic/templates/desktop_gui/app/core/add.ge.py +13 -0
- package/python/pyeffic/templates/desktop_gui/app/core/factorial.ge.py +20 -0
- package/python/pyeffic/templates/desktop_gui/app/core/fibonacci.ge.py +25 -0
- package/python/pyeffic/templates/desktop_gui/app/core/gcd.ge.py +19 -0
- package/python/pyeffic/templates/desktop_gui/app/core/is_prime.ge.py +24 -0
- package/python/pyeffic/templates/desktop_gui/app/core/multiply.ge.py +13 -0
- package/python/pyeffic/templates/desktop_gui/app/core/power.ge.py +25 -0
- package/python/pyeffic/templates/desktop_gui/app/main.ge.py +49 -0
- package/python/pyeffic/templates/desktop_gui/app/memory/__init__.py +0 -0
- package/python/pyeffic/templates/desktop_gui/app/memory/buffer.ge.py +26 -0
- package/python/pyeffic/templates/desktop_gui/app/memory/limits.ge.py +47 -0
- package/python/pyeffic/templates/desktop_gui/app/memory/state.ge.py +44 -0
- package/python/pyeffic/templates/desktop_gui/app/ui/__init__.py +0 -0
- package/python/pyeffic/templates/desktop_gui/app/ui/layout.ge.py +64 -0
- package/python/pyeffic/templates/desktop_gui/app/ui/render.ge.py +87 -0
- package/python/pyeffic/templates/desktop_gui/app/ui/theme.ge.py +147 -0
- package/python/pyeffic/templates/desktop_gui/app/ui/widgets.ge.py +105 -0
- package/python/pyeffic/templates/desktop_gui/desktop/__init__.py +1 -0
- package/python/pyeffic/templates/desktop_gui/desktop/main.ge.py +258 -0
- package/python/pyeffic/templates/desktop_gui/ge.toml +16 -0
- package/python/pyeffic/templates/desktop_gui/tests/__init__.py +0 -0
- package/python/pyeffic/templates/desktop_gui/tests/ge_loader.py +76 -0
- package/python/pyeffic/templates/desktop_gui/tests/test_app.py +173 -0
- package/python/pyeffic/templates/web_react/README.md +115 -0
- package/python/pyeffic/templates/web_react/app/__init__.py +0 -0
- package/python/pyeffic/templates/web_react/app/core/__init__.py +0 -0
- package/python/pyeffic/templates/web_react/app/core/add.ge.py +9 -0
- package/python/pyeffic/templates/web_react/app/core/factorial.ge.py +16 -0
- package/python/pyeffic/templates/web_react/app/core/fibonacci.ge.py +21 -0
- package/python/pyeffic/templates/web_react/app/core/is_prime.ge.py +20 -0
- package/python/pyeffic/templates/web_react/app/core/multiply.ge.py +9 -0
- package/python/pyeffic/templates/web_react/app/main.ge.py +25 -0
- package/python/pyeffic/templates/web_react/app/memory/__init__.py +0 -0
- package/python/pyeffic/templates/web_react/app/memory/buffer.ge.py +25 -0
- package/python/pyeffic/templates/web_react/app/memory/limits.ge.py +51 -0
- package/python/pyeffic/templates/web_react/ge.toml +23 -0
- package/python/pyeffic/templates/web_react/tests/__init__.py +0 -0
- package/python/pyeffic/templates/web_react/tests/ge_loader.py +68 -0
- package/python/pyeffic/templates/web_react/tests/test_app.py +105 -0
- package/python/pyeffic/templates/web_react/ui/main.ge.ui +33 -0
- package/python/pyeffic/templates/web_react/web/__init__.py +0 -0
- package/python/pyeffic/templates/web_react/web/server.ge.py +78 -0
- package/python/pyeffic/ts2py.py +657 -0
- package/python/pyeffic/typecheck.py +232 -0
- package/python/pyeffic/ui.py +154 -0
- package/python/pyeffic/ui_dsl.py +618 -0
- package/python/pyeffic/widgets.py +87 -0
- package/scripts/README.md +42 -0
- package/scripts/check-toolchains.py +85 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
"""Go backend spec + program assembly.
|
|
2
|
+
|
|
3
|
+
Go compiles to a shared library via cgo. Exports C ABI functions with
|
|
4
|
+
`//export` directive. Interops with C/C++/Rust/Zig via the C ABI.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import ast
|
|
9
|
+
|
|
10
|
+
from .base import Emitter, Spec
|
|
11
|
+
from ..analyzer import FuncUnit
|
|
12
|
+
|
|
13
|
+
SPEC = Spec(
|
|
14
|
+
name="go",
|
|
15
|
+
types={"int": "int64", "float": "float64", "bool": "bool", "str": "string", "None": ""},
|
|
16
|
+
list_type="[]int64",
|
|
17
|
+
list_param_type="[]int64",
|
|
18
|
+
list_elem_type="int64",
|
|
19
|
+
borrow_list_arg=False,
|
|
20
|
+
range_call="({lo}..{hi})",
|
|
21
|
+
range_step_call="({lo}..{hi})",
|
|
22
|
+
len_call="int64(len({x}))",
|
|
23
|
+
list_len="int64(len({x}))",
|
|
24
|
+
print_int='fmt.Println({v})',
|
|
25
|
+
print_float='fmt.Println({v})',
|
|
26
|
+
print_str='fmt.Println({v})',
|
|
27
|
+
print_bool='fmt.Println({v})',
|
|
28
|
+
print_generic='fmt.Println({v})',
|
|
29
|
+
int_cast="int64({x})",
|
|
30
|
+
float_cast="float64({x})",
|
|
31
|
+
float_div="(float64({l}) / float64({r}))",
|
|
32
|
+
floor_div="({l} / {r})",
|
|
33
|
+
sum_call="func() int64 {{ var s int64; for _, v := range {it} {{ s += v }}; return s }}()",
|
|
34
|
+
abs_int="abs({x})",
|
|
35
|
+
abs_float="math.Abs({x})",
|
|
36
|
+
min_call="func() int64 {{ var m int64; for i, v := range {it} {{ if i == 0 || v < m {{ m = v }} }}; return m }}()",
|
|
37
|
+
max_call="func() int64 {{ var m int64; for i, v := range {it} {{ if i == 0 || v > m {{ m = v }} }}; return m }}()",
|
|
38
|
+
pow_call="int64(math.Pow(float64({l}), float64({r})))",
|
|
39
|
+
append_call="{x} = append({x}, {v})",
|
|
40
|
+
index_call="{x}[{i}]",
|
|
41
|
+
comment="//",
|
|
42
|
+
fn_template="{sig} {{\n{body}\n}}",
|
|
43
|
+
main_template="",
|
|
44
|
+
var_decl_template="var {target} {nt} = {val}",
|
|
45
|
+
ffi_prefix="//export ",
|
|
46
|
+
indent="\t",
|
|
47
|
+
str_concat="{l} + {r}",
|
|
48
|
+
str_len="int64(len({x}))",
|
|
49
|
+
str_index="int64({x}[{i}])",
|
|
50
|
+
str_slice="{x}[{start}:{end}]",
|
|
51
|
+
str_slice_start="{x}[{start}:]",
|
|
52
|
+
str_slice_end="{x}[:{end}]",
|
|
53
|
+
list_concat="append({l}, {r}...)",
|
|
54
|
+
condition_parens=False,
|
|
55
|
+
foreach_template="for _, {var} := range {iter}",
|
|
56
|
+
try_template="// try/except not supported in Go\n// {body}\n// {handler}",
|
|
57
|
+
struct_template="type {name} struct {{\n{fields}\n}}",
|
|
58
|
+
struct_field_template="\t{type} {name}",
|
|
59
|
+
struct_new_template="func {name}_New({params}) {name} {{\n{body}\n}}",
|
|
60
|
+
dict_type="map[string]{V}",
|
|
61
|
+
dict_get="{d}[{k}]",
|
|
62
|
+
dict_set="{d}[{k}] = {v}",
|
|
63
|
+
dict_contains="dictContains({d}, {k})",
|
|
64
|
+
tuple_type="struct {{ a {T}; b {T} }}",
|
|
65
|
+
tuple_get="{t}.{field}",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class GoEmitter(Emitter):
|
|
70
|
+
def _try_stmt(self, node, ind):
|
|
71
|
+
"""Go doesn't have try/catch — emit body directly, comment handler."""
|
|
72
|
+
self.lines.append(f"{ind}// try/except: Go has no exceptions")
|
|
73
|
+
for s in node.body:
|
|
74
|
+
self.stmt(s)
|
|
75
|
+
if node.handlers:
|
|
76
|
+
self.lines.append(f"{ind}// except handler (not emitted in Go):")
|
|
77
|
+
for s in node.handlers[0].body:
|
|
78
|
+
save = self.lines
|
|
79
|
+
self.lines = []
|
|
80
|
+
self.stmt(s)
|
|
81
|
+
for line in self.lines:
|
|
82
|
+
save.append(f"{ind}// {line.strip()}")
|
|
83
|
+
self.lines = save
|
|
84
|
+
if node.finalbody:
|
|
85
|
+
for s in node.finalbody:
|
|
86
|
+
self.stmt(s)
|
|
87
|
+
|
|
88
|
+
def _is_mutated_list_param(self, name: str) -> bool:
|
|
89
|
+
"""Check if a name is a mutated list parameter."""
|
|
90
|
+
return name in self.mutated_params and self.var_types.get(name) == "list"
|
|
91
|
+
|
|
92
|
+
def expr(self, node: ast.AST) -> str: # type: ignore[override]
|
|
93
|
+
# Go: dereference mutated list params when used as append target
|
|
94
|
+
if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute):
|
|
95
|
+
if node.func.attr == "append" and isinstance(node.func.value, ast.Name):
|
|
96
|
+
base_name = node.func.value.id
|
|
97
|
+
if self._is_mutated_list_param(base_name):
|
|
98
|
+
val = self.expr(node.args[0]) if node.args else ""
|
|
99
|
+
return f"*{base_name} = append(*{base_name}, {val})"
|
|
100
|
+
# Go: pass &var for mutated list params at call sites
|
|
101
|
+
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name):
|
|
102
|
+
fname = node.func.id
|
|
103
|
+
mutated_indices = self.func_mutated_params.get(fname, set())
|
|
104
|
+
if mutated_indices:
|
|
105
|
+
args = []
|
|
106
|
+
for i, a in enumerate(node.args):
|
|
107
|
+
if i in mutated_indices and isinstance(a, ast.Name):
|
|
108
|
+
args.append(f"&{self.expr(a)}")
|
|
109
|
+
else:
|
|
110
|
+
args.append(self.expr(a))
|
|
111
|
+
return f"{fname}({', '.join(args)})"
|
|
112
|
+
# Go: dereference mutated list params for subscript access
|
|
113
|
+
if isinstance(node, ast.Subscript) and isinstance(node.value, ast.Name):
|
|
114
|
+
base_name = node.value.id
|
|
115
|
+
if self._is_mutated_list_param(base_name):
|
|
116
|
+
idx = self.expr(node.slice)
|
|
117
|
+
return f"(*{base_name})[{idx}]"
|
|
118
|
+
# Go: dereference mutated list params for len()
|
|
119
|
+
if isinstance(node, ast.Call) and getattr(node.func, "id", None) == "len":
|
|
120
|
+
if node.args and isinstance(node.args[0], ast.Name):
|
|
121
|
+
base_name = node.args[0].id
|
|
122
|
+
if self._is_mutated_list_param(base_name):
|
|
123
|
+
return f"int64(len(*{base_name}))"
|
|
124
|
+
return super().expr(node)
|
|
125
|
+
|
|
126
|
+
def for_loop(self, node, ind): # type: ignore[override]
|
|
127
|
+
var = node.target.id if isinstance(node.target, ast.Name) else "_"
|
|
128
|
+
it = node.iter
|
|
129
|
+
is_tuple_target = isinstance(node.target, ast.Tuple)
|
|
130
|
+
if isinstance(it, ast.Call) and getattr(it.func, "id", None) == "range":
|
|
131
|
+
args = it.args
|
|
132
|
+
if len(args) == 1:
|
|
133
|
+
lo, hi = "int64(0)", self.expr(args[0])
|
|
134
|
+
else:
|
|
135
|
+
lo, hi = "int64(" + self.expr(args[0]) + ")", self.expr(args[1])
|
|
136
|
+
self.var_types[var] = "int64"
|
|
137
|
+
self.lines.append(f"{ind}for {var} := {lo}; {var} < {hi}; {var}++ {{")
|
|
138
|
+
elif isinstance(it, ast.Call) and getattr(it.func, "id", None) == "enumerate" and is_tuple_target:
|
|
139
|
+
# for i, x in enumerate(items):
|
|
140
|
+
target = node.target
|
|
141
|
+
idx_var = target.elts[0].id
|
|
142
|
+
val_var = target.elts[1].id
|
|
143
|
+
inner_it = self.expr(it.args[0])
|
|
144
|
+
self.var_types[idx_var] = "int64"
|
|
145
|
+
self.var_types[val_var] = "int64"
|
|
146
|
+
self.lines.append(f"{ind}for {idx_var}, {val_var} := range {inner_it} {{")
|
|
147
|
+
elif isinstance(it, ast.Call) and getattr(it.func, "id", None) == "zip" and is_tuple_target:
|
|
148
|
+
# for a, b in zip(x, y):
|
|
149
|
+
target = node.target
|
|
150
|
+
var_a = target.elts[0].id
|
|
151
|
+
var_b = target.elts[1].id
|
|
152
|
+
it_a = self.expr(it.args[0])
|
|
153
|
+
it_b = self.expr(it.args[1])
|
|
154
|
+
self.var_types[var_a] = "int64"
|
|
155
|
+
self.var_types[var_b] = "int64"
|
|
156
|
+
self.lines.append(f"{ind}for __i := int64(0); __i < int64(len({it_a})) && __i < int64(len({it_b})); __i++ {{")
|
|
157
|
+
self.lines.append(f"{ind} {var_a} := {it_a}[__i]; {var_b} := {it_b}[__i]")
|
|
158
|
+
else:
|
|
159
|
+
iter_s = self.expr(it)
|
|
160
|
+
self.var_types[var] = "long"
|
|
161
|
+
self.lines.append(f"{ind}for _, {var} := range {iter_s} {{")
|
|
162
|
+
self.indent_lvl += 1
|
|
163
|
+
for s in node.body:
|
|
164
|
+
self.stmt(s)
|
|
165
|
+
self.indent_lvl -= 1
|
|
166
|
+
self.lines.append(f"{ind}}}")
|
|
167
|
+
|
|
168
|
+
def signature(self, unit: FuncUnit) -> str:
|
|
169
|
+
emit_name = unit.name.replace(".", "_")
|
|
170
|
+
params = []
|
|
171
|
+
for pname, ptype in unit.params:
|
|
172
|
+
nt = self.param_native_type(ptype)
|
|
173
|
+
# Go: mutated list params need *[]int64 (pointer to slice) for append to propagate
|
|
174
|
+
if ptype == "list" and pname in self.mutated_params:
|
|
175
|
+
nt = "*[]int64"
|
|
176
|
+
params.append(f"{pname} {nt}")
|
|
177
|
+
ret = self.py_to_native(unit.ret_type) if unit.ret_type != "None" else ""
|
|
178
|
+
param_str = ", ".join(params)
|
|
179
|
+
if ret:
|
|
180
|
+
return f"func {emit_name}({param_str}) {ret}"
|
|
181
|
+
return f"func {emit_name}({param_str})"
|
|
182
|
+
|
|
183
|
+
def stmt(self, node: ast.AST) -> None:
|
|
184
|
+
# Go uses `var name type = value` for typed declarations
|
|
185
|
+
if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
|
|
186
|
+
name = node.target.id
|
|
187
|
+
ann_type = self._ann_type(node.annotation)
|
|
188
|
+
nt = self.py_to_native(ann_type)
|
|
189
|
+
if node.value is not None:
|
|
190
|
+
val = self.expr(node.value)
|
|
191
|
+
self.lines.append(f"{self.spec.indent * self.indent_lvl}var {name} {nt} = {val}")
|
|
192
|
+
self.var_types[name] = ann_type
|
|
193
|
+
self.declared.add(name)
|
|
194
|
+
else:
|
|
195
|
+
self.lines.append(f"{self.spec.indent * self.indent_lvl}var {name} {nt}")
|
|
196
|
+
self.var_types[name] = ann_type
|
|
197
|
+
self.declared.add(name)
|
|
198
|
+
return
|
|
199
|
+
# Go uses `for` instead of `while`
|
|
200
|
+
if isinstance(node, ast.While):
|
|
201
|
+
ind = self.spec.indent * self.indent_lvl
|
|
202
|
+
self.lines.append(f"{ind}for {self._condition(node.test)} {{")
|
|
203
|
+
self.indent_lvl += 1
|
|
204
|
+
for s in node.body:
|
|
205
|
+
self.stmt(s)
|
|
206
|
+
self.indent_lvl -= 1
|
|
207
|
+
self.lines.append(f"{ind}}}")
|
|
208
|
+
return
|
|
209
|
+
super().stmt(node)
|
|
210
|
+
|
|
211
|
+
def _ann_type(self, node: ast.AST) -> str:
|
|
212
|
+
if isinstance(node, ast.Name):
|
|
213
|
+
t = node.id
|
|
214
|
+
if t in ("int", "float", "bool", "str", "list", "dict", "tuple"):
|
|
215
|
+
return t
|
|
216
|
+
if t in self.class_names:
|
|
217
|
+
return t
|
|
218
|
+
return "any"
|
|
219
|
+
if isinstance(node, ast.Subscript):
|
|
220
|
+
return "list"
|
|
221
|
+
return "any"
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _entry_unit(units: list[FuncUnit], entry: str | None) -> FuncUnit:
|
|
225
|
+
if entry:
|
|
226
|
+
for u in units:
|
|
227
|
+
if u.name == entry:
|
|
228
|
+
return u
|
|
229
|
+
for u in units:
|
|
230
|
+
if u.name == "main":
|
|
231
|
+
return u
|
|
232
|
+
return units[0]
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _emit_structs_go(classes: list) -> str:
|
|
236
|
+
"""Emit Go struct definitions and constructors for ClassUnits."""
|
|
237
|
+
out: list[str] = []
|
|
238
|
+
for cls in classes:
|
|
239
|
+
fields_str = ""
|
|
240
|
+
for fname, ftype in cls.fields:
|
|
241
|
+
nt = SPEC.types.get(ftype, SPEC.types.get("float"))
|
|
242
|
+
if ftype == "list":
|
|
243
|
+
nt = "[]int64"
|
|
244
|
+
elif ftype == "None" or not nt:
|
|
245
|
+
nt = "int64"
|
|
246
|
+
elif ftype not in SPEC.types and ftype not in ("int", "float", "bool", "str"):
|
|
247
|
+
nt = ftype # class type
|
|
248
|
+
# Go puts field name before type
|
|
249
|
+
fields_str += f"\t{fname} {nt}\n"
|
|
250
|
+
out.append(f"type {cls.name} struct {{\n{fields_str}}}\n")
|
|
251
|
+
if cls.constructor_params:
|
|
252
|
+
params_str = ", ".join(
|
|
253
|
+
f"{pname} {SPEC.types.get(ptype, 'float64')}"
|
|
254
|
+
for pname, ptype in cls.constructor_params
|
|
255
|
+
)
|
|
256
|
+
init_lines = "\n".join(
|
|
257
|
+
f" {fname}: {fname}," for fname, _ in cls.constructor_params
|
|
258
|
+
)
|
|
259
|
+
out.append(f"func {cls.name}_new({params_str}) {cls.name} {{\n"
|
|
260
|
+
f" return {cls.name}{{\n{init_lines}\n }}\n}}\n")
|
|
261
|
+
else:
|
|
262
|
+
out.append(f"func {cls.name}_new() {cls.name} {{\n"
|
|
263
|
+
f" return {cls.name}{{}}\n}}\n")
|
|
264
|
+
return "\n".join(out)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def emit_go(units: list[FuncUnit], entry: str | None,
|
|
268
|
+
library_mode: bool = False,
|
|
269
|
+
extern_fns: list[FuncUnit] | None = None,
|
|
270
|
+
classes: list | None = None,
|
|
271
|
+
constants: dict | None = None,
|
|
272
|
+
preamble: dict | None = None) -> tuple[str, dict[str, str]]:
|
|
273
|
+
"""Return (full_program_source, {func_name: emitted_source}).
|
|
274
|
+
|
|
275
|
+
In library_mode: emit //export functions for C ABI FFI.
|
|
276
|
+
extern_fns: functions from other backends that this Go code calls.
|
|
277
|
+
"""
|
|
278
|
+
emitter = GoEmitter(SPEC)
|
|
279
|
+
emitter.library_mode = library_mode
|
|
280
|
+
emitter.constants = constants or {}
|
|
281
|
+
emitted: dict[str, str] = {}
|
|
282
|
+
fns: list[str] = []
|
|
283
|
+
|
|
284
|
+
# register class names
|
|
285
|
+
if classes:
|
|
286
|
+
for cls in classes:
|
|
287
|
+
emitter.class_names.add(cls.name)
|
|
288
|
+
emitter.class_fields[cls.name] = cls.fields
|
|
289
|
+
emitter.class_bases[cls.name] = cls.bases
|
|
290
|
+
emitter.class_properties[cls.name] = cls.properties
|
|
291
|
+
emitter.class_static_methods[cls.name] = cls.static_methods
|
|
292
|
+
|
|
293
|
+
prelude = (
|
|
294
|
+
"package main\n\n"
|
|
295
|
+
'import "fmt"\n'
|
|
296
|
+
'import "strings"\n'
|
|
297
|
+
'import "sort"\n'
|
|
298
|
+
'import "os"\n'
|
|
299
|
+
'import "math"\n'
|
|
300
|
+
)
|
|
301
|
+
# add dictContains helper for dict membership tests
|
|
302
|
+
prelude += "\nfunc dictContains(m map[string]int64, k string) bool {\n _, ok := m[k]\n return ok\n}\n"
|
|
303
|
+
# add strContains helper for string membership tests (uses strings import)
|
|
304
|
+
prelude += 'func strContains(s, sub string) bool {\n return strings.Contains(s, sub)\n}\n'
|
|
305
|
+
prelude += 'var _ = strContains\n'
|
|
306
|
+
prelude += 'var _ = sort.Slice\n'
|
|
307
|
+
prelude += 'var _ = os.ReadFile\n'
|
|
308
|
+
prelude += 'var _ = math.Sqrt\n\n'
|
|
309
|
+
# only add import "C" in library mode (cgo needed for //export)
|
|
310
|
+
if library_mode:
|
|
311
|
+
prelude += 'import "C"\n\n'
|
|
312
|
+
else:
|
|
313
|
+
prelude += "\n"
|
|
314
|
+
|
|
315
|
+
# emit extern declarations for cross-backend calls
|
|
316
|
+
if extern_fns:
|
|
317
|
+
decls = []
|
|
318
|
+
for u in extern_fns:
|
|
319
|
+
if not u.supported:
|
|
320
|
+
continue
|
|
321
|
+
ret = SPEC.types.get(u.ret_type, "") if u.ret_type != "None" else ""
|
|
322
|
+
decls.append(f"// {u.name} — from other backend (linked at runtime)")
|
|
323
|
+
prelude += "\n// cross-backend declarations\n" + "\n".join(decls) + "\n\n"
|
|
324
|
+
|
|
325
|
+
# emit struct definitions
|
|
326
|
+
if classes:
|
|
327
|
+
struct_code = _emit_structs_go(classes)
|
|
328
|
+
if struct_code:
|
|
329
|
+
prelude += struct_code + "\n"
|
|
330
|
+
|
|
331
|
+
# build function return type lookup for type inference
|
|
332
|
+
for u in units:
|
|
333
|
+
if u.supported:
|
|
334
|
+
emitter.func_return_types[u.name] = u.ret_type
|
|
335
|
+
|
|
336
|
+
if library_mode:
|
|
337
|
+
for u in units:
|
|
338
|
+
if not u.supported:
|
|
339
|
+
continue
|
|
340
|
+
if u.is_method and u.name.endswith(".__init__"):
|
|
341
|
+
continue
|
|
342
|
+
code = emitter.emit(u)
|
|
343
|
+
if emitter.unsupported_emissions:
|
|
344
|
+
u.supported = False
|
|
345
|
+
u.unsupported_reasons.extend(emitter.unsupported_emissions)
|
|
346
|
+
continue
|
|
347
|
+
# prefix with //export for C ABI
|
|
348
|
+
emit_name = u.name.replace(".", "_")
|
|
349
|
+
code = code.replace(f"func {emit_name}(", f"//export {emit_name}\nfunc {emit_name}(")
|
|
350
|
+
emitted[u.name] = code
|
|
351
|
+
fns.append(code)
|
|
352
|
+
return prelude + "\n".join(fns) + "\n", emitted
|
|
353
|
+
|
|
354
|
+
entry_u = _entry_unit(units, entry)
|
|
355
|
+
for u in units:
|
|
356
|
+
if u.is_method and u.name.endswith(".__init__"):
|
|
357
|
+
continue
|
|
358
|
+
code = emitter.emit(u)
|
|
359
|
+
if emitter.unsupported_emissions:
|
|
360
|
+
u.supported = False
|
|
361
|
+
u.unsupported_reasons.extend(emitter.unsupported_emissions)
|
|
362
|
+
continue
|
|
363
|
+
emitted[u.name] = code
|
|
364
|
+
fns.append(code)
|
|
365
|
+
|
|
366
|
+
if entry_u.name != "main":
|
|
367
|
+
if entry_u.ret_type == "None":
|
|
368
|
+
wrapper = "func main() {\n\t" + entry_u.name + "()\n}\n"
|
|
369
|
+
else:
|
|
370
|
+
wrapper = f"func main() {{\n\t_ = {entry_u.name}()\n}}\n"
|
|
371
|
+
fns.append(wrapper)
|
|
372
|
+
else:
|
|
373
|
+
# Go main() must have no arguments and no return values
|
|
374
|
+
# Rename the user's main() to __ge_main() and add a wrapper
|
|
375
|
+
if entry_u.ret_type != "None":
|
|
376
|
+
fns[-1] = fns[-1].replace(f"func {entry_u.name}(", "func __ge_main(")
|
|
377
|
+
wrapper = "func main() {\n\t__ge_main()\n}\n"
|
|
378
|
+
fns.append(wrapper)
|
|
379
|
+
else:
|
|
380
|
+
fns[-1] = fns[-1].replace(f"func {entry_u.name}(", "func main(")
|
|
381
|
+
|
|
382
|
+
program = prelude + "\n".join(fns) + "\n"
|
|
383
|
+
# inject stdlib runtime (after package/imports, before functions)
|
|
384
|
+
from ..stdlib import get_runtime
|
|
385
|
+
runtime = get_runtime("go")
|
|
386
|
+
if runtime:
|
|
387
|
+
program = prelude + runtime + "\n" + "\n".join(fns) + "\n"
|
|
388
|
+
return program, emitted
|