Skip to content

Commit e38d5a2

Browse files
hyperpolymathclaude
andcommitted
feat(stdlib): alib.affine conformance module (closes #416)
aLib roadmap T1 item #10. Adds `stdlib/alib.affine` exposing the 22 aLib v0.1.0 operations under their canonical aLib names: * 5 arithmetic: add, subtract, multiply, divide, modulo * 6 comparison: less_than, greater_than, equal, not_equal, less_equal, greater_equal * 3 logical: and, or, not * 3 string: concat, length, substring * 4 collection: map, filter, fold, contains * 1 conditional: if_then_else Note the count correction in the roadmap row: was "20", current aggregate.json v0.1.0 has 22 ops across 6 categories. The signature of each export matches aLib's `signature_string` field (Number ↦ Int for v0.1.0; a parallel `alib_float` set can be added when Float-typed test vectors land). The module wraps existing prelude / builtin operations rather than re-implementing — `map`/`filter` are written explicitly to avoid the cross-module visibility quirk (visibility audit pending); `fold`/`contains` re-export from prelude via aliased import. Type-checks clean under `affinescript check stdlib/alib.affine`. Unlocks alib roadmap items #11 (schema loader) and #12 (test-vector executor). Closes #416. Refs umbrella #413. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c871f7f commit e38d5a2

2 files changed

Lines changed: 191 additions & 3 deletions

File tree

docs/alib-roadmap.adoc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,10 @@ edge-case requirements.
135135
|Mechanical comparison; ensures cross-language portability of code written against the aLib surface.
136136

137137
|10
138-
|*Conformance module* — `stdlib/alib.affine` re-exporting the 20 ops under their aLib names
139-
|``
138+
|*Conformance module* — `stdlib/alib.affine` re-exporting the 22 ops under their aLib names (was "20"; current aggregate.json v0.1.0 has 22 across 6 categories: 5 arithmetic, 6 comparison, 3 logical, 3 string, 4 collection, 1 conditional)
139+
|``
140140
|T1
141-
|Single import point for consumers wanting the *aLib surface* rather than the *AffineScript-idiomatic surface*.
141+
|Single import point for consumers wanting the *aLib surface* rather than the *AffineScript-idiomatic surface*. Landed 2026-05-28.
142142
|===
143143

144144
== Tier 2 — Conformance runner & infrastructure

stdlib/alib.affine

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
// SPDX-FileCopyrightText: 2026 hyperpolymath
3+
//
4+
// AffineScript Standard Library — aLib Conformance Module
5+
//
6+
// Re-exports the 22 aLib v0.1.0 operations under their aLib-canonical
7+
// names, so consumers wanting the *aLib surface* can write:
8+
//
9+
// use stdlib::alib::{add, multiply, fold, contains};
10+
//
11+
// rather than reaching for the AffineScript-idiomatic operators and
12+
// prelude functions directly. This is the Phase-A scaffolding step
13+
// per docs/alib-roadmap.adoc item #10; downstream items #11 (schema
14+
// loader) and #12 (test-vector executor) depend on this module
15+
// existing.
16+
//
17+
// Number ↦ Int. aLib's abstract `Number` type maps to AffineScript's
18+
// `Int` in this module. A parallel `alib_float` set could be added
19+
// when Float-typed test vectors land in aggregate.json; currently
20+
// (v0.1.0) test vectors use integer arithmetic.
21+
//
22+
// Each operation's signature in this file MUST match aLib's
23+
// `signature_string` field in
24+
// `developer-ecosystem/aggregate-library/data/aggregate.json`. The
25+
// future `alib #9` signature-alignment audit checks that property.
26+
27+
module alib;
28+
29+
use prelude::{ fold as prelude_fold, contains as prelude_contains };
30+
31+
// ============================================================================
32+
// Arithmetic (5 ops) — aLib category: "arithmetic"
33+
// ============================================================================
34+
35+
/// add :: Number, Number -> Number
36+
pub fn add(a: Int, b: Int) -> Int {
37+
a + b
38+
}
39+
40+
/// subtract :: Number, Number -> Number
41+
pub fn subtract(a: Int, b: Int) -> Int {
42+
a - b
43+
}
44+
45+
/// multiply :: Number, Number -> Number
46+
pub fn multiply(a: Int, b: Int) -> Int {
47+
a * b
48+
}
49+
50+
/// divide :: Number, Number -> Number
51+
///
52+
/// Integer division. Division by zero is the consumer's responsibility;
53+
/// aLib v0.1.0 does not specify the zero-divisor behaviour, so this
54+
/// surfaces the underlying interpreter's response.
55+
pub fn divide(a: Int, b: Int) -> Int {
56+
a / b
57+
}
58+
59+
/// modulo :: Number, Number -> Number
60+
pub fn modulo(a: Int, b: Int) -> Int {
61+
a % b
62+
}
63+
64+
// ============================================================================
65+
// Comparison (6 ops) — aLib category: "comparison"
66+
// ============================================================================
67+
68+
/// less_than :: Number, Number -> Boolean
69+
pub fn less_than(a: Int, b: Int) -> Bool {
70+
a < b
71+
}
72+
73+
/// greater_than :: Number, Number -> Boolean
74+
pub fn greater_than(a: Int, b: Int) -> Bool {
75+
a > b
76+
}
77+
78+
/// equal :: Number, Number -> Boolean
79+
pub fn equal(a: Int, b: Int) -> Bool {
80+
a == b
81+
}
82+
83+
/// not_equal :: Number, Number -> Boolean
84+
pub fn not_equal(a: Int, b: Int) -> Bool {
85+
a != b
86+
}
87+
88+
/// less_equal :: Number, Number -> Boolean
89+
pub fn less_equal(a: Int, b: Int) -> Bool {
90+
a <= b
91+
}
92+
93+
/// greater_equal :: Number, Number -> Boolean
94+
pub fn greater_equal(a: Int, b: Int) -> Bool {
95+
a >= b
96+
}
97+
98+
// ============================================================================
99+
// Logical (3 ops) — aLib category: "logical"
100+
// ============================================================================
101+
102+
/// and :: Boolean, Boolean -> Boolean
103+
pub fn and(a: Bool, b: Bool) -> Bool {
104+
a && b
105+
}
106+
107+
/// or :: Boolean, Boolean -> Boolean
108+
pub fn or(a: Bool, b: Bool) -> Bool {
109+
a || b
110+
}
111+
112+
/// not :: Boolean -> Boolean
113+
pub fn not(a: Bool) -> Bool {
114+
!a
115+
}
116+
117+
// ============================================================================
118+
// String (3 ops) — aLib category: "string"
119+
// ============================================================================
120+
121+
/// concat :: String, String -> String
122+
pub fn concat(a: String, b: String) -> String {
123+
a ++ b
124+
}
125+
126+
/// length :: String -> Number
127+
pub fn length(s: String) -> Int {
128+
len(s)
129+
}
130+
131+
/// substring :: String, Number, Number -> String
132+
///
133+
/// aLib's substring is `(s, start, length)`. The interpreter builtin
134+
/// `string_sub(s, start, length)` matches that contract directly.
135+
pub fn substring(s: String, start: Int, n: Int) -> String {
136+
string_sub(s, start, n)
137+
}
138+
139+
// ============================================================================
140+
// Collection (4 ops) — aLib category: "collection"
141+
// ============================================================================
142+
143+
/// map :: Collection[A], Function[A -> B] -> Collection[B]
144+
pub fn map<A, B>(coll: [A], f: A -> B) -> [B] {
145+
let mut result = [];
146+
for x in coll {
147+
result = result ++ [f(x)];
148+
}
149+
result
150+
}
151+
152+
/// filter :: Collection[A], Function[A -> Boolean] -> Collection[A]
153+
pub fn filter<A>(coll: [A], predicate: A -> Bool) -> [A] {
154+
let mut result = [];
155+
for x in coll {
156+
if predicate(x) {
157+
result = result ++ [x];
158+
}
159+
}
160+
result
161+
}
162+
163+
/// fold :: Collection[A], B, Function[B, A -> B] -> B
164+
pub fn fold<A, B>(coll: [A], init: B, f: (B, A) -> B) -> B {
165+
prelude_fold(coll, init, f)
166+
}
167+
168+
/// contains :: Collection[A], A -> Boolean
169+
pub fn contains<A>(coll: [A], element: A) -> Bool {
170+
prelude_contains(coll, element)
171+
}
172+
173+
// ============================================================================
174+
// Conditional (1 op) — aLib category: "conditional"
175+
// ============================================================================
176+
177+
/// if_then_else :: Boolean, A, A -> A
178+
///
179+
/// Eager evaluation of both branches per the aLib spec. Consumers
180+
/// requiring lazy evaluation should use the language `if … else …`
181+
/// expression directly.
182+
pub fn if_then_else<A>(cond: Bool, then_val: A, else_val: A) -> A {
183+
if cond {
184+
then_val
185+
} else {
186+
else_val
187+
}
188+
}

0 commit comments

Comments
 (0)