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.
Files changed (96) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/LICENSE +21 -0
  3. package/README.md +535 -0
  4. package/bin/ge.js +112 -0
  5. package/package.json +62 -0
  6. package/python/pyeffic/__init__.py +9 -0
  7. package/python/pyeffic/__main__.py +6 -0
  8. package/python/pyeffic/analyzer.py +464 -0
  9. package/python/pyeffic/apisurface.py +238 -0
  10. package/python/pyeffic/autoselect.py +327 -0
  11. package/python/pyeffic/backends.py +87 -0
  12. package/python/pyeffic/bench.py +233 -0
  13. package/python/pyeffic/cli.py +184 -0
  14. package/python/pyeffic/compiler.py +421 -0
  15. package/python/pyeffic/config.py +383 -0
  16. package/python/pyeffic/dartgen.py +441 -0
  17. package/python/pyeffic/deploy.py +586 -0
  18. package/python/pyeffic/diagnostics.py +194 -0
  19. package/python/pyeffic/difftest.py +424 -0
  20. package/python/pyeffic/downloader.py +307 -0
  21. package/python/pyeffic/emitters/__init__.py +11 -0
  22. package/python/pyeffic/emitters/base.py +2359 -0
  23. package/python/pyeffic/emitters/cpp.py +266 -0
  24. package/python/pyeffic/emitters/csharp.py +342 -0
  25. package/python/pyeffic/emitters/dart.py +349 -0
  26. package/python/pyeffic/emitters/go.py +388 -0
  27. package/python/pyeffic/emitters/kotlin.py +314 -0
  28. package/python/pyeffic/emitters/rust.py +314 -0
  29. package/python/pyeffic/emitters/zig.py +411 -0
  30. package/python/pyeffic/ffi.py +49 -0
  31. package/python/pyeffic/frontends/__init__.py +94 -0
  32. package/python/pyeffic/frontends/hybrid.py +709 -0
  33. package/python/pyeffic/frontends/typescript.py +965 -0
  34. package/python/pyeffic/ge_cli.py +1148 -0
  35. package/python/pyeffic/golden.py +348 -0
  36. package/python/pyeffic/idents.py +206 -0
  37. package/python/pyeffic/modules.py +220 -0
  38. package/python/pyeffic/packer.py +222 -0
  39. package/python/pyeffic/pipeline.py +797 -0
  40. package/python/pyeffic/reactgen.py +966 -0
  41. package/python/pyeffic/researcher.py +177 -0
  42. package/python/pyeffic/scaffold.py +397 -0
  43. package/python/pyeffic/stdlib.py +246 -0
  44. package/python/pyeffic/styling.py +220 -0
  45. package/python/pyeffic/templates/desktop_gui/README.md +106 -0
  46. package/python/pyeffic/templates/desktop_gui/app/__init__.py +0 -0
  47. package/python/pyeffic/templates/desktop_gui/app/core/__init__.py +0 -0
  48. package/python/pyeffic/templates/desktop_gui/app/core/add.ge.py +13 -0
  49. package/python/pyeffic/templates/desktop_gui/app/core/factorial.ge.py +20 -0
  50. package/python/pyeffic/templates/desktop_gui/app/core/fibonacci.ge.py +25 -0
  51. package/python/pyeffic/templates/desktop_gui/app/core/gcd.ge.py +19 -0
  52. package/python/pyeffic/templates/desktop_gui/app/core/is_prime.ge.py +24 -0
  53. package/python/pyeffic/templates/desktop_gui/app/core/multiply.ge.py +13 -0
  54. package/python/pyeffic/templates/desktop_gui/app/core/power.ge.py +25 -0
  55. package/python/pyeffic/templates/desktop_gui/app/main.ge.py +49 -0
  56. package/python/pyeffic/templates/desktop_gui/app/memory/__init__.py +0 -0
  57. package/python/pyeffic/templates/desktop_gui/app/memory/buffer.ge.py +26 -0
  58. package/python/pyeffic/templates/desktop_gui/app/memory/limits.ge.py +47 -0
  59. package/python/pyeffic/templates/desktop_gui/app/memory/state.ge.py +44 -0
  60. package/python/pyeffic/templates/desktop_gui/app/ui/__init__.py +0 -0
  61. package/python/pyeffic/templates/desktop_gui/app/ui/layout.ge.py +64 -0
  62. package/python/pyeffic/templates/desktop_gui/app/ui/render.ge.py +87 -0
  63. package/python/pyeffic/templates/desktop_gui/app/ui/theme.ge.py +147 -0
  64. package/python/pyeffic/templates/desktop_gui/app/ui/widgets.ge.py +105 -0
  65. package/python/pyeffic/templates/desktop_gui/desktop/__init__.py +1 -0
  66. package/python/pyeffic/templates/desktop_gui/desktop/main.ge.py +258 -0
  67. package/python/pyeffic/templates/desktop_gui/ge.toml +16 -0
  68. package/python/pyeffic/templates/desktop_gui/tests/__init__.py +0 -0
  69. package/python/pyeffic/templates/desktop_gui/tests/ge_loader.py +76 -0
  70. package/python/pyeffic/templates/desktop_gui/tests/test_app.py +173 -0
  71. package/python/pyeffic/templates/web_react/README.md +115 -0
  72. package/python/pyeffic/templates/web_react/app/__init__.py +0 -0
  73. package/python/pyeffic/templates/web_react/app/core/__init__.py +0 -0
  74. package/python/pyeffic/templates/web_react/app/core/add.ge.py +9 -0
  75. package/python/pyeffic/templates/web_react/app/core/factorial.ge.py +16 -0
  76. package/python/pyeffic/templates/web_react/app/core/fibonacci.ge.py +21 -0
  77. package/python/pyeffic/templates/web_react/app/core/is_prime.ge.py +20 -0
  78. package/python/pyeffic/templates/web_react/app/core/multiply.ge.py +9 -0
  79. package/python/pyeffic/templates/web_react/app/main.ge.py +25 -0
  80. package/python/pyeffic/templates/web_react/app/memory/__init__.py +0 -0
  81. package/python/pyeffic/templates/web_react/app/memory/buffer.ge.py +25 -0
  82. package/python/pyeffic/templates/web_react/app/memory/limits.ge.py +51 -0
  83. package/python/pyeffic/templates/web_react/ge.toml +23 -0
  84. package/python/pyeffic/templates/web_react/tests/__init__.py +0 -0
  85. package/python/pyeffic/templates/web_react/tests/ge_loader.py +68 -0
  86. package/python/pyeffic/templates/web_react/tests/test_app.py +105 -0
  87. package/python/pyeffic/templates/web_react/ui/main.ge.ui +33 -0
  88. package/python/pyeffic/templates/web_react/web/__init__.py +0 -0
  89. package/python/pyeffic/templates/web_react/web/server.ge.py +78 -0
  90. package/python/pyeffic/ts2py.py +657 -0
  91. package/python/pyeffic/typecheck.py +232 -0
  92. package/python/pyeffic/ui.py +154 -0
  93. package/python/pyeffic/ui_dsl.py +618 -0
  94. package/python/pyeffic/widgets.py +87 -0
  95. package/scripts/README.md +42 -0
  96. 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())