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,238 @@
1
+ """API surface dump and check.
2
+
3
+ A committed description of the surfaces users depend on, so a change to them
4
+ is a deliberate, reviewable act rather than an accident.
5
+
6
+ api/cli.api every command, flag and argument
7
+ api/diagnostics.api every GE*** code and its meaning
8
+ api/intrinsics.api the compiler intrinsics and their backends
9
+
10
+ Modelled on Kotlin's `apiDump`/`apiCheck` and Go's `apidiff`: the dump is
11
+ committed, `--check` fails when it drifts, and changing it means running
12
+ `--update`, reviewing the diff, and committing it.
13
+
14
+ Usage
15
+ -----
16
+ ge api-check fail on unexplained surface change
17
+ ge api-dump regenerate the dumps (review, then commit)
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import sys
22
+ from pathlib import Path
23
+
24
+ #: backend decorators and the intrinsics every frontend exposes
25
+ INTRINSICS = {
26
+ "ge_inline": "target-specific inline code, function body",
27
+ "ge_raw": "unconditional raw code, function body",
28
+ "ge_preamble": "file-scope code injection, module level",
29
+ "gePreamble": "TypeScript-flavour spelling of ge_preamble",
30
+ "geInline": "TypeScript-flavour spelling of ge_inline",
31
+ "geRaw": "TypeScript-flavour spelling of ge_raw",
32
+ }
33
+
34
+ BACKEND_DECORATORS = ("rust", "cpp", "csharp", "zig", "go", "kotlin", "dart")
35
+
36
+ #: source extensions and the frontend each selects
37
+ FRONTENDS = {
38
+ ".ge": "hybrid",
39
+ ".ge.py": "python",
40
+ ".ge.ts": "typescript",
41
+ ".ts.ge.py": "typescript (legacy alias)",
42
+ ".ge.ui": "ui DSL",
43
+ }
44
+
45
+ #: where the dumps live, relative to the repo root
46
+ API_DIR = "api"
47
+
48
+
49
+ def api_root() -> Path:
50
+ return Path(__file__).resolve().parent.parent / API_DIR
51
+
52
+
53
+ # ---------------------------------------------------------------------------
54
+ # Dump generation
55
+ # ---------------------------------------------------------------------------
56
+
57
+ def dump_cli() -> str:
58
+ """Every command, flag and argument, sorted and stable."""
59
+ import argparse
60
+
61
+ from . import ge_cli
62
+
63
+ # build the parser the same way the CLI does
64
+ parser = argparse.ArgumentParser(prog="ge", add_help=False)
65
+ sub = parser.add_subparsers(dest="cmd")
66
+
67
+ # ge_cli.main() constructs the parser inline; capture it by running the
68
+ # module's parser builder if exposed, otherwise introspect the actions
69
+ # of a freshly built parser via a dry run.
70
+ import io
71
+ from contextlib import redirect_stderr, redirect_stdout
72
+
73
+ captured = None
74
+
75
+ real_add_subparsers = argparse.ArgumentParser.add_subparsers
76
+
77
+ def spy(self, *a, **kw):
78
+ nonlocal captured
79
+ sp = real_add_subparsers(self, *a, **kw)
80
+ captured = self
81
+ return sp
82
+
83
+ argparse.ArgumentParser.add_subparsers = spy
84
+ try:
85
+ with redirect_stdout(io.StringIO()), redirect_stderr(io.StringIO()):
86
+ try:
87
+ ge_cli.main(["compilers"])
88
+ except SystemExit:
89
+ pass
90
+ finally:
91
+ argparse.ArgumentParser.add_subparsers = real_add_subparsers
92
+
93
+ lines: list[str] = []
94
+ if captured is not None:
95
+ for action in captured._actions:
96
+ if isinstance(action, argparse._SubParsersAction):
97
+ for name in sorted(action.choices):
98
+ sub_parser = action.choices[name]
99
+ # one line per (command, surface) pair so a diff names the
100
+ # exact command that changed
101
+ lines.append(f"{name} <command>")
102
+ for sub_action in sub_parser._actions:
103
+ if sub_action.option_strings:
104
+ opts = ", ".join(sorted(sub_action.option_strings))
105
+ lines.append(f"{name} option {opts}")
106
+ elif sub_action.dest not in ("help",):
107
+ suffix = "" if sub_action.nargs != "?" else "?"
108
+ lines.append(f"{name} arg {sub_action.dest}{suffix}")
109
+ lines.sort()
110
+ return "\n".join(lines) + "\n"
111
+
112
+
113
+ def dump_diagnostics() -> str:
114
+ """Every diagnostic code the compiler can emit."""
115
+ from .diagnostics import ErrorReporter # noqa: F401
116
+
117
+ known = {
118
+ "GE001": "unsupported construct / import warning",
119
+ "GE002": "type mismatch or lossy conversion",
120
+ "GE003": "reserved for future use",
121
+ "GE004": "reserved for future use",
122
+ "GE005": "native compilation failed",
123
+ "GE006": "reserved for future use",
124
+ "GE007": "function called but not defined",
125
+ "GE008": "entry point not found",
126
+ "GE009": "reserved for future use",
127
+ "GE010": "source frontend error (TypeScript/hybrid lowering)",
128
+ "GE011": "identifier renamed to avoid a target-language keyword",
129
+ "GE020": "deprecated form (planned)",
130
+ }
131
+ lines = [f"{code} {text}" for code, text in sorted(known.items())]
132
+ return "\n".join(lines) + "\n"
133
+
134
+
135
+ def dump_intrinsics() -> str:
136
+ lines: list[str] = []
137
+ for name, desc in sorted(INTRINSICS.items()):
138
+ lines.append(f"intrinsic {name}: {desc}")
139
+ for dec in sorted(BACKEND_DECORATORS):
140
+ lines.append(f"decorator @{dec}")
141
+ for ext, frontend in sorted(FRONTENDS.items()):
142
+ lines.append(f"extension {ext} -> {frontend}")
143
+ return "\n".join(lines) + "\n"
144
+
145
+
146
+ def all_dumps() -> dict[str, str]:
147
+ return {
148
+ "cli.api": dump_cli(),
149
+ "diagnostics.api": dump_diagnostics(),
150
+ "intrinsics.api": dump_intrinsics(),
151
+ }
152
+
153
+
154
+ # ---------------------------------------------------------------------------
155
+ # Check / update
156
+ # ---------------------------------------------------------------------------
157
+
158
+ def check(root: Path | None = None) -> tuple[list[str], list[str]]:
159
+ """Return (changed, missing) file names."""
160
+ root = root or api_root()
161
+ changed: list[str] = []
162
+ missing: list[str] = []
163
+ for name, content in all_dumps().items():
164
+ p = root / name
165
+ if not p.exists():
166
+ missing.append(name)
167
+ continue
168
+ if p.read_text(encoding="utf-8").replace("\r\n", "\n") != content:
169
+ changed.append(name)
170
+ return changed, missing
171
+
172
+
173
+ def update(root: Path | None = None) -> list[str]:
174
+ root = root or api_root()
175
+ root.mkdir(parents=True, exist_ok=True)
176
+ written: list[str] = []
177
+ for name, content in all_dumps().items():
178
+ p = root / name
179
+ old = p.read_text(encoding="utf-8") if p.exists() else None
180
+ if old is None or old.replace("\r\n", "\n") != content:
181
+ p.write_text(content, encoding="utf-8")
182
+ written.append(name)
183
+ return written
184
+
185
+
186
+ # ---------------------------------------------------------------------------
187
+ # CLI
188
+ # ---------------------------------------------------------------------------
189
+
190
+ def main(argv: list[str] | None = None) -> int:
191
+ import argparse
192
+
193
+ p = argparse.ArgumentParser(
194
+ prog="ge api-check",
195
+ description="Check or regenerate the committed API surface dumps")
196
+ p.add_argument("--update", action="store_true",
197
+ help="regenerate the dumps (review the diff, then commit)")
198
+ args = p.parse_args(argv)
199
+
200
+ root = api_root()
201
+
202
+ if args.update:
203
+ written = update(root)
204
+ print("GE API surface — update")
205
+ print(f" dir : {root}")
206
+ print(f" written: {len(written)}")
207
+ for name in written:
208
+ print(f" {name}")
209
+ if not written:
210
+ print(" (no changes)")
211
+ print()
212
+ print("review the diff, then commit it")
213
+ return 0
214
+
215
+ changed, missing = check(root)
216
+ print("GE API surface — check")
217
+ print(f" dir: {root}")
218
+ print()
219
+ if not changed and not missing:
220
+ print("the committed API surface matches the compiler")
221
+ return 0
222
+
223
+ if missing:
224
+ print("missing dumps (run: ge api-check --update):")
225
+ for name in missing:
226
+ print(f" {name}")
227
+ if changed:
228
+ print("changed surface (review, then: ge api-check --update):")
229
+ for name in changed:
230
+ print(f" {name}")
231
+ print()
232
+ print("a change here is a change users can see — treat it as breaking")
233
+ print("unless it is purely additive")
234
+ return 1
235
+
236
+
237
+ if __name__ == "__main__":
238
+ sys.exit(main())
@@ -0,0 +1,327 @@
1
+ """Auto-selection rule engine for GE backend selection.
2
+
3
+ When the user does NOT specify a backend with @rust/@cpp/@csharp/@zig/@go/@kotlin,
4
+ the auto-selector analyzes each function's characteristics and picks the best
5
+ backend based on research-backed rules.
6
+
7
+ Rules are derived from web research on best use cases for each language:
8
+
9
+ Rust — memory safety, concurrency, systems, security-sensitive, long-running services
10
+ C++ — game engines, CUDA, OpenCV, performance-critical computation, template metaprogramming
11
+ C# — enterprise logic, game dev (Unity), .NET ecosystem, business logic, data processing
12
+ Dart — Flutter UI, async event loop, state management, cross-platform UI
13
+ Zig — embedded systems, toolchains, C replacement, manual memory control, no runtime
14
+ Go — network services, microservices, cloud infrastructure, goroutine concurrency
15
+ Kotlin — Android, Kotlin Multiplatform, JVM backend, coroutines, null-safety
16
+
17
+ The engine scores each backend for each function based on detected features,
18
+ then picks the highest-scoring backend. If all scores are zero, it defaults to
19
+ Rust (the safest general-purpose systems language).
20
+ """
21
+ from __future__ import annotations
22
+
23
+ import ast
24
+ import re
25
+ from dataclasses import dataclass, field
26
+ from .analyzer import FuncUnit
27
+
28
+
29
+ @dataclass
30
+ class BackendScore:
31
+ """Score for a single backend for a single function."""
32
+ backend: str
33
+ score: float = 0.0
34
+ reasons: list[str] = field(default_factory=list)
35
+
36
+ def add(self, points: float, reason: str) -> None:
37
+ self.score += points
38
+ self.reasons.append(f"+{points:.1f} {self.backend} ({reason})")
39
+
40
+
41
+ # ---- Feature detection rules ----
42
+ # Each rule: (pattern_name, feature_keywords, backend_scores)
43
+ # The analyzer's feature detection already classifies functions; we map those
44
+ # features to backend scores based on research.
45
+
46
+ # Research-backed scoring rules:
47
+ # - Higher score = better fit for that backend
48
+ # - Negative score = poor fit (avoid)
49
+ # - Zero = neutral
50
+
51
+ RULES: list[tuple[str, dict[str, float]]] = [
52
+ # ---- Memory safety / security ----
53
+ # Rust is the premier choice for memory safety without GC
54
+ ("memory_safety_critical", {
55
+ "rust": +3.0, # Rust: ownership model eliminates memory bugs
56
+ "zig": +1.5, # Zig: safer than C but manual memory
57
+ "csharp": +0.5, # C#: GC-managed, safe but not systems-level
58
+ "kotlin": +0.5, # Kotlin: GC-managed, null-safe
59
+ "cpp": -1.0, # C++: many unsafe-by-default edges
60
+ "go": +0.5, # Go: GC-managed, safe for ordinary code
61
+ }),
62
+
63
+ # ---- Concurrency ----
64
+ # Go excels at goroutine concurrency; Rust has type-system-enforced concurrency
65
+ ("concurrency", {
66
+ "go": +3.0, # Go: goroutines are the killer feature
67
+ "rust": +2.5, # Rust: Send/Sync type-system-enforced concurrency
68
+ "kotlin": +2.0, # Kotlin: coroutines for lightweight async
69
+ "csharp": +1.5, # C#: async/await, Task Parallel Library
70
+ "cpp": +1.0, # C++: threads, atomics, but complex
71
+ "zig": +0.5, # Zig: evolving async model
72
+ "dart": +1.0, # Dart: async/await, isolates
73
+ }),
74
+
75
+ # ---- Numeric computation / arithmetic ----
76
+ # C++ and Rust are top performers; C# for enterprise data processing
77
+ ("arithmetic", {
78
+ "cpp": +2.0, # C++: excellent performance, SIMD, auto-vectorization
79
+ "rust": +2.0, # Rust: competitive with C++, SIMD auto-vectorization
80
+ "csharp": +1.5, # C#: good performance, .NET numerics
81
+ "zig": +1.5, # Zig: excellent potential, explicit allocation
82
+ "go": +1.0, # Go: good but GC affects some workloads
83
+ "kotlin": +0.5, # Kotlin: JVM overhead for pure computation
84
+ "dart": +0.5, # Dart: not optimized for heavy computation
85
+ }),
86
+
87
+ # ---- Loops / iteration ----
88
+ # C++ and Rust excel at tight loops; Go for simple iteration
89
+ ("numeric_loop", {
90
+ "cpp": +2.0, # C++: tight loop optimization, auto-vectorization
91
+ "rust": +2.0, # Rust: zero-cost iterators, auto-vectorization
92
+ "zig": +1.5, # Zig: explicit control, fast compilation
93
+ "csharp": +1.0, # C#: good loop performance
94
+ "go": +1.0, # Go: simple loops, fast enough
95
+ "kotlin": +0.5, # Kotlin: functional iteration overhead
96
+ "dart": +0.3, # Dart: not for heavy loops
97
+ }),
98
+
99
+ # ---- Comparisons / conditionals ----
100
+ # All languages handle this well; slight edge to systems languages
101
+ ("comparison", {
102
+ "rust": +1.0, # Rust: pattern matching, exhaustive
103
+ "cpp": +1.0, # C++: standard conditionals
104
+ "zig": +1.0, # Zig: no hidden control flow
105
+ "csharp": +0.5, # C#: standard
106
+ "go": +0.5, # Go: simple conditionals
107
+ "kotlin": +0.5, # Kotlin: when expressions
108
+ "dart": +0.3, # Dart: standard
109
+ }),
110
+
111
+ # ---- I/O (print, input) ----
112
+ # All handle I/O; Dart for UI output, Go for network I/O
113
+ ("io_print", {
114
+ "dart": +2.0, # Dart: UI output, console for debugging
115
+ "go": +1.5, # Go: excellent I/O stdlib
116
+ "kotlin": +1.0, # Kotlin: JVM I/O
117
+ "csharp": +1.0, # C#: Console I/O
118
+ "rust": +0.5, # Rust: basic I/O
119
+ "cpp": +0.5, # C++: iostream
120
+ "zig": +0.5, # Zig: basic I/O
121
+ }),
122
+
123
+ # ---- List / array operations ----
124
+ # C++ for performance; Rust for safety; Go for simplicity
125
+ ("list_operation", {
126
+ "cpp": +2.0, # C++: std::vector, performance
127
+ "rust": +2.0, # Rust: Vec, safe and fast
128
+ "zig": +1.5, # Zig: slices, explicit allocators
129
+ "csharp": +1.5, # C#: LINQ, List<T>
130
+ "go": +1.0, # Go: slices, simple
131
+ "kotlin": +1.0, # Kotlin: collections, functional
132
+ "dart": +0.5, # Dart: List, not performance-focused
133
+ }),
134
+
135
+ # ---- Function calls (inter-function) ----
136
+ # All handle this; slight edge to languages with good inlining
137
+ ("function_call", {
138
+ "rust": +1.0, # Rust: zero-cost abstractions, inlining
139
+ "cpp": +1.0, # C++: inlining, templates
140
+ "zig": +1.0, # Zig: comptime, inlining
141
+ "csharp": +0.5, # C#: JIT inlining (AOT less aggressive)
142
+ "go": +0.5, # Go: inlining but interface overhead
143
+ "kotlin": +0.5, # Kotlin: JVM inlining
144
+ "dart": +0.3, # Dart: standard
145
+ }),
146
+ ]
147
+
148
+
149
+ # ---- Name-based heuristics ----
150
+ # Function name patterns that suggest a specific backend
151
+ NAME_PATTERNS: list[tuple[re.Pattern, dict[str, float]]] = [
152
+ # UI / widget / screen / page → Dart
153
+ (re.compile(r"^(build|render|widget|screen|page|view|drawer|dialog|snackbar|appbar)", re.I), {
154
+ "dart": +5.0, # Flutter UI is Dart's domain
155
+ "kotlin": +1.0, # Compose also possible
156
+ }),
157
+ # Network / HTTP / API / request → Go
158
+ (re.compile(r"^(fetch|request|http|api|endpoint|route|handler|serve|listen|connect|socket|websocket)", re.I), {
159
+ "go": +5.0, # Go: network services, goroutines
160
+ "kotlin": +1.5, # Kotlin: Ktor, coroutines
161
+ "csharp": +1.0, # C#: ASP.NET
162
+ }),
163
+ # Compute / calculate / process → C++ or Rust
164
+ (re.compile(r"^(compute|calculate|process|transform|optimize|render|encode|decode|compress|encrypt|decrypt|hash)", re.I), {
165
+ "cpp": +3.0, # C++: performance-critical computation
166
+ "rust": +2.5, # Rust: safe computation
167
+ "zig": +2.0, # Zig: explicit control
168
+ "csharp": +1.0, # C#: data processing
169
+ }),
170
+ # Memory / allocate / free / buffer → Zig or Rust
171
+ (re.compile(r"^(alloc|free|buffer|memory|pointer|register|hardware|device|firmware|embedded)", re.I), {
172
+ "zig": +5.0, # Zig: manual memory control, embedded
173
+ "rust": +2.0, # Rust: ownership model
174
+ "cpp": +1.0, # C++: manual memory
175
+ }),
176
+ # Business / enterprise / model / service / validate → C# or Kotlin
177
+ (re.compile(r"^(business|enterprise|model|service|validate|validate|repository|entity|domain|logic|rule|policy)", re.I), {
178
+ "csharp": +4.0, # C#: enterprise logic, .NET
179
+ "kotlin": +3.0, # Kotlin: JVM backend, domain logic
180
+ "go": +1.0, # Go: services
181
+ }),
182
+ # Android / mobile / jetpack / compose → Kotlin
183
+ (re.compile(r"^(android|mobile|jetpack|compose|lifecycle|viewmodel|navigation)", re.I), {
184
+ "kotlin": +5.0, # Kotlin: Android-native
185
+ "dart": +2.0, # Dart: Flutter mobile
186
+ }),
187
+ # Concurrent / async / parallel / goroutine → Go
188
+ (re.compile(r"^(concurrent|async|parallel|goroutine|channel|pipeline|worker|spawn|schedule)", re.I), {
189
+ "go": +5.0, # Go: goroutines, channels
190
+ "rust": +2.0, # Rust: async, threads
191
+ "kotlin": +2.0, # Kotlin: coroutines
192
+ "csharp": +1.5, # C#: async/await
193
+ }),
194
+ # Safe / secure / verify / check / guard → Rust
195
+ (re.compile(r"^(safe|secure|verify|check|guard|protect|sanitize|validate_token|auth|permission)", re.I), {
196
+ "rust": +5.0, # Rust: memory safety, security
197
+ "kotlin": +1.0, # Kotlin: null-safety
198
+ "csharp": +1.0, # C#: safe by default
199
+ }),
200
+ # Game / render / physics / collision / sprite → C++
201
+ (re.compile(r"^(game|render|physics|collision|sprite|mesh|shader|texture|vertex|polygon|raycast)", re.I), {
202
+ "cpp": +5.0, # C++: game engines, rendering
203
+ "csharp": +2.0, # C#: Unity game dev
204
+ "rust": +1.0, # Rust: emerging game dev
205
+ }),
206
+ # Data / dataframe / query / filter / map / reduce → C# or Kotlin
207
+ (re.compile(r"^(data|dataframe|query|filter|map|reduce|aggregate|group|sort|search|index)", re.I), {
208
+ "csharp": +3.0, # C#: LINQ, data processing
209
+ "kotlin": +2.5, # Kotlin: collections, functional
210
+ "go": +1.0, # Go: simple data handling
211
+ "rust": +1.0, # Rust: iterators
212
+ }),
213
+ ]
214
+
215
+
216
+ # ---- Default backend preference order (when scores are tied) ----
217
+ # Based on research: Rust is the safest default for general-purpose code
218
+ DEFAULT_PREFERENCE = ["rust", "cpp", "csharp", "go", "kotlin", "zig", "dart"]
219
+
220
+
221
+ def score_backend(unit: FuncUnit) -> dict[str, BackendScore]:
222
+ """Score all backends for a given function.
223
+
224
+ Returns a dict mapping backend name to BackendScore.
225
+ """
226
+ scores = {b: BackendScore(backend=b) for b in
227
+ ["rust", "cpp", "csharp", "zig", "go", "kotlin", "dart"]}
228
+
229
+ # 1) Apply feature-based rules
230
+ for feature in unit.features:
231
+ for pattern_name, backend_scores in RULES:
232
+ if pattern_name == feature or feature.startswith(pattern_name):
233
+ for backend, points in backend_scores.items():
234
+ if points > 0:
235
+ scores[backend].add(points, feature)
236
+ elif points < 0:
237
+ scores[backend].score += points
238
+ scores[backend].reasons.append(f"{points:.1f} {backend} ({feature})")
239
+
240
+ # 2) Apply name-based heuristics
241
+ for pattern, backend_scores in NAME_PATTERNS:
242
+ if pattern.match(unit.name):
243
+ for backend, points in backend_scores.items():
244
+ if points > 0:
245
+ scores[backend].add(points, f"name pattern: {unit.name}")
246
+
247
+ # 3) Check for UI-related patterns in the function body (build function)
248
+ if unit.name == "build" or unit.name.startswith("build_"):
249
+ scores["dart"].add(5.0, "UI build function")
250
+
251
+ # 4) Check for async/event-loop patterns
252
+ if unit.body:
253
+ for node in ast.walk(unit.body):
254
+ if isinstance(node, ast.Call) and isinstance(node.func, ast.Name):
255
+ fname = node.func.id
256
+ # network-like calls suggest Go
257
+ if fname in ("fetch", "request", "send", "receive", "listen", "connect"):
258
+ scores["go"].add(2.0, f"network call: {fname}")
259
+ # async-like patterns suggest Go or Kotlin
260
+ if fname in ("async", "await", "spawn", "go"):
261
+ scores["go"].add(1.5, f"async call: {fname}")
262
+ scores["kotlin"].add(1.0, f"async call: {fname}")
263
+
264
+ return scores
265
+
266
+
267
+ def select_backend(unit: FuncUnit) -> tuple[str, list[str]]:
268
+ """Select the best backend for a function based on auto-selection rules.
269
+
270
+ Returns (backend_name, list_of_reasons).
271
+ """
272
+ scores = score_backend(unit)
273
+
274
+ # find the highest-scoring backend
275
+ best_backend = None
276
+ best_score = 0.0
277
+ for backend in DEFAULT_PREFERENCE:
278
+ s = scores[backend].score
279
+ if s > best_score:
280
+ best_score = s
281
+ best_backend = backend
282
+
283
+ # if all scores are zero, default to Rust
284
+ if best_backend is None or best_score <= 0:
285
+ best_backend = "rust"
286
+ return best_backend, ["default: no strong signal -> rust (safest general-purpose)"]
287
+
288
+ # collect reasons
289
+ reasons = scores[best_backend].reasons.copy()
290
+ # add comparison summary
291
+ summary_parts = []
292
+ for backend in DEFAULT_PREFERENCE:
293
+ s = scores[backend].score
294
+ if s != 0:
295
+ summary_parts.append(f"{backend}={s:.1f}")
296
+ if summary_parts:
297
+ reasons.append(f"scores: {', '.join(summary_parts)}")
298
+
299
+ return best_backend, reasons
300
+
301
+
302
+ def auto_select_backends(units: list[FuncUnit]) -> dict[str, tuple[str, list[str]]]:
303
+ """Auto-select backends for all functions.
304
+
305
+ Returns a dict mapping function name to (backend, reasons).
306
+ Only selects for functions that don't have a forced_backend.
307
+ """
308
+ result: dict[str, tuple[str, list[str]]] = {}
309
+ for u in units:
310
+ if u.forced_backend:
311
+ result[u.name] = (u.forced_backend, [f"forced: @{u.forced_backend}"])
312
+ elif u.supported and u.name != "build":
313
+ backend, reasons = select_backend(u)
314
+ result[u.name] = (backend, reasons)
315
+ return result
316
+
317
+
318
+ def explain_selection(unit: FuncUnit) -> str:
319
+ """Explain why a particular backend was selected for a function.
320
+
321
+ Returns a human-readable explanation string.
322
+ """
323
+ backend, reasons = select_backend(unit)
324
+ lines = [f"Function '{unit.name}' -> {backend}"]
325
+ for r in reasons:
326
+ lines.append(f" {r}")
327
+ return "\n".join(lines)
@@ -0,0 +1,87 @@
1
+ """Backend selection decorators for GE.
2
+
3
+ Usage in GE source:
4
+ from pyeffic.backends import rust, cpp, dart, csharp, zig, go, kotlin
5
+
6
+ @rust
7
+ def safe_function(x: int) -> int:
8
+ return x * 2
9
+
10
+ @cpp
11
+ def cpp_function(x: int) -> int:
12
+ return x * 3
13
+
14
+ @dart
15
+ def ui_function():
16
+ pass
17
+
18
+ @csharp
19
+ def enterprise_logic(x: int) -> int:
20
+ return x * 4
21
+
22
+ @zig
23
+ def systems_function(x: int) -> int:
24
+ return x * 5
25
+
26
+ @go
27
+ def concurrent_function(x: int) -> int:
28
+ return x * 6
29
+
30
+ @kotlin
31
+ def android_function(x: int) -> int:
32
+ return x * 7
33
+
34
+ The analyzer reads these decorators from the AST to determine which native
35
+ backend each function should be compiled to. At Python runtime (when running
36
+ the source directly for testing), these are identity decorators — they just
37
+ return the function unchanged.
38
+
39
+ Backend strengths (from web research):
40
+ @rust — memory safety without GC, fearless concurrency, systems, security-sensitive
41
+ @cpp — C++ ecosystem (CUDA, OpenCV, game engines), template metaprogramming, performance-critical
42
+ @dart — stays in Dart (Flutter UI, async event loop, state management, cross-platform)
43
+ @csharp — .NET ecosystem, enterprise logic, game dev (Unity), NativeAOT to C ABI
44
+ @zig — no runtime, fast compilation, manual memory control, embedded, C replacement
45
+ @go — goroutine concurrency, network services, cloud infrastructure, microservices
46
+ @kotlin — Android-native, Kotlin Multiplatform, JVM backend, coroutines, null-safety
47
+ """
48
+ from __future__ import annotations
49
+
50
+ from typing import TypeVar, Callable
51
+
52
+ T = TypeVar("T")
53
+
54
+
55
+ def rust(func: T) -> T:
56
+ """Mark a function for compilation to Rust. No-op at Python runtime."""
57
+ return func
58
+
59
+
60
+ def cpp(func: T) -> T:
61
+ """Mark a function for compilation to C++. No-op at Python runtime."""
62
+ return func
63
+
64
+
65
+ def dart(func: T) -> T:
66
+ """Mark a function to stay in Dart (not compiled to native). No-op at Python runtime."""
67
+ return func
68
+
69
+
70
+ def csharp(func: T) -> T:
71
+ """Mark a function for compilation to C# via .NET NativeAOT. No-op at Python runtime."""
72
+ return func
73
+
74
+
75
+ def zig(func: T) -> T:
76
+ """Mark a function for compilation to Zig. No-op at Python runtime."""
77
+ return func
78
+
79
+
80
+ def go(func: T) -> T:
81
+ """Mark a function for compilation to Go (cgo shared library). No-op at Python runtime."""
82
+ return func
83
+
84
+
85
+ def kotlin(func: T) -> T:
86
+ """Mark a function for compilation to Kotlin/Native. No-op at Python runtime."""
87
+ return func