sutra-dev 0.2.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.
@@ -0,0 +1,97 @@
1
+ // Canonical Sutra definitions for vector-similarity operations.
2
+ //
3
+ // Today these are hardcoded as runtime methods in codegen.py —
4
+ // see `def eq`, `def similarity`, and the vectorized `_argmax_cosine` /
5
+ // `_select_softmax` helpers emitted by _emit_prelude. This file is
6
+ // the target shape for the function-expansion pass to consume. See
7
+ // stdlib/README.md.
8
+
9
+
10
+ // ==========================================================================
11
+ // Section 1 — implemented in pure Sutra
12
+ // ==========================================================================
13
+
14
+
15
+ // neq — negation of cosine-similarity equality. Composed, not primitive.
16
+ // Once `eq` inlines, this collapses to `logical_not(eq(a, b))` which is
17
+ // one extra polynomial on top of the eq result.
18
+ function fuzzy neq(vector a, vector b) {
19
+ return !(a == b);
20
+ }
21
+
22
+
23
+ // ==========================================================================
24
+ // Section 2 — blocked on intrinsics
25
+ // ==========================================================================
26
+ //
27
+ // eq — cosine similarity, placed on the truth axis.
28
+ //
29
+ // function fuzzy eq(vector a, vector b) {
30
+ // scalar na = sqrt(dot(a, a));
31
+ // scalar nb = sqrt(dot(b, b));
32
+ // scalar cos = dot(a, b) / (na * nb + finfo_tiny);
33
+ // return make_truth(cos);
34
+ // }
35
+ //
36
+ // Blocked on: `dot`, `sqrt`, `make_truth`, `finfo_tiny`.
37
+
38
+
39
+ // similarity / Cosine — cosine similarity as a scalar (no truth-axis
40
+ // placement). Same dot/norm pipeline as eq, but returns the scalar
41
+ // cosine instead of wrapping it on the truth axis.
42
+ //
43
+ // function scalar similarity(vector a, vector b) {
44
+ // scalar na = sqrt(dot(a, a));
45
+ // scalar nb = sqrt(dot(b, b));
46
+ // return dot(a, b) / (na * nb + finfo_tiny);
47
+ // }
48
+ //
49
+ // Blocked on: `dot`, `sqrt`.
50
+
51
+
52
+ // argmax_cosine — highest-cosine match from a candidate list.
53
+ //
54
+ // function vector argmax_cosine(vector query, vector[] candidates) {
55
+ // matrix C = stack(candidates);
56
+ // vector scores = (C @ query) / (row_norms(C) * norm(query) + tiny);
57
+ // int best = argmax(scores);
58
+ // return candidates[best];
59
+ // }
60
+ //
61
+ // Blocked on: `stack`, `@` matmul, `row_norms`, `argmax`, array indexing
62
+ // with a runtime int.
63
+
64
+
65
+ // select — softmax-weighted superposition of option vectors.
66
+ //
67
+ // function vector select(scalar[] scores, vector[] options) {
68
+ // scalar[] s = scores - max(scores);
69
+ // scalar[] w = exp(s);
70
+ // w = w / sum(w);
71
+ // return sum(w[i] * options[i] for i);
72
+ // }
73
+ //
74
+ // Blocked on: `max`, `exp`, `sum` reductions, broadcast-multiply over
75
+ // an array, comprehension-style reduction.
76
+
77
+
78
+ // snap — cleanup to nearest stored pattern. The canonical "lookup"
79
+ // operation on a memory substrate. Not supported on the default CPU
80
+ // runtime — requires a substrate-level cleanup circuit. See
81
+ // `planning/sutra-spec/operations.md` for the semantic contract —
82
+ // cleanup is a substrate-level primitive, not composable from other
83
+ // Sutra ops.
84
+
85
+
86
+ // ==========================================================================
87
+ // Section 3 — intrinsic declarations
88
+ // ==========================================================================
89
+ //
90
+ // These are runtime-implemented. Calls compile to `_VSA.<name>(...)`.
91
+ // The stubs above show the target Sutra-level definitions once the
92
+ // underlying primitives (dot, sqrt, tanh) get first-class surfaces;
93
+ // for now the body lives in the runtime class and these declarations
94
+ // expose the signatures at the Sutra level.
95
+
96
+ intrinsic function fuzzy eq(vector a, vector b);
97
+ intrinsic function scalar similarity(vector a, vector b);
@@ -0,0 +1,56 @@
1
+ // Canonical Sutra declaration for the String class and its
2
+ // 1-length subclass Character.
3
+ //
4
+ // Spec: planning/sutra-spec/strings.md.
5
+ //
6
+ // A String is a synthetic-axis-encoded array of codepoints:
7
+ // - AXIS_STRING_FLAG (synthetic[3]) = 1.0 marks the value as a String.
8
+ // - char[0] at AXIS_REAL (synthetic[0]).
9
+ // - char[1] at AXIS_IMAG (synthetic[1]).
10
+ // - char[k] for k >= 2 at synthetic[k + 3].
11
+ // - Trailing zero codepoints are treated as end-of-string.
12
+ // At the substrate level a String is "the same shape as a complex
13
+ // vector" — pairs of codepoints occupy the real/imag positions of
14
+ // each successive 2D slot.
15
+ //
16
+ // A Character is a String of length 1. Same flag, same encoding,
17
+ // just a typing refinement so the type system can say "this slot
18
+ // holds exactly one codepoint" where useful. Character is a
19
+ // subclass of String.
20
+ //
21
+ // Surface forms wired today:
22
+ // - `String.make_string("hello")` builds a String from a Sutra
23
+ // string literal.
24
+ // - `s.string_length()` returns the length as an int.
25
+ // - `s.string_char_at(i)` returns the codepoint at position i.
26
+ // - `String.is_string(v)` checks whether a vector is flag-set.
27
+ //
28
+ // Method-syntax-without-prefix (`s.length()`, `s[i]`, `s == t`) is
29
+ // the eventual user-facing target; today the prefixed runtime names
30
+ // are what dispatch.
31
+ //
32
+ // What is deferred:
33
+ // - String concatenation: `String.concat(a, b)` — would build a
34
+ // new String whose chars are the chars of a followed by the
35
+ // chars of b. Need to decide overflow semantics.
36
+ // - String equality: `String.equals(a, b)` — bit-equality on the
37
+ // codepoint axes, ignoring everything else.
38
+ // - String indexing as `s[i]` — sugar for string_char_at; needs
39
+ // the existing subscript dispatch to recognize String-typed
40
+ // locals.
41
+ // - Operator overloading for `+` (concat) and `==`. Sutra's
42
+ // operator-method support is partial; this is a class-system
43
+ // follow-up.
44
+
45
+ class String extends vector {
46
+ static intrinsic method String make_string(string s);
47
+ static intrinsic method int string_length(String v);
48
+ static intrinsic method int string_char_at(String v, int i);
49
+ static intrinsic method bool is_string(vector v);
50
+ }
51
+
52
+ // A Character is a 1-length String. Same substrate encoding, same
53
+ // AXIS_STRING_FLAG, just narrower static type. Today the only thing
54
+ // that distinguishes them statically is the declared type; the
55
+ // runtime returns the same vector shape from make_char and make_string.
56
+ class Character extends String { }
@@ -0,0 +1,82 @@
1
+ // Sutra's leaf VSA primitives — the bottom of the function-expansion
2
+ // chain.
3
+ //
4
+ // Higher-level operations like `bind`, `unbind`, `bundle`, and
5
+ // `similarity` are NOT magical substrate calls. They beta-reduce to
6
+ // the operations declared in this file:
7
+ //
8
+ // bind(role, filler) -> MatrixMul(RotationFor(role), filler)
9
+ // unbind(role, record) -> MatrixMul(Transpose(RotationFor(role)), record)
10
+ // bundle(a, b, c, ...) -> Normalize(a + b + c + ...)
11
+ // similarity(a, b) -> Dot(a, b) / (Norm(a) * Norm(b))
12
+ //
13
+ // The point of this file is **transparency**. When a programmer asks
14
+ // "what does `bind` actually do at the lowest level?" — the answer
15
+ // is `MatrixMul(RotationFor(role), filler)` and they can verify that
16
+ // by reading the source here. Same for `bundle` (vector add +
17
+ // normalize), `similarity` (dot / norm), and so on.
18
+ //
19
+ // These ARE the VSA primitives. Every other operation in the
20
+ // language reduces to a chain of calls to these. The bind /
21
+ // unbind / bundle / similarity operations are convenience names
22
+ // for common compositions; the actual computational substrate is
23
+ // matmul + tensor product + outer + dot + transpose.
24
+ //
25
+ // Both PascalCase and snake_case names are exposed:
26
+ //
27
+ // vector x = Tensor.MatrixMul(M, v); // preferred
28
+ // vector x = Tensor.matmul(M, v); // also works
29
+ //
30
+ // And the bare-name aliases via the stdlib_loader's dual
31
+ // registration:
32
+ //
33
+ // vector x = MatrixMul(M, v); // bare-name shortcut
34
+
35
+ class Tensor extends vector {
36
+ // Matrix multiplication. Works on 1-D, 2-D, or higher-rank
37
+ // arguments per numpy/torch matmul semantics:
38
+ // (n,) @ (n,) -> scalar (dot)
39
+ // (m, k) @ (k, n) -> (m, n)
40
+ // (m, k) @ (k,) -> (m,) — vector treated as column
41
+ // (k,) @ (k, n) -> (n,) — vector treated as row
42
+ static intrinsic method vector MatrixMul(vector a, vector b);
43
+ static intrinsic method vector matmul(vector a, vector b);
44
+
45
+ // Kronecker / tensor product. For two vectors of length n and m,
46
+ // produces a vector of length n*m: every pairwise product. For
47
+ // higher-rank inputs, produces the standard Kronecker block
48
+ // matrix.
49
+ static intrinsic method vector TensorProduct(vector a, vector b);
50
+ static intrinsic method vector tensor_product(vector a, vector b);
51
+
52
+ // Outer product. For two 1-D vectors a (length n) and b (length
53
+ // m), produces a 2-D matrix `M` where `M[i, j] = a[i] * b[j]`.
54
+ static intrinsic method vector Outer(vector a, vector b);
55
+ static intrinsic method vector outer(vector a, vector b);
56
+
57
+ // Inner / dot product. Scalar result.
58
+ static intrinsic method scalar Dot(vector a, vector b);
59
+ static intrinsic method scalar dot(vector a, vector b);
60
+
61
+ // Transpose. For 2-D input, swaps the two axes; for 1-D, returns
62
+ // the input unchanged (numpy convention — there's no "row" vs
63
+ // "column" distinction at rank-1).
64
+ static intrinsic method vector Transpose(vector m);
65
+ static intrinsic method vector transpose(vector m);
66
+
67
+ // L2 norm of a vector. Scalar result.
68
+ static intrinsic method scalar Norm(vector v);
69
+ static intrinsic method scalar norm(vector v);
70
+
71
+ // L2 normalization. Returns v / Norm(v) with an eps-guard to
72
+ // make the zero-norm case behave (returns zero, no NaN).
73
+ static intrinsic method vector Normalize(vector v);
74
+ static intrinsic method vector normalize(vector v);
75
+
76
+ // Cached rotation matrix for a role vector. The role-vector hash
77
+ // keys a Haar-random orthogonal rotation in the semantic block
78
+ // (identity in the synthetic block) — the substrate's binding
79
+ // operator. Same role always produces the same rotation.
80
+ static intrinsic method vector RotationFor(vector role);
81
+ static intrinsic method vector rotation_for(vector role);
82
+ }
@@ -0,0 +1,119 @@
1
+ // Canonical Sutra definitions for VSA vector operations — bind,
2
+ // unbind, bundle, permute. These are the core hypervector primitives
3
+ // the language is built around.
4
+ //
5
+ // Today they're hardcoded as runtime methods in codegen.py — see
6
+ // `def bind`, `def unbind`, `def bundle`, `def bundle_of_binds`,
7
+ // `def sign_flip`, `def make_sign_flip_key`. This file is the target
8
+ // shape for the inliner. See stdlib/README.md.
9
+ //
10
+ // Every VSA primitive below is currently blocked on intrinsics —
11
+ // they need matrix literals, matmul, variadic reductions, or
12
+ // seeded-random primitives that aren't Sutra-callable yet. The
13
+ // bodies below are pseudo-Sutra showing the target expansion.
14
+
15
+
16
+ // ==========================================================================
17
+ // Section 2 — blocked on intrinsics
18
+ // ==========================================================================
19
+ //
20
+ // bind — role-seeded Haar-random orthogonal rotation applied to filler.
21
+ // The rotation is block-diagonal: Haar in the semantic block, identity
22
+ // in the synthetic block, so bind acts only on semantic content and
23
+ // leaves the real / imag / truth / char-flag axes untouched.
24
+ //
25
+ // function vector bind(vector role, vector filler) {
26
+ // matrix Q = rotation_for_role(role); // cached Haar rotation
27
+ // return Q @ filler;
28
+ // }
29
+ //
30
+ // Blocked on: `rotation_for_role` primitive (role-hash → cached
31
+ // rotation matrix), `@` matmul. The caching is what makes repeat
32
+ // bind with the same role one lookup + one matmul.
33
+
34
+
35
+ // unbind — inverse of bind. Same block-diagonal rotation, transposed.
36
+ //
37
+ // function vector unbind(vector role, vector bound) {
38
+ // matrix Q = rotation_for_role(role);
39
+ // return transpose(Q) @ bound;
40
+ // }
41
+ //
42
+ // Blocked on: `rotation_for_role`, `transpose`, `@` matmul.
43
+
44
+
45
+ // bundle — normalized superposition. Variadic: takes any number of
46
+ // vectors, sums them, L2-normalizes the result.
47
+ //
48
+ // function vector bundle(vector... args) {
49
+ // vector s = zero_vector();
50
+ // foreach (v in args) {
51
+ // s = s + v;
52
+ // }
53
+ // return s / (norm(s) + finfo_tiny);
54
+ // }
55
+ //
56
+ // Blocked on: variadic parameter surface (`vector... args`), `norm`,
57
+ // `finfo_tiny`, scalar-divide-vector.
58
+ //
59
+ // The fused bundle-of-binds shape the codegen already emits
60
+ // (`bundle_of_binds((r1,f1), (r2,f2), ...)` → one einsum) is a
61
+ // compile-time peephole on top of this spec definition.
62
+
63
+
64
+ // basis_vector — alias for `embed`. Allocates a deterministic vector
65
+ // keyed by the string name. Kept as a separate spelling for clarity
66
+ // in VSA-style programs (role / filler allocation) vs pure embedding.
67
+ //
68
+ // function vector basis_vector(string name) {
69
+ // return embed(name);
70
+ // }
71
+ //
72
+ // This one is actually writable as Sutra today — `embed(x)` is the
73
+ // parsable primitive. Not listed as implemented above only because
74
+ // the inliner to consume it doesn't exist yet; when it does, this
75
+ // function becomes real.
76
+
77
+
78
+ // permute — apply a permutation key to a vector. The permutation
79
+ // scheme is a deterministic ±1 mask multiplied elementwise with
80
+ // the vector.
81
+ //
82
+ // function vector permute(vector v, permutation key) {
83
+ // return v * sign_flip_mask(key);
84
+ // }
85
+ //
86
+ // Blocked on: `sign_flip_mask` primitive (key → ±1 vector),
87
+ // element-wise multiply between a vector and a mask.
88
+
89
+
90
+ // permutation_key — allocate a deterministic permutation key from a
91
+ // string name.
92
+ //
93
+ // function permutation permutation_key(string name) {
94
+ // int seed = string_hash(name);
95
+ // return random_sign_mask(seed);
96
+ // }
97
+ //
98
+ // Blocked on: `string_hash`, `random_sign_mask` primitives.
99
+
100
+
101
+ // identity_permutation — the no-op permutation. Applying it leaves
102
+ // a vector unchanged. Useful as a sentinel / default.
103
+ //
104
+ // function permutation identity_permutation() {
105
+ // return ones_mask();
106
+ // }
107
+ //
108
+ // Blocked on: `ones_mask` primitive (a ±1 mask that's all +1).
109
+
110
+
111
+ // compose — compose two permutations. With the ±1-mask
112
+ // representation, composition is element-wise multiply of the
113
+ // two masks.
114
+ //
115
+ // function permutation compose(permutation p, permutation q) {
116
+ // return p * q;
117
+ // }
118
+ //
119
+ // Blocked on: permutation type's Sutra-level operator semantics.
@@ -0,0 +1,219 @@
1
+ """Loader for the Sutra standard library.
2
+
3
+ Walks `sutra_compiler/stdlib/*.su` at compiler init, parses each file,
4
+ and returns a symbol table mapping function names to their parsed
5
+ `FunctionDecl` AST nodes. The inliner pass consumes this table: when
6
+ it sees a `Call(Identifier(name), args)` whose name is in the table,
7
+ it beta-reduces the function body into the caller's AST.
8
+
9
+ This is the first stage in the stdlib lowering pipeline. Subsequent
10
+ stages (the inliner pass itself, unroll propagation, runtime-method
11
+ deletion, intrinsic mechanism, fusion pass) build on the symbol
12
+ table this module produces.
13
+
14
+ The loader is deliberately simple:
15
+ - No caching across processes. The parse is fast (7 files, ~600
16
+ lines total today) and runs once per compiler instantiation.
17
+ - No transitive resolution. If stdlib function A calls stdlib
18
+ function B, the inliner handles the nesting by inlining A first
19
+ and then running the pass again (or in one fixpoint pass) — this
20
+ module just returns the raw declarations.
21
+ - Duplicate names across files are a hard error. A function should
22
+ live in exactly one .su file; see stdlib/README.md's category
23
+ split.
24
+ - Parse diagnostics from stdlib files are fatal — the stdlib is the
25
+ compiler's own code, not user input. A broken stdlib is a compiler
26
+ bug.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import os
32
+ from typing import Dict, List, Optional
33
+
34
+ from . import ast_nodes as ast
35
+ from .lexer import Lexer
36
+ from .parser import Parser
37
+
38
+
39
+ STDLIB_DIR = os.path.join(os.path.dirname(__file__), "stdlib")
40
+
41
+
42
+ class StdlibLoadError(Exception):
43
+ """Raised when the stdlib fails to load. Always a compiler bug —
44
+ never a user error. Carries the file path and underlying reason
45
+ so the stacktrace points at the real problem."""
46
+
47
+
48
+ def load_stdlib(stdlib_dir: str = STDLIB_DIR) -> Dict[str, ast.FunctionDecl]:
49
+ """Return `{function_name → FunctionDecl}` for every function
50
+ declaration in every `*.su` file under the stdlib directory.
51
+
52
+ Both shapes are picked up:
53
+ 1. Top-level `function ...` and `intrinsic function ...;` —
54
+ the original stdlib form. Goes into the table by its bare
55
+ name (`log`, `bind`, etc.).
56
+ 2. Class-body static methods on stdlib classes (`class Math {
57
+ static intrinsic method scalar log(scalar x); ... }`) — the
58
+ post-2026-05-01 namespaced form. Goes into the table under
59
+ BOTH the bare name (`log`) and the namespaced name
60
+ (`Math.log`). The bare-name entry preserves backward
61
+ compatibility for user code that still calls `log(x)`; the
62
+ namespaced entry supports the future call shape
63
+ `Math.log(x)`. Class-bodied static methods are repackaged
64
+ as `FunctionDecl` so callers don't have to know which
65
+ shape they came from.
66
+
67
+ Method declarations on instances (non-static), top-level
68
+ statements, and class bodies that aren't static-method bearing
69
+ are ignored — the stdlib is callable-namespace-only by design.
70
+ """
71
+ table: Dict[str, ast.FunctionDecl] = {}
72
+
73
+ def _add(name: str, decl: ast.FunctionDecl, path: str) -> None:
74
+ if name in table:
75
+ existing = table[name]
76
+ raise StdlibLoadError(
77
+ f"duplicate stdlib function {name!r}: "
78
+ f"declared at {path}:{decl.span.start.line} and "
79
+ f"previously at {existing.span.start.line} in an "
80
+ f"earlier file. Each function should live in one "
81
+ f"stdlib file; see stdlib/README.md for the "
82
+ f"category split."
83
+ )
84
+ table[name] = decl
85
+
86
+ for fname in sorted(os.listdir(stdlib_dir)):
87
+ if not fname.endswith(".su"):
88
+ continue
89
+ path = os.path.join(stdlib_dir, fname)
90
+ module = _parse_stdlib_file(path)
91
+ for item in module.items:
92
+ if isinstance(item, ast.FunctionDecl):
93
+ _add(item.name, item, path)
94
+ elif isinstance(item, ast.ClassDecl):
95
+ for m in item.methods:
96
+ if (m.modifiers.is_static
97
+ and not m.is_operator
98
+ and not m.type_params):
99
+ # Repackage as a FunctionDecl so callers don't
100
+ # need to special-case class-bodied entries.
101
+ repackaged = ast.FunctionDecl(
102
+ modifiers=m.modifiers,
103
+ return_type=m.return_type,
104
+ name=m.name,
105
+ type_params=m.type_params,
106
+ params=m.params,
107
+ body=m.body,
108
+ is_operator=False,
109
+ is_intrinsic=m.is_intrinsic,
110
+ span=m.span,
111
+ )
112
+ # Bare-name entry (backward compat).
113
+ _add(m.name, repackaged, path)
114
+ # Namespaced entry (`Math.log`) — duplicates
115
+ # the AST node, which is fine because lookups
116
+ # don't mutate it.
117
+ _add(f"{item.name}.{m.name}", repackaged, path)
118
+
119
+ return table
120
+
121
+
122
+ def _parse_stdlib_file(path: str) -> ast.Module:
123
+ """Lex + parse one stdlib .su file. Fatal on any diagnostic."""
124
+ with open(path, encoding="utf-8") as fp:
125
+ src = fp.read()
126
+ lexer = Lexer(src, file=path)
127
+ tokens = lexer.tokenize()
128
+ if lexer.diagnostics.has_errors():
129
+ raise StdlibLoadError(
130
+ f"{path}: stdlib lex errors — compiler bug:\n"
131
+ + "\n".join(d.format() for d in lexer.diagnostics)
132
+ )
133
+ parser = Parser(tokens, file=path, diagnostics=lexer.diagnostics)
134
+ module = parser.parse_module()
135
+ if lexer.diagnostics.has_errors():
136
+ raise StdlibLoadError(
137
+ f"{path}: stdlib parse errors — compiler bug:\n"
138
+ + "\n".join(d.format() for d in lexer.diagnostics)
139
+ )
140
+ return module
141
+
142
+
143
+ def stdlib_function_names(stdlib_dir: str = STDLIB_DIR) -> List[str]:
144
+ """Names of every function declaration the stdlib contributes.
145
+
146
+ Convenience wrapper around `load_stdlib` — returns just the names,
147
+ sorted, for diagnostics and documentation. A runtime-methods
148
+ removal pass can use this as the authoritative list of "stdlib
149
+ callers exist; this runtime method is a candidate for deletion
150
+ once the inliner is wired."
151
+ """
152
+ return sorted(load_stdlib(stdlib_dir).keys())
153
+
154
+
155
+ # Module-level cache of the intrinsic names — names declared via
156
+ # `intrinsic function ...;` in any stdlib file. The codegen uses this
157
+ # to route `Call(Identifier(name), args)` to `_VSA.<name>(args)`
158
+ # when name is an intrinsic (the runtime class implements it).
159
+ # Populated lazily on first access; safe because stdlib source is
160
+ # frozen for a given process.
161
+ _INTRINSIC_NAMES_CACHE: Optional[frozenset] = None
162
+
163
+
164
+ def intrinsic_names(stdlib_dir: str = STDLIB_DIR) -> frozenset:
165
+ """Return the frozenset of intrinsic function names declared in
166
+ the stdlib — the leaves the runtime must implement.
167
+
168
+ Includes both bare-name and namespaced (`Math.log`) entries since
169
+ the dual-registration pattern (load_stdlib) puts both into the
170
+ table. Callers that need only one form can filter with `.`."""
171
+ global _INTRINSIC_NAMES_CACHE
172
+ if _INTRINSIC_NAMES_CACHE is None:
173
+ table = load_stdlib(stdlib_dir)
174
+ _INTRINSIC_NAMES_CACHE = frozenset(
175
+ name for name, decl in table.items()
176
+ if getattr(decl, "is_intrinsic", False)
177
+ )
178
+ return _INTRINSIC_NAMES_CACHE
179
+
180
+
181
+ # Per-class intrinsic-method registry, populated lazily. Maps
182
+ # `class_name -> frozenset of intrinsic method names`. Used by the
183
+ # codegen to dispatch `Tensor.MatrixMul(a, b)` to `_VSA.MatrixMul(a, b)`
184
+ # when the class is declared in stdlib (and therefore not in the
185
+ # user's module AST).
186
+ _STDLIB_CLASS_INTRINSICS_CACHE: Optional[Dict[str, frozenset]] = None
187
+
188
+
189
+ def stdlib_class_intrinsic_methods(
190
+ stdlib_dir: str = STDLIB_DIR,
191
+ ) -> Dict[str, frozenset]:
192
+ """Return `{class_name: frozenset(method_names)}` for every
193
+ static-intrinsic method declared inside a stdlib class body.
194
+
195
+ Lets the codegen dispatch namespaced stdlib calls
196
+ (`Tensor.MatrixMul(a, b)`) to the runtime alongside its existing
197
+ user-class dispatch path.
198
+ """
199
+ global _STDLIB_CLASS_INTRINSICS_CACHE
200
+ if _STDLIB_CLASS_INTRINSICS_CACHE is None:
201
+ result: Dict[str, set] = {}
202
+ for fname in sorted(os.listdir(stdlib_dir)):
203
+ if not fname.endswith(".su"):
204
+ continue
205
+ path = os.path.join(stdlib_dir, fname)
206
+ module = _parse_stdlib_file(path)
207
+ for item in module.items:
208
+ if not isinstance(item, ast.ClassDecl):
209
+ continue
210
+ for m in item.methods:
211
+ if (m.is_intrinsic
212
+ and m.modifiers.is_static
213
+ and not m.is_operator
214
+ and not m.type_params):
215
+ result.setdefault(item.name, set()).add(m.name)
216
+ _STDLIB_CLASS_INTRINSICS_CACHE = {
217
+ cls: frozenset(names) for cls, names in result.items()
218
+ }
219
+ return _STDLIB_CLASS_INTRINSICS_CACHE