Repository navigation
Releases: TickPoints/prima-language
Release list
v0.5.2-alpha
Fixed
- Release workflow no longer tears down successful builds. A single failing matrix target used
to rungh run cancel, which cancelled every other in-progress build and left the GitHub Release
with no assets. That step is gone: withfail-fast: falseeach target now builds and uploads
independently, so every target that does build still contributes its binary and checksum. The
release job is also idempotent — it creates the release, or updates it when a partial run is
re-run — instead of aborting because the release already exists.
v0.5.2
Fixed
- 32-bit builds no longer fail the
Valuesize guard. Theprima-corecompile-time assertion
pinnedValueat 24 bytes, the size it takes on 64-bit targets. On 32-bit targets (e.g.
armv7-unknown-linux-gnueabihf) the tag packs intoNumber's spare discriminants andValueis
16 bytes, so the release build aborted withE0080. The guard is now pointer-width aware
(24 bytes on 64-bit, 16 on 32-bit) while still catching any growth of the hot-path value.
v0.5.0
Added
-
Four-tier standard library (Cargo features).
prima-stdlibnow splits its modules into
opt-in tiers —core(default: built-in classes +num),system(io/time/sys),
advanced(linalg/stats/physics/plot; pullsnalgebra), andrender(formula
rendering; the heaviest dependency) — with afullaggregate. Theprimabinary now defaults to
full(all tiers, includingrender); theprima-stdliblibrary baseline remainscore, and
leaner binaries use--no-default-features --features <tier>. CI runs--all-features, with a
tier job compiling each level on its own. This isolates the heavy optional dependencies (e.g. the
LaTeX→SVG backend) from the default library build. -
mathstandard-library module (spec §18.6). Integer number theory —gcd/lcm,
factor(prime factorization),primes(sieve),mod_pow, andcrt(Chinese remainder) —
plus polynomial tools over lowest-degree-firstArray<F64>coefficients (poly_eval,
poly_add,poly_mul,poly_derivative,poly_rootsvia Durand–Kerner returning
Array<Complex>) andcontinued_fraction.primesis a layered@builtin(O1)with a.pra
fallback; allocation is bounded by explicit limits.tayloris not yet implemented (it needs a
call-site interceptor) andfactoruses deterministic trial division. -
sys::process/sys::fs/sys::term(spec §18.6). Run external commands through the
platform shell and capture output (process::run,process::exit_code), filesystem queries
(fs::exists/is_file/is_dir/size/read_dir/metadata), and terminal detection
(term::size/is_tty). All fallible operations returnResultand never panic; the
arbitrary-command and arbitrary-path trust boundaries are documented in the module docs. -
physicsformulas andVector3(spec §18.6). Thephysicsmodule is now an embedded
.pramodule rather than a Rust-hosted constants namespace: the CODATA 2022 constants are
declared as typedpub consts (SI-exactInteger/Rationalforms preserved), and the module
gains elementary formulas implemented as Rust@builtins — kinematics (velocity,
displacement,projectile_*), mechanics (force,momentum,kinetic_energy,
potential_energy,work,power), simple harmonic motion (shm_*,simple_pendulum),
thermodynamics (celsius_to_kelvin,kelvin_to_celsius,heat,ideal_gas_pressure), and
electromagnetism (coulomb_force,ohm_*,electrical_power) — plus aVector3class with
new/add/sub/scale/dot/cross/length/normalize. All formulas take and return
F64and report wrong arity or non-real arguments as errors. -
plotheatmaps and contours (spec §18.6).plot::heatmaprenders a scalar grid as a
color-mapped image (viridis-like ramp with a colorbar in the figure margin),plot::contour
draws evenly spaced iso-lines with marching squares, andplot::histrenders a histogram.
Ragged/empty grids and invalid level/bin counts report aRuntimeError;NaN/Infcells are
skipped rather than panicking. -
renderformula rendering (spec §18.6).render::to_svgrenders a symbolic expression or
LaTeX string to a self-contained SVG (core LaTeX view → RaTeXparse/layout/to_display_list
→render_to_svgwith embedded glyph outlines), andrender::to_terminaltransliterates the
same view into Unicode terminal math text. Available behind therendertier, which keeps the
RaTeX dependencies out of the default build. PNG output and theprintterminal-formula
term-renderintegration remain deferred.
Changed
-
The
primabinary now defaults to thefullstdlib tier (all modules, includingrender).
The root package's default Cargo feature isfull, socargo build/cargo install/cargo run
produce a complete binary without--features; theprima-stdliblibrary baseline remains the
leancore(built-in classes +num), and lean binaries use
--no-default-features --features <tier>. -
Numberslimmed to 16 bytes andValueto 24 bytes (spec §5/§6.1). The interpreter/VM
hot-path value types lost their oversized payloads with semantics unchanged.Number::Complex
is now a single boxed pointer (Number::Complex(Box<Complex>), built viaNumber::from_complex
and read viaNumber::as_complex_parts); previously its two inlineBoxfields formed a
16-byte payload with no spare niche, forcing the enum tag out to 24 bytes.Value::Stringand
Value::Errornow holdBox<str>instead ofString, andValue::TupleholdsBox<[Value]>
instead ofVec<Value>— 16-byte fat pointers rather than 24-byte headers, with no extra
allocation (construction goes throughinto_boxed_str/into_boxed_slice). Compile-time
const _: () = assert!(...)guards pin both sizes so a future variant cannot silently regrow
the hot path. Note: flattening theRealenum was not needed — withComplexboxed the
Realvariant's spare discriminants host theNumbertag, soNumberreaches 16 bytes without
touching the ~500Number::Real(…)sites. The default whole-function JIT path is unchanged;
benches/RESULTS.mdwas regenerated (interpreted paths within run-to-run noise).
v0.4.2
Added
- Structured diagnostic codes (spec §16.4, appendix C). Every compiler and runtime diagnostic
now carries a first-class code and renders rustc-style aserror[E0011]:/warning[W0003]:/
error[R0005]:instead of burying the code in the message text.SyntaxError/TypeErrorgained
code/helpfields andRuntimeErrorgainedcode()plus aCodedvariant, so the
E####/R####tables in appendix C are now enforced by the implementation. - Previously unimplemented appendix C validations.
E0041 duplicate_definition(same-scope
fn/class/const, duplicate parameters, duplicate fields, duplicate methods),E0051 missing_type_ann(@builtin/@c_api::externsignatures must be typed),E0052 unknown_type
(withdid you mean, coveringlet/const/parameter/return annotations),E0063 self_not_first,E0081 op_overload_bad_arity, and runtimeR0002 underflow(integer collapse
below the target minimum, distinct fromR0001overflow). - Actionable suggestions.
did you mean ...?now covers undefined names (E0040), unknown
types (E0052), unknown/missing fields (E0060/E0061), unknown config keys (E0022),
@builtinoptimization tiers (E0057), call-site arity, and method-name typos, alongside
rule-based hints (for example the explicit-collapse hint forExpr/numeric mismatches). --jsonmachine-readable diagnostics. All diagnostics can be emitted as rustc-style NDJSON
on stderr (severity/code/message/spans/notes/help), keeping the program's stdout clean
for editors and CI.--color auto|always|neverand--quiet/-qglobal switches.- Project scaffolding:
prima new <name>andprima initwrite the spec §20 layout
(prima.toml/config.toml/README.md/.gitignore/src/main.pra). - Project-root discovery:
prima run/check/fmt/docaccept an omitted file and use
the nearest ancestorprima.toml(orsrc/main.pra);prima testdefaults tosrc/inside a
project. prima completions <shell>emits a shell completion script viaclap_complete.prima test --jsonemits one NDJSON event per test ({"type":"test","file","status","message"})
plus a final{"type":"summary","passed","failed","skipped"}on stderr;--quietsuppresses
passing/skipped lines while keeping failures and the summary.
Changed
- REPL errors now print the structured
error[CODE]:header, and location-less runtime errors keep
theirR####code.
v0.4.1-alpha
Performance
This round adds whole-function native compilation (opt_level >= O2), and the cross-language
benchmark now runs 7×–1700× faster than CPython on every kernel (benches/RESULTS.md,
default path). An earlier register channel plus lock-free arrays had already brought the VM to
CPython parity (Python × 0.3–0.9); the JIT supersedes it on pure numeric bodies.
-
Whole-function JIT for the pure numeric subset (spec §19.2). A typed register IR
(prima_jit::ir) and a cranelift lowering (prima_jit::func) compile completefnbodies:
i64/f64/boollocals, dense local arrays, arithmetic/comparisons,to_f64,
if/while/for/return,local.push/lenand indexing. The interpreter lowers a body to
the IR (prima-runtime::jit_fn); only pure numeric bodies lower, everything else stays on the
VM/AST. Generated code is exact — integer arithmetic is checked and array accesses are
bounds-checked, and an overflow or out-of-range index sets an error flag so the call is re-run
on the interpreter (the JIT is pure). Loop back-edges poll host cancellation, so interruption
behaves like the interpreter. Dense arrays are arena-allocated and freed at the single
epilogue; compilations are cached per function definition. -
Pooled VM frame and operand-stack buffers (spec §19.5).
run_vmreuses frame/stack buffers
across calls (and nested calls), avoiding reallocation. -
VM subset: dict index assignment and slice assignment (spec §11.3/§11.6).
d[k] = vand
arr[lo..hi] = rhsnow run natively in the VM (dict/set literals compile too), removing AST
fallbacks for them; dict/set mutating method calls still fall back to the AST. -
Register-form local instructions (spec §19.5). A new instruction family operates directly
on local slots (RegBin/RegBinImm,RegMulAdd,RegNeg,RegToF64,RegMove,RegIndex,
RegIndexPush,RegIndexStore/Imm,RegPush/Imm), plus fused loop forms
(BranchLocalCmpSum,RegIndexBranchFalse) and a bulkRegFillfor the
for _ in lo..hi { a.push(const) }idiom. The compiler emits them when operands are local
slots or inline immediates; every non-numeric/non-array shape falls back to the existing stack
path, so observable behavior is unchanged.Opis nowCopy, and the dispatch loop matches
slot references directly on theSmall/F64fast paths instead of cloning operands. -
Lock-free copy-on-write arrays (spec §11.3).
ArrayValnow wrapsArc<Vec<Value>>and
copies on write throughArc::make_mutinstead ofArc<RwLock<Vec<Value>>>. Array reads are
plain dereferences (no lock), removing the per-elementRwLockatomics from every index read
and array loop;Send + Sync(and therefore theparfor/@parallelpaths) is preserved. -
Fast-hash environments.
Env's value/function namespaces and the VM's function-dispatch
table userustc_hash::FxHashMapinstead of SipHash, cutting the cost of every non-local name
lookup and call resolution.
Fixed
-
to_f64register fast path honors a user shadow. The registerto_f64lowering now
validates against the process-wide function-definition epoch and calls a userfn to_f64when
one is defined, matching the AST interpreter (regression-tested). -
ExprPool::numberno longer panics on complex numbers. A newtry_numberreturnsNone
for values with no symbolic representation (complex); callers that can receive one report a
clear error instead of panicking (spec §6.1). -
CSV parsing is UTF-8-correct (spec §18.1).
io::csv_parsedecoded fields byte-by-byte as
Latin-1, corrupting any non-ASCII data; it now parses by Unicode scalar values. -
VM operand truncation is rejected. Constant-pool indices, parameter/argument counts, and
array/tuple element counts beyond theu16instruction-field range now reject compilation
(AST fallback) instead of silently wrapping (spec §19.5). -
Terminal escape injection. Program output is filtered for C0 control characters and DEL at
theprint/printlnsink, so untrusted strings cannot emit ANSI escapes; newline/tab are
preserved. The stdlib arbitrary-path I/O trust boundary is now documented. -
f-string
{:spec}width counts characters, not bytes. Multi-byte content and fill
characters now pad to the correct visual width (spec §18.1). -
Rounding collapse no longer saturates.
Number::rounded_digitsand the collapse rounding
family use checked float→integer conversions and reject out-of-range digit counts instead of
saturating toi64::MAX(spec §9.6). -
REPL no longer replays the whole session. Each entry is evaluated once against a persistent
environment (Evaluator::eval_value_keep_env), removing the O(n²) session replay that made
long sessions lag (spec §20). -
Dedicated nesting-depth error code. "Expression nesting is too deep" now reports
E0012 expression_nesting_too_deep(new spec appendix C entry) instead of borrowing the
genericE0010.
v0.4.0
Performance
This release is a ground-up pass over the interpreter's hot paths. The cross-language benchmark
(benches/RESULTS.md) improves from 11.8×–4159× slower than CPython to 1.07×–7.1×: sumsq is at
parity (1.07×) and fib is at parity, while pi/poly/dot sit at 2.1×–2.8× and sieve at
7.1× (the remaining gap is the boxed-Value stack-machine cost measured by perf; closing it
fully needs register-style specialization).
-
Bytecode VM promoted to the default execution path (spec §19.5).
vm := trueis now the
default; the AST interpreter remains the authoritative fallback outside the compiled subset. The
dispatch loop was restructured around a cached top frame with the hot instruction set handled
inline (local load/store, constants, jumps, typed arithmetic, fused loop forms), so numeric loops
no longer pay a per-instruction delegation to the evaluator. -
Number/
Valueshrank from 64 to 32/24 bytes.Number::Integer/Number::Rational(and the
fixed-widthI128/U128) now box their payloads, andValueboxesDict/Set/Result
payloads, halving every clone/push/pop in the interpreter and VM (number semantics, rendering,
keys, and equality are unchanged — regression-tested). -
Fused bytecode instructions (spec §14/§12.2):
SetLocalNc(bind without stack round-trip),
AddImmLocal(x += <small literal>in place),AddToSlot(fusedx = x + expr), and
BranchLocalLt/BranchLocalLe(fusedwhile/forloop tests on two local slots). Awhile
loop iteration compiles to 3–4 instructions instead of 10–18. -
Typed arithmetic fast paths in the VM dispatch (spec §6.1/§6.5):
Small/Smalluses checked
i64 arithmetic,F64/F64plain IEEE+ - * / %(division keeps thefractionpolicy and the
exact-layer zero-divisor diagnostics), mixed exact/F64division promotes directly, and array
indexing/index-assignment with integer indices run inline with the authoritative diagnostics. -
Per-call-site callee cache (spec §19.5).
CallNamesites cache their resolved builtin or
program-function callee, validated against a process-wide function-definition epoch (bumped by
everyEnv::set_func), so a user redefinition (e.g. shadowing a core builtin) always
re-resolves while repeated calls skip the environment walk.to_f64with a numeric argument
converts directly on the cached path. -
Inlined small-integer representation for
Number(spec §6.1). The numeric tower's integer
layer gains an internalSmall(i64)variant that is semantically identical toInteger
(Small(5) == Integer(5), same rendering, conversions, and hash keys) but avoids the per-value
heap allocation ofnum_bigint::BigInt. Integer literals,as_i64, and the add/subtract/
multiply hot paths now run allocation-free with checked i64 arithmetic, falling back to the
exact BigInt path on overflow (results stay exact). -
Shared copy-on-write array representation for
Value::Array(spec §11.3).Value::Array
now holds a shared handle (prima_core::ArrayVal, anArc<RwLock<Vec<Value>>>—Arc/RwLock
keepValue: Send + Syncfor theparfor/@parallelrayon paths) instead of an owned
Vec<Value>, so cloning an array value is O(1) instead of a full element copy. Mutation goes
throughArrayVal::with_mut, which mutates the buffer in place when the handle is uniquely
owned and copy-on-writes when shared, preserving value semantics exactly:let b = a; b[0] = 9
(orb.push(x)) still leavesaunchanged, and storing an array into its own buffer (a[0] = a) stores a snapshot copy rather than creating a reference cycle. Equality, hashing keys,
rendering/printoutput, and error messages are unchanged. -
Quadratic lexing on large inputs (spec §3). The lexer's
cur/peek_chardecoded the
current character by runningstr::from_utf8over the entire remaining input — O(n²) overall
(lexing 400 000 tokens took ~70 s, a hang-DoS on untrusted source). Decoding is now a single O(1)
incremental read: 400 000 tokens lex in ~0.18 s (~400× faster at that size). -
Exact
BigIntarithmetic-sum closed form (spec §10/§19.1). Thefor i in 0..n { acc += i }
closed-form optimization computedn(n-1)/2ini64; it now computes the product inBigInt,
matching the real loop's exact accumulation at every magnitude.
Fixed
-
C-ABI panic across the FFI boundary (spec §18.4). Generated
extern "C"wrappers called
call_file_exportunguarded, so any interpreter panic unwound across the C boundary — aborting
the host process on Rust ≥ 1.81.call_file_exportnow wraps its body incatch_unwind: the
panic payload is logged to stderr, the per-thread module cache is dropped, and the call surfaces
as aRuntimeErrorso each wrapper returns its documented default value. -
Parser/evaluator/checker stack exhaustion on deeply nested source (spec §16.4). Tens of
thousands of nested parentheses or a 100 000-term flat expression (0+0+0+…, which the iterative
Pratt loop wraps into a deep chain without recursing) drove recursive consumers into
stack-overflow SIGSEGV;prima checkon untrusted source was the hard exposure. The parser now
enforces an exact per-node AST depth budget (2 000) plus a balanced recursion guard (512) and
reportsE0010-coded syntax errors (appendix C has no dedicated "too deep" code), running on a
dedicated 32 MB thread; the evaluator and static checker gained matchingDrop-safe depth
guards. Allexamples/still parse unchanged. -
JIT parameter-index truncation and slot-offset overflow (spec §19.2).
Op::Paramcarries a
u8index but the compiler cast parameter positions withidx as u8(≥ 257 parameters silently
read the wrong slot), and the parameter-buffer offset was computed asi32::from(8 * i)with
i: u8— a debug panic at ≥ 32 parameters and a silently wrong slot in release. Indices beyond
the instruction set's range are now rejected (interpreter fallback, never a wrong value), the
offset is computed ini32, andvalidate_bytecodeenforces the arity limit. 32- and
33-parameter functions are regression-tested. -
JIT engine initialization panics.
JITBuilder::newanddeclare_functionusedunwrap();
an environment where cranelift cannot initialize now permanently marks the JIT unavailable and
every compilation degrades to the interpreter fallback. -
Silent lost write in the bytecode VM's array index assignment (spec §19.5). Under the VM,
A[i] = vevaluated the store against a stack copy of the array and never wrote back. The
compiler now lowers index assignment toIndexStoreLocal(slot)/IndexStoreName(name)(in-place
mutation through the slot or the environment chain) and mutatingArraymethods on local slots
run throughMethodLocal, mutating the slot's array directly. -
Multi-parameter functions misbound in the bytecode VM (spec §11/§19.5). Function chunks
bound parameters with per-parameterSetLocalinstructions, which pop the last-pushed
argument and push it back — with two or more parameters every parameter received the wrong
argument and the operand stack leaked the arguments. A singleBindParamsinstruction now
distributes the call arguments to the parameter slots in order (single-parameter kernels had
worked by accident; two-parameter VM/AST parity is regression-tested). -
Float→integer saturation in
Number::as_bigint/as_rational(spec §9.2). Converting a
fractional-free float beyond thei64range (e.g.to_bigint(1e19)) silently saturated to
i64::MAX. Both conversions now apply the samei64round-trip guard used byas_i64and
returnNone; collapse callers report proper errors instead of a silently wrong result. -
i64 overflow in loop index stepping and the
parforiteration count (spec §16.1 R0001).
for … step s,range(start, end, step?), andparforstepped/materialized with unchecked
arithmetic (debug panic, or silent wrap → dropped iterations/infinite loop in release). All
paths now usechecked_add/i128counting and reportRuntimeError::Overflow. -
Hash-consing collision unsoundness (spec §8.1).
ExprPool::interndeduplicated by 64-bit
content hash alone, so a hash collision silently replaced one symbolic expression with another
(DefaultHasheris keyed identically in every process, making collisions constructible).
Interning now keeps per-hash candidate buckets and confirmsExprDataequality before reusing
anExprId, preserving the equal-content ⇒ equal-ExprIdinvariant under collisions. -
parfor+ JIT-fallback data race (spec §17.2). Ajit(...)callable whose compilation
failed runs through an interpreted fallback that dereferences the registering thread's
Rc<RefCell<Env>>; inside aparforrayon task that is a cross-threadRcrace (UB). Worker
threads are now marked for the duration of aparfortask and the JIT fallback refuses to run
on them with a clear error; single-threaded fallback behavior is unchanged. -
No interruption path for long-running evaluations (spec §16). The evaluator now checks a
process-wide cancellation flag at loop back-edges and statement boundaries (RuntimeError "interrupted"), and the C ABI exportsprima_cancel/prima_cancel_resetso an embedded host
can stop a runaway export instead of hanging forever. -
Process-level leaks. The JIT registry (spec §19.2) grew without bound for loops like
while … { f = jit(x^2); f(1.0); }— callables are now evicted (oldest-first) once the registry
reaches its capacity, andValue::JitFunctionlookups prune dead entries. The generated C-ABI
wrappers'CSTR_KEEPbuffer (spec §18.4) accumulated oneCStringper string-returning call;
it now uses a double buffer that is recycled across calls. -
Dict/Set key semantics diverged from membership tests (spec §11.6).
d[1]a...
v0.3.5
Added
-
Cross-language benchmark suite (benches/RESULTS.md). A new
cargo bench --bench bench_suite(harness-free) measures six deterministic, scalar-valued kernels (integer accumulation, Leibniz π, iterative Fibonacci, Sieve of Eratosthenes, sparse dot-product, Horner polynomial) across three implementations of identical semantics: Prima (in-process, warmEvaluator::call_function), CPython (bench_ref.py, kernel timed withperf_counterto exclude startup), and a native Rust closure. Each run verifies the three results agree (cross-language correctness), then writes a Markdown table tobenches/RESULTS.md. This is the AST-interpreter baseline; thevm := truebytecode VM (spec §19.5) and JIT hot path (spec §19.2) are the mechanisms tracked against it. -
Doc-test support in
prima doc(spec §20 / §4.1).prima doc --testextracts ```pra fenced code blocks from////`//!` doc comments, statically checks each with `check_src_checked`, and (`--run`) executes them and compares captured `print`/`println` output to a trailing `// expect: ` line — Rust doc-test style. The Markdown renderer now preserves fenced code blocks verbatim plus Markdown list/heading lines, and reports per-block pass/fail with correct exit codes. -
Conservative name/scope checks in
prima check(spec §16.2 / appendix C). A newcheck/namespass detects statically-decidable name errors without evaluating:E0040 undefined_name(single-segment path/symbol outside scope),E0080 return_outside_fn,E0062 self_outside_method, and theW0003 unused_bindingwarning, with the pre-importedcorebuiltins and primitive type names seeded into the root scope to avoid false positives.prima checkemits these via a newcheck_src_checkedAPI that also returns warnings, and--deny W0003promotes them. -
anyhowerror propagation at the CLI (spec §16). The rootprima-languagecrate now depends onanyhow: every CLI subcommand (run/parse/check/compile/repl/fmt/test/doc) returnsanyhow::Result<ExitCode>, andread_srcplus the I/O/build steps incabi/fmt/doccarry a contextualsourcechain (rendered ascaused by:lines). The library crates keep structuredthiserrorenums (RuntimeError/SyntaxError/CoreError) and the rustc-style diagnostics renderer still owns source-level output, so the numbered error/warning-code surface is unchanged. -
Bytecode VM execution path (spec §19.5, gated, default off). A working stack bytecode VM in
prima-runtimevm/(opinstruction set +Chunk/ProgramIR,compAST→bytecode compiler,execdispatch loop,helpersvalue-level utilities).compile_program/compile_function_bodylower a numeric/control-flow/function-call subset (literals, local/name loads, binary/unary ops,let/assignment/if/while/for/return, array/tuple literals, indexing, calls by name and methods); the executor delegates every value-producing op to theEvaluator(eval_binary/eval_compare/call_method/apply_function) so VM results equal AST results by construction.Evaluator::vm_call_functionexposes an explicit VM entry. Unsupported constructs cause a whole-function fallback to the AST interpreter (the authoritative path). On the benchmark kernels the VM is ≈1.5–1.8× faster than the AST interpreter (benches/RESULTS.md), withsieve/dot(mutating array methods) at parity via fallback.
Changed
-
Memory-strategy docs reconciled to reference counting (spec §12.3/12.4). The spec and implementation docs no longer plan a host-layer tracing GC: class instances are
Rc<RefCell<ClassInstance>>(matching the implementation), themem::collect()mutation/GC control is removed, andmem::Arcremains a planned explicit-reference-counting wrapper (Phase 12). The ADR, risk table, W_host memory row, andmemstdlib rows were updated in both the Chinese (authoritative) and English mirror docs.docs/AGENTS.mdnow records the "spec-first" priority rule (conflict → ask →SPECIFICATIONS → IMPLEMENTATION → code). -
Modularized the interpreter (spec §4.8). The single 6.5k-line
prima-runtimeeval.rsgod module is split into asrc/eval/module directory with one cohesive file per concern:env(environment/function values),helpers(stateless diagnostic & numeric helpers),entry(construction + module system),stmt(statement/control-flow),expr(expression + numeric ops),call(call dispatch/JIT/higher-order),class(classes & builtin value-type methods),apply(indexing/function application/broadcast/SIMD),pattern(match/pattern-routing), andbuiltin(builtins + I/O).eval.rsnow holds only the module root (type/Flow/re-exports). All 607 tests pass unchanged; no behavior, formatting, or public API changed.
v0.3.0
Added
- Builtin method system for every core type (spec §18.1/§11.3/§11.6/§9, Phase 10). The embedded
core::<class>.pramodules (String/Array/Dict/Set/Number/Char/Tuple/Option/Result) are now the single source of truth for each type's method set and///docs, and builtin-value method calls dispatch through their class definitions with the@builtin(ON)layering (spec §18.4): the registered Rust fast path runs atopt_level >= Nand the.prafallback body is the semantic authority.Stringgains the full Python-str-inspired set (~50 methods: predicates, case transforms, padding,count/rfind/removeprefix/removesuffix/splitlines/expandtabs/partition/…;split("")yields the single characters);Array/Dict/Setfill the Python gaps (copy,setdefault,popitem,symmetric_difference,issubset/issuperset/isdisjoint,pop/clear/update);Numberadds predicates/accessors (is_integer/abs/sign/floor/ceil/round/sqrt/numerator/denominator/real/imag/bit_length/is_*);Char/Tuple/Option/Resultget their full small method sets (is_*/code/count/get/is_some/is_ok/value_or/…).prima doc --stdliband failed-call diagnostics now cover every core type. Builtin-class method definitions live in the standard library; withoutprima_stdlib::init()the methods are unavailable (the interpreter reports a clear error).
Fixed
- Windows io tests.
crates/prima-stdlib/tests/io.rsembedded the temp-file path directly into a Prima string literal; on Windows the path's backslashes (e.g.\UinC:\Users\...) were read as invalid escape sequences and every test failed withsyntax error: invalid escape sequence. Paths are now escaped withprimed_str(doubling backslashes, quoting double-quotes, spec §18.1) before interpolation.
v0.2.4-beta
Added
- Quick-install scripts.
install.sh(POSIX bash) andinstall.ps1(Windows PowerShell) at the repo root download the latestprimarelease binary for the detected OS/architecture (mapping to the release matrix targets), verify the SHA-256 checksum, and install to~/.local/bin(override withPRIMA_INSTALL_DIR;PRIMA_TARGET/PRIMA_VERSIONoverride detection). The README now shows the one-line install commands. - CI workflow (
.github/workflows/ci.yml). Runscargo fmt --all --checkandcargo clippy --workspace --all-targets -- -D warningson ubuntu andcargo test --workspaceon ubuntu + windows, for every push tomain/devand every pull request. tests/cli.rs:run_all_examples_succeednow also runs the previously unasserted examplesautodiff,builtin_layers,capi,config_simplify,fstring,jit,mymath,opt_levels.
Changed
- Release workflow macOS matrix.
x86_64-apple-darwinwas pinned tomacos-13, a GitHub-hosted runner retired in Dec 2025 — a retiredruns-onlabel leaves the job queued forever instead of failing. Both macOS targets now run onmacos-15(arm64):x86_64-apple-darwinis cross-compiled on Apple Silicon viarustup target add+cargo build --target(the Xcode SDK is universal), andaarch64-apple-darwinbuilds natively. The build job gained atimeout-minutessafety net and verifies the x86_64 macOS artifact's architecture withfile. - Comment/consistency housekeeping. Removed stray Chinese text from code comments (
src/diagnostics.rs,examples/jit.pra), fixed a duplicated phrase inexamples/linear_algebra.pra, dropped stale "prima-jit is a stub" wording inbenches/bench_jit.rs(the crate is fully implemented), and reworded "deferred to a later phase/stage" doc comments to "later release".docs/IMPLEMENTATION-*.md§5 roadmap heading updated to reflect Phases 0–12 scope. - Workspace reformatted with
cargo fmt(rustfmt 1.9). The whole workspace is now canonical rustfmt output (cargo fmt --all --checkpasses), so formatting is enforced in CI like clippy and the tests. - README (English + Chinese) rewritten with badges, an install section, and updated document version references (v2.1 → v2.3); the broken
cargo run -- release runquick-start command was corrected.
v0.2.3-beta
Added
opt_leveloptimization tiers (spec §10.2/§13.2, Phase 8). A newOptLevelpolicy (O0–O3, defaultO2) gates the compiler optimization channels: the arithmetic-series loop closed form now requiresopt_level >= O1, automatic JIT hot-path compilation and tail-call optimization requireopt_level >= O2, and tierO3enables SIMD vectorization of denseF64array elementwise binary ops (runtime::simd, via the portablewidecrate on stable Rust).simplify_levelwas wired into the symbolic simplify pipeline (simplify_at), so lowering it reduces only how deeply a symbolic value is canonicalized — never its mathematical value. Results are semantically identical across tiers (equivalence tests; IEEE lane arithmetic is bit-identical to scalar).
Changed
@builtinlayered optimization (spec §18.4, Phase 9).@builtinmay now take a tier,@builtin(O0)–@builtin(O3): tierO0(bare) stays signature-only and must bind to a registered Rust implementation (E0055/E0056); tiersO1–O3carry a.prafallback body (the semantic authority,E0056if absent) plus an optional Rust fast path used whenopt_level >= N, with an invalid tier reported asE0057. A newFunction::Layeredvariant dispatches between the two implementations at call time.register_implwas augmented withregister_impl_leveland a declarativebuiltin!macro (builtin!("num::fibonacci", fibonacci_impl, O1)) that replaces the manual string-keyed registration calls across the stdlib crates; the.pra@builtin(ON)annotation remains the authoritative dispatch tier.