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,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`.
|
|
File without changes
|
|
File without changes
|
|
@@ -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
|