Files
AILang/crates/ail/tests/floats_e2e.rs
T
Brummel 13946219f5 fix(runtime): mirror surface .0-fallback in ailang_float_to_str
`runtime/str.c::ailang_float_to_str` now applies the `.0`-fallback
already enforced by the surface printer's `write_float_lit`
(crates/ailang-surface/src/print.rs lines 624-637): after the
existing `%g` snprintf, if `x` is finite and the rendered buffer
contains neither `.` nor `e`/`E`, append `.0` so a whole-valued
Float renders with Float-shaped text. The `isfinite(x)` fence
keeps `nan`/`inf`/`-inf` from matching the predicate and turning
into `nan.0`/`inf.0`. The 64-byte stack buffer's truncation guard
is extended by 2 bytes for the fallback path so the defensive
`abort()` discipline holds.

The alternative (document `%g`-without-fallback in the design/
ledger as a deliberate human-friendly contract) was rejected: the
language's stated ethos is machine-readability and round-trip
identity; the surface printer is the reference, runtime drift
from it is a defect not a feature.

Golden updates flow from the contract change. Three fixtures
encoded the old Int-shaped output for Float values:
- `crates/ail/tests/floats_e2e.rs`: `"4\n42\n-1.5\n"` →
  `"4.0\n42.0\n-1.5\n"` (1.5+2.5=4.0, int_to_float(42)=42.0).
- `crates/ail/tests/e2e.rs::mut_sum_floats_prints_55`: `"55"` →
  `"55.0"`. The accompanying doc comment used to canonicalise the
  bug ("`%g` strips the trailing `.0` — so the canonical stdout
  for Float 55.0 is `55`"); rewritten to reflect the new contract.
- `examples/mut_sum_floats.ail` docstring: same correction.

Two doc comments are updated where the post-fix contract differs
from the old text but the fixture golden does not change:
- `examples/float_to_str_smoke.ail` docstring (3.5 still renders
  as `3.5` because `.` is already present — the fallback does not
  fire).
- `crates/ail/tests/e2e.rs::float_to_str_smoke` doc comment (same
  observation, made explicit so the trip-wire's intent is clear).

Verified: `cargo test -p ail --test print_float_whole_e2e` green
(was RED at f19b0dd); `cargo test --workspace` 0 failed across
all crates and integration suites.

closes #7
2026-05-20 18:24:57 +02:00

72 lines
2.3 KiB
Rust

//! Floats milestone iter 4.6 — end-to-end fixture.
//!
//! `examples/floats.ail.json` is built via the public `ail build`
//! CLI and run; stdout is matched against the expected three
//! lines (`4.0`, `42.0`, `-1.5`). This test exercises the full
//! pipeline — Float literal lowering, polymorphic `+` Float arm,
//! `int_to_float` (sitofp), `neg` Float (fneg), and the polymorphic
//! `print` for Float (routes through `Show Float.show` →
//! `float_to_str`, libc `%g` plus the runtime `.0`-fallback for
//! whole-valued Float results, per Gitea #7).
use std::path::PathBuf;
use std::process::Command;
fn workspace_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent().unwrap()
.parent().unwrap()
.to_path_buf()
}
#[test]
fn floats_example_prints_expected_stdout() {
let root = workspace_root();
let example = root.join("examples").join("floats.ail");
assert!(example.exists(), "expected fixture at {:?}", example);
// Build via `cargo run -p ail -- build` to use the in-tree CLI.
let target_bin = std::env::temp_dir().join(format!(
"ailang-floats-e2e-{}",
std::process::id()
));
let build = Command::new("cargo")
.current_dir(&root)
.args([
"run", "--quiet", "-p", "ail", "--",
"build",
example.to_str().unwrap(),
"-o", target_bin.to_str().unwrap(),
])
.output()
.expect("cargo run -p ail -- build");
assert!(
build.status.success(),
"ail build failed:\nstdout: {}\nstderr: {}",
String::from_utf8_lossy(&build.stdout),
String::from_utf8_lossy(&build.stderr),
);
let run = Command::new(&target_bin)
.output()
.expect("run floats binary");
assert!(
run.status.success(),
"floats binary exited non-zero: status {:?}\nstdout: {}\nstderr: {}",
run.status,
String::from_utf8_lossy(&run.stdout),
String::from_utf8_lossy(&run.stderr),
);
let stdout = String::from_utf8_lossy(&run.stdout);
let expected = "4.0\n42.0\n-1.5\n";
assert_eq!(
stdout, expected,
"stdout mismatch.\nGot:\n{}\nExpected:\n{}",
stdout, expected,
);
let _ = std::fs::remove_file(&target_bin);
}