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,87 @@
|
|
|
1
|
+
"""Widget DSL stubs.
|
|
2
|
+
|
|
3
|
+
These exist so a UI Python file is valid and importable in pure Python (you can
|
|
4
|
+
even render a text preview). pyeffic does NOT execute them at build time — it
|
|
5
|
+
interprets the `build()` function's AST directly (see ui.py). But importing this
|
|
6
|
+
module lets you run your UI file as plain Python for quick checking.
|
|
7
|
+
|
|
8
|
+
The `style` parameter accepts a CSS-like string on ALL widgets:
|
|
9
|
+
Text("Hello", style="fontSize:20; bold:true; color:#2196F3")
|
|
10
|
+
Container(child=..., style="bg:#f0f0f0; radius:12; padding:16; border:1,#ccc")
|
|
11
|
+
Column([...], style="align:spaceBetween")
|
|
12
|
+
|
|
13
|
+
Named shortcuts still work: style="headline", "title", "subtitle", "body".
|
|
14
|
+
The same style dict emits to Flutter (now), Slint/Rust/C++ GUI (later) — one
|
|
15
|
+
syntax, multiple native targets. See pyeffic/styling.py.
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Widget:
|
|
21
|
+
def __init__(self, **kwargs):
|
|
22
|
+
self.kwargs = kwargs
|
|
23
|
+
|
|
24
|
+
def __repr__(self):
|
|
25
|
+
return f"{type(self).__name__}({self.kwargs})"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Text(Widget):
|
|
29
|
+
def __init__(self, text: str = "", *, style: str = "body", state: str = ""):
|
|
30
|
+
super().__init__(text=text, style=style, state=state)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Column(Widget):
|
|
34
|
+
def __init__(self, children, *, align: str = "center"):
|
|
35
|
+
super().__init__(children=children, align=align)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Row(Widget):
|
|
39
|
+
def __init__(self, children, *, align: str = "center"):
|
|
40
|
+
super().__init__(children=children, align=align)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Container(Widget):
|
|
44
|
+
def __init__(self, child=None, *, padding: int = 8, color: str = ""):
|
|
45
|
+
super().__init__(child=child, padding=padding, color=color)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ElevatedButton(Widget):
|
|
49
|
+
def __init__(self, label: str = "", *, on_click=None, style: str = ""):
|
|
50
|
+
super().__init__(label=label, on_click=on_click, style=style)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class SizedBox(Widget):
|
|
54
|
+
"""Explicit spacing widget. height=N or width=N."""
|
|
55
|
+
def __init__(self, *, height: float = 0, width: float = 0, style: str = ""):
|
|
56
|
+
super().__init__(height=height, width=width, style=style)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Divider(Widget):
|
|
60
|
+
"""A horizontal line separator."""
|
|
61
|
+
def __init__(self, *, style: str = ""):
|
|
62
|
+
super().__init__(style=style)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class Expanded(Widget):
|
|
66
|
+
"""Expands to fill available space in a Row/Column."""
|
|
67
|
+
def __init__(self, child=None, *, flex: int = 1, style: str = ""):
|
|
68
|
+
super().__init__(child=child, flex=flex, style=style)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Action:
|
|
72
|
+
"""Declares a button handler: call an FFI function, store result in state.
|
|
73
|
+
|
|
74
|
+
on_click can be a single Action or a LIST of Actions (multi-action: one
|
|
75
|
+
button click triggers several FFI calls and updates several state fields).
|
|
76
|
+
"""
|
|
77
|
+
def __init__(self, call: str, args, update: str = ""):
|
|
78
|
+
self.call = call
|
|
79
|
+
self.args = args
|
|
80
|
+
self.update = update
|
|
81
|
+
|
|
82
|
+
def __repr__(self):
|
|
83
|
+
return f"Action(call={self.call!r}, args={self.args!r}, update={self.update!r})"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
__all__ = ["Widget", "Text", "Column", "Row", "Container", "ElevatedButton",
|
|
87
|
+
"SizedBox", "Divider", "Expanded", "Action"]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# scripts/
|
|
2
|
+
|
|
3
|
+
Build, packaging, and release tooling. Nothing here is part of the compiler.
|
|
4
|
+
|
|
5
|
+
| Script | Purpose |
|
|
6
|
+
|---|---|
|
|
7
|
+
| `build-npm.js` | Assemble the npm payload — copies `pyeffic/` into `python/` so the published tarball is self-contained. Runs automatically via `prepublishOnly`. |
|
|
8
|
+
| `release.js` | Run every guard, assemble, pack, and print the publish commands. `--publish` publishes for real. |
|
|
9
|
+
| `check-toolchains.py` | Toolchain report for CI. `--json`, `--require rust,cpp`. |
|
|
10
|
+
| `build-binary.py` | PyInstaller standalone binary (optional distribution). |
|
|
11
|
+
| `ge_entry.py` | Entry point for the standalone binary. |
|
|
12
|
+
| `installer/` | Inno Setup script for the Windows installer. |
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
node scripts/build-npm.js # assemble the payload
|
|
18
|
+
node scripts/build-npm.js --check # verify without writing
|
|
19
|
+
node scripts/release.js # dry run: all guards + pack
|
|
20
|
+
node scripts/release.js --publish # publish to npm (needs `npm login`)
|
|
21
|
+
python scripts/check-toolchains.py --require rust,cpp
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## The npm payload
|
|
25
|
+
|
|
26
|
+
The compiler is Python, so the npm package ships the Python source and a
|
|
27
|
+
Node shim runs it:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
gelang-0.1.0.tgz
|
|
31
|
+
bin/ge.js finds Python 3.10+, sets PYTHONPATH, spawns the CLI
|
|
32
|
+
python/pyeffic/ the compiler (generated by build-npm.js)
|
|
33
|
+
scripts/check-toolchains.py
|
|
34
|
+
README.md LICENSE CHANGELOG.md
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`python/` is generated and gitignored. `prepublishOnly` rebuilds it, so the
|
|
38
|
+
tarball can never ship stale compiler code.
|
|
39
|
+
|
|
40
|
+
The shim deliberately does **not** pip-install anything: no network access at
|
|
41
|
+
install time, no virtualenv to manage, and the compiler version is pinned to
|
|
42
|
+
the npm version by construction.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Report available toolchains in a CI-friendly form.
|
|
3
|
+
|
|
4
|
+
python scripts/check-toolchains.py human-readable, exit 0/1
|
|
5
|
+
python scripts/check-toolchains.py --json machine-readable
|
|
6
|
+
python scripts/check-toolchains.py --require rust,cpp
|
|
7
|
+
|
|
8
|
+
Exits non-zero only when a *required* backend is missing, so a CI matrix can
|
|
9
|
+
install what it needs and fail fast otherwise.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import json
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
19
|
+
|
|
20
|
+
from pyeffic.config import detect_compilers # noqa: E402
|
|
21
|
+
|
|
22
|
+
BACKENDS = ("rust", "cpp", "csharp", "zig", "go", "kotlin")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def snapshot() -> dict:
|
|
26
|
+
info = detect_compilers()
|
|
27
|
+
paths = {
|
|
28
|
+
"rust": info.rustc,
|
|
29
|
+
"cpp": info.cpp,
|
|
30
|
+
"csharp": info.dotnet,
|
|
31
|
+
"zig": info.zig,
|
|
32
|
+
"go": info.go,
|
|
33
|
+
"kotlin": info.kotlinc,
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
"python": f"{sys.version_info.major}.{sys.version_info.minor}."
|
|
37
|
+
f"{sys.version_info.micro}",
|
|
38
|
+
"python_ok": sys.version_info >= (3, 10),
|
|
39
|
+
"backends": {name: bool(p) for name, p in paths.items()},
|
|
40
|
+
"paths": {name: (p or "") for name, p in paths.items()},
|
|
41
|
+
"versions": info.versions,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def main(argv: list[str] | None = None) -> int:
|
|
46
|
+
p = argparse.ArgumentParser(description=__doc__)
|
|
47
|
+
p.add_argument("--json", action="store_true")
|
|
48
|
+
p.add_argument("--require", default="",
|
|
49
|
+
help="comma-separated backends that must be present")
|
|
50
|
+
args = p.parse_args(argv)
|
|
51
|
+
|
|
52
|
+
snap = snapshot()
|
|
53
|
+
required = [b.strip() for b in args.require.split(",") if b.strip()]
|
|
54
|
+
|
|
55
|
+
if args.json:
|
|
56
|
+
snap["required"] = required
|
|
57
|
+
snap["missing_required"] = [b for b in required
|
|
58
|
+
if not snap["backends"].get(b)]
|
|
59
|
+
print(json.dumps(snap, indent=2))
|
|
60
|
+
else:
|
|
61
|
+
print(f"python {snap['python']}"
|
|
62
|
+
+ ("" if snap["python_ok"] else " (needs 3.10+)"))
|
|
63
|
+
for name in BACKENDS:
|
|
64
|
+
ok = snap["backends"][name]
|
|
65
|
+
mark = "OK" if ok else "--"
|
|
66
|
+
detail = snap["paths"][name] or "not found"
|
|
67
|
+
print(f" [{mark}] {name:<8} {detail}")
|
|
68
|
+
|
|
69
|
+
problems = []
|
|
70
|
+
if not snap["python_ok"]:
|
|
71
|
+
problems.append("python >= 3.10 is required")
|
|
72
|
+
for name in required:
|
|
73
|
+
if not snap["backends"].get(name):
|
|
74
|
+
problems.append(f"required backend '{name}' is not installed")
|
|
75
|
+
|
|
76
|
+
if problems:
|
|
77
|
+
print()
|
|
78
|
+
for msg in problems:
|
|
79
|
+
print(f"error: {msg}", file=sys.stderr)
|
|
80
|
+
return 1
|
|
81
|
+
return 0
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
sys.exit(main())
|