Skip to content

v0.92.0 — std::format built in, zero-include cout << value, UFCS, print_r over any type

Choose a tag to compare

@derekbsnider derekbsnider released this 20 Aug 02:20
· 5 commits to master since this release

madc v0.92.0

Binaries: download the packages for this feature line from v0.92.1 — the binary-shipping patch (Linux deb/rpm, Windows zip, macOS tarballs).

Everything since v0.82.0 — ten releases of language features, with the
madc dialect growing a modern, zero-ceremony surface.

Features

std::format, std::print, std::println — built into madc

The C++23 formatting library works in any madc script with zero
includes and zero header parsing
— no <format>, no <print>,
nothing to set up:

std::println("x={} y={:.3f} hex={:#x}", x, y, 255);
std::string s = std::format("{:>8}|{:08.2f}", name, price);

The compiler itself is the implementation: format strings are checked
at compile time (a bad {:q} or a missing argument is a compile
error, exactly as C++23 requires), and the output is byte-for-byte
identical to real libstdc++ std::format — verified against 1,430
generated oracle cases covering shortest-round-trip floats, hex floats,
alignment, fill, precision, and every presentation type. madc's own
var/value arguments format as whatever they currently hold.

Streaming madc values: cout << value, no includes needed

A var streams exactly as its contained type would — std::hex,
setprecision, boolalpha all apply as if you had written the plain
int / double / bool:

var test = "hello";
cout << test << endl;        // just works — no #include at all

The <iomanip> manipulators (setprecision, setw, setfill) work
too, on plain types and streamed values alike.

UFCS — uniform function call syntax

In the madc dialect, x.f(y) and f(x, y) are interchangeable
spellings, in both directions: a free function can be called like a
method, and a method like a free function. Calls chain naturally
(n.twice().inc().twice()), any receiver type participates, and the
fallback only fires where the code was already an error — strict
--std=c* / --std=c++* modes are byte-identical to before.

FILE *fp = fopen("data.txt", "r");
fp->fclose();                // fclose(fp) — the same operator
count(m, key);               // m.count(key)

php::print_r and php::var_dump over ANY type

Dump anything — structs, nested aggregates, pointers (cycle-safe, with
PHP's *RECURSION* marker), fixed arrays, std::vector, std::map,
std::set, std::list, std::string, enums, and madc values — and
the output matches php-cli to the byte. The compiler generates the
dumper for each concrete type, so a program that never dumps pays
nothing.

Range-for, grown up

for (auto &kv : m) over a std::map, for (auto x : v) over vectors
and raw arrays — the element type is deduced at parse time from the
container itself. Over a madc array, for (value v : a) gives you the
raw carrier element, kind and all, while for (string s : a) keeps the
string view.

The value carrier matured

  • Real constructors: value v(7);, value(3.5) temporaries — every
    kind, correctly typed.
  • .count() / .size() answer the owner semantics: containers count
    elements, text counts length, and a non-countable kind throws a real,
    catchable madc exception instead of silently answering 0.
  • php::array_push is one overloaded name matching PHP exactly —
    push a string, int, float, bool, or another array, and get PHP's
    new-element-count return.

Headerless C that behaves like gcc

An undeclared C library call now gets its real signature: strcmp
returns a real int (comparisons work), floor returns through the
right register, floorf(3.9f) is 3.0 — with the full C99 math family's
argument and return shapes carried in one signature table, measured
against gcc -O0. Legal C89 without includes no longer returns garbage
with exit 0.

madc --version

madc --version / -V, kept in lockstep with the MADC_VERSION macro
and madc::sys.version.

Fixes and internals

  • Front end: a cast binds a paren-less sizeof operand
    ((long long)sizeof buf — valid C89, previously rejected); for (string s("ab"); ...) parses; range-for binds its range before
    declaring the element (a silent shadowing divergence from g++, one
    shape of which compiled and read garbage).
  • Overload resolution: numeric ranking is graded by type domain, so a
    float argument selects the double overload instead of truncating;
    plain structs (the <iomanip> manipulator kind) resolve through free
    operator templates like classes do.
  • Codegen/runtime: loop-header temporaries are constructed and
    destroyed per iteration in their own scope; external-constructor
    prototypes type scalar parameters correctly (a double no longer
    marshalled through an integer register); one generic c2mir fix — a
    statement expression's value is its last statement as written, not
    a scope-exit cleanup call appended after it (MIR's own C test suite
    at exact baseline).
  • The forest pack (madc's pre-parsed standard-library store) is gated
    against silent degradation across all three platform packs; the
    gate's first run found and fixed a long double member vanishing at
    bind time.
  • One libstdc++ 13 defect found while pinning the format oracle, and
    excluded with evidence: std::format("{:#.0f}", DBL_MAX) returns a
    corrupt buffer (leading NUL, shifted digits). madc keeps the correct
    output.

Validation

One merge-wave battery on the release commit: 1104/0 integration tests
(9 skipped), 1,113 tests compiled with zero warnings, native EXE and
OBJ lanes 1063/0, packed suite 1104/0, headerless (no headers on disk
anywhere) 1077/0, plus 4,334 formatting-engine assertions against the
generated libstdc++ oracle.