monogate-forge 0.1.0__py3-none-any.whl

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 (95) hide show
  1. lang/loader/__init__.py +26 -0
  2. lang/loader/resolver.py +288 -0
  3. lang/optimizer/__init__.py +111 -0
  4. lang/optimizer/constant_folding.py +301 -0
  5. lang/optimizer/cse.py +249 -0
  6. lang/optimizer/fusion.py +18 -0
  7. lang/optimizer/inliner.py +176 -0
  8. lang/optimizer/ml_routing.py +182 -0
  9. lang/optimizer/superbest.py +198 -0
  10. lang/optimizer/tree_shaker.py +88 -0
  11. lang/parser/__init__.py +53 -0
  12. lang/parser/ast_nodes.py +232 -0
  13. lang/parser/errors.py +37 -0
  14. lang/parser/lexer.py +195 -0
  15. lang/parser/parser.py +706 -0
  16. lang/parser/type_checker.py +71 -0
  17. lang/profiler/__init__.py +14 -0
  18. lang/profiler/ast_to_sympy.py +254 -0
  19. lang/profiler/dynamics.py +36 -0
  20. lang/profiler/eml_interpreter.py +254 -0
  21. lang/profiler/profiler.py +218 -0
  22. lang/profiler/sympy_to_ast.py +152 -0
  23. lang/spec/EML_LANG_DESIGN.md +698 -0
  24. lang/spec/SPEC.md +213 -0
  25. lang/spec/__init__.py +0 -0
  26. lang/spec/grammar/__init__.py +0 -0
  27. lang/spec/grammar/eml_lang.g4 +123 -0
  28. lang/spec/grammar/examples/arrhenius.eml +12 -0
  29. lang/spec/grammar/examples/bessel_fm.eml +18 -0
  30. lang/spec/grammar/examples/hello.eml +9 -0
  31. lang/spec/grammar/examples/kalman.eml +19 -0
  32. lang/spec/grammar/examples/motor_control.eml +84 -0
  33. lang/spec/grammar/examples/motor_foc.eml +16 -0
  34. lang/spec/grammar/examples/orbit.eml +23 -0
  35. lang/spec/grammar/examples/pid_basic.eml +13 -0
  36. lang/spec/grammar/examples/pid_nonlinear.eml +22 -0
  37. lang/spec/grammar/examples/sigmoid.eml +18 -0
  38. lang/spec/grammar/examples/trajectory.eml +16 -0
  39. lang/spec/grammar/lexer_rules.g4 +58 -0
  40. lang/spec/stdlib/STDLIB.md +127 -0
  41. lang/spec/stdlib/__init__.py +0 -0
  42. lang/spec/stdlib/constants.eml +23 -0
  43. lang/spec/stdlib/control.eml +134 -0
  44. lang/spec/stdlib/linalg.eml +135 -0
  45. lang/spec/stdlib/math.eml +149 -0
  46. lang/spec/stdlib/ml.eml +143 -0
  47. lang/spec/stdlib/signal.eml +118 -0
  48. lang/spec/types/TYPES.md +58 -0
  49. lang/spec/types/__init__.py +0 -0
  50. lang/spec/types/chain_order_types.md +91 -0
  51. lang/spec/types/domain_types.md +33 -0
  52. lang/spec/types/precision_types.md +48 -0
  53. monogate_forge-0.1.0.dist-info/METADATA +224 -0
  54. monogate_forge-0.1.0.dist-info/RECORD +95 -0
  55. monogate_forge-0.1.0.dist-info/WHEEL +5 -0
  56. monogate_forge-0.1.0.dist-info/entry_points.txt +3 -0
  57. monogate_forge-0.1.0.dist-info/licenses/LICENSE +32 -0
  58. monogate_forge-0.1.0.dist-info/top_level.txt +3 -0
  59. software/backends/__init__.py +14 -0
  60. software/backends/c_backend.py +422 -0
  61. software/backends/cpp_backend.py +405 -0
  62. software/backends/go_backend.py +370 -0
  63. software/backends/java_backend.py +399 -0
  64. software/backends/kotlin_backend.py +441 -0
  65. software/backends/matlab_backend.py +386 -0
  66. software/backends/python_backend.py +307 -0
  67. software/backends/rust_backend.py +379 -0
  68. software/verification/__init__.py +0 -0
  69. software/verification/lean/LeanBackend.py +548 -0
  70. tools/benchmarks/__init__.py +29 -0
  71. tools/benchmarks/dashboard.py +148 -0
  72. tools/benchmarks/regen_baselines.py +100 -0
  73. tools/benchmarks/snapshot.py +137 -0
  74. tools/cli/audit.py +449 -0
  75. tools/cli/explain.py +510 -0
  76. tools/cli/init_cmd.py +162 -0
  77. tools/cli/live_counts.py +389 -0
  78. tools/cli/main.py +1190 -0
  79. tools/cli/manpage.py +189 -0
  80. tools/equivalence/__init__.py +23 -0
  81. tools/equivalence/c_runner.py +217 -0
  82. tools/equivalence/harness.py +288 -0
  83. tools/equivalence/lean_runner.py +246 -0
  84. tools/equivalence/python_runner.py +177 -0
  85. tools/equivalence/rust_runner.py +314 -0
  86. tools/fmt/__init__.py +28 -0
  87. tools/fmt/formatter.py +364 -0
  88. tools/forge_graph.py +464 -0
  89. tools/ide/lsp/__init__.py +7 -0
  90. tools/ide/lsp/server.py +845 -0
  91. tools/ide/lsp/workspace.py +251 -0
  92. tools/license/__init__.py +26 -0
  93. tools/license/issuer.py +87 -0
  94. tools/license/verifier.py +187 -0
  95. tools/scripts/auto_prove.py +568 -0
@@ -0,0 +1,26 @@
1
+ """Module loader for `use` declarations.
2
+
3
+ Resolves `use <root>::<name>(...);` paths to `.eml` source files
4
+ on disk, parses them once, and caches the results so a function
5
+ imported by ten different modules is parsed only once per
6
+ process.
7
+
8
+ Search-path table:
9
+
10
+ stdlib -> lang/spec/stdlib/<name>.eml
11
+
12
+ (More roots will land as the import system grows -- e.g.
13
+ `local::X` for sibling files relative to the importing file.)
14
+
15
+ Cycles are detected by tracking the in-progress load set; when a
16
+ module already on that set is requested again, ImportError is
17
+ raised with the cycle path.
18
+ """
19
+
20
+ from lang.loader.resolver import (
21
+ LoaderError,
22
+ ModuleLoader,
23
+ resolve_imports,
24
+ )
25
+
26
+ __all__ = ["LoaderError", "ModuleLoader", "resolve_imports"]
@@ -0,0 +1,288 @@
1
+ """Import resolver -- finds, parses, caches, and merges modules."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from copy import deepcopy
6
+ from dataclasses import dataclass, field
7
+ from pathlib import Path
8
+
9
+ from lang.parser.ast_nodes import (
10
+ EMLConstant,
11
+ EMLFunction,
12
+ EMLImport,
13
+ EMLModule,
14
+ EMLTypeAlias,
15
+ )
16
+
17
+
18
+ # Repo root -- two levels up from this file (lang/loader/resolver.py).
19
+ _REPO_ROOT = Path(__file__).resolve().parents[2]
20
+
21
+
22
+ # Search-path table mapping the FIRST path segment to a directory.
23
+ # When `use stdlib::math;` is parsed, the loader looks for
24
+ # `<DEFAULT_SEARCH_PATHS["stdlib"]>/math.eml`.
25
+ #
26
+ # The reserved root `"local"` does NOT appear here -- it resolves
27
+ # relative to the importing file's directory at load time.
28
+ DEFAULT_SEARCH_PATHS: dict[str, Path] = {
29
+ "stdlib": _REPO_ROOT / "lang" / "spec" / "stdlib",
30
+ }
31
+
32
+ LOCAL_ROOT = "local"
33
+
34
+
35
+ class LoaderError(Exception):
36
+ """Loader-level error: not-found, cycle, or symbol clash."""
37
+
38
+
39
+ @dataclass
40
+ class ModuleLoader:
41
+ """Resolves + parses + caches imports.
42
+
43
+ Construct one per parse session if you want test isolation;
44
+ construct one and reuse it across many parse calls if you
45
+ want maximum cache hit rate.
46
+
47
+ Cache keys are RESOLVED file-paths (strings), not joined
48
+ import paths -- so `local::helpers` from `/a/x.eml` and
49
+ `/b/x.eml` correctly cache as two different modules even
50
+ though they share a joined-path spelling."""
51
+ search_paths: dict[str, Path] = field(
52
+ default_factory=lambda: dict(DEFAULT_SEARCH_PATHS),
53
+ )
54
+ cache: dict[str, EMLModule] = field(default_factory=dict)
55
+ _in_progress: set[str] = field(default_factory=set)
56
+
57
+ def load(
58
+ self,
59
+ joined_path: str,
60
+ *,
61
+ source_dir: Path | None = None,
62
+ ) -> EMLModule:
63
+ """Load the module identified by `joined_path` (e.g.
64
+ 'stdlib::math' or 'local::helpers').
65
+
66
+ `source_dir` is the directory of the file doing the import.
67
+ Required when `joined_path` starts with `local::`; ignored
68
+ otherwise.
69
+
70
+ Raises LoaderError on:
71
+ - unknown root segment (not in search_paths and not 'local')
72
+ - missing `local::` source_dir
73
+ - missing .eml file
74
+ - import cycle (resolved file path already in progress)
75
+ """
76
+ file_path = self.resolve(joined_path, source_dir=source_dir)
77
+ cache_key = str(file_path.resolve())
78
+
79
+ if cache_key in self.cache:
80
+ return self.cache[cache_key]
81
+
82
+ if cache_key in self._in_progress:
83
+ raise LoaderError(
84
+ f"import cycle while loading {joined_path!r} "
85
+ f"({file_path}) -- in progress: "
86
+ f"{sorted(self._in_progress)}"
87
+ )
88
+
89
+ if not file_path.is_file():
90
+ raise LoaderError(
91
+ f"module {joined_path!r} not found "
92
+ f"(looked for {file_path})"
93
+ )
94
+
95
+ # Local import to avoid a parser <-> loader cycle.
96
+ from lang.parser.parser import parse_source
97
+
98
+ self._in_progress.add(cache_key)
99
+ try:
100
+ text = file_path.read_text(encoding="utf-8")
101
+ mod = parse_source(text, source_file=str(file_path))
102
+ # Recursively resolve transitive imports BEFORE caching.
103
+ mod = resolve_imports(mod, loader=self)
104
+ self.cache[cache_key] = mod
105
+ return mod
106
+ finally:
107
+ self._in_progress.discard(cache_key)
108
+
109
+ def resolve(
110
+ self,
111
+ joined_path: str,
112
+ *,
113
+ source_dir: Path | None = None,
114
+ ) -> Path:
115
+ """Translate `<root>::<name>(::<sub>...)` into a file path
116
+ without loading.
117
+
118
+ `local::name` resolves against `source_dir`; every other
119
+ root resolves against `self.search_paths`."""
120
+ parts = joined_path.split("::")
121
+ if len(parts) < 2:
122
+ raise LoaderError(
123
+ f"path {joined_path!r} must have at least 2 segments"
124
+ )
125
+ root, *rest = parts
126
+
127
+ if root == LOCAL_ROOT:
128
+ if source_dir is None:
129
+ raise LoaderError(
130
+ f"`use local::{ '::'.join(rest) };` requires a "
131
+ f"source-file directory but none was supplied "
132
+ f"(use parse_file or pass source_dir=)"
133
+ )
134
+ base = source_dir
135
+ else:
136
+ if root not in self.search_paths:
137
+ raise LoaderError(
138
+ f"unknown import root {root!r} "
139
+ f"(known: {sorted(self.search_paths) + [LOCAL_ROOT]})"
140
+ )
141
+ base = self.search_paths[root]
142
+
143
+ # rest = ["math"] -> math.eml
144
+ # rest = ["foo", "bar"] -> foo/bar.eml
145
+ rel = Path(*rest[:-1]) / f"{rest[-1]}.eml"
146
+ return base / rel
147
+
148
+
149
+ def resolve_imports(
150
+ mod: EMLModule,
151
+ *,
152
+ loader: ModuleLoader | None = None,
153
+ ) -> EMLModule:
154
+ """Return a new EMLModule with every `use ...;` import resolved
155
+ and its constants/types/functions merged into `mod`'s namespace.
156
+
157
+ `local::` imports are resolved against the directory of
158
+ `mod.source_file`; if that is "<unknown>" or "<string>"
159
+ (parsed from a string, not a file), local imports raise
160
+ LoaderError -- callers wanting local imports must set the
161
+ EMLModule.source_file before calling.
162
+
163
+ Conflicts (an imported name collides with a local one, or two
164
+ imports both define the same name) raise LoaderError.
165
+ """
166
+ if not mod.imports:
167
+ return mod
168
+
169
+ if loader is None:
170
+ loader = ModuleLoader()
171
+
172
+ # Compute the importing file's directory once so every
173
+ # local::... lookup uses the same base.
174
+ source_dir: Path | None = None
175
+ src = mod.source_file
176
+ if src and not src.startswith("<"):
177
+ source_dir = Path(src).resolve().parent
178
+
179
+ out = deepcopy(mod)
180
+
181
+ # Build name tables seeded with the local module's own decls.
182
+ # Imports CANNOT shadow a locally-defined name; the local
183
+ # definition wins and the import is rejected as a clash so the
184
+ # author sees the conflict explicitly.
185
+ const_names = {c.name for c in out.constants}
186
+ type_names = {t.name for t in out.types}
187
+ func_names = {f.name for f in out.functions}
188
+
189
+ # Track which import provided each name so error messages can
190
+ # point at the offending source.
191
+ provided_by: dict[str, str] = {}
192
+
193
+ for imp in mod.imports:
194
+ sub = loader.load(imp.joined, source_dir=source_dir)
195
+
196
+ # Selective-import filter. When `imp.only` is set, only
197
+ # names in that allowlist are merged. Names outside it are
198
+ # NOT brought into the importing module's namespace; they
199
+ # remain in the imported sub-module and the tree-shaker
200
+ # later sees them as unreached.
201
+ wanted = set(imp.only) if imp.only is not None else None
202
+
203
+ # Alias map (None -> empty dict). Key is the original
204
+ # name in the imported module; value is what it should be
205
+ # called in the importing module's namespace.
206
+ alias_map: dict[str, str] = imp.aliases or {}
207
+
208
+ # Validate the selective-import allowlist against what the
209
+ # imported module actually exports -- a typo in the user's
210
+ # `use ::{misspelled}` should surface as a clear error
211
+ # rather than silently importing nothing.
212
+ if wanted is not None:
213
+ exported_names = (
214
+ {c.name for c in sub.constants}
215
+ | {t.name for t in sub.types}
216
+ | {f.name for f in sub.functions}
217
+ )
218
+ unknown = sorted(wanted - exported_names)
219
+ if unknown:
220
+ raise LoaderError(
221
+ f"`use {imp.joined}::{{...}};` requested name(s) "
222
+ f"{unknown} not exported by {imp.joined!r} "
223
+ f"(available: {sorted(exported_names)[:20]})"
224
+ )
225
+
226
+ for c in sub.constants:
227
+ if wanted is not None and c.name not in wanted:
228
+ continue
229
+ local_name = alias_map.get(c.name, c.name)
230
+ _check_clash(local_name, "constant", imp, provided_by,
231
+ local_names=const_names)
232
+ new_c = deepcopy(c)
233
+ new_c.name = local_name
234
+ out.constants.append(new_c)
235
+ const_names.add(local_name)
236
+ provided_by[local_name] = imp.joined
237
+
238
+ for t in sub.types:
239
+ if wanted is not None and t.name not in wanted:
240
+ continue
241
+ local_name = alias_map.get(t.name, t.name)
242
+ _check_clash(local_name, "type alias", imp, provided_by,
243
+ local_names=type_names)
244
+ new_t = deepcopy(t)
245
+ new_t.name = local_name
246
+ out.types.append(new_t)
247
+ type_names.add(local_name)
248
+ provided_by[local_name] = imp.joined
249
+
250
+ for f in sub.functions:
251
+ if wanted is not None and f.name not in wanted:
252
+ continue
253
+ local_name = alias_map.get(f.name, f.name)
254
+ _check_clash(local_name, "function", imp, provided_by,
255
+ local_names=func_names)
256
+ new_fn = deepcopy(f)
257
+ new_fn.name = local_name
258
+ # Tag for the tree-shaker: this function arrived via
259
+ # `use ...;` and is droppable if no local function
260
+ # ends up calling it.
261
+ new_fn.imported_from = imp.joined
262
+ out.functions.append(new_fn)
263
+ func_names.add(local_name)
264
+ provided_by[local_name] = imp.joined
265
+
266
+ return out
267
+
268
+
269
+ def _check_clash(
270
+ name: str, kind: str, imp: EMLImport,
271
+ provided_by: dict[str, str],
272
+ *,
273
+ local_names: set[str],
274
+ ) -> None:
275
+ """Raise if `name` is already in scope, distinguishing local
276
+ redefinitions from import conflicts in the message."""
277
+ if name in local_names:
278
+ # Was it locally-defined or imported earlier?
279
+ if name in provided_by:
280
+ raise LoaderError(
281
+ f"`use {imp.joined};` brings in {kind} {name!r}, "
282
+ f"but it was already imported via "
283
+ f"`{provided_by[name]}`",
284
+ )
285
+ raise LoaderError(
286
+ f"`use {imp.joined};` brings in {kind} {name!r}, "
287
+ f"but the importing module already defines that name",
288
+ )
@@ -0,0 +1,111 @@
1
+ """SuperBEST routing + fusion + CSE + constant folding.
2
+
3
+ The optimizer is the DEFAULT (not opt-in). Every function passes
4
+ through it before any backend emits code, so all backends share
5
+ the same optimal node count + the same per-node operator choice.
6
+
7
+ Public entries:
8
+
9
+ optimize_module(mod) Run the full pass sequence over every
10
+ function in `mod`. Mutates the module
11
+ in place and returns it for chaining.
12
+ optimize_function(fn) Same, single function.
13
+ fold_constants(node) Just the constant-folding pass.
14
+ apply_cse(fn) Just the CSE pass.
15
+ route_superbest(node) Just SuperBEST routing (today: identity).
16
+
17
+ Default pass sequence (in order):
18
+
19
+ 1. constant_folding -- fold pure-literal sub-trees first so
20
+ downstream passes see a smaller tree.
21
+ 2. cse -- hoist remaining duplicates into lets.
22
+ 3. superbest -- per-node operator-family selection
23
+ (placeholder; wired in for Phase 2.1).
24
+ """
25
+
26
+ from copy import deepcopy
27
+
28
+ from lang.optimizer.constant_folding import fold_constants, fold_in_place
29
+ from lang.optimizer.cse import apply_cse, apply_cse_module
30
+ from lang.optimizer.inliner import inline_calls
31
+ from lang.optimizer.ml_routing import route_ml_activations_module
32
+ from lang.optimizer.superbest import (
33
+ route_superbest,
34
+ superbest_function,
35
+ superbest_module,
36
+ )
37
+ from lang.optimizer.tree_shaker import shake_imports
38
+ from lang.parser.ast_nodes import EMLFunction, EMLModule, NodeKind
39
+
40
+
41
+ def optimize_function(fn: EMLFunction) -> EMLFunction:
42
+ """Run the per-function pass sequence (constant_folding + CSE +
43
+ superbest). Returns a new function -- input is not mutated.
44
+
45
+ NOTE: the call-inliner is a MODULE-level pass (it needs to
46
+ look up callees in the module's function table) so it lives
47
+ in `optimize_module`, not here. Calling `optimize_function`
48
+ directly skips inlining."""
49
+ if fn.body is None:
50
+ return fn
51
+
52
+ out = deepcopy(fn)
53
+
54
+ # Pass 1: constant folding (in-place on the deep copy).
55
+ if out.body is not None:
56
+ out.body = fold_in_place(out.body)
57
+ # Pass 2: CSE -- operates on the whole function.
58
+ out = apply_cse(out)
59
+ # Pass 3: superbest (no-op today).
60
+ if out.body is not None:
61
+ out.body = route_superbest(out.body)
62
+ return out
63
+
64
+
65
+ def optimize_module(mod: EMLModule, *,
66
+ ml_routing: bool = False) -> EMLModule:
67
+ """Run the default pass sequence on every function in `mod`.
68
+ Returns a new module; the input is not mutated.
69
+
70
+ Pass order:
71
+ 0. inline_calls -- substitute eligible same-module
72
+ CALLs with the callee's body
73
+ 1. constant_folding -- fold pure-literal sub-trees
74
+ (incl. ones exposed by inlining)
75
+ 2. cse -- hoist remaining duplicates
76
+ 3. superbest -- per-node operator-family selection
77
+ 3.5 ml_routing -- (opt-in) pattern-rewrite sigmoid /
78
+ softplus to libmonogate runtime calls
79
+ for HIGH-drift functions
80
+ 4. shake_imports -- drop unused imports
81
+
82
+ `ml_routing` defaults to False because the pass emits CALL
83
+ nodes targeting libmonogate runtime symbols, which only the
84
+ C and Rust backends know how to resolve. Enable when
85
+ targeting C / Rust on a known-drifty workload.
86
+ """
87
+ # Pass 0: module-level inliner.
88
+ out = inline_calls(mod)
89
+ # Passes 1 -> 2: per-function (constant_folding + CSE).
90
+ out.functions = [optimize_function(fn) for fn in out.functions]
91
+ # Pass 3: module-level SuperBEST routing -- needs SymPy
92
+ # bridge access so it lives outside optimize_function.
93
+ out = superbest_module(out)
94
+ # Pass 3.5: opt-in ML pattern rewriter (libmonogate runtime).
95
+ if ml_routing:
96
+ out = route_ml_activations_module(out)
97
+ # Pass 4: drop unused imports (after inlining so reachable set
98
+ # reflects the post-inline call graph).
99
+ out = shake_imports(out)
100
+ return out
101
+
102
+
103
+ __all__ = [
104
+ "optimize_function",
105
+ "optimize_module",
106
+ "fold_constants",
107
+ "apply_cse",
108
+ "apply_cse_module",
109
+ "route_superbest",
110
+ "route_ml_activations_module",
111
+ ]