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.
@@ -156,6 +156,64 @@ fn ge_exp(x: f64) -> f64 { x.exp() }
156
156
  ''',
157
157
  "cpp": '''
158
158
  // GE Math runtime (C++)
159
+ std::string geStrUpper(std::string s) {
160
+ for (auto& c : s) c = (char)std::toupper((unsigned char)c);
161
+ return s;
162
+ }
163
+
164
+ std::string geStrLower(std::string s) {
165
+ for (auto& c : s) c = (char)std::tolower((unsigned char)c);
166
+ return s;
167
+ }
168
+
169
+ std::string geStrReplace(std::string s, const std::string& from,
170
+ const std::string& to) {
171
+ if (from.empty()) return s;
172
+ size_t pos = 0;
173
+ while ((pos = s.find(from, pos)) != std::string::npos) {
174
+ s.replace(pos, from.size(), to);
175
+ pos += to.size();
176
+ }
177
+ return s;
178
+ }
179
+
180
+ std::vector<std::string> geStrSplit(const std::string& s, const std::string& sep) {
181
+ std::vector<std::string> out;
182
+ if (sep.empty()) { out.push_back(s); return out; }
183
+ size_t pos = 0, found;
184
+ while ((found = s.find(sep, pos)) != std::string::npos) {
185
+ out.push_back(s.substr(pos, found - pos));
186
+ pos = found + sep.size();
187
+ }
188
+ out.push_back(s.substr(pos));
189
+ return out;
190
+ }
191
+
192
+ std::string geStrJoin(const std::vector<std::string>& v, const std::string& sep) {
193
+ std::string out;
194
+ for (size_t i = 0; i < v.size(); ++i) {
195
+ if (i) out += sep;
196
+ out += v[i];
197
+ }
198
+ return out;
199
+ }
200
+
201
+ std::vector<std::string> geDictKeys(const std::map<std::string, int64_t>& d) {
202
+ std::vector<std::string> ks;
203
+ ks.reserve(d.size());
204
+ for (const auto& kv : d) ks.push_back(kv.first);
205
+ return ks;
206
+ }
207
+
208
+ std::string geListStr(const std::vector<int64_t>& v) {
209
+ std::string s = "[";
210
+ for (size_t i = 0; i < v.size(); ++i) {
211
+ if (i) s += ", ";
212
+ s += std::to_string(v[i]);
213
+ }
214
+ return s + "]";
215
+ }
216
+
159
217
  double ge_sqrt(double x) { return std::sqrt(x); }
160
218
  double ge_floor(double x) { return std::floor(x); }
161
219
  double ge_ceil(double x) { return std::ceil(x); }
@@ -180,6 +238,42 @@ static double GeExp(double x) { return System.Math.Exp(x); }
180
238
  ''',
181
239
  "go": '''
182
240
  // GE Math runtime (Go)
241
+ func geAbsInt(x int64) int64 {
242
+ if x < 0 {
243
+ return -x
244
+ }
245
+ return x
246
+ }
247
+
248
+ func geMin2(a, b int64) int64 {
249
+ if a < b {
250
+ return a
251
+ }
252
+ return b
253
+ }
254
+
255
+ func geMax2(a, b int64) int64 {
256
+ if a > b {
257
+ return a
258
+ }
259
+ return b
260
+ }
261
+
262
+ func geListStr(v []int64) string {
263
+ parts := make([]string, len(v))
264
+ for i, x := range v {
265
+ parts[i] = strconv.FormatInt(x, 10)
266
+ }
267
+ return "[" + strings.Join(parts, ", ") + "]"
268
+ }
269
+
270
+ func geBoolStr(b bool) string {
271
+ if b {
272
+ return "True"
273
+ }
274
+ return "False"
275
+ }
276
+
183
277
  func geSqrt(x float64) float64 { return math.Sqrt(x) }
184
278
  func geFloor(x float64) float64 { return math.Floor(x) }
185
279
  func geCeil(x float64) float64 { return math.Ceil(x) }
@@ -204,7 +298,125 @@ fun ge_log(x: Double): Double = ln(x)
204
298
  fun ge_exp(x: Double): Double = exp(x)
205
299
  ''',
206
300
  "zig": '''
207
- // GE Math runtime (Zig)
301
+ // GE runtime (Zig)
302
+ //
303
+ // GE lists are growable, so they need an allocator. A generated program
304
+ // runs start-to-finish, so a single arena is the right fit: it amortises
305
+ // the page-grain cost across many small lists and everything is released
306
+ // in one call. (std.heap.page_allocator alone would issue a syscall per
307
+ // allocation, and GeneralPurposeAllocator is a debugging allocator.)
308
+ var ge_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
309
+ const ge_alloc = ge_arena.allocator();
310
+
311
+ fn geListNew(comptime T: type) std.ArrayList(T) {
312
+ return std.ArrayList(T).init(ge_alloc);
313
+ }
314
+
315
+ fn geListFrom(comptime T: type, items: []const T) std.ArrayList(T) {
316
+ var l = std.ArrayList(T).init(ge_alloc);
317
+ l.appendSlice(items) catch unreachable;
318
+ return l;
319
+ }
320
+
321
+ fn geListCopy(comptime T: type, src: []const T) std.ArrayList(T) {
322
+ return geListFrom(T, src);
323
+ }
324
+
325
+ fn geListSlice(comptime T: type, src: []const T, lo: usize, hi: usize) std.ArrayList(T) {
326
+ var l = std.ArrayList(T).init(ge_alloc);
327
+ l.appendSlice(src[lo..hi]) catch unreachable;
328
+ return l;
329
+ }
330
+
331
+ fn geListConcat(comptime T: type, a: []const T, b: []const T) std.ArrayList(T) {
332
+ var l = std.ArrayList(T).init(ge_alloc);
333
+ l.appendSlice(a) catch unreachable;
334
+ l.appendSlice(b) catch unreachable;
335
+ return l;
336
+ }
337
+
338
+ // Python renders a sequence as [1, 2, 3] and quotes strings with '.
339
+ fn gePrintList(v: anytype) void {
340
+ const w = std.io.getStdOut().writer();
341
+ w.print("[", .{}) catch unreachable;
342
+ for (v, 0..) |x, i| {
343
+ if (i > 0) w.print(", ", .{}) catch unreachable;
344
+ switch (@typeInfo(@TypeOf(x))) {
345
+ .pointer => w.print("'{s}'", .{x}) catch unreachable,
346
+ .float => w.print("{d}", .{x}) catch unreachable,
347
+ .bool => w.print("{s}", .{if (x) "True" else "False"}) catch unreachable,
348
+ else => w.print("{d}", .{x}) catch unreachable,
349
+ }
350
+ }
351
+ w.print("]\\n", .{}) catch unreachable;
352
+ }
353
+
354
+ fn geSetNew(comptime T: type) std.AutoHashMap(T, void) {
355
+ return std.AutoHashMap(T, void).init(ge_alloc);
356
+ }
357
+
358
+ fn geSetFrom(comptime T: type, items: []const T) std.AutoHashMap(T, void) {
359
+ var s = std.AutoHashMap(T, void).init(ge_alloc);
360
+ for (items) |x| s.put(x, {{}}) catch unreachable;
361
+ return s;
362
+ }
363
+ fn geStrSplit(s: []const u8, sep: []const u8) std.ArrayList([]const u8) {
364
+ var l = std.ArrayList([]const u8).init(ge_alloc);
365
+ if (sep.len == 0) {
366
+ l.append(s) catch unreachable;
367
+ return l;
368
+ }
369
+ var it = std.mem.splitSequence(u8, s, sep);
370
+ while (it.next()) |part| l.append(part) catch unreachable;
371
+ return l;
372
+ }
373
+
374
+ fn geStrJoin(parts: []const []const u8, sep: []const u8) []const u8 {
375
+ var total: usize = 0;
376
+ for (parts, 0..) |p, i| {
377
+ total += p.len;
378
+ if (i > 0) total += sep.len;
379
+ }
380
+ const buf = ge_alloc.alloc(u8, total) catch unreachable;
381
+ var pos: usize = 0;
382
+ for (parts, 0..) |p, i| {
383
+ if (i > 0) {
384
+ @memcpy(buf[pos..][0..sep.len], sep);
385
+ pos += sep.len;
386
+ }
387
+ @memcpy(buf[pos..][0..p.len], p);
388
+ pos += p.len;
389
+ }
390
+ return buf;
391
+ }
392
+
393
+ fn geStrReplace(s: []const u8, from: []const u8, to: []const u8) []const u8 {
394
+ if (from.len == 0) return s;
395
+ var out = std.ArrayList(u8).init(ge_alloc);
396
+ var i: usize = 0;
397
+ while (i < s.len) {
398
+ if (i + from.len <= s.len and std.mem.eql(u8, s[i..][0..from.len], from)) {
399
+ out.appendSlice(to) catch unreachable;
400
+ i += from.len;
401
+ } else {
402
+ out.append(s[i]) catch unreachable;
403
+ i += 1;
404
+ }
405
+ }
406
+ return out.items;
407
+ }
408
+
409
+ fn geStrUpper(s: []const u8) []const u8 {
410
+ const buf = ge_alloc.alloc(u8, s.len) catch unreachable;
411
+ for (s, 0..) |c, i| buf[i] = std.ascii.toUpper(c);
412
+ return buf;
413
+ }
414
+
415
+ fn geStrLower(s: []const u8) []const u8 {
416
+ const buf = ge_alloc.alloc(u8, s.len) catch unreachable;
417
+ for (s, 0..) |c, i| buf[i] = std.ascii.toLower(c);
418
+ return buf;
419
+ }
208
420
  fn ge_sqrt(x: f64) f64 { return @sqrt(x); }
209
421
  fn ge_floor(x: f64) f64 { return @floor(x); }
210
422
  fn ge_ceil(x: f64) f64 { return @ceil(x); }
@@ -34,6 +34,8 @@ class TypeChecker:
34
34
  self.file = file
35
35
  self.errors = ErrorReporter()
36
36
  self.func_sigs: dict[str, tuple[list[str], str]] = {} # name -> (param_types, ret_type)
37
+
38
+ self.func_required: dict[str, int] = {}
37
39
  self.func_names: set[str] = set()
38
40
  self.class_names: set[str] = set()
39
41
 
@@ -49,6 +51,8 @@ class TypeChecker:
49
51
  if u.supported and u.body:
50
52
  param_types = [t for _, t in u.params]
51
53
  self.func_sigs[u.name] = (param_types, u.ret_type)
54
+ self.func_required[u.name] = getattr(
55
+ u, "n_required_params", len(param_types)) or len(param_types)
52
56
  self.func_names.add(u.name)
53
57
  # collect class names
54
58
  if classes:
@@ -97,16 +101,25 @@ class TypeChecker:
97
101
  # check if calling a known function with wrong number of args
98
102
  if fname in self.func_sigs:
99
103
  param_types, _ = self.func_sigs[fname]
100
- if len(node.args) != len(param_types):
104
+ n_req = self.func_required.get(fname, len(param_types))
105
+ n_given = len(node.args)
106
+ if n_given < n_req or n_given > len(param_types):
107
+ if n_req == len(param_types):
108
+ expect = f"{len(param_types)} args"
109
+ else:
110
+ expect = (f"between {n_req} and "
111
+ f"{len(param_types)} args")
101
112
  self.errors.error(
102
113
  "GE002",
103
- f"function '{fname}' expects {len(param_types)} args, "
104
- f"got {len(node.args)}",
114
+ f"function '{fname}' expects {expect}, "
115
+ f"got {n_given}",
105
116
  file=self.file, line=node.lineno,
106
117
  )
107
118
  # check for undefined function calls
108
119
  elif fname not in ("print", "len", "range", "abs", "sum", "min", "max",
109
120
  "float", "int", "str", "bool", "dict", "list", "tuple",
121
+ "round", "sorted", "reversed", "enumerate", "zip",
122
+ "any", "all", "pow", "divmod", "ord", "chr",
110
123
  "ge_inline", "ge_raw", "ge_preamble"):
111
124
  if fname not in self.class_names:
112
125
  self.errors.error(