our-basic CHANGELOG -- calog's modifications to upstream MY-BASIC ================================================================ Every change below is marked in-source with the comment tag "[calog fork]". Baseline: ourBasic.c.upstream (pristine upstream MY-BASIC). Run diff ourBasic.c.upstream ourBasic.c to see the full patch set. Concurrency - The _mb_allocated allocation counter was changed from `volatile` to `_Atomic`, so independent interpreters (one per calog context thread) can be created and destroyed concurrently without racing the counter. Execution already runs unlocked; only lifecycle is serialized, in the adapter. Memory - A my-basic routine (lambda) popped as a refused/errored native argument is now disposed on the error path so its scope is not leaked. (In the adapter's arg-marshal loop, src/mybasic/mybasicAdapter.c.) First-class routines -- the reason for the fork - Bare-def-as-value: in _calc_expression, a routine identifier NOT followed by '(' now pushes the routine as an operand (a first-class value) instead of raising "Open bracket expected". A script can now pass a `def` (or a lambda) to a native and store it in a variable -- required for cross-engine callbacks (pubsub / timer / export). It mirrors the array-index branch beside it; calls, recursion, and closures are unchanged. - mb_eval_routine_cold(): a new public entry (declared in ourBasic.h) that invokes a routine value from an IDLE interpreter -- one with no live call frame. It supplies the last AST node as a clean return landing and passes arguments directly. calog uses it to fire script callbacks that were registered earlier and delivered when the interpreter is otherwise idle. Constraint: a callback must be a TOP-LEVEL def/lambda (persistent scope); a routine local to a function would dangle once that function returns -- the same rule every engine's closures follow. Bare-name resolution for dynamic functions - mb_dynamic_func_handler / mb_set_dynamic_func_handler(): a new hook (a field on mb_interpreter_t plus its setter, declared in ourBasic.h). In _calc_expression, where a bare identifier called like a function -- foo(args) -- that is not a variable/routine/collection would raise "Invalid identifier usage", the interpreter now first offers the (uppercased) name to the registered handler. The handler returns MB_FUNC_OK (handled -- the result is left in the running intermediate value), MB_FUNC_IGNORE (unknown -- the normal error is then raised), or an error code; the pending argument list is left untouched on IGNORE. calog uses it to resolve a bare name to a broker export, so exportedFn(args) works without an explicit calogCall -- the same convenience the hook engines have. (The adapter resolves case-insensitively, since my-basic uppercases identifiers at parse time.) Verified: full calog `make test` (28 binaries, 0 failed) plus the my-basic engine suite (testMyBasic / testEngineMyBasic / testPolyglot / testLoad / testTask), ASan/UBSan-clean, with def and lambda callbacks firing via timer/pubsub/export and cross-engine (a Lua task calling a my-basic def). Portability -- number classification does not trust endptr alone - `_get_symbol_type` (and VAL / INPUT) decided "this symbol is a number" from strtoll/strtod alone reaching the string terminator. That test is not portable: musl's strtoll ADVANCES endptr past leading whitespace and a sign even when no conversion happens, where glibc leaves endptr at the start. On musl this made "+" and "-" -- the operators -- and "\n" -- the statement separator -- each classify as the integer 0, so every expression containing an operator failed to parse and every numeric assignment failed to run, both with "Operator expected". A new `_conv_matched_nothing(start, end)` rejects a consumed span that is nothing but whitespace and sign; a span that consumed anything else (digits, "inf", "nan") classifies exactly as before, on every libc, and an empty span is left alone so VAL("")/INPUT with an empty line keep their existing behavior. Reproduced with a pure my-basic program (no calog code): `x = 1` returned MB_FUNC_ERR on musl and MB_FUNC_OK on glibc. Verified: testEngineMyBasic 20/20 on a fully static musl build via zig (was 13 of 20 failing), unchanged 20/20 natively, and calog `make test` 943 checks / 0 failed.