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,258 @@
1
+ """Desktop entry point for {app_name} — thin Rust shell.
2
+
3
+ The shell owns only the window and the message loop. All rendering is
4
+ delegated to the C++ UI layer; all computation to the C++ core layer.
5
+
6
+ Rust — window, event loop, bounded state, input handling
7
+ C++ — ui_draw_frame(...) rendering, factorial/fibonacci/... compute
8
+
9
+ Memory model (Rust): fixed-size scalar state, no collections that grow,
10
+ no per-frame heap allocation, ownership handles all cleanup.
11
+
12
+ Build: python build.py --run
13
+ """
14
+ from __future__ import annotations
15
+ from app.main import (
16
+ mem_clamp_input, mem_step_up, mem_step_down, mem_normalize_input,
17
+ mem_initial_input, mem_initial_result, mem_initial_op, mem_next_op,
18
+ factorial, fibonacci, is_prime, gcd,
19
+ )
20
+
21
+ # File-scope Win32 FFI declarations for the Rust shell.
22
+ ge_preamble("rust", """
23
+ // Note: warnings are suppressed by the build (rustc -A warnings) because
24
+ // this file is generated; inner attributes cannot appear mid-file.
25
+
26
+ // === Win32 type aliases ===
27
+ type HWND = *mut std::ffi::c_void;
28
+ type HDC = *mut std::ffi::c_void;
29
+ type HINSTANCE = *mut std::ffi::c_void;
30
+ type HMENU = *mut std::ffi::c_void;
31
+ type HCURSOR = *mut std::ffi::c_void;
32
+ type HICON = *mut std::ffi::c_void;
33
+ type WPARAM = usize;
34
+ type LPARAM = isize;
35
+ type LRESULT = isize;
36
+ type UINT = u32;
37
+ type DWORD = u32;
38
+ type LONG = i32;
39
+ type BOOL = i32;
40
+ type BYTE = u8;
41
+ type ATOM = u16;
42
+
43
+ #[repr(C)]
44
+ struct RECT { left: LONG, top: LONG, right: LONG, bottom: LONG }
45
+ #[repr(C)]
46
+ struct POINT { x: LONG, y: LONG }
47
+ #[repr(C)]
48
+ struct MSG {
49
+ hwnd: HWND,
50
+ message: UINT,
51
+ wParam: WPARAM,
52
+ lParam: LPARAM,
53
+ time: DWORD,
54
+ pt: POINT,
55
+ }
56
+ #[repr(C)]
57
+ struct PAINTSTRUCT {
58
+ hdc: HDC,
59
+ fErase: BOOL,
60
+ rcPaint: RECT,
61
+ fRestore: BOOL,
62
+ fIncUpdate: BOOL,
63
+ rgbReserved: [BYTE; 32],
64
+ }
65
+ #[repr(C)]
66
+ struct WNDCLASSEXA {
67
+ cbSize: UINT,
68
+ style: UINT,
69
+ lpfnWndProc: Option<unsafe extern "system" fn(HWND, UINT, WPARAM, LPARAM) -> LRESULT>,
70
+ cbClsExtra: i32,
71
+ cbWndExtra: i32,
72
+ hInstance: HINSTANCE,
73
+ hIcon: HICON,
74
+ hCursor: HCURSOR,
75
+ hbrBackground: *mut std::ffi::c_void,
76
+ lpszMenuName: *const u8,
77
+ lpszClassName: *const u8,
78
+ hIconSm: HICON,
79
+ }
80
+
81
+ // === Win32 constants ===
82
+ const CS_HREDRAW: u32 = 2;
83
+ const CS_VREDRAW: u32 = 1;
84
+ const CW_USEDEFAULT: i32 = -2147483648;
85
+ const SW_SHOWDEFAULT: i32 = 10;
86
+ const WM_PAINT: u32 = 15;
87
+ const WM_DESTROY: u32 = 2;
88
+ const WM_KEYDOWN: u32 = 256;
89
+ const WM_CLOSE: u32 = 16;
90
+ const WM_ERASEBKGND: u32 = 20;
91
+ const VK_ESCAPE: i32 = 27;
92
+ const VK_RETURN: i32 = 13;
93
+ const VK_UP: i32 = 0x26;
94
+ const VK_DOWN: i32 = 0x28;
95
+ const VK_F: i32 = 0x46;
96
+ const VK_P: i32 = 0x50;
97
+ const VK_G: i32 = 0x47;
98
+ const WS_OVERLAPPEDWINDOW: u32 = 13565952;
99
+
100
+ // === Win32 FFI ===
101
+ extern "system" {
102
+ fn BeginPaint(hwnd: HWND, lpPaint: *mut PAINTSTRUCT) -> HDC;
103
+ fn EndPaint(hwnd: HWND, lpPaint: *const PAINTSTRUCT) -> BOOL;
104
+ fn GetClientRect(hwnd: HWND, lpRect: *mut RECT) -> BOOL;
105
+ fn InvalidateRect(hwnd: HWND, lpRect: *const RECT, bErase: BOOL) -> BOOL;
106
+ fn RegisterClassExA(lpwcx: *const WNDCLASSEXA) -> ATOM;
107
+ fn CreateWindowExA(dwExStyle: u32, lpClassName: *const u8, lpWindowName: *const u8,
108
+ dwStyle: u32, x: i32, y: i32, nWidth: i32, nHeight: i32,
109
+ hWndParent: HWND, hMenu: HMENU, hInstance: HINSTANCE,
110
+ lpParam: *mut std::ffi::c_void) -> HWND;
111
+ fn ShowWindow(hwnd: HWND, nCmdShow: i32) -> BOOL;
112
+ fn UpdateWindow(hwnd: HWND) -> BOOL;
113
+ fn GetMessageA(lpMsg: *mut MSG, hwnd: HWND, wMsgFilterMin: u32, wMsgFilterMax: u32) -> BOOL;
114
+ fn TranslateMessage(lpMsg: *const MSG) -> BOOL;
115
+ fn DispatchMessageA(lpMsg: *const MSG) -> LRESULT;
116
+ fn DefWindowProcA(hwnd: HWND, msg: u32, wParam: WPARAM, lParam: LPARAM) -> LRESULT;
117
+ fn PostQuitMessage(nExitCode: i32);
118
+ fn DestroyWindow(hwnd: HWND) -> BOOL;
119
+ fn GetModuleHandleA(lpModuleName: *const u8) -> HINSTANCE;
120
+ fn LoadCursorA(hInstance: HINSTANCE, lpCursorName: *const u8) -> HCURSOR;
121
+ fn LoadIconA(hInstance: HINSTANCE, lpIconName: *const u8) -> HICON;
122
+ }
123
+
124
+ // The C++ functions this shell calls are declared automatically by
125
+ // `ge build` (mixed-backend link step), so no hand-written extern block
126
+ // is needed here.
127
+
128
+ // === Bounded state ===
129
+ static mut G_HWND: HWND = std::ptr::null_mut();
130
+ static mut G_INPUT: i64 = 10;
131
+ static mut G_RESULT: i64 = 0;
132
+ static mut G_OP: i64 = 0;
133
+
134
+ fn run_op(op: i64, input: i64) -> i64 {
135
+ unsafe {
136
+ match op {
137
+ 0 => factorial(input),
138
+ 1 => fibonacci(input),
139
+ 2 => is_prime(input),
140
+ 3 => gcd(input, input / 2 + 1),
141
+ _ => 0,
142
+ }
143
+ }
144
+ }
145
+
146
+ unsafe extern "system" fn wnd_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
147
+ unsafe {
148
+ match msg {
149
+ WM_PAINT => {
150
+ let mut ps: PAINTSTRUCT = std::mem::zeroed();
151
+ let hdc = BeginPaint(hwnd, &mut ps);
152
+ let mut rc: RECT = std::mem::zeroed();
153
+ GetClientRect(hwnd, &mut rc);
154
+ let w = (rc.right - rc.left) as i64;
155
+ let h = (rc.bottom - rc.top) as i64;
156
+ ui_draw_frame(hdc as i64, w, h, G_INPUT, G_RESULT, G_OP);
157
+ EndPaint(hwnd, &ps);
158
+ 0
159
+ }
160
+ WM_ERASEBKGND => 1,
161
+ WM_KEYDOWN => {
162
+ let k = wparam as i32;
163
+ if k == VK_ESCAPE {
164
+ DestroyWindow(hwnd);
165
+ } else if k == VK_RETURN {
166
+ G_RESULT = run_op(G_OP, G_INPUT);
167
+ InvalidateRect(hwnd, std::ptr::null(), 0);
168
+ } else if k == VK_UP {
169
+ G_INPUT = mem_step_up(G_INPUT);
170
+ InvalidateRect(hwnd, std::ptr::null(), 0);
171
+ } else if k == VK_DOWN {
172
+ G_INPUT = mem_step_down(G_INPUT);
173
+ InvalidateRect(hwnd, std::ptr::null(), 0);
174
+ } else if k == VK_F {
175
+ G_OP = 1;
176
+ G_RESULT = run_op(1, G_INPUT);
177
+ InvalidateRect(hwnd, std::ptr::null(), 0);
178
+ } else if k == VK_P {
179
+ G_OP = 2;
180
+ G_RESULT = run_op(2, G_INPUT);
181
+ InvalidateRect(hwnd, std::ptr::null(), 0);
182
+ } else if k == VK_G {
183
+ G_OP = 3;
184
+ G_RESULT = run_op(3, G_INPUT);
185
+ InvalidateRect(hwnd, std::ptr::null(), 0);
186
+ }
187
+ 0
188
+ }
189
+ WM_CLOSE => { DestroyWindow(hwnd); 0 }
190
+ WM_DESTROY => { PostQuitMessage(0); 0 }
191
+ _ => DefWindowProcA(hwnd, msg, wparam, lparam),
192
+ }
193
+ }
194
+ }
195
+ """)
196
+
197
+
198
+ @rust
199
+ def desktop_bootstrap() -> int:
200
+ """Initialize bounded state from the memory layer."""
201
+ ge_inline("rust", """
202
+ unsafe {
203
+ G_INPUT = mem_normalize_input(mem_initial_input());
204
+ G_RESULT = mem_initial_result();
205
+ G_OP = mem_initial_op();
206
+ }
207
+ return 0;
208
+ """)
209
+ return 0
210
+
211
+
212
+ @rust
213
+ def desktop_run() -> int:
214
+ """Create the window and run the message loop."""
215
+ ge_inline("rust", """
216
+ unsafe {
217
+ let class_name = b"GEAppWnd\0".as_ptr();
218
+ let hinst = GetModuleHandleA(std::ptr::null());
219
+ let wc = WNDCLASSEXA {
220
+ cbSize: std::mem::size_of::<WNDCLASSEXA>() as u32,
221
+ style: CS_HREDRAW | CS_VREDRAW,
222
+ lpfnWndProc: Some(wnd_proc),
223
+ cbClsExtra: 0,
224
+ cbWndExtra: 0,
225
+ hInstance: hinst,
226
+ hIcon: LoadIconA(std::ptr::null_mut(), std::ptr::null()),
227
+ hCursor: LoadCursorA(std::ptr::null_mut(), std::ptr::null()),
228
+ hbrBackground: std::ptr::null_mut(),
229
+ lpszMenuName: std::ptr::null(),
230
+ lpszClassName: class_name,
231
+ hIconSm: std::ptr::null_mut(),
232
+ };
233
+ if RegisterClassExA(&wc) == 0 { return 1; }
234
+
235
+ let title = b"{app_name} - Rust + C++ Desktop\0".as_ptr();
236
+ G_HWND = CreateWindowExA(0, class_name, title,
237
+ WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 1040, 680,
238
+ std::ptr::null_mut(), std::ptr::null_mut(), hinst, std::ptr::null_mut());
239
+ if G_HWND.is_null() { return 1; }
240
+
241
+ ShowWindow(G_HWND, SW_SHOWDEFAULT);
242
+ UpdateWindow(G_HWND);
243
+
244
+ let mut msg: MSG = std::mem::zeroed();
245
+ while GetMessageA(&mut msg, std::ptr::null_mut(), 0, 0) > 0 {
246
+ TranslateMessage(&msg);
247
+ DispatchMessageA(&msg);
248
+ }
249
+ msg.wParam as i64
250
+ }
251
+ """)
252
+ return 0
253
+
254
+
255
+ def main() -> int:
256
+ """Entry point: bootstrap state, then run the window loop."""
257
+ desktop_bootstrap()
258
+ return desktop_run()
@@ -0,0 +1,16 @@
1
+ # GE project configuration for {app_name}
2
+ [project]
3
+ name = "{app_name}"
4
+ version = "0.1.0"
5
+ description = "Native desktop app — Rust shell + C++ core + C++ UI"
6
+ template = "desktop-gui"
7
+
8
+ [build]
9
+ platforms = ["desktop"]
10
+ languages = ["rust", "cpp"]
11
+
12
+ [layers]
13
+ memory = "app/memory" # Rust
14
+ core = "app/core" # C++
15
+ ui = "app/ui" # C++
16
+ entry = "desktop/main.ge.py"
@@ -0,0 +1,76 @@
1
+ """Import hook for .ge.py modules.
2
+
3
+ Python cannot import `app/core/add.ge.py` as `app.core.add`, so this
4
+ module installs a meta path finder that maps dotted names onto .ge.py
5
+ files. After install(), normal imports just work, with Python handling
6
+ ordering, caching, and circular-import semantics:
7
+
8
+ from ge_loader import install
9
+ install()
10
+ from app.core.factorial import factorial
11
+ """
12
+ import importlib.abc
13
+ import importlib.util
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ ROOT = Path(__file__).parent.parent
18
+ _installed = False
19
+
20
+
21
+ def _stub_preamble(backend, code): # noqa: ARG001
22
+ """No-op stand-in for the GE ge_preamble intrinsic."""
23
+ return None
24
+
25
+
26
+ def _stub_value(*args, **kwargs): # noqa: ARG001
27
+ """No-op stand-in for ge_inline / ge_raw; returns int 0."""
28
+ return 0
29
+
30
+
31
+ class GeFinder(importlib.abc.MetaPathFinder, importlib.abc.Loader):
32
+ """Resolve `pkg.mod` to `pkg/mod.ge.py` under the project root."""
33
+
34
+ @staticmethod
35
+ def _ge_path(parts: list[str]) -> Path:
36
+ return ROOT.joinpath(*parts[:-1]) / (parts[-1] + ".ge.py")
37
+
38
+ def find_spec(self, fullname, path=None, target=None):
39
+ parts = fullname.split(".")
40
+ ge_path = self._ge_path(parts)
41
+ if ge_path.is_file():
42
+ return importlib.util.spec_from_loader(fullname, self)
43
+ pkg_dir = ROOT.joinpath(*parts)
44
+ init = pkg_dir / "__init__.py"
45
+ if init.is_file():
46
+ return importlib.util.spec_from_file_location(
47
+ fullname, init, submodule_search_locations=[str(pkg_dir)])
48
+ return None
49
+
50
+ def create_module(self, spec):
51
+ return None
52
+
53
+ def exec_module(self, module):
54
+ parts = module.__name__.split(".")
55
+ ge_path = self._ge_path(parts)
56
+ code = ge_path.read_text(encoding="utf-8")
57
+ # GE compiler intrinsics are not Python builtins; provide no-op
58
+ # stubs so the module can be imported for unit testing.
59
+ ns = module.__dict__
60
+ ns.setdefault("ge_preamble", _stub_preamble)
61
+ ns.setdefault("ge_inline", _stub_value)
62
+ ns.setdefault("ge_raw", _stub_value)
63
+ exec(compile(code, str(ge_path), "exec"), ns)
64
+
65
+
66
+ def install() -> None:
67
+ """Install the .ge.py import hook (idempotent)."""
68
+ global _installed
69
+ if _installed:
70
+ return
71
+ if not any(isinstance(f, GeFinder) for f in sys.meta_path):
72
+ sys.meta_path.insert(0, GeFinder())
73
+ root = str(ROOT)
74
+ if root not in sys.path:
75
+ sys.path.insert(0, root)
76
+ _installed = True
@@ -0,0 +1,173 @@
1
+ """{app_name} test suite.
2
+
3
+ Run with:
4
+
5
+ python -m unittest discover tests
6
+ """
7
+ import sys
8
+ import unittest
9
+ from pathlib import Path
10
+
11
+ ROOT = Path(__file__).parent.parent
12
+ sys.path.insert(0, str(ROOT / "tests"))
13
+
14
+ from ge_loader import install # noqa: E402
15
+
16
+ install()
17
+
18
+ # .ge.py modules are importable now.
19
+ from app.core.add import add # noqa: E402
20
+ from app.core.multiply import multiply # noqa: E402
21
+ from app.core.factorial import factorial # noqa: E402
22
+ from app.core.fibonacci import fibonacci # noqa: E402
23
+ from app.core.is_prime import is_prime # noqa: E402
24
+ from app.core.gcd import gcd # noqa: E402
25
+ from app.core.power import power # noqa: E402
26
+ from app.memory.limits import ( # noqa: E402
27
+ mem_input_min, mem_input_max, mem_clamp_input, mem_step_up, mem_step_down,
28
+ )
29
+ from app.memory.buffer import ( # noqa: E402
30
+ mem_buffer_capacity, mem_digits_capacity, mem_state_slots,
31
+ )
32
+ from app.memory.state import ( # noqa: E402
33
+ mem_initial_input, mem_initial_result, mem_initial_op,
34
+ mem_op_count, mem_next_op, mem_normalize_input,
35
+ )
36
+ from app.ui.theme import ui_color_bg, ui_color_accent, ui_font_body, ui_radius # noqa: E402
37
+ from app.ui.layout import ( # noqa: E402
38
+ ui_card_width, ui_card_height, ui_sidebar_width, ui_content_x,
39
+ ui_center_x, ui_row_y,
40
+ )
41
+
42
+
43
+ class TestCoreFunctions(unittest.TestCase):
44
+ """C++ core layer - computation."""
45
+
46
+ def test_add(self):
47
+ self.assertEqual(add(3, 4), 7)
48
+ self.assertEqual(add(-1, 1), 0)
49
+
50
+ def test_multiply(self):
51
+ self.assertEqual(multiply(3, 4), 12)
52
+ self.assertEqual(multiply(0, 5), 0)
53
+
54
+ def test_factorial(self):
55
+ self.assertEqual(factorial(0), 1)
56
+ self.assertEqual(factorial(5), 120)
57
+ self.assertEqual(factorial(10), 3628800)
58
+
59
+ def test_fibonacci(self):
60
+ self.assertEqual(fibonacci(0), 0)
61
+ self.assertEqual(fibonacci(1), 1)
62
+ self.assertEqual(fibonacci(10), 55)
63
+
64
+ def test_is_prime(self):
65
+ self.assertEqual(is_prime(2), 1)
66
+ self.assertEqual(is_prime(3), 1)
67
+ self.assertEqual(is_prime(4), 0)
68
+ self.assertEqual(is_prime(17), 1)
69
+ self.assertEqual(is_prime(100), 0)
70
+
71
+ def test_gcd(self):
72
+ self.assertEqual(gcd(12, 8), 4)
73
+ self.assertEqual(gcd(17, 5), 1)
74
+ self.assertEqual(gcd(100, 50), 50)
75
+
76
+ def test_power(self):
77
+ self.assertEqual(power(2, 0), 1)
78
+ self.assertEqual(power(2, 10), 1024)
79
+ self.assertEqual(power(3, 3), 27)
80
+
81
+
82
+ class TestMemoryLayer(unittest.TestCase):
83
+ """Rust memory layer - bounds and state."""
84
+
85
+ def test_clamp_input(self):
86
+ self.assertEqual(mem_clamp_input(-5), mem_input_min())
87
+ self.assertEqual(mem_clamp_input(999), mem_input_max())
88
+ self.assertEqual(mem_clamp_input(7), 7)
89
+
90
+ def test_step_up_down(self):
91
+ self.assertEqual(mem_step_up(1), 2)
92
+ self.assertEqual(mem_step_down(1), 0)
93
+ self.assertEqual(mem_step_up(mem_input_max()),
94
+ mem_input_max())
95
+ self.assertEqual(mem_step_down(mem_input_min()),
96
+ mem_input_min())
97
+
98
+ def test_factorial_fits_in_i64(self):
99
+ # mem_input_max() must keep factorial within i64 range
100
+ self.assertLess(factorial(mem_input_max()),
101
+ 2 ** 63)
102
+
103
+ def test_state_defaults(self):
104
+ self.assertEqual(mem_initial_result(), 0)
105
+ self.assertEqual(mem_initial_op(), 0)
106
+ self.assertIsInstance(mem_initial_input(), int)
107
+
108
+ def test_op_cycle(self):
109
+ self.assertEqual(mem_next_op(mem_op_count() - 1), 0)
110
+ self.assertEqual(mem_next_op(0), 1)
111
+
112
+ def test_normalize(self):
113
+ self.assertEqual(mem_normalize_input(999), mem_input_max())
114
+
115
+ def test_buffers_bounded(self):
116
+ self.assertGreater(mem_buffer_capacity(),
117
+ mem_digits_capacity())
118
+ self.assertGreater(mem_state_slots(), 0)
119
+
120
+
121
+ class TestUILayer(unittest.TestCase):
122
+ """C++ UI layer - design tokens and layout math."""
123
+
124
+ def test_theme_tokens(self):
125
+ self.assertIsInstance(ui_color_bg(), int)
126
+ self.assertIsInstance(ui_color_accent(), int)
127
+ self.assertGreater(ui_font_body(), 0)
128
+ self.assertGreater(ui_radius(), 0)
129
+
130
+ def test_layout_math(self):
131
+ self.assertGreater(ui_card_width(1200), 0)
132
+ self.assertGreater(ui_content_x(), ui_sidebar_width())
133
+ self.assertGreater(ui_card_height(800), 0)
134
+
135
+ def test_centering(self):
136
+ self.assertEqual(ui_center_x(0, 100, 20), 40)
137
+
138
+ def test_rows_increase(self):
139
+ self.assertGreater(ui_row_y(2), ui_row_y(1))
140
+
141
+
142
+ class TestProjectStructure(unittest.TestCase):
143
+ """Layered architecture is present on disk."""
144
+
145
+ def test_layers_exist(self):
146
+ for layer in ["memory", "core", "ui"]:
147
+ self.assertTrue((ROOT / "app" / layer).is_dir(),
148
+ "app/" + layer + "/ should exist")
149
+
150
+ def test_one_function_per_core_file(self):
151
+ core = ROOT / "app" / "core"
152
+ for f in ["add.ge.py", "multiply.ge.py", "factorial.ge.py",
153
+ "fibonacci.ge.py", "is_prime.ge.py", "gcd.ge.py", "power.ge.py"]:
154
+ self.assertTrue((core / f).exists(), "app/core/" + f + " should exist")
155
+
156
+ def test_ui_files(self):
157
+ ui = ROOT / "app" / "ui"
158
+ for f in ["theme.ge.py", "layout.ge.py", "widgets.ge.py", "render.ge.py"]:
159
+ self.assertTrue((ui / f).exists(), "app/ui/" + f + " should exist")
160
+
161
+ def test_memory_files(self):
162
+ mem = ROOT / "app" / "memory"
163
+ for f in ["limits.ge.py", "buffer.ge.py", "state.ge.py"]:
164
+ self.assertTrue((mem / f).exists(), "app/memory/" + f + " should exist")
165
+
166
+ def test_entry_and_config(self):
167
+ # the build is `ge build desktop/main.ge.py` — no project build script
168
+ self.assertTrue((ROOT / "desktop" / "main.ge.py").exists())
169
+ self.assertTrue((ROOT / "ge.toml").exists())
170
+
171
+
172
+ if __name__ == "__main__":
173
+ unittest.main()
@@ -0,0 +1,115 @@
1
+ # {app_name}
2
+
3
+ Full-stack web application built with the GE programming language.
4
+
5
+ ## Architecture
6
+
7
+ ```
8
+ {app_name}/
9
+ app/
10
+ memory/ Rust — request bounds, buffer capacities
11
+ limits.ge.py
12
+ buffer.ge.py
13
+ core/ Rust — computation, one function per file
14
+ add.ge.py multiply.ge.py factorial.ge.py
15
+ fibonacci.ge.py is_prime.ge.py
16
+ main.ge.py aggregator
17
+ web/
18
+ server.ge.py Rust backend (HTTP API + static SPA)
19
+ frontend/ React 19 + TypeScript + Vite (generated, then yours)
20
+ src/App.tsx
21
+ src/api/client.ts
22
+ src/components/Ge*.tsx one component per file
23
+ ui/
24
+ main.ge.ui declarative UI — source of truth for the frontend
25
+ tests/
26
+ ```
27
+
28
+ ### Layers
29
+
30
+ | Layer | Language | Responsibility |
31
+ |-------|----------|----------------|
32
+ | Frontend | React + TypeScript | UI, calls `/api/call` |
33
+ | Backend | Rust | HTTP, dispatch, bounds enforcement |
34
+ | Memory | Rust | clamp inputs, cap buffers |
35
+ | Core | Rust | pure computation |
36
+
37
+ ### Data flow
38
+
39
+ ```
40
+ browser -> POST /api/call {name, args}
41
+ |
42
+ v
43
+ Rust backend -> mem_clamp_arg(args) -> core function
44
+ |
45
+ v
46
+ JSON {ok, value} -> React state -> GeText/GeValue
47
+ ```
48
+
49
+ ## Build output layout
50
+
51
+ Everything lands under `build/`, bucketed by platform so a multi-target
52
+ project stays readable:
53
+
54
+ ```
55
+ build/
56
+ desktop/ native desktop build
57
+ backend/ generated native sources
58
+ obj/ object files
59
+ bin/ executables
60
+ web/ web build
61
+ backend/ generated Rust backend
62
+ bin/ server executable
63
+ mobile/ Flutter project
64
+ backend/ ui/ lib/
65
+ ```
66
+
67
+ ## Build
68
+
69
+ ```bash
70
+ ge build web/server.ge.py --run # compile the Rust backend
71
+ ge react ui/main.ge.ui --app-name {app_name} # generate the React frontend
72
+ cd web/frontend && npm install && npm run build # bundle the SPA
73
+ ```
74
+
75
+ `ge build` compiles the backend straight from the GE sources. `ge react`
76
+ generates the frontend from the UI DSL, and only writes files that do not
77
+ already exist unless you pass `--force`.
78
+
79
+ ## Frontend workflow
80
+
81
+ `ui/main.ge.ui` is the source of truth for the layout:
82
+
83
+ ```bash
84
+ ge react ui/main.ge.ui --app-name {app_name} # regenerate (keeps edits)
85
+ ge react ui/main.ge.ui --force # regenerate, overwrite
86
+ cd web/frontend && npm run dev # dev server, proxies /api
87
+ ```
88
+
89
+ Files under `web/frontend/src` are ordinary React/TypeScript. Edit them
90
+ freely: regeneration only writes files that do not exist unless `--force`.
91
+
92
+ ## TypeScript source support
93
+
94
+ Any module can be written in TypeScript flavour instead of Python flavour.
95
+ Both lower to the same IR, so both compile to the same native code:
96
+
97
+ ```
98
+ app/core/add.ge.py Python flavour
99
+ app/core/add.ts.ge.py TypeScript flavour (same file name, .ts.ge)
100
+ app/core/add.ge.ts TypeScript flavour (alternate extension)
101
+ ```
102
+
103
+ TypeScript flavour example:
104
+
105
+ ```typescript
106
+ export function add(a: number, b: number): number {
107
+ return a + b;
108
+ }
109
+ ```
110
+
111
+ ## Test
112
+
113
+ ```bash
114
+ python -m unittest discover tests
115
+ ```
@@ -0,0 +1,9 @@
1
+ """Core function: add. Compiled to the selected native backend."""
2
+ from __future__ import annotations
3
+ from pyeffic.backends import rust
4
+
5
+
6
+ @rust
7
+ def add(a: int, b: int) -> int:
8
+ """Add two integers."""
9
+ return a + b
@@ -0,0 +1,16 @@
1
+ """Core function: factorial. Compiled to Rust."""
2
+ from __future__ import annotations
3
+ from pyeffic.backends import rust
4
+
5
+
6
+ @rust
7
+ def factorial(n: int) -> int:
8
+ """Compute factorial of n (n is clamped by the memory layer)."""
9
+ if n <= 1:
10
+ return 1
11
+ result: int = 1
12
+ i: int = 2
13
+ while i <= n:
14
+ result = result * i
15
+ i = i + 1
16
+ return result
@@ -0,0 +1,21 @@
1
+ """Core function: fibonacci. Compiled to Rust."""
2
+ from __future__ import annotations
3
+ from pyeffic.backends import rust
4
+
5
+
6
+ @rust
7
+ def fibonacci(n: int) -> int:
8
+ """Compute the nth Fibonacci number iteratively."""
9
+ if n <= 0:
10
+ return 0
11
+ if n == 1:
12
+ return 1
13
+ a: int = 0
14
+ b: int = 1
15
+ i: int = 2
16
+ while i <= n:
17
+ temp: int = a + b
18
+ a = b
19
+ b = temp
20
+ i = i + 1
21
+ return b