gelang 0.1.3 → 0.1.5
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 +112 -0
- package/package.json +1 -1
- package/python/pyeffic/analyzer.py +20 -0
- package/python/pyeffic/downloader.py +14 -2
- package/python/pyeffic/emitters/base.py +319 -27
- package/python/pyeffic/emitters/cpp.py +31 -3
- package/python/pyeffic/emitters/csharp.py +37 -3
- package/python/pyeffic/emitters/go.py +46 -7
- package/python/pyeffic/emitters/kotlin.py +34 -6
- package/python/pyeffic/emitters/rust.py +30 -4
- package/python/pyeffic/emitters/zig.py +122 -73
- package/python/pyeffic/ge_cli.py +111 -5
- package/python/pyeffic/stdlib.py +213 -1
- package/python/pyeffic/typecheck.py +16 -3
|
@@ -14,9 +14,18 @@ from ..analyzer import FuncUnit
|
|
|
14
14
|
SPEC = Spec(
|
|
15
15
|
name="csharp",
|
|
16
16
|
types={"int": "long", "float": "double", "bool": "bool", "str": "string", "None": "void"},
|
|
17
|
-
list_type="List<
|
|
17
|
+
list_type="List<{T}>",
|
|
18
18
|
list_param_type="List<long>",
|
|
19
19
|
list_elem_type="long",
|
|
20
|
+
list_sort="{x}.Sort()",
|
|
21
|
+
list_reverse="{x}.Reverse()",
|
|
22
|
+
str_split="{x}.Split({sep}).ToList()",
|
|
23
|
+
str_upper="{x}.ToUpper()",
|
|
24
|
+
str_lower="{x}.ToLower()",
|
|
25
|
+
str_replace="{x}.Replace({a}, {b})",
|
|
26
|
+
str_join="string.Join({sep}, {x})",
|
|
27
|
+
list_slice="{x}.GetRange((int)({start}), (int)(({stop}) - ({start})))",
|
|
28
|
+
list_copy="new List<long>({x})",
|
|
20
29
|
borrow_list_arg=False,
|
|
21
30
|
range_call="({lo}..{hi})",
|
|
22
31
|
range_step_call="({lo}..{hi}).Step({step})",
|
|
@@ -27,6 +36,7 @@ SPEC = Spec(
|
|
|
27
36
|
print_str='Console.WriteLine({v})',
|
|
28
37
|
print_bool='Console.WriteLine({v})',
|
|
29
38
|
print_generic='Console.WriteLine({v})',
|
|
39
|
+
print_list='Console.WriteLine("[" + string.Join(", ", {v}) + "]")',
|
|
30
40
|
int_cast="(long)({x})",
|
|
31
41
|
float_cast="(double)({x})",
|
|
32
42
|
float_div="((double)({l}) / (double)({r}))",
|
|
@@ -34,9 +44,13 @@ SPEC = Spec(
|
|
|
34
44
|
sum_call="{it}.Sum()",
|
|
35
45
|
abs_int="Math.Abs({x})",
|
|
36
46
|
abs_float="Math.Abs({x})",
|
|
47
|
+
min2_call="Math.Min({a}, {b})",
|
|
48
|
+
max2_call="Math.Max({a}, {b})",
|
|
37
49
|
min_call="{it}.Min()",
|
|
38
50
|
max_call="{it}.Max()",
|
|
39
51
|
pow_call="Math.Pow({l}, {r})",
|
|
52
|
+
pow_int="(long)Math.Pow({l}, {r})",
|
|
53
|
+
pow_float="Math.Pow({l}, {r})",
|
|
40
54
|
append_call="{x}.Add({v})",
|
|
41
55
|
index_call="{x}[(int)({i})]",
|
|
42
56
|
comment="//",
|
|
@@ -57,8 +71,10 @@ SPEC = Spec(
|
|
|
57
71
|
struct_field_template=" public {type} {name};",
|
|
58
72
|
struct_new_template="static {name} {name}_New({params}) {{\n{body}\n}}",
|
|
59
73
|
dict_type="Dictionary<string, {V}>",
|
|
74
|
+
set_type="HashSet<long>",
|
|
60
75
|
dict_get="{d}[{k}]",
|
|
61
76
|
dict_set="{d}[{k}] = {v}",
|
|
77
|
+
dict_keys="{x}.Keys",
|
|
62
78
|
dict_contains="{d}.ContainsKey({k})",
|
|
63
79
|
tuple_type="({T}, {T})",
|
|
64
80
|
tuple_get="{t}.Item{i1}",
|
|
@@ -108,8 +124,12 @@ class CSharpEmitter(Emitter):
|
|
|
108
124
|
self.lines.append(f"{ind}for (long {var} = {lo}; {var} < {hi}; {var}++) {{")
|
|
109
125
|
else:
|
|
110
126
|
iter_s = self.expr(it)
|
|
111
|
-
self.
|
|
112
|
-
|
|
127
|
+
if self.infer_type(it) == "dict":
|
|
128
|
+
self.var_types[var] = "str"
|
|
129
|
+
self.lines.append(f"{ind}foreach (var {var} in {iter_s}.Keys) {{")
|
|
130
|
+
else:
|
|
131
|
+
self.var_types[var] = "long"
|
|
132
|
+
self.lines.append(f"{ind}foreach (var {var} in {iter_s}) {{")
|
|
113
133
|
self.indent_lvl += 1
|
|
114
134
|
for s in node.body:
|
|
115
135
|
self.stmt(s)
|
|
@@ -133,9 +153,15 @@ class CSharpEmitter(Emitter):
|
|
|
133
153
|
def stmt(self, node): # type: ignore[override]
|
|
134
154
|
# escape C# keywords in variable declarations
|
|
135
155
|
if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
|
|
156
|
+
# bare `list` gets its element type from the value
|
|
157
|
+
_ann = self._ann_type(node.annotation)
|
|
158
|
+
_elem = (self._list_elem_of_value(node.value)
|
|
159
|
+
if (_ann == "list" and node.value is not None) else "")
|
|
136
160
|
name = self._esc(node.target.id)
|
|
137
161
|
ann_type = self._ann_type(node.annotation)
|
|
138
162
|
nt = self.py_to_native(ann_type)
|
|
163
|
+
if _elem and _elem != self.spec.list_elem_type:
|
|
164
|
+
nt = self.spec.list_type.format(T=_elem)
|
|
139
165
|
if node.value is not None:
|
|
140
166
|
val = self.expr(node.value)
|
|
141
167
|
self.lines.append(f"{self.spec.indent * self.indent_lvl}{nt} {name} = {val};")
|
|
@@ -227,6 +253,11 @@ def emit_csharp(units: list[FuncUnit], entry: str | None,
|
|
|
227
253
|
extern_fns: functions from other backends that this C# code calls.
|
|
228
254
|
"""
|
|
229
255
|
emitter = CSharpEmitter(SPEC)
|
|
256
|
+
emitter.func_signatures = {
|
|
257
|
+
u.name: ([p for p, _t in u.params],
|
|
258
|
+
dict(getattr(u, 'param_defaults', {})))
|
|
259
|
+
for u in units
|
|
260
|
+
}
|
|
230
261
|
emitter.library_mode = library_mode
|
|
231
262
|
emitter.constants = constants or {}
|
|
232
263
|
emitted: dict[str, str] = {}
|
|
@@ -317,6 +348,9 @@ def emit_csharp(units: list[FuncUnit], entry: str | None,
|
|
|
317
348
|
for line in fn.split("\n"):
|
|
318
349
|
indented_fns.append(" " + line)
|
|
319
350
|
indented_fns.append(wrapper.rstrip())
|
|
351
|
+
elif not fns:
|
|
352
|
+
# entry rejected during emission; see the pipeline diagnostic
|
|
353
|
+
return prelude, emitted
|
|
320
354
|
else:
|
|
321
355
|
# rename main to Main for C# entry point
|
|
322
356
|
# C# Main must return void or int (32-bit), not long
|
|
@@ -13,9 +13,18 @@ from ..analyzer import FuncUnit
|
|
|
13
13
|
SPEC = Spec(
|
|
14
14
|
name="go",
|
|
15
15
|
types={"int": "int64", "float": "float64", "bool": "bool", "str": "string", "None": ""},
|
|
16
|
-
list_type="[]
|
|
16
|
+
list_type="[]{T}",
|
|
17
17
|
list_param_type="[]int64",
|
|
18
18
|
list_elem_type="int64",
|
|
19
|
+
list_sort="sort.Slice({x}, func(i, j int) bool {{ return {x}[i] < {x}[j] }})",
|
|
20
|
+
list_reverse="sort.Slice({x}, func(i, j int) bool {{ return i > j }})",
|
|
21
|
+
str_split="strings.Split({x}, {sep})",
|
|
22
|
+
str_upper="strings.ToUpper({x})",
|
|
23
|
+
str_lower="strings.ToLower({x})",
|
|
24
|
+
str_replace="strings.ReplaceAll({x}, {a}, {b})",
|
|
25
|
+
str_join="strings.Join({x}, {sep})",
|
|
26
|
+
list_slice="append([]int64(nil), {x}[{start}:{stop}]...)",
|
|
27
|
+
list_copy="append([]int64(nil), {x}...)",
|
|
19
28
|
borrow_list_arg=False,
|
|
20
29
|
range_call="({lo}..{hi})",
|
|
21
30
|
range_step_call="({lo}..{hi})",
|
|
@@ -24,18 +33,25 @@ SPEC = Spec(
|
|
|
24
33
|
print_int='fmt.Println({v})',
|
|
25
34
|
print_float='fmt.Println({v})',
|
|
26
35
|
print_str='fmt.Println({v})',
|
|
27
|
-
print_bool='fmt.Println({v})',
|
|
36
|
+
print_bool='fmt.Println(geBoolStr({v}))',
|
|
28
37
|
print_generic='fmt.Println({v})',
|
|
29
|
-
|
|
38
|
+
print_list='fmt.Println(geListStr({v}))',
|
|
39
|
+
# Go rejects int64(3.9) for an untyped constant; math.Trunc works
|
|
40
|
+
# for both constants and variables and matches Python int().
|
|
41
|
+
int_cast="int64(math.Trunc({x}))",
|
|
30
42
|
float_cast="float64({x})",
|
|
31
43
|
float_div="(float64({l}) / float64({r}))",
|
|
32
44
|
floor_div="({l} / {r})",
|
|
33
45
|
sum_call="func() int64 {{ var s int64; for _, v := range {it} {{ s += v }}; return s }}()",
|
|
34
|
-
abs_int="
|
|
46
|
+
abs_int="geAbsInt({x})",
|
|
35
47
|
abs_float="math.Abs({x})",
|
|
48
|
+
min2_call="geMin2({a}, {b})",
|
|
49
|
+
max2_call="geMax2({a}, {b})",
|
|
36
50
|
min_call="func() int64 {{ var m int64; for i, v := range {it} {{ if i == 0 || v < m {{ m = v }} }}; return m }}()",
|
|
37
51
|
max_call="func() int64 {{ var m int64; for i, v := range {it} {{ if i == 0 || v > m {{ m = v }} }}; return m }}()",
|
|
38
52
|
pow_call="int64(math.Pow(float64({l}), float64({r})))",
|
|
53
|
+
pow_int="int64(math.Pow(float64({l}), float64({r})))",
|
|
54
|
+
pow_float="math.Pow({l}, {r})",
|
|
39
55
|
append_call="{x} = append({x}, {v})",
|
|
40
56
|
index_call="{x}[{i}]",
|
|
41
57
|
comment="//",
|
|
@@ -58,8 +74,11 @@ SPEC = Spec(
|
|
|
58
74
|
struct_field_template="\t{type} {name}",
|
|
59
75
|
struct_new_template="func {name}_New({params}) {name} {{\n{body}\n}}",
|
|
60
76
|
dict_type="map[string]{V}",
|
|
77
|
+
set_type="map[int64]bool",
|
|
61
78
|
dict_get="{d}[{k}]",
|
|
62
79
|
dict_set="{d}[{k}] = {v}",
|
|
80
|
+
dict_keys="{x}",
|
|
81
|
+
foreach_dict_template="for {var} := range {iter}",
|
|
63
82
|
dict_contains="dictContains({d}, {k})",
|
|
64
83
|
tuple_type="struct {{ a {T}; b {T} }}",
|
|
65
84
|
tuple_get="{t}.{field}",
|
|
@@ -157,8 +176,13 @@ class GoEmitter(Emitter):
|
|
|
157
176
|
self.lines.append(f"{ind} {var_a} := {it_a}[__i]; {var_b} := {it_b}[__i]")
|
|
158
177
|
else:
|
|
159
178
|
iter_s = self.expr(it)
|
|
160
|
-
self.
|
|
161
|
-
|
|
179
|
+
if self.infer_type(it) == "dict":
|
|
180
|
+
# range over a map yields keys
|
|
181
|
+
self.var_types[var] = "str"
|
|
182
|
+
self.lines.append(f"{ind}for {var} := range {iter_s} {{")
|
|
183
|
+
else:
|
|
184
|
+
self.var_types[var] = "long"
|
|
185
|
+
self.lines.append(f"{ind}for _, {var} := range {iter_s} {{")
|
|
162
186
|
self.indent_lvl += 1
|
|
163
187
|
for s in node.body:
|
|
164
188
|
self.stmt(s)
|
|
@@ -186,6 +210,10 @@ class GoEmitter(Emitter):
|
|
|
186
210
|
name = node.target.id
|
|
187
211
|
ann_type = self._ann_type(node.annotation)
|
|
188
212
|
nt = self.py_to_native(ann_type)
|
|
213
|
+
if ann_type == "list" and node.value is not None:
|
|
214
|
+
elem = self._list_elem_of_value(node.value)
|
|
215
|
+
if elem and elem != self.spec.list_elem_type:
|
|
216
|
+
nt = self.spec.list_type.format(T=elem)
|
|
189
217
|
if node.value is not None:
|
|
190
218
|
val = self.expr(node.value)
|
|
191
219
|
self.lines.append(f"{self.spec.indent * self.indent_lvl}var {name} {nt} = {val}")
|
|
@@ -211,7 +239,8 @@ class GoEmitter(Emitter):
|
|
|
211
239
|
def _ann_type(self, node: ast.AST) -> str:
|
|
212
240
|
if isinstance(node, ast.Name):
|
|
213
241
|
t = node.id
|
|
214
|
-
if t in ("int", "float", "bool", "str", "list", "dict",
|
|
242
|
+
if t in ("int", "float", "bool", "str", "list", "dict",
|
|
243
|
+
"tuple", "set"):
|
|
215
244
|
return t
|
|
216
245
|
if t in self.class_names:
|
|
217
246
|
return t
|
|
@@ -276,6 +305,11 @@ def emit_go(units: list[FuncUnit], entry: str | None,
|
|
|
276
305
|
extern_fns: functions from other backends that this Go code calls.
|
|
277
306
|
"""
|
|
278
307
|
emitter = GoEmitter(SPEC)
|
|
308
|
+
emitter.func_signatures = {
|
|
309
|
+
u.name: ([p for p, _t in u.params],
|
|
310
|
+
dict(getattr(u, 'param_defaults', {})))
|
|
311
|
+
for u in units
|
|
312
|
+
}
|
|
279
313
|
emitter.library_mode = library_mode
|
|
280
314
|
emitter.constants = constants or {}
|
|
281
315
|
emitted: dict[str, str] = {}
|
|
@@ -294,6 +328,7 @@ def emit_go(units: list[FuncUnit], entry: str | None,
|
|
|
294
328
|
"package main\n\n"
|
|
295
329
|
'import "fmt"\n'
|
|
296
330
|
'import "strings"\n'
|
|
331
|
+
'import "strconv"\n'
|
|
297
332
|
'import "sort"\n'
|
|
298
333
|
'import "os"\n'
|
|
299
334
|
'import "math"\n'
|
|
@@ -369,6 +404,10 @@ def emit_go(units: list[FuncUnit], entry: str | None,
|
|
|
369
404
|
else:
|
|
370
405
|
wrapper = f"func main() {{\n\t_ = {entry_u.name}()\n}}\n"
|
|
371
406
|
fns.append(wrapper)
|
|
407
|
+
elif not fns:
|
|
408
|
+
# the entry function was rejected during emission; the pipeline
|
|
409
|
+
# already holds the reason, so return what we have
|
|
410
|
+
return prelude + "\n".join(fns), emitted
|
|
372
411
|
else:
|
|
373
412
|
# Go main() must have no arguments and no return values
|
|
374
413
|
# Rename the user's main() to __ge_main() and add a wrapper
|
|
@@ -14,9 +14,18 @@ from ..analyzer import FuncUnit
|
|
|
14
14
|
SPEC = Spec(
|
|
15
15
|
name="kotlin",
|
|
16
16
|
types={"int": "Long", "float": "Double", "bool": "Boolean", "str": "String", "None": "Unit"},
|
|
17
|
-
list_type="MutableList<
|
|
17
|
+
list_type="MutableList<{T}>",
|
|
18
18
|
list_param_type="MutableList<Long>",
|
|
19
19
|
list_elem_type="Long",
|
|
20
|
+
list_sort="{x}.sort()",
|
|
21
|
+
list_reverse="{x}.reverse()",
|
|
22
|
+
str_split="{x}.split({sep}).toMutableList()",
|
|
23
|
+
str_upper="{x}.uppercase()",
|
|
24
|
+
str_lower="{x}.lowercase()",
|
|
25
|
+
str_replace="{x}.replace({a}, {b})",
|
|
26
|
+
str_join="{x}.joinToString({sep})",
|
|
27
|
+
list_slice="{x}.subList(({start}).toInt(), ({stop}).toInt()).toMutableList()",
|
|
28
|
+
list_copy="{x}.toMutableList()",
|
|
20
29
|
borrow_list_arg=False,
|
|
21
30
|
range_call="({lo}..{hi})",
|
|
22
31
|
range_step_call="({lo}..{hi} step {step})",
|
|
@@ -25,8 +34,9 @@ SPEC = Spec(
|
|
|
25
34
|
print_int='println({v})',
|
|
26
35
|
print_float='println({v})',
|
|
27
36
|
print_str='println({v})',
|
|
28
|
-
print_bool='println({v})',
|
|
37
|
+
print_bool='println(if ({v}) "True" else "False")',
|
|
29
38
|
print_generic='println({v})',
|
|
39
|
+
print_list='println({v}.joinToString(", ", "[", "]"))',
|
|
30
40
|
int_cast="{x}.toLong()",
|
|
31
41
|
float_cast="{x}.toDouble()",
|
|
32
42
|
float_div="({l}.toDouble() / {r}.toDouble())",
|
|
@@ -34,9 +44,13 @@ SPEC = Spec(
|
|
|
34
44
|
sum_call="{it}.sum()",
|
|
35
45
|
abs_int="abs({x})",
|
|
36
46
|
abs_float="abs({x})",
|
|
47
|
+
min2_call="minOf({a}, {b})",
|
|
48
|
+
max2_call="maxOf({a}, {b})",
|
|
37
49
|
min_call="{it}.minOrNull()!!",
|
|
38
50
|
max_call="{it}.maxOrNull()!!",
|
|
39
|
-
pow_call="
|
|
51
|
+
pow_call="{l}.toDouble().pow({r}.toDouble()).toLong()",
|
|
52
|
+
pow_int="{l}.toDouble().pow({r}.toDouble()).toLong()",
|
|
53
|
+
pow_float="{l}.pow({r})",
|
|
40
54
|
append_call="{x}.add({v})",
|
|
41
55
|
index_call="{x}[{i}.toInt()]",
|
|
42
56
|
comment="//",
|
|
@@ -58,8 +72,10 @@ SPEC = Spec(
|
|
|
58
72
|
struct_field_template=" val {type}: {name},",
|
|
59
73
|
struct_new_template="fun {name}_new({params}): {name} {{\n{body}\n}}",
|
|
60
74
|
dict_type="HashMap<String, {V}>",
|
|
61
|
-
|
|
75
|
+
set_type="MutableSet<Long>",
|
|
76
|
+
dict_get="{d}[{k}]!!",
|
|
62
77
|
dict_set="{d}[{k}] = {v}",
|
|
78
|
+
dict_keys="{x}.keys",
|
|
63
79
|
dict_contains="{d}.containsKey({k})",
|
|
64
80
|
tuple_type="Pair<{T}, {T}>",
|
|
65
81
|
tuple_get="{t}.{field}",
|
|
@@ -146,8 +162,12 @@ class KotlinEmitter(Emitter):
|
|
|
146
162
|
self.lines.append(f"{ind}for ({var} in {lo} until {hi}) {{")
|
|
147
163
|
else:
|
|
148
164
|
iter_s = self.expr(it)
|
|
149
|
-
self.
|
|
150
|
-
|
|
165
|
+
if self.infer_type(it) == "dict":
|
|
166
|
+
self.var_types[var] = "str"
|
|
167
|
+
self.lines.append(f"{ind}for ({var} in {iter_s}.keys) {{")
|
|
168
|
+
else:
|
|
169
|
+
self.var_types[var] = "long"
|
|
170
|
+
self.lines.append(f"{ind}for ({var} in {iter_s}) {{")
|
|
151
171
|
self.indent_lvl += 1
|
|
152
172
|
for s in node.body:
|
|
153
173
|
self.stmt(s)
|
|
@@ -217,6 +237,11 @@ def emit_kotlin(units: list[FuncUnit], entry: str | None,
|
|
|
217
237
|
extern_fns: functions from other backends that this Kotlin code calls.
|
|
218
238
|
"""
|
|
219
239
|
emitter = KotlinEmitter(SPEC)
|
|
240
|
+
emitter.func_signatures = {
|
|
241
|
+
u.name: ([p for p, _t in u.params],
|
|
242
|
+
dict(getattr(u, 'param_defaults', {})))
|
|
243
|
+
for u in units
|
|
244
|
+
}
|
|
220
245
|
emitter.library_mode = library_mode
|
|
221
246
|
emitter.constants = constants or {}
|
|
222
247
|
emitted: dict[str, str] = {}
|
|
@@ -295,6 +320,9 @@ def emit_kotlin(units: list[FuncUnit], entry: str | None,
|
|
|
295
320
|
else:
|
|
296
321
|
wrapper = f"fun main() {{\n {entry_u.name}()\n}}\n"
|
|
297
322
|
fns.append(wrapper)
|
|
323
|
+
elif not fns:
|
|
324
|
+
# entry rejected during emission; see the pipeline diagnostic
|
|
325
|
+
return prelude + "\n".join(fns), emitted
|
|
298
326
|
else:
|
|
299
327
|
# Kotlin main() must return Unit (void)
|
|
300
328
|
# Rename the user's main() to __ge_main() and add a wrapper
|
|
@@ -12,6 +12,15 @@ SPEC = Spec(
|
|
|
12
12
|
list_type="Vec<{T}>",
|
|
13
13
|
list_param_type="&[i64]",
|
|
14
14
|
list_elem_type="i64",
|
|
15
|
+
list_sort="{x}.sort()",
|
|
16
|
+
list_reverse="{x}.reverse()",
|
|
17
|
+
str_split="{x}.split({sep}.as_str()).map(|s| s.to_string()).collect::<Vec<String>>()",
|
|
18
|
+
str_upper="{x}.to_uppercase()",
|
|
19
|
+
str_lower="{x}.to_lowercase()",
|
|
20
|
+
str_replace="{x}.replace({a}.as_str(), {b}.as_str())",
|
|
21
|
+
str_join="{x}.join({sep}.as_str())",
|
|
22
|
+
list_slice="{x}[({start}) as usize..({stop}) as usize].to_vec()",
|
|
23
|
+
list_copy="{x}.clone()",
|
|
15
24
|
borrow_list_arg=True,
|
|
16
25
|
range_call="({lo}..{hi})",
|
|
17
26
|
range_step_call="({lo}..{hi}).step_by({step} as usize)",
|
|
@@ -20,25 +29,32 @@ SPEC = Spec(
|
|
|
20
29
|
print_int='println!("{{}}", {v})',
|
|
21
30
|
print_float='println!("{{}}", {v})',
|
|
22
31
|
print_str='println!("{{}}", {v})',
|
|
23
|
-
print_bool='println!("{{}}", {v})',
|
|
32
|
+
print_bool='println!("{{}}", if {v} {{ "True" }} else {{ "False" }})',
|
|
24
33
|
print_generic='println!("{{:?}}", {v})',
|
|
34
|
+
print_list='println!("{{:?}}", {v})',
|
|
25
35
|
int_cast="{x} as i64",
|
|
26
36
|
float_cast="{x} as f64",
|
|
27
37
|
float_div="({l} as f64 / {r} as f64)",
|
|
28
38
|
floor_div="({l} / {r})",
|
|
29
39
|
sum_call="{it}.iter().sum::<i64>()",
|
|
30
|
-
abs_int="{x}
|
|
40
|
+
abs_int="i64::abs({x})",
|
|
31
41
|
abs_float="{x}.abs()",
|
|
42
|
+
min2_call="std::cmp::min({a}, {b})",
|
|
43
|
+
max2_call="std::cmp::max({a}, {b})",
|
|
32
44
|
min_call="{it}.iter().min().unwrap()",
|
|
33
45
|
max_call="{it}.iter().max().unwrap()",
|
|
34
46
|
pow_call="{l}.pow({r} as u32)",
|
|
47
|
+
pow_int="i64::pow({l}, {r} as u32)",
|
|
48
|
+
pow_float="f64::powf({l}, {r})",
|
|
35
49
|
append_call="{x}.push({v})",
|
|
36
50
|
index_call="{x}[{i} as usize]",
|
|
37
51
|
comment="//",
|
|
38
52
|
fn_template="{sig} {{\n{body}\n}}",
|
|
39
53
|
main_template="",
|
|
40
54
|
ffi_prefix="#[no_mangle]\npub extern \"C\" ",
|
|
41
|
-
|
|
55
|
+
# `String + &String` is not Add in Rust and `a + b` moves `a`, so a
|
|
56
|
+
# format! is used instead — always correct, and it accepts &str too.
|
|
57
|
+
str_concat="format!(\"{{}}{{}}\", {l}, {r})",
|
|
42
58
|
str_len="{x}.len() as i64",
|
|
43
59
|
str_index="{x}.as_bytes()[{i} as usize] as i64",
|
|
44
60
|
str_slice="{x}[{start}..{end}].to_string()",
|
|
@@ -46,14 +62,19 @@ SPEC = Spec(
|
|
|
46
62
|
str_slice_end="{x}[..{end}].to_string()",
|
|
47
63
|
list_concat="{{ let mut t = {l}; t.extend({r}.iter()); t }}",
|
|
48
64
|
condition_parens=False,
|
|
49
|
-
|
|
65
|
+
# .cloned() so the loop variable is owned: iterating a &[i64]
|
|
66
|
+
# otherwise yields &i64 and `x == 5` does not compile.
|
|
67
|
+
foreach_template="for {var} in {iter}.iter().cloned()",
|
|
50
68
|
try_template="// try/except not natively supported in Rust\n// {body}\n// {handler}",
|
|
51
69
|
struct_template="#[derive(Clone)]\nstruct {name} {{\n{fields}\n}}",
|
|
52
70
|
struct_field_template=" {type}: {name},",
|
|
53
71
|
struct_new_template="fn {name}_new({params}) -> {name} {{\n{body}\n}}",
|
|
54
72
|
dict_type="std::collections::HashMap<String, {V}>",
|
|
73
|
+
set_type="std::collections::HashSet<i64>",
|
|
55
74
|
dict_get="{d}.get(&{k}).copied().unwrap_or(0)",
|
|
56
75
|
dict_set="{d}.insert({k}, {v})",
|
|
76
|
+
dict_keys="{x}.keys().cloned().collect::<Vec<String>>()",
|
|
77
|
+
foreach_dict_template="for {var} in {iter}",
|
|
57
78
|
dict_contains="{d}.contains_key(&{k})",
|
|
58
79
|
tuple_type="({T}, {T})",
|
|
59
80
|
tuple_get="{t}.{i}",
|
|
@@ -188,6 +209,11 @@ def emit_rust(units: list[FuncUnit], entry: str | None,
|
|
|
188
209
|
constants: module-level constants to inline (name -> value).
|
|
189
210
|
"""
|
|
190
211
|
emitter = RustEmitter(SPEC)
|
|
212
|
+
emitter.func_signatures = {
|
|
213
|
+
u.name: ([p for p, _t in u.params],
|
|
214
|
+
dict(getattr(u, 'param_defaults', {})))
|
|
215
|
+
for u in units
|
|
216
|
+
}
|
|
191
217
|
emitter.library_mode = library_mode
|
|
192
218
|
emitter.export_names = set(export_fns) if export_fns else set()
|
|
193
219
|
emitter.constants = constants or {}
|