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
package/python/pyeffic/stdlib.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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 {
|
|
104
|
-
f"got {
|
|
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(
|