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.
@@ -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<long>",
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.var_types[var] = "long"
112
- self.lines.append(f"{ind}foreach (var {var} in {iter_s}) {{")
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="[]int64",
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
- int_cast="int64({x})",
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="abs({x})",
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.var_types[var] = "long"
161
- self.lines.append(f"{ind}for _, {var} := range {iter_s} {{")
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", "tuple"):
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<Long>",
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="Math.pow({l}.toDouble(), {r}.toDouble()).toLong()",
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
- dict_get="{d}[{k}]",
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.var_types[var] = "long"
150
- self.lines.append(f"{ind}for ({var} in {iter_s}) {{")
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}.abs()",
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
- str_concat="{l} + &{r}",
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
- foreach_template="for {var} in {iter}.iter()",
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 {}