diff --git a/docs/alib-roadmap.adoc b/docs/alib-roadmap.adoc index 6c49d09c..619ba1ee 100644 --- a/docs/alib-roadmap.adoc +++ b/docs/alib-roadmap.adoc @@ -135,10 +135,10 @@ edge-case requirements. |Mechanical comparison; ensures cross-language portability of code written against the aLib surface. |10 -|*Conformance module* — `stdlib/alib.affine` re-exporting the 20 ops under their aLib names -|`○` +|*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) +|`●` |T1 -|Single import point for consumers wanting the *aLib surface* rather than the *AffineScript-idiomatic surface*. +|Single import point for consumers wanting the *aLib surface* rather than the *AffineScript-idiomatic surface*. Landed 2026-05-28. |=== == Tier 2 — Conformance runner & infrastructure diff --git a/stdlib/alib.affine b/stdlib/alib.affine new file mode 100644 index 00000000..3eaa9883 --- /dev/null +++ b/stdlib/alib.affine @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: 2026 hyperpolymath +// +// AffineScript Standard Library — aLib Conformance Module +// +// Re-exports the 22 aLib v0.1.0 operations under their aLib-canonical +// names, so consumers wanting the *aLib surface* can write: +// +// use stdlib::alib::{add, multiply, fold, contains}; +// +// rather than reaching for the AffineScript-idiomatic operators and +// prelude functions directly. This is the Phase-A scaffolding step +// per docs/alib-roadmap.adoc item #10; downstream items #11 (schema +// loader) and #12 (test-vector executor) depend on this module +// existing. +// +// Number ↦ Int. aLib's abstract `Number` type maps to AffineScript's +// `Int` in this module. A parallel `alib_float` set could be added +// when Float-typed test vectors land in aggregate.json; currently +// (v0.1.0) test vectors use integer arithmetic. +// +// Each operation's signature in this file MUST match aLib's +// `signature_string` field in +// `developer-ecosystem/aggregate-library/data/aggregate.json`. The +// future `alib #9` signature-alignment audit checks that property. + +module alib; + +use prelude::{ fold as prelude_fold, contains as prelude_contains }; + +// ============================================================================ +// Arithmetic (5 ops) — aLib category: "arithmetic" +// ============================================================================ + +/// add :: Number, Number -> Number +pub fn add(a: Int, b: Int) -> Int { + a + b +} + +/// subtract :: Number, Number -> Number +pub fn subtract(a: Int, b: Int) -> Int { + a - b +} + +/// multiply :: Number, Number -> Number +pub fn multiply(a: Int, b: Int) -> Int { + a * b +} + +/// divide :: Number, Number -> Number +/// +/// Integer division. Division by zero is the consumer's responsibility; +/// aLib v0.1.0 does not specify the zero-divisor behaviour, so this +/// surfaces the underlying interpreter's response. +pub fn divide(a: Int, b: Int) -> Int { + a / b +} + +/// modulo :: Number, Number -> Number +pub fn modulo(a: Int, b: Int) -> Int { + a % b +} + +// ============================================================================ +// Comparison (6 ops) — aLib category: "comparison" +// ============================================================================ + +/// less_than :: Number, Number -> Boolean +pub fn less_than(a: Int, b: Int) -> Bool { + a < b +} + +/// greater_than :: Number, Number -> Boolean +pub fn greater_than(a: Int, b: Int) -> Bool { + a > b +} + +/// equal :: Number, Number -> Boolean +pub fn equal(a: Int, b: Int) -> Bool { + a == b +} + +/// not_equal :: Number, Number -> Boolean +pub fn not_equal(a: Int, b: Int) -> Bool { + a != b +} + +/// less_equal :: Number, Number -> Boolean +pub fn less_equal(a: Int, b: Int) -> Bool { + a <= b +} + +/// greater_equal :: Number, Number -> Boolean +pub fn greater_equal(a: Int, b: Int) -> Bool { + a >= b +} + +// ============================================================================ +// Logical (3 ops) — aLib category: "logical" +// ============================================================================ + +/// and :: Boolean, Boolean -> Boolean +pub fn and(a: Bool, b: Bool) -> Bool { + a && b +} + +/// or :: Boolean, Boolean -> Boolean +pub fn or(a: Bool, b: Bool) -> Bool { + a || b +} + +/// not :: Boolean -> Boolean +pub fn not(a: Bool) -> Bool { + !a +} + +// ============================================================================ +// String (3 ops) — aLib category: "string" +// ============================================================================ + +/// concat :: String, String -> String +pub fn concat(a: String, b: String) -> String { + a ++ b +} + +/// length :: String -> Number +pub fn length(s: String) -> Int { + len(s) +} + +/// substring :: String, Number, Number -> String +/// +/// aLib's substring is `(s, start, length)`. The interpreter builtin +/// `string_sub(s, start, length)` matches that contract directly. +pub fn substring(s: String, start: Int, n: Int) -> String { + string_sub(s, start, n) +} + +// ============================================================================ +// Collection (4 ops) — aLib category: "collection" +// ============================================================================ + +/// map :: Collection[A], Function[A -> B] -> Collection[B] +pub fn map(coll: [A], f: A -> B) -> [B] { + let mut result = []; + for x in coll { + result = result ++ [f(x)]; + } + result +} + +/// filter :: Collection[A], Function[A -> Boolean] -> Collection[A] +pub fn filter(coll: [A], predicate: A -> Bool) -> [A] { + let mut result = []; + for x in coll { + if predicate(x) { + result = result ++ [x]; + } + } + result +} + +/// fold :: Collection[A], B, Function[B, A -> B] -> B +pub fn fold(coll: [A], init: B, f: (B, A) -> B) -> B { + prelude_fold(coll, init, f) +} + +/// contains :: Collection[A], A -> Boolean +pub fn contains(coll: [A], element: A) -> Bool { + prelude_contains(coll, element) +} + +// ============================================================================ +// Conditional (1 op) — aLib category: "conditional" +// ============================================================================ + +/// if_then_else :: Boolean, A, A -> A +/// +/// Eager evaluation of both branches per the aLib spec. Consumers +/// requiring lazy evaluation should use the language `if … else …` +/// expression directly. +pub fn if_then_else(cond: Bool, then_val: A, else_val: A) -> A { + if cond { + then_val + } else { + else_val + } +}