v. vanilla 2.0
THE VANILLA HANDBOOK / 2.0

History & compatibility

Preserved source, restoration choices and the modern language.

This reference describes the implemented native runtime. Historical paths and internal version labels are retained where relevant. Later extension chapters supersede older limitations.

What was supplied, and what was not

original/PROVENANCE.md describes the preserved handoff text and its extraction. The historical sources supplied were original/script.cpp and original/vanilla1.cpp; the original script.h, vstdlib.h, Windows DLLs, and CGI Webio implementation were not supplied. Historical buildability, intended header layouts, and behavior of missing external components cannot be independently established from those two files.

The restoration was grounded directly in both C++ sources. It is a source-informed reimplementation, not a binary-compatible port or an exact execution oracle for undefined behavior. original/ is not compiled and was not edited.

Recognizable model retained

  • vScript: source loading, function registry, command dispatch, execution-wide state.
  • vFunc: function name, declared argument count, ordered code lines.
  • CodeLine: token sequence, now also source location and validated control-flow links.
  • variable: named local values, now owned strings/arrays rather than manual character buffers.
  • Load and Run, function ... / kill ..., and the old command vocabulary remain recognizable.

restored/script.h is a newly reconstructed interface, explicitly labeled as such. It consolidates the required declarations. No fictitious recovered vstdlib.h is claimed: helper declarations are now in script.h, implementations are in script.cpp/math.cpp, and DLL-only historical helpers/types are not recreated. The Linux main is rewritten in restored/vanilla1.cpp.

Evidence vs decisions

Line references below refer to the untouched supplied text, including its blank lines. “Observed” means visible in that source, not proven behavior of a working historical binary.

Area Observed historical source Restoration / inference or correction
CLI vanilla1.cpp:15–43 has void main, run, then Load/Run; no-argument path attempts Webio and PATH_TRANSLATED. Standard int main, checked arguments, help/version/limits. CGI explicitly unavailable; no-argument invocation is a usage error.
Functions script.cpp:472–495 reads function name argc until kill name; Run starts main at 504–514. Same syntax and entry name. Main arity zero, exact call arity, duplicate/reserved names, nesting, and missing kill are now validated.
Locals script.cpp:542–546 creates invocation-local variable vectors. Per-invocation local map; no global or block-local variable scope.
Variables script_var at 1084–1090 passes bare declaration names; setvar searches names at 161–174. var x, destination $x; declaring twice preserves existing value. This idempotent rule is chosen, rather than reproducing duplicate entries or accidental first-match behavior. Undefined variables are errors, not the historical "(0)" fallback at 157.
Set/print script_set:1118–1160 concatenates values; script_print:1306–1365 emits without automatic separators/newlines. Same concatenation; bounded owned strings. Escape handling is consistently lexical, not only a print-time special case.
Arguments SetArgVales:252–257 uses atoi(tmp)+1, while script_print:1330–1336 uses atoi(tmp); dispatch order/copies complicate substitution. The inconsistent historical indexing is not emulated. %1 always means first argument, including math and nested/direct calls; %0 is invalid. This is an explicit normalization, not a claim about missing headers.
If/loop/do Run:635–747 handles if, end if, pre-test loop/end loop, and do/while. Same readable block syntax; prelinked matching blocks and lexical loop owners replace unbounded scans and unsafe stacks. else, for, break, continue are new.
Labels Run:615–633 records point when reached and looks it up for goto. Only already visited points, per invocation; repeated points overwrite safely. Same-block restriction is a new safety rule.
Return/exit Run:608–609 returns from function or exits process. Preserve return-vs-global-exit distinction; scalar return values and call $dest are new.
Includes Load:465–468 recursively calls Load for includes; Load:453 also contains a self-call on vanilla1.v. Explicit relative-to-including-file semantics are chosen; canonical-path cycle detection, depth/size limits, checked regular files. The self-recursion bug is not preserved. Includes remain top-level.
Environment script_env:1254–1290 attempts getenv/putenv, with temporary string lifetime and substitution problems. Get inherited values safely; set a script-wide bounded overlay. This standard-library-only choice differs from modifying the process environment. Missing keys become empty.
DLLs Loader/free/Windows callbacks at 320–328, 380–440, 1168–1252, 1560–1645. No Windows loader or speculative replacement ABI. dll/dllimport explicitly fail as unavailable.
Parser ReadLine:771–858 uses fixed buffers, quote counters, and unchecked neighbor indexing. Entire lexer rewritten, with owned tokens, exact escapes, consistent literal quoting, bounded line/token counts, and errors for malformed input. Quoted reference tokens are now literal.
Extensions No supplied code establishes the newly requested APIs. String/array/file/time/random functions and captured returns are deliberately new; exact syntax is specified in LANGUAGE.md, not attributed to history.

Math is deliberately corrected

The old mathemu (script.cpp:1647–1976) splits into parallel number/operator vectors and mutates intermediate formulas. Its power pass at 1830–1836 casts to long, then repeatedly multiplies; this does not implement general real powers, handles zero/negative exponents incorrectly, and can overflow signed integers. The multiplication/division pass uses changing parallel offsets. Bracket replacement and unary-negative rewriting use fragile buffers and underscore sentinels. Division by zero returns 0 at 1882–1885; integer modulo at 1897 has no zero guard. None of those unsafe/erroneous behaviors are compatibility targets.

The new math.cpp uses bounded precedence parsing and explicit numeric operands:

  • 2+3*4 is 14; 24/3*2/4 is 4 (left-associative products).
  • 2^3^2 is 512 (right-associative power), 7^0 is 1, and 9^0.5 is 3.
  • Unary minus is lower precedence than power: -2^2 is -4; negative exponents work.
  • Binary % is defined as floating remainder, not unchecked integer truncation.
  • Zero division/remainder, nonfinite results, domain errors, malformed expressions, and excessive expression nesting are diagnosed.
  • Output is no longer the old %f six-decimal rendering with trailing-zero stripping; binary64 values are rendered with 17 significant digits for round-tripping.

The old condition classifier (script.cpp:1445–1449) recognizes only digits and dots, so a leading minus marks a number as a string and can make negative comparisons lexicographic. It also accepts malformed digit/dot patterns. The restoration uses complete finite decimal parsing, including signs and scientific notation: -10 < -2 now evaluates true numerically. If either operand is not a complete finite number, string comparison is still available.

Ownership and safety components rewritten

The supplied text contains raw new[]/scalar delete mismatches, unchecked buffer sizes, unchecked indices, mutable C-string conversions, leaked operator results, and error paths that exit successfully. Reusing those components would not be a responsible restoration.

New components: RAII strings/vectors/maps; bounded lexer and source loader; canonical include-cycle tracking; block linker; safe per-call state; precedence parser; bounded conversions; checked array/string expansion; structured exceptions; CLI validation; test harness and all extensions. Error exit status is now nonzero. See SAFETY.md for concrete limits and remaining trust boundaries.