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,246 @@
1
+ """GE Standard Library
2
+
3
+ This module provides GE runtime helper functions that are emitted as
4
+ backend-specific runtime code. These are NOT Python functions — they are
5
+ templates that get inlined into the generated native code.
6
+
7
+ Available categories:
8
+ - file I/O: open, read, write, close
9
+ - JSON: parse, stringify
10
+ - string: upper, lower, strip, contains, split, join
11
+ - math: sqrt, sin, cos, tan, log, exp, floor, ceil, round
12
+
13
+ Each function maps to backend-specific runtime code emitted by the
14
+ backend's Spec or emitter.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ # File I/O runtime templates per backend
19
+ FILE_IO_RUNTIME = {
20
+ "rust": '''
21
+ // GE File I/O runtime (Rust)
22
+ use std::io::{Read, Write};
23
+
24
+ fn ge_read_file(path: &str) -> String {
25
+ std::fs::read_to_string(path).unwrap_or_default()
26
+ }
27
+
28
+ fn ge_write_file(path: &str, content: &str) -> bool {
29
+ std::fs::write(path, content).is_ok()
30
+ }
31
+
32
+ fn ge_append_file(path: &str, content: &str) -> bool {
33
+ use std::io::Write;
34
+ let mut f = std::fs::OpenOptions::new()
35
+ .append(true)
36
+ .create(true)
37
+ .open(path);
38
+ match f {
39
+ Ok(mut f) => f.write_all(content.as_bytes()).is_ok(),
40
+ Err(_) => false,
41
+ }
42
+ }
43
+ ''',
44
+ "cpp": '''
45
+ // GE File I/O runtime (C++)
46
+ #include <fstream>
47
+ #include <sstream>
48
+
49
+ std::string ge_read_file(const std::string& path) {
50
+ std::ifstream f(path);
51
+ std::stringstream ss;
52
+ ss << f.rdbuf();
53
+ return ss.str();
54
+ }
55
+
56
+ bool ge_write_file(const std::string& path, const std::string& content) {
57
+ std::ofstream f(path);
58
+ if (!f) return false;
59
+ f << content;
60
+ return true;
61
+ }
62
+
63
+ bool ge_append_file(const std::string& path, const std::string& content) {
64
+ std::ofstream f(path, std::ios::app);
65
+ if (!f) return false;
66
+ f << content;
67
+ return true;
68
+ }
69
+ ''',
70
+ "csharp": '''
71
+ // GE File I/O runtime (C#)
72
+ static string GeReadFile(string path) {
73
+ return System.IO.File.ReadAllText(path);
74
+ }
75
+
76
+ static bool GeWriteFile(string path, string content) {
77
+ try { System.IO.File.WriteAllText(path, content); return true; }
78
+ catch { return false; }
79
+ }
80
+
81
+ static bool GeAppendFile(string path, string content) {
82
+ try { System.IO.File.AppendAllText(path, content); return true; }
83
+ catch { return false; }
84
+ }
85
+ ''',
86
+ "go": '''
87
+ // GE File I/O runtime (Go)
88
+ func geReadFile(path string) string {
89
+ data, err := os.ReadFile(path)
90
+ if err != nil { return "" }
91
+ return string(data)
92
+ }
93
+
94
+ func geWriteFile(path string, content string) bool {
95
+ return os.WriteFile(path, []byte(content), 0644) == nil
96
+ }
97
+
98
+ func geAppendFile(path string, content string) bool {
99
+ f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
100
+ if err != nil { return false }
101
+ defer f.Close()
102
+ _, err = f.WriteString(content)
103
+ return err == nil
104
+ }
105
+ ''',
106
+ "kotlin": '''
107
+ // GE File I/O runtime (Kotlin) — file I/O requires JDK
108
+ // geReadFile/geWriteFile/geAppendFile omitted (JDK not in default classpath)
109
+ ''',
110
+ "zig": '''
111
+ // GE File I/O runtime (Zig)
112
+ fn ge_read_file(path: []const u8) []const u8 {
113
+ const file = std.fs.cwd().openFile(path, .{}) catch return "";
114
+ defer file.close();
115
+ const stat = file.stat() catch return "";
116
+ const buf = std.heap.page_allocator.alloc(u8, stat.size) catch return "";
117
+ _ = file.read(buf) catch return "";
118
+ return buf;
119
+ }
120
+
121
+ fn ge_write_file(path: []const u8, content: []const u8) bool {
122
+ const file = std.fs.cwd.createFile(path, .{}) catch return false;
123
+ defer file.close();
124
+ _ = file.write(content) catch return false;
125
+ return true;
126
+ }
127
+
128
+ fn ge_append_file(path: []const u8, content: []const u8) bool {
129
+ const file = std.fs.cwd.openFile(path, .{ .mode = .write_only }) catch {
130
+ const f2 = std.fs.cwd.createFile(path, .{}) catch return false;
131
+ defer f2.close();
132
+ _ = f2.write(content) catch return false;
133
+ return true;
134
+ };
135
+ defer file.close();
136
+ file.seekFromEnd(0) catch return false;
137
+ _ = file.write(content) catch return false;
138
+ return true;
139
+ }
140
+ ''',
141
+ }
142
+
143
+ # Math runtime templates per backend
144
+ MATH_RUNTIME = {
145
+ "rust": '''
146
+ // GE Math runtime (Rust)
147
+ fn ge_sqrt(x: f64) -> f64 { x.sqrt() }
148
+ fn ge_floor(x: f64) -> f64 { x.floor() }
149
+ fn ge_ceil(x: f64) -> f64 { x.ceil() }
150
+ fn ge_round(x: f64) -> f64 { x.round() }
151
+ fn ge_sin(x: f64) -> f64 { x.sin() }
152
+ fn ge_cos(x: f64) -> f64 { x.cos() }
153
+ fn ge_tan(x: f64) -> f64 { x.tan() }
154
+ fn ge_log(x: f64) -> f64 { x.ln() }
155
+ fn ge_exp(x: f64) -> f64 { x.exp() }
156
+ ''',
157
+ "cpp": '''
158
+ // GE Math runtime (C++)
159
+ double ge_sqrt(double x) { return std::sqrt(x); }
160
+ double ge_floor(double x) { return std::floor(x); }
161
+ double ge_ceil(double x) { return std::ceil(x); }
162
+ double ge_round(double x) { return std::round(x); }
163
+ double ge_sin(double x) { return std::sin(x); }
164
+ double ge_cos(double x) { return std::cos(x); }
165
+ double ge_tan(double x) { return std::tan(x); }
166
+ double ge_log(double x) { return std::log(x); }
167
+ double ge_exp(double x) { return std::exp(x); }
168
+ ''',
169
+ "csharp": '''
170
+ // GE Math runtime (C#)
171
+ static double GeSqrt(double x) { return System.Math.Sqrt(x); }
172
+ static double GeFloor(double x) { return System.Math.Floor(x); }
173
+ static double GeCeil(double x) { return System.Math.Ceiling(x); }
174
+ static double GeRound(double x) { return System.Math.Round(x); }
175
+ static double GeSin(double x) { return System.Math.Sin(x); }
176
+ static double GeCos(double x) { return System.Math.Cos(x); }
177
+ static double GeTan(double x) { return System.Math.Tan(x); }
178
+ static double GeLog(double x) { return System.Math.Log(x); }
179
+ static double GeExp(double x) { return System.Math.Exp(x); }
180
+ ''',
181
+ "go": '''
182
+ // GE Math runtime (Go)
183
+ func geSqrt(x float64) float64 { return math.Sqrt(x) }
184
+ func geFloor(x float64) float64 { return math.Floor(x) }
185
+ func geCeil(x float64) float64 { return math.Ceil(x) }
186
+ func geRound(x float64) float64 { return math.Round(x) }
187
+ func geSin(x float64) float64 { return math.Sin(x) }
188
+ func geCos(x float64) float64 { return math.Cos(x) }
189
+ func geTan(x float64) float64 { return math.Tan(x) }
190
+ func geLog(x float64) float64 { return math.Log(x) }
191
+ func geExp(x float64) float64 { return math.Exp(x) }
192
+ ''',
193
+ "kotlin": '''
194
+ // GE Math runtime (Kotlin)
195
+ import kotlin.math.*
196
+ fun ge_sqrt(x: Double): Double = sqrt(x)
197
+ fun ge_floor(x: Double): Double = floor(x)
198
+ fun ge_ceil(x: Double): Double = ceil(x)
199
+ fun ge_round(x: Double): Double = round(x)
200
+ fun ge_sin(x: Double): Double = sin(x)
201
+ fun ge_cos(x: Double): Double = cos(x)
202
+ fun ge_tan(x: Double): Double = tan(x)
203
+ fun ge_log(x: Double): Double = ln(x)
204
+ fun ge_exp(x: Double): Double = exp(x)
205
+ ''',
206
+ "zig": '''
207
+ // GE Math runtime (Zig)
208
+ fn ge_sqrt(x: f64) f64 { return @sqrt(x); }
209
+ fn ge_floor(x: f64) f64 { return @floor(x); }
210
+ fn ge_ceil(x: f64) f64 { return @ceil(x); }
211
+ fn ge_round(x: f64) f64 { return @round(x); }
212
+ fn ge_sin(x: f64) f64 { return std.math.sin(x); }
213
+ fn ge_cos(x: f64) f64 { return std.math.cos(x); }
214
+ fn ge_tan(x: f64) f64 { return std.math.tan(x); }
215
+ fn ge_log(x: f64) f64 { return @log(x); }
216
+ fn ge_exp(x: f64) f64 { return @exp(x); }
217
+ ''',
218
+ }
219
+
220
+
221
+ def get_runtime(backend: str, include_math: bool = True) -> str:
222
+ """Get the standard library runtime code for a backend.
223
+
224
+ Args:
225
+ backend: one of rust, cpp, csharp, zig, go, kotlin
226
+ include_math: whether to include math functions
227
+
228
+ Returns:
229
+ Backend-specific runtime code as a string.
230
+ """
231
+ parts = []
232
+ if backend in FILE_IO_RUNTIME:
233
+ parts.append(FILE_IO_RUNTIME[backend])
234
+ if include_math and backend in MATH_RUNTIME:
235
+ parts.append(MATH_RUNTIME[backend])
236
+ return "\n".join(parts)
237
+
238
+
239
+ def get_file_io_runtime(backend: str) -> str:
240
+ """Get just the file I/O runtime for a backend."""
241
+ return FILE_IO_RUNTIME.get(backend, "")
242
+
243
+
244
+ def get_math_runtime(backend: str) -> str:
245
+ """Get just the math runtime for a backend."""
246
+ return MATH_RUNTIME.get(backend, "")
@@ -0,0 +1,220 @@
1
+ """GE styling: a CSS-like style DSL that emits to multiple native targets.
2
+
3
+ One syntax, many backends. Write:
4
+ style="fontSize:20; bold:true; color:#2196F3; padding:16; radius:12"
5
+
6
+ The parser turns it into a target-agnostic dict:
7
+ {"fontSize": 20, "bold": True, "color": "#2196F3", "padding": 16, "radius": 12}
8
+
9
+ Emitters then convert that dict to target-native code:
10
+ - style_to_flutter() -> TextStyle / EdgeInsets / BoxDecoration / Color
11
+ - style_to_slint() -> Slint property blocks (Rust/C++ GUI) [stub for later]
12
+
13
+ This is the same pattern Slint (HTML/CSS-like DSL for Rust) and Wind (Tailwind
14
+ for Flutter) use, but unified under GE's single-source model.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import re
19
+
20
+ # ---- named style shortcuts (backward-compatible with the old style="headline") ----
21
+ NAMED_STYLES: dict[str, str] = {
22
+ "headline": "fontSize:28; bold:true",
23
+ "title": "fontSize:20; bold:true",
24
+ "subtitle": "fontSize:16; color:grey700",
25
+ "body": "fontSize:16",
26
+ "caption": "fontSize:13; color:grey600",
27
+ "button": "fontSize:16; bold:true; color:white",
28
+ }
29
+
30
+ # named colors -> Flutter Colors.xxx (None = use hex Color(0xFF...))
31
+ NAMED_COLORS: dict[str, str] = {
32
+ "red": "Colors.red", "blue": "Colors.blue", "green": "Colors.green",
33
+ "white": "Colors.white", "black": "Colors.black", "grey": "Colors.grey",
34
+ "orange": "Colors.orange", "yellow": "Colors.yellow", "purple": "Colors.purple",
35
+ "pink": "Colors.pink", "teal": "Colors.teal", "cyan": "Colors.cyan",
36
+ "indigo": "Colors.indigo", "lime": "Colors.lime", "amber": "Colors.amber",
37
+ "brown": "Colors.brown",
38
+ }
39
+
40
+ # grey shade tokens like "grey700" -> Colors.grey[700]
41
+ def _grey_shade(token: str) -> str | None:
42
+ m = re.fullmatch(r"grey(\d+)", token)
43
+ if m:
44
+ return f"Colors.grey[{m.group(1)}]"
45
+ return None
46
+
47
+
48
+ def parse_style(style_str: str) -> dict:
49
+ """Parse a CSS-like style string into a dict. Handles named shortcuts."""
50
+ if not style_str:
51
+ return {}
52
+ s = style_str.strip()
53
+ # named shortcut?
54
+ if s in NAMED_STYLES:
55
+ return parse_style(NAMED_STYLES[s])
56
+ # also allow "headline; color:blue" (named + overrides)
57
+ parts = [p.strip() for p in s.split(";") if p.strip()]
58
+ result: dict = {}
59
+ for part in parts:
60
+ if part in NAMED_STYLES:
61
+ result.update(parse_style(NAMED_STYLES[part]))
62
+ continue
63
+ if ":" not in part:
64
+ continue
65
+ key, val = part.split(":", 1)
66
+ key = key.strip()
67
+ val = val.strip()
68
+ result[key] = _parse_value(key, val)
69
+ return result
70
+
71
+
72
+ def _parse_value(key: str, val: str):
73
+ """Convert a string value to the right Python type per property."""
74
+ if key in ("bold", "italic", "underline"):
75
+ return val.lower() in ("true", "yes", "1")
76
+ if key in ("fontSize", "padding", "margin", "radius", "width", "height",
77
+ "letterSpacing", "border", "gap", "spacing"):
78
+ try:
79
+ return int(val)
80
+ except ValueError:
81
+ try:
82
+ return float(val)
83
+ except ValueError:
84
+ return val
85
+ # color / bg / border color: keep as string
86
+ return val
87
+
88
+
89
+ # ---- Flutter emitter ----
90
+
91
+ def _flutter_color(val: str) -> str:
92
+ """Convert a color value to a Flutter Dart expression."""
93
+ if not isinstance(val, str):
94
+ val = str(val)
95
+ if val.startswith("#"):
96
+ hexv = val[1:]
97
+ if len(hexv) == 3:
98
+ hexv = "".join(c * 2 for c in hexv)
99
+ return f"Color(0xFF{hexv})"
100
+ grey = _grey_shade(val)
101
+ if grey:
102
+ return grey
103
+ # shade tokens like "blue.shade50"
104
+ if ".shade" in val:
105
+ name, shade = val.split(".shade")
106
+ if name in NAMED_COLORS:
107
+ return f"{NAMED_COLORS[name]}.shade{shade}"
108
+ if val in NAMED_COLORS:
109
+ return NAMED_COLORS[val]
110
+ # fallback: treat as hex without #
111
+ return f"Color(0xFF{val})"
112
+
113
+
114
+ def style_to_flutter_text(d: dict) -> str:
115
+ """Emit a Flutter TextStyle from a style dict (text-related props)."""
116
+ if not d:
117
+ return "TextStyle(fontSize: 16)"
118
+ parts = []
119
+ if "fontSize" in d:
120
+ parts.append(f"fontSize: {d['fontSize']}")
121
+ if d.get("bold"):
122
+ parts.append("fontWeight: FontWeight.bold")
123
+ if d.get("italic"):
124
+ parts.append("fontStyle: FontStyle.italic")
125
+ if "color" in d:
126
+ parts.append(f"color: {_flutter_color(d['color'])}")
127
+ if "letterSpacing" in d:
128
+ parts.append(f"letterSpacing: {d['letterSpacing']}")
129
+ if not parts:
130
+ return "TextStyle(fontSize: 16)"
131
+ return f"TextStyle({', '.join(parts)})"
132
+
133
+
134
+ def style_to_flutter_padding(d: dict, key: str = "padding") -> str:
135
+ """Emit Flutter EdgeInsets from padding/margin."""
136
+ if key not in d:
137
+ return "EdgeInsets.all(0)"
138
+ v = d[key]
139
+ return f"EdgeInsets.all({v})"
140
+
141
+
142
+ def style_to_flutter_box(d: dict) -> str:
143
+ """Emit a Flutter BoxDecoration from bg/radius/border props."""
144
+ decs = []
145
+ if "bg" in d:
146
+ decs.append(f"color: {_flutter_color(d['bg'])}")
147
+ if "radius" in d:
148
+ decs.append(f"borderRadius: BorderRadius.circular({d['radius']})")
149
+ if "border" in d:
150
+ bval = d["border"]
151
+ if isinstance(bval, (int, float)):
152
+ decs.append(f"border: Border.all(width: {bval})")
153
+ elif isinstance(bval, str) and "," in bval:
154
+ w, col = bval.split(",", 1)
155
+ decs.append(
156
+ f"border: Border.all(color: {_flutter_color(col.strip())}, width: {w.strip()})")
157
+ if not decs:
158
+ return ""
159
+ return f"BoxDecoration({', '.join(decs)})"
160
+
161
+
162
+ def style_to_flutter_main_align(d: dict) -> str:
163
+ """MainAxisAlignment from align prop."""
164
+ a = d.get("align", "center")
165
+ mapping = {"center": "center", "start": "start", "end": "end",
166
+ "spaceBetween": "spaceBetween", "spaceEvenly": "spaceEvenly",
167
+ "spaceAround": "spaceAround"}
168
+ return f"MainAxisAlignment.{mapping.get(a, 'center')}"
169
+
170
+
171
+ # ---- Slint emitter (stub — proves the same dict targets Rust/C++ GUI later) ----
172
+
173
+ SLINT_TYPE_MAP = {
174
+ "fontSize": "font-size", "bold": "font-weight", "color": "color",
175
+ "padding": "padding", "bg": "background", "radius": "border-radius",
176
+ "width": "width", "height": "height", "border": "border-width",
177
+ }
178
+
179
+
180
+ def style_to_slint(d: dict, indent: int = 2) -> str:
181
+ """Emit Slint property assignments from the same style dict.
182
+
183
+ Slint is a declarative GUI DSL for Rust/C++ with CSS-like properties.
184
+ This stub shows the SAME dict that emits Flutter Dart above can emit
185
+ Slint properties — proving multi-target UI from one style syntax.
186
+ """
187
+ pad = " " * indent
188
+ lines = []
189
+ for key, slint_key in SLINT_TYPE_MAP.items():
190
+ if key not in d:
191
+ continue
192
+ v = d[key]
193
+ if key == "bold":
194
+ v = "700" if v else "400"
195
+ if key == "border" and isinstance(v, str) and "," in v:
196
+ w, col = v.split(",", 1)
197
+ lines.append(f"{pad}border-width: {w.strip()};")
198
+ lines.append(f"{pad}border-color: {col.strip()};")
199
+ continue
200
+ if key in ("color", "bg") and isinstance(v, str):
201
+ if not v.startswith("#"):
202
+ v = "#000000" # fallback
203
+ lines.append(f"{pad}{slint_key}: {v};")
204
+ return "\n".join(lines)
205
+
206
+
207
+ def style_to_rust_egui(d: dict) -> str:
208
+ """Emit egui (Rust immediate-mode GUI) style calls from the same dict.
209
+
210
+ Another target proving the dict is portable. egui is a popular Rust GUI lib.
211
+ """
212
+ parts = []
213
+ if "fontSize" in d:
214
+ parts.append(f".font_size({d['fontSize']} as f32)")
215
+ if d.get("bold"):
216
+ parts.append(".strong()")
217
+ if "color" in d and isinstance(d["color"], str) and d["color"].startswith("#"):
218
+ hexv = d["color"][1:]
219
+ parts.append(f".color(Color32::from_rgb(0x{hexv[0:2]}, 0x{hexv[2:4]}, 0x{hexv[4:6]}))")
220
+ return "".join(parts) if parts else ""
@@ -0,0 +1,106 @@
1
+ # {app_name}
2
+
3
+ Native desktop application built with the GE programming language.
4
+
5
+ ## Architecture
6
+
7
+ ```
8
+ {app_name}/
9
+ app/
10
+ memory/ Rust — bounded state, buffers, limits
11
+ limits.ge.py input bounds + clamping
12
+ buffer.ge.py fixed-size buffer capacities
13
+ state.ge.py initial state + operation cycling
14
+ core/ C++ — computation, one function per file
15
+ add.ge.py multiply.ge.py factorial.ge.py
16
+ fibonacci.ge.py is_prime.ge.py gcd.ge.py power.ge.py
17
+ ui/ C++ — design, one concern per file
18
+ theme.ge.py colors, font sizes, spacing
19
+ layout.ge.py geometry math
20
+ widgets.ge.py drawing primitives
21
+ render.ge.py frame orchestrator
22
+ main.ge.py aggregator (single import surface)
23
+ desktop/
24
+ main.ge.py Rust shell — window, event loop, input
25
+ tests/ unit tests for all three layers
26
+ ```
27
+
28
+ ### Layer responsibilities
29
+
30
+ | Layer | Language | Responsibility | Why |
31
+ |-------|----------|----------------|-----|
32
+ | Shell | Rust | window, message loop, input, state | memory safety — no leaks, no use-after-free |
33
+ | Core | C++ | computation (factorial, fibonacci, gcd, ...) | raw speed for hot math |
34
+ | UI | C++ | theme, layout, widgets, rendering | native GDI, no runtime, small binary |
35
+
36
+ The Rust shell holds **fixed-size scalar state only**. Nothing grows at
37
+ runtime, so memory use is constant no matter how long the app runs.
38
+
39
+ ### Data flow
40
+
41
+ ```
42
+ keyboard -> Rust wnd_proc -> mem_step_up / run_op
43
+ | |
44
+ | +-> C++ core (factorial, ...)
45
+ +-> bounded state (G_INPUT, G_RESULT, G_OP)
46
+ |
47
+ WM_PAINT v
48
+ Rust calls ui_draw_frame(hdc, w, h, ...)
49
+ |
50
+ C++ UI draws: bg -> grid -> sidebar -> card
51
+ ```
52
+
53
+ ## Build
54
+
55
+ ```bash
56
+ ge build desktop/main.ge.py --run # one command: C++ object + Rust exe
57
+ ge build desktop/main.ge.py -o out # custom output dir
58
+ ```
59
+
60
+ `ge build` detects that the program mixes backends and links them itself:
61
+ it emits the C++ functions in library mode (`extern "C"`), compiles them to
62
+ an object file, emits the Rust shell with matching `extern "C"` declarations,
63
+ and links one executable. No project build script is needed.
64
+
65
+ ## Test
66
+
67
+ ```bash
68
+ python -m unittest discover tests
69
+ ```
70
+
71
+ ## Controls
72
+
73
+ | Key | Action |
74
+ |-----|--------|
75
+ | Up / Down | change input (clamped by memory layer) |
76
+ | Enter | run the selected operation (C++ core) |
77
+ | F / P / G | fibonacci / is_prime / gcd |
78
+ | Esc | quit |
79
+
80
+ ## Adding a core function
81
+
82
+ 1. Create `app/core/my_func.ge.py`:
83
+
84
+ ```python
85
+ from pyeffic.backends import cpp
86
+
87
+ @cpp
88
+ def my_func(x: int) -> int:
89
+ return x * 2
90
+ ```
91
+
92
+ 2. Re-export it in `app/main.ge.py`.
93
+ 3. Declare it in the FFI block of `desktop/main.ge.py`:
94
+
95
+ ```rust
96
+ fn my_func(x: i64) -> i64;
97
+ ```
98
+
99
+ 4. Call it from the Rust shell and add a test.
100
+
101
+ ## Adding a UI element
102
+
103
+ 1. Add tokens to `app/ui/theme.ge.py` if you need new colors/sizes.
104
+ 2. Add geometry to `app/ui/layout.ge.py`.
105
+ 3. Add a drawing primitive to `app/ui/widgets.ge.py`.
106
+ 4. Compose it in `app/ui/render.ge.py`.
@@ -0,0 +1,13 @@
1
+ """Core function: add. Compiled to C++ for performance.
2
+
3
+ One function per file (C++ Core Guidelines SF.1-SF.5): each function is a
4
+ separate compilation unit with a clear dependency list.
5
+ """
6
+ from __future__ import annotations
7
+ from pyeffic.backends import cpp
8
+
9
+
10
+ @cpp
11
+ def add(a: int, b: int) -> int:
12
+ """Add two integers."""
13
+ return a + b
@@ -0,0 +1,20 @@
1
+ """Core function: factorial. Compiled to C++ for performance.
2
+
3
+ One function per file (C++ Core Guidelines SF.1-SF.5): each function is a
4
+ separate compilation unit with a clear dependency list.
5
+ """
6
+ from __future__ import annotations
7
+ from pyeffic.backends import cpp
8
+
9
+
10
+ @cpp
11
+ def factorial(n: int) -> int:
12
+ """Compute factorial of n."""
13
+ if n <= 1:
14
+ return 1
15
+ result: int = 1
16
+ i: int = 2
17
+ while i <= n:
18
+ result = result * i
19
+ i = i + 1
20
+ return result
@@ -0,0 +1,25 @@
1
+ """Core function: fibonacci. Compiled to C++ for performance.
2
+
3
+ One function per file (C++ Core Guidelines SF.1-SF.5): each function is a
4
+ separate compilation unit with a clear dependency list.
5
+ """
6
+ from __future__ import annotations
7
+ from pyeffic.backends import cpp
8
+
9
+
10
+ @cpp
11
+ def fibonacci(n: int) -> int:
12
+ """Compute the nth Fibonacci number iteratively."""
13
+ if n <= 0:
14
+ return 0
15
+ if n == 1:
16
+ return 1
17
+ a: int = 0
18
+ b: int = 1
19
+ i: int = 2
20
+ while i <= n:
21
+ temp: int = a + b
22
+ a = b
23
+ b = temp
24
+ i = i + 1
25
+ return b
@@ -0,0 +1,19 @@
1
+ """Core function: gcd. Compiled to C++ for performance.
2
+
3
+ One function per file (C++ Core Guidelines SF.1-SF.5): each function is a
4
+ separate compilation unit with a clear dependency list.
5
+ """
6
+ from __future__ import annotations
7
+ from pyeffic.backends import cpp
8
+
9
+
10
+ @cpp
11
+ def gcd(a: int, b: int) -> int:
12
+ """Greatest common divisor (Euclidean algorithm)."""
13
+ while b != 0:
14
+ temp: int = b
15
+ b = a % b
16
+ a = temp
17
+ if a < 0:
18
+ a = -a
19
+ return a
@@ -0,0 +1,24 @@
1
+ """Core function: is_prime. Compiled to C++ for performance.
2
+
3
+ One function per file (C++ Core Guidelines SF.1-SF.5): each function is a
4
+ separate compilation unit with a clear dependency list.
5
+ """
6
+ from __future__ import annotations
7
+ from pyeffic.backends import cpp
8
+
9
+
10
+ @cpp
11
+ def is_prime(n: int) -> int:
12
+ """Return 1 if n is prime, 0 otherwise."""
13
+ if n < 2:
14
+ return 0
15
+ if n == 2:
16
+ return 1
17
+ if n % 2 == 0:
18
+ return 0
19
+ i: int = 3
20
+ while i * i <= n:
21
+ if n % i == 0:
22
+ return 0
23
+ i = i + 2
24
+ return 1