6b7ee45e49
The headline acceptance of #61: the simple-moving-average worked example from design/models/0007-kernel-extensions.md must build via `ail build --alloc=rc`, run, and print the correct moving averages. Green ⇒ the kernel-tier `Series` library extension works end to end (ring-buffer push, financial-style indexing at index 0 = newest, count/total bookkeeping). The fixture is the 0007 worked example with two corrections applied, both verified against the live tool this session: - `>=` is not a surface symbol; the polymorphic Ord helper is `ge` (here on Int operands: total_count vs window size). - `print` on Float emits no trailing newline, so the four averages would concatenate into an unreadable blob. The emit branch now prints the average followed by `io/print_str "\n"`, so each average lands on its own line and the output is deterministic and assertable. (This legibility fix is also queued to be carried back into model 0007.) RED reason (right-for-the-right-reason): `ail check` rejects with [bare-cross-module-type-ref] on `Series` — the kernel-tier module does not exist yet. The fixture itself parses and is otherwise valid. Expected stdout was derived by probing the live %g float formatter independently (the SMA averages are 9/3, 16/3, 17/3, 16/3 over the stream 1,5,3,8,6,2 with window 3): `3.0\n5.33333\n5.66667\n5.33333`. GREEN side (the `series` kernel module + the five ops) is the next mini-mode iteration.
115 lines
5.1 KiB
Rust
115 lines
5.1 KiB
Rust
//! Headline RED-pin for issue #61 ("[series] Step 1 — primitive
|
|
//! Series over {Int, Float}"): the simple-moving-average (SMA) worked
|
|
//! example from `design/models/0007-kernel-extensions.md` §"Worked
|
|
//! example — `Series` for streaming analytics" must `ail build
|
|
//! --alloc=rc` to a native binary, run, and print the correct moving
|
|
//! averages.
|
|
//!
|
|
//! Property protected: the kernel-tier `Series` library extension
|
|
//! works end-to-end as an ordinary AILang consumer sees it — the bare
|
|
//! kernel-tier type `(con Series (con Float))`, construction via
|
|
//! `(new Series (con Float) 3)`, type-scoped ops `Series.push`,
|
|
//! `Series.at`, `Series.total_count`, the bounded ring-buffer
|
|
//! semantics (window size 3), financial-style indexing (index 0 =
|
|
//! newest), and count/total bookkeeping. This single fixture
|
|
//! transitively forces the whole Series surface to be correct: a
|
|
//! wrong ring-buffer, a wrong index orientation, or wrong total_count
|
|
//! bookkeeping would print different averages.
|
|
//!
|
|
//! The SMA fixture (`examples/series_sma.ail`) is the worked example
|
|
//! from model 0007 with two VERIFIED corrections applied:
|
|
//! - `>=` (line 77 of 0007) replaced with the polymorphic Ord
|
|
//! prelude helper `ge` — `>=` does not exist as a symbol; both
|
|
//! operands here are `Int`.
|
|
//! - a newline separator (`(do io/print_str "\n")`) appended to each
|
|
//! emitted average — `print` emits no trailing newline, so without
|
|
//! it the four averages concatenate into an unreadable, unassertable
|
|
//! blob.
|
|
//! Everything else in the worked example is valid against today's tool.
|
|
//!
|
|
//! Why this is RED right now: the `series` kernel module does not exist
|
|
//! yet. `ail check examples/series_sma.ail` rejects with
|
|
//! `[bare-cross-module-type-ref]` — bare type `Series` not in scope,
|
|
//! candidate list empty — i.e. the feature is ABSENT, not a parse /
|
|
//! operator / scaffolding error. The GREEN side (authoring the kernel
|
|
//! `series` module + its 5 ops) is a later `implement` mini-mode
|
|
//! dispatch; this pin authors only the SMA *consumer*.
|
|
//!
|
|
//! This is a CLI-boundary E2E test because the acceptance spans the
|
|
//! check -> mono -> codegen -> link -> run pipeline and the symptom is
|
|
//! the printed moving averages of the produced binary.
|
|
//!
|
|
//! Expected stdout was derived by probing the live `%g`-style float
|
|
//! formatter INDEPENDENTLY of the (nonexistent) Series, so the
|
|
//! assertion encodes the intended averages formatted exactly as the
|
|
//! tool will format them once Series exists. Push stream
|
|
//! `1.0, 5.0, 3.0, 8.0, 6.0, 2.0`, window size 3, financial index
|
|
//! (0 = newest), `emit_if_full` emits once `total_count >= n`:
|
|
//! - after push 3.0: (3+5+1)/3 = 9/3 -> "3.0"
|
|
//! - after push 8.0: (8+3+5)/3 = 16/3 -> "5.33333"
|
|
//! - after push 6.0: (6+8+3)/3 = 17/3 -> "5.66667"
|
|
//! - after push 2.0: (2+6+8)/3 = 16/3 -> "5.33333"
|
|
//! Probe command (run this session):
|
|
//! ./target/release/ail run /tmp/sma_probe.ail --alloc=rc
|
|
//! where /tmp/sma_probe.ail printed each of 9.0/3, 16.0/3, 17.0/3,
|
|
//! 16.0/3 via `(app print (app / X (app int_to_float 3)))` separated
|
|
//! by `(do io/print_str "\n")`. Captured bytes (od -c):
|
|
//! `3.0\n5.33333\n5.66667\n5.33333\n`. After `.trim()`:
|
|
//! `3.0\n5.33333\n5.66667\n5.33333`.
|
|
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
|
|
fn repo_root() -> PathBuf {
|
|
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
|
Path::new(manifest_dir)
|
|
.parent()
|
|
.unwrap()
|
|
.parent()
|
|
.unwrap()
|
|
.to_path_buf()
|
|
}
|
|
|
|
/// The SMA worked example from model 0007 (a pure consumer of the
|
|
/// kernel-tier `Series` type) must build under `--alloc=rc`, run, and
|
|
/// print the four correct moving averages, one per line. Green ⇒ the
|
|
/// `Series` ring buffer, financial indexing, and count bookkeeping all
|
|
/// work end-to-end.
|
|
#[test]
|
|
fn series_sma_worked_example_builds_runs_correct_output() {
|
|
let fixture = repo_root().join("examples/series_sma.ail");
|
|
let tmp = std::env::temp_dir().join(format!(
|
|
"ailang_series_sma_{}",
|
|
std::process::id()
|
|
));
|
|
std::fs::create_dir_all(&tmp).unwrap();
|
|
let out = tmp.join("bin");
|
|
|
|
let build = Command::new(env!("CARGO_BIN_EXE_ail"))
|
|
.args(["build", fixture.to_str().unwrap(), "--alloc=rc", "-o"])
|
|
.arg(&out)
|
|
.output()
|
|
.expect("ail build failed to spawn");
|
|
assert!(
|
|
build.status.success(),
|
|
"ail build --alloc=rc failed for the SMA worked example (the \
|
|
kernel-tier `Series` library extension): stderr={}",
|
|
String::from_utf8_lossy(&build.stderr)
|
|
);
|
|
|
|
let run = Command::new(&out).output().expect("execute binary");
|
|
assert!(
|
|
run.status.success(),
|
|
"binary exited non-zero: {:?}",
|
|
run.status
|
|
);
|
|
let stdout = String::from_utf8_lossy(&run.stdout);
|
|
assert_eq!(
|
|
stdout.trim(),
|
|
"3.0\n5.33333\n5.66667\n5.33333",
|
|
"expected the four simple-moving-averages of the stream \
|
|
1.0,5.0,3.0,8.0,6.0,2.0 (window 3, financial index, emit once \
|
|
total_count>=3), formatted by the live float formatter; got: {stdout:?}"
|
|
);
|
|
}
|