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,307 @@
|
|
|
1
|
+
"""GE toolchain downloader — fetches missing compilers from official sources.
|
|
2
|
+
|
|
3
|
+
When a required toolchain (Rust, Go, Zig, etc.) is not found locally, GE can
|
|
4
|
+
download it from official sources and cache it in the GE tools directory.
|
|
5
|
+
|
|
6
|
+
Download URLs (official sources):
|
|
7
|
+
Rust: https://static.rust-lang.org/rustup/dist/{target}/rustup-init[.exe]
|
|
8
|
+
Go: https://go.dev/dl/go{version}.{platform}-{arch}.{zip|tar.gz}
|
|
9
|
+
Zig: https://ziglang.org/download/{version}/zig-{platform}-{arch}-{version}.zip
|
|
10
|
+
C++: Uses MSVC (Visual Studio) or MinGW — not downloadable by GE
|
|
11
|
+
C#: Uses .NET SDK from Microsoft — detected, not downloaded
|
|
12
|
+
Kotlin: Uses kotlinc — detected, not downloaded
|
|
13
|
+
Dart/Flutter: https://storage.googleapis.com/flutter_infra_release/releases/...
|
|
14
|
+
|
|
15
|
+
Cache location: ~/.ge/tools/{name}/
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import platform
|
|
21
|
+
import shutil
|
|
22
|
+
import stat
|
|
23
|
+
import sys
|
|
24
|
+
import tarfile
|
|
25
|
+
import urllib.request
|
|
26
|
+
import zipfile
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
# Official download URLs
|
|
30
|
+
DOWNLOAD_URLS = {
|
|
31
|
+
"rust": {
|
|
32
|
+
"windows-x86_64": "https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe",
|
|
33
|
+
"linux-x86_64": "https://sh.rustup.rs",
|
|
34
|
+
"macos-x86_64": "https://sh.rustup.rs",
|
|
35
|
+
},
|
|
36
|
+
"go": {
|
|
37
|
+
"windows-x86_64": "https://go.dev/dl/go1.23.4.windows-amd64.zip",
|
|
38
|
+
"linux-x86_64": "https://go.dev/dl/go1.23.4.linux-amd64.tar.gz",
|
|
39
|
+
"macos-x86_64": "https://go.dev/dl/go1.23.4.darwin-amd64.tar.gz",
|
|
40
|
+
"macos-aarch64": "https://go.dev/dl/go1.23.4.darwin-arm64.tar.gz",
|
|
41
|
+
},
|
|
42
|
+
"zig": {
|
|
43
|
+
"windows-x86_64": "https://ziglang.org/download/0.13.0/zig-x86_64-windows-0.13.0.zip",
|
|
44
|
+
"linux-x86_64": "https://ziglang.org/download/0.13.0/zig-x86_64-linux-0.13.0.tar.xz",
|
|
45
|
+
"macos-x86_64": "https://ziglang.org/download/0.13.0/zig-x86_64-macos-0.13.0.tar.xz",
|
|
46
|
+
"macos-aarch64": "https://ziglang.org/download/0.13.0/zig-aarch64-macos-0.13.0.tar.xz",
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Toolchain binary names after extraction
|
|
51
|
+
TOOLCHAIN_BINARIES = {
|
|
52
|
+
"rust": "rustc",
|
|
53
|
+
"go": "go",
|
|
54
|
+
"zig": "zig",
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Minimum required versions (kept loose for downloaded toolchains)
|
|
58
|
+
MIN_VERSIONS = {
|
|
59
|
+
"rust": "1.70",
|
|
60
|
+
"go": "1.21",
|
|
61
|
+
"zig": "0.13",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _get_platform_key() -> str:
|
|
66
|
+
"""Return platform key like 'windows-x86_64' or 'linux-x86_64'."""
|
|
67
|
+
os_name = platform.system().lower()
|
|
68
|
+
if os_name == "windows":
|
|
69
|
+
os_name = "windows"
|
|
70
|
+
elif os_name == "darwin":
|
|
71
|
+
os_name = "macos"
|
|
72
|
+
arch = platform.machine().lower()
|
|
73
|
+
if arch in ("x86_64", "amd64"):
|
|
74
|
+
arch = "x86_64"
|
|
75
|
+
elif arch in ("arm64", "aarch64"):
|
|
76
|
+
arch = "aarch64"
|
|
77
|
+
return f"{os_name}-{arch}"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_tools_dir() -> Path:
|
|
81
|
+
"""Get the GE tools cache directory."""
|
|
82
|
+
home = Path.home()
|
|
83
|
+
tools_dir = home / ".ge" / "tools"
|
|
84
|
+
tools_dir.mkdir(parents=True, exist_ok=True)
|
|
85
|
+
return tools_dir
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_toolchain_dir(name: str) -> Path:
|
|
89
|
+
"""Get the directory for a specific toolchain."""
|
|
90
|
+
return get_tools_dir() / name
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def is_toolchain_available(name: str) -> bool:
|
|
94
|
+
"""Check if a toolchain is available (either on PATH or in GE tools cache)."""
|
|
95
|
+
binary = TOOLCHAIN_BINARIES.get(name, name)
|
|
96
|
+
if shutil.which(binary):
|
|
97
|
+
return True
|
|
98
|
+
# Check GE tools cache
|
|
99
|
+
tool_dir = get_toolchain_dir(name)
|
|
100
|
+
if tool_dir.exists():
|
|
101
|
+
# Look for binary in common subdirectory patterns
|
|
102
|
+
for pattern in ["bin", ".", "go/bin"]:
|
|
103
|
+
bin_dir = tool_dir / pattern
|
|
104
|
+
if bin_dir.exists():
|
|
105
|
+
exe_name = binary + (".exe" if platform.system() == "Windows" else "")
|
|
106
|
+
if (bin_dir / exe_name).exists():
|
|
107
|
+
return True
|
|
108
|
+
# Check if any file matching the binary name exists
|
|
109
|
+
for p in tool_dir.rglob(f"{binary}*"):
|
|
110
|
+
if p.is_file():
|
|
111
|
+
return True
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def get_toolchain_path(name: str) -> str | None:
|
|
116
|
+
"""Get the executable path for a toolchain, or None."""
|
|
117
|
+
binary = TOOLCHAIN_BINARIES.get(name, name)
|
|
118
|
+
# Check PATH first
|
|
119
|
+
path = shutil.which(binary)
|
|
120
|
+
if path:
|
|
121
|
+
return path
|
|
122
|
+
# Check GE tools cache
|
|
123
|
+
tool_dir = get_toolchain_dir(name)
|
|
124
|
+
if tool_dir.exists():
|
|
125
|
+
exe_name = binary + (".exe" if platform.system() == "Windows" else "")
|
|
126
|
+
for pattern in ["bin", ".", "go/bin"]:
|
|
127
|
+
bin_dir = tool_dir / pattern
|
|
128
|
+
if bin_dir.exists():
|
|
129
|
+
exe_path = bin_dir / exe_name
|
|
130
|
+
if exe_path.exists():
|
|
131
|
+
return str(exe_path)
|
|
132
|
+
# Search recursively
|
|
133
|
+
for p in tool_dir.rglob(f"{binary}*"):
|
|
134
|
+
if p.is_file():
|
|
135
|
+
return str(p)
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _download(url: str, dest: Path) -> bool:
|
|
140
|
+
"""Download a file from URL to dest. Returns True on success."""
|
|
141
|
+
try:
|
|
142
|
+
print(f" Downloading from {url}...")
|
|
143
|
+
urllib.request.urlretrieve(url, dest)
|
|
144
|
+
return True
|
|
145
|
+
except Exception as e:
|
|
146
|
+
print(f" Download failed: {e}")
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _extract_archive(archive_path: Path, dest_dir: Path) -> bool:
|
|
151
|
+
"""Extract a zip or tar archive. Returns True on success."""
|
|
152
|
+
try:
|
|
153
|
+
if archive_path.suffix == ".zip":
|
|
154
|
+
with zipfile.ZipFile(archive_path, "r") as z:
|
|
155
|
+
z.extractall(dest_dir)
|
|
156
|
+
elif archive_path.suffix in (".gz", ".xz", ".tgz"):
|
|
157
|
+
with tarfile.open(archive_path, "r:*") as t:
|
|
158
|
+
t.extractall(dest_dir)
|
|
159
|
+
else:
|
|
160
|
+
print(f" Unknown archive format: {archive_path.suffix}")
|
|
161
|
+
return False
|
|
162
|
+
return True
|
|
163
|
+
except Exception as e:
|
|
164
|
+
print(f" Extract failed: {e}")
|
|
165
|
+
return False
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _find_extracted_root(dest_dir: Path) -> Path:
|
|
169
|
+
"""Find the actual root directory after extraction (handles nested dirs)."""
|
|
170
|
+
# If there's a single subdirectory, return it
|
|
171
|
+
entries = [p for p in dest_dir.iterdir() if p.is_dir()]
|
|
172
|
+
if len(entries) == 1:
|
|
173
|
+
return entries[0]
|
|
174
|
+
return dest_dir
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def download_toolchain(name: str, force: bool = False) -> bool:
|
|
178
|
+
"""Download and install a toolchain from official sources.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
name: toolchain name (rust, go, zig)
|
|
182
|
+
force: re-download even if already available
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
True if toolchain is available after this call.
|
|
186
|
+
"""
|
|
187
|
+
if name not in DOWNLOAD_URLS:
|
|
188
|
+
print(f" Toolchain '{name}' is not auto-downloadable (needs manual install).")
|
|
189
|
+
return is_toolchain_available(name)
|
|
190
|
+
|
|
191
|
+
if not force and is_toolchain_available(name):
|
|
192
|
+
return True
|
|
193
|
+
|
|
194
|
+
plat_key = _get_platform_key()
|
|
195
|
+
urls = DOWNLOAD_URLS[name]
|
|
196
|
+
url = urls.get(plat_key) or urls.get(plat_key.split("-")[0] + "-x86_64")
|
|
197
|
+
if not url:
|
|
198
|
+
print(f" No download URL for {name} on {plat_key}")
|
|
199
|
+
return False
|
|
200
|
+
|
|
201
|
+
tool_dir = get_toolchain_dir(name)
|
|
202
|
+
tool_dir.mkdir(parents=True, exist_ok=True)
|
|
203
|
+
|
|
204
|
+
# Download to temp file
|
|
205
|
+
archive_name = url.split("/")[-1]
|
|
206
|
+
archive_path = tool_dir / archive_name
|
|
207
|
+
|
|
208
|
+
if not _download(url, archive_path):
|
|
209
|
+
return False
|
|
210
|
+
|
|
211
|
+
# Handle rustup specially (it's an installer, not an archive)
|
|
212
|
+
if name == "rust":
|
|
213
|
+
if archive_path.suffix == ".exe":
|
|
214
|
+
# Run rustup-init.exe with default settings
|
|
215
|
+
import subprocess
|
|
216
|
+
print(" Running rustup-init...")
|
|
217
|
+
try:
|
|
218
|
+
subprocess.run([str(archive_path), "-y"], check=True, timeout=300)
|
|
219
|
+
archive_path.unlink()
|
|
220
|
+
return is_toolchain_available("rust")
|
|
221
|
+
except Exception as e:
|
|
222
|
+
print(f" rustup-init failed: {e}")
|
|
223
|
+
return False
|
|
224
|
+
else:
|
|
225
|
+
# Unix: run the shell script
|
|
226
|
+
import subprocess
|
|
227
|
+
os.chmod(str(archive_path), os.stat(str(archive_path)).st_mode | stat.S_IEXEC)
|
|
228
|
+
print(" Running rustup-init...")
|
|
229
|
+
try:
|
|
230
|
+
subprocess.run(["sh", str(archive_path), "-y"], check=True, timeout=300)
|
|
231
|
+
archive_path.unlink()
|
|
232
|
+
return is_toolchain_available("rust")
|
|
233
|
+
except Exception as e:
|
|
234
|
+
print(f" rustup-init failed: {e}")
|
|
235
|
+
return False
|
|
236
|
+
|
|
237
|
+
# Extract archive
|
|
238
|
+
print(f" Extracting {archive_name}...")
|
|
239
|
+
if not _extract_archive(archive_path, tool_dir):
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
# Clean up archive
|
|
243
|
+
archive_path.unlink()
|
|
244
|
+
|
|
245
|
+
# For Go, the archive extracts to a "go" subdirectory
|
|
246
|
+
if name == "go":
|
|
247
|
+
extracted = _find_extracted_root(tool_dir)
|
|
248
|
+
# Go archives extract to "go/" — move contents to tool_dir if needed
|
|
249
|
+
if extracted.name == "go" and extracted != tool_dir:
|
|
250
|
+
# Already in the right place (tool_dir/go/)
|
|
251
|
+
pass
|
|
252
|
+
|
|
253
|
+
# For Zig, archives extract to "zig-..." directory
|
|
254
|
+
if name == "zig":
|
|
255
|
+
extracted = _find_extracted_root(tool_dir)
|
|
256
|
+
if extracted != tool_dir:
|
|
257
|
+
# Move contents from zig-x86_64-.../ to tool_dir/
|
|
258
|
+
for item in extracted.iterdir():
|
|
259
|
+
shutil.move(str(item), str(tool_dir / item.name))
|
|
260
|
+
extracted.rmdir()
|
|
261
|
+
|
|
262
|
+
available = is_toolchain_available(name)
|
|
263
|
+
if available:
|
|
264
|
+
print(f" {name} installed successfully.")
|
|
265
|
+
else:
|
|
266
|
+
print(f" {name} downloaded but binary not found. Check {tool_dir}")
|
|
267
|
+
return available
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def ensure_toolchains(names: list[str], auto_download: bool = True) -> dict[str, bool]:
|
|
271
|
+
"""Ensure all specified toolchains are available.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
names: list of toolchain names
|
|
275
|
+
auto_download: download missing toolchains automatically
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
Dict mapping toolchain name to availability status.
|
|
279
|
+
"""
|
|
280
|
+
result = {}
|
|
281
|
+
for name in names:
|
|
282
|
+
if is_toolchain_available(name):
|
|
283
|
+
result[name] = True
|
|
284
|
+
elif auto_download and name in DOWNLOAD_URLS:
|
|
285
|
+
print(f"Toolchain '{name}' not found. Downloading...")
|
|
286
|
+
result[name] = download_toolchain(name)
|
|
287
|
+
else:
|
|
288
|
+
result[name] = False
|
|
289
|
+
return result
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def get_tools_status() -> dict[str, dict]:
|
|
293
|
+
"""Get status of all known toolchains."""
|
|
294
|
+
from .config import detect_compilers
|
|
295
|
+
detected = detect_compilers()
|
|
296
|
+
status = {}
|
|
297
|
+
for name in ["rust", "cpp", "csharp", "zig", "go", "kotlin", "dart"]:
|
|
298
|
+
available = is_toolchain_available(name) or getattr(detected, name, None) is not None
|
|
299
|
+
path = get_toolchain_path(name) or getattr(detected, name, None)
|
|
300
|
+
downloadable = name in DOWNLOAD_URLS
|
|
301
|
+
status[name] = {
|
|
302
|
+
"available": bool(available),
|
|
303
|
+
"path": str(path) if path else None,
|
|
304
|
+
"downloadable": downloadable,
|
|
305
|
+
"cache_dir": str(get_toolchain_dir(name)),
|
|
306
|
+
}
|
|
307
|
+
return status
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Code emitters for the supported Python subset."""
|
|
2
|
+
from .rust import emit_rust
|
|
3
|
+
from .cpp import emit_cpp
|
|
4
|
+
from .dart import emit_dart_functions
|
|
5
|
+
from .csharp import emit_csharp
|
|
6
|
+
from .zig import emit_zig
|
|
7
|
+
from .go import emit_go
|
|
8
|
+
from .kotlin import emit_kotlin
|
|
9
|
+
|
|
10
|
+
__all__ = ["emit_rust", "emit_cpp", "emit_dart_functions",
|
|
11
|
+
"emit_csharp", "emit_zig", "emit_go", "emit_kotlin"]
|