This note records the design discussions from 2026-10-25 so the
questions do not have to be re-derived later. It covers TBITSIZE,
address arithmetic, set ordering, complex types, pragmas, UTF-8 string
libraries, collections, the TopSpeed B-tree Toolkit, PIM versus ISO
coroutines, external assembly, and the dynamic-set design. It is not a
commitment to implement everything.
State at the time of writing: suite 310/310, one-shot fixpoint OK
(3,845,439 bytes), per-object fixpoint OK (11 objects), corpus
521/612 compile-OK, 0 crashes, 64 LANG and 27 LIB.
TBITSIZETBITSIZE is a GNU Modula-2 SYSTEM procedure function:
PROCEDURE TBITSIZE (<type>) : CARDINAL;
It returns the minimum number of bits needed to represent a type,
not its normal storage size in bytes. It is mainly useful for packed
records/bitfields. A BOOLEAN may be one bit logically but occupy a
word in an unpacked record.
M2WIDESET).SIZE/TSIZE report storage size in bytes.TBITSIZE(BYTE) = 8 works in CONST;It is a small-to-medium language gap, not a runtime feature.
ADDRESS arithmeticISO SYSTEM defines:
PROCEDURE ADDADR (addr: ADDRESS; offset: CARDINAL): ADDRESS;
PROCEDURE SUBADR (addr: ADDRESS; offset: CARDINAL): ADDRESS;
PROCEDURE DIFADR (addr1, addr2: ADDRESS): INTEGER;
PROCEDURE MAKEADR (high: <type>; ...): ADDRESS;
V3 currently supports:
ADRADDRESS/pointer assignmentADDRESS/pointer equalityVAL/CAST for compatible scalar representationsV3 does not support:
ADDADR/SUBADR/DIFADR/MAKEADRADDRESS + offset, ADDRESS - offset, ADDRESS - ADDRESSPOINTER TO valuesADDRESS is an opaque pointer-like value in V3's type system. Raw
pointer arithmetic is not part of the PIM/ISO base language and would
weaken V3's separation between typed pointers and addresses.
Add the ISO SYSTEM functions first, not general pointer arithmetic:
ADDADR/SUBADR;DIFADR returning the byte difference (range-checked for INTEGER);MAKEADR;For external C/assembly interaction, a C helper remains the pragmatic escape hatch.
PIM/ISO set operations are:
=, inequality #, membership IN;+, difference -, intersection *, symmetric difference /.There is no standard <, <=, >, >= ordering for sets. GNU
Modula-2 adds inclusion ordering:
a <= b (* a is a subset of b *)
a < b (* a is a proper subset of b *)
a >= b (* a is a superset of b *)
a > b (* a is a proper superset of b *)
V3 currently rejects these; corpus case largeset5 remains a LANG
failure.
Fixed/inline sets:
a <= b iff (a AND NOT b) == 0 for every word
a < b iff a <= b AND a # b
Dynamic sorted-range sets:
a <= b iff every range of a is covered by a range of b
a < b iff a <= b AND a # b
Compiler work:
SymTab.RelCheck/OrdCheck: accept set ordering;Lower: branch fixed/dynamic set ordering;QbeGen: subset/superset comparisons;largeset5 would likely move from LANG to compile-OK.This is a safe gm2 compatibility extension, not standard PIM/ISO.
Low-level SYSTEM primitives:
TYPE PROCESS;
PROCEDURE NEWPROCESS (body: PROC; workspace: ADDRESS;
size: CARDINAL; VAR p: PROCESS);
PROCEDURE TRANSFER (VAR from: PROCESS; to: PROCESS);
PROCEDURE IOTRANSFER (VAR from, to: PROCESS;
interruptNo: CARDINAL);
IOTRANSFER uses an explicit interrupt number.V3 implements this with ucontext; IOTRANSFER currently degrades to a
normal transfer because there is no interrupt subsystem.
COROUTINESDEFINITION MODULE COROUTINES;
TYPE
COROUTINE;
INTERRUPTSOURCE = CARDINAL;
PROTECTION = [UnassignedPriority .. 7];
PROCEDURE NEWCOROUTINE (procBody: PROC; workspace: ADDRESS;
size: CARDINAL; VAR cr: COROUTINE;
[initProtection: PROTECTION]);
PROCEDURE TRANSFER (VAR from: COROUTINE; to: COROUTINE);
PROCEDURE IOTRANSFER (VAR from: COROUTINE; to: COROUTINE);
PROCEDURE ATTACH (source: INTERRUPTSOURCE);
PROCEDURE DETACH (source: INTERRUPTSOURCE);
PROCEDURE IsATTACHED (source: INTERRUPTSOURCE): BOOLEAN;
PROCEDURE HANDLER (source: INTERRUPTSOURCE): COROUTINE;
PROCEDURE CURRENT (): COROUTINE;
PROCEDURE LISTEN (p: PROTECTION);
PROCEDURE PROT (): PROTECTION;
PROCEDURE TurnInterrupts (to: PROTECTION): PROTECTION;
PROCEDURE ListenLoop;
END COROUTINES.
Main differences:
| Area | PIM | ISO |
|---|---|---|
| Context type | PROCESS |
COROUTINE |
| Creation | NEWPROCESS |
NEWCOROUTINE + optional protection |
| Switch | TRANSFER |
TRANSFER |
| Interrupt transfer | IOTRANSFER(..., interruptNo) |
IOTRANSFER(from,to) with attached source |
| Current context | none | CURRENT |
| Interrupt registration | none | ATTACH/DETACH/IsATTACHED/HANDLER |
| Protection/priority | none | PROTECTION, LISTEN, PROT, TurnInterrupts |
| Event loop | user-written | ListenLoop |
V3 has PIM coroutines but no ISO COROUTINES module. A pure
coroutine subset (NEWCOROUTINE, TRANSFER, CURRENT, workspace
management) could map onto the existing ucontext layer. The real
interrupt/protection parts need an OS signal/event layer.
ASMExternal assembly works today through EXTERNAL and the normal linker;
no compiler support for inline ASM is required.
DEFINITION MODULE AsmOps;
PROCEDURE Add (a, b : CARDINAL) : CARDINAL;
END AsmOps.
IMPLEMENTATION MODULE AsmOps;
PROCEDURE Add (a, b : CARDINAL) : CARDINAL;
EXTERNAL;
END AsmOps.
.text
.globl Add
.type Add,@function
Add:
movl %edi, %eax
addl %esi, %eax
ret
cc -c asm_add.s -o asm_add.o
M2 -c AsmOps.def AsmOps.mod
qbe -o AsmOps.s AsmOps.ssa
cc -c AsmOps.s -o AsmOps.o
cc Prog.o AsmOps.o asm_add.o runtime/syslib/shim.c -o Prog -lm
For M2make --separate, add the assembly object with --extra
asm_add.o or link manually.
amd64_sysv.INTEGER/CARDINAL are 32-bit; LONGINT is 64-bit.l values.VAR parameters receive addresses.ARRAY OF CHAR, are passed as descriptor
addresses, not raw C arrays.For anything beyond scalar arguments, use a small C wrapper around the assembly routine so the V3-facing signature is C-compatible.
This is the recommended route for low-level code; inline ASM is
unnecessary.
SET OF CARDINAL / INTEGER — chosen designV3 now uses a sorted linked list of disjoint inclusive ranges:
set = pointer to
{ lo : CARDINAL; hi : CARDINAL; next : pointer }
This was chosen over a full 2^32-bit bit-vector (512 MiB per set value) and over a linked list of individual bits (too much per-value overhead). The range-list representation is:
INTEGER values map with VAL(CARDINAL, x) XOR 0x80000000.Implemented operations include empty/range literals, INCL/EXCL/IN,
assignment, equality, union/intersection/difference and symmetric
difference. Bounded sets such as SET OF [0..100000] keep the inline
bit-vector form.
SET CONST is rejected (loud 230), because no static
range-list layout exists yet;malloc; assignment frees old lists but there is no GC or
program-exit cleanup;PACKEDSET remains capped at 32 bits.Future work: runtime-initialised dynamic constants, deep composite copy, ownership/lifetime rules, and optionally a hash/paged bitset if range lists become too fragmented.
V3 currently has no complex arithmetic. COMPLEX32, COMPLEX64 and
COMPLEX128 are declaration-compatible aliases of REAL (currently 8
bytes each), so they are placeholders, not real complex types. Their
names imply two 4-, 8- and 16-byte components (8, 16 and 32 bytes
respectively), so the current aliases are also the wrong size. The
comparison table correctly marks COMPLEX/LONGCOMPLEX as unsupported.
ISO Modula-2 treats COMPLEX/LONGCOMPLEX as predefined types.
The language is expected to provide:
+, -, *, /);CMPLX constructor;RE/IM accessors;Transcendental functions live in the separate ComplexMath and
LongComplexMath library modules. The ISO architecture is therefore:
compiler-known COMPLEX/LONGCOMPLEX types
+ operator lowering / builtins
+ ComplexMath / LongComplexMath libraries
A separate library can provide complex-number functions, but it cannot
by itself provide natural Modula-2 syntax such as a + b or CMPLX.
Operator overloading does not exist in Modula-2, so a library-only design
is necessarily a nonstandard utility API.
Classes are a poor fit for complex numbers:
a.Add(b) instead of a + b;CLASS is Clarion-form);RE/IM syntax.Use a value record or a compiler-known type, not a class.
V3 has ISO 10514-2 generics, so a generic implementation could share code between real and long-real variants:
GENERIC MODULE Complex(T);
TYPE Value = RECORD re, im : T END;
...
END Complex.
That is useful for implementation sharing, but generics still do not
provide operators, literals or ISO source compatibility. Since V3's
REAL and LONGREAL are both 8 bytes today, two small concrete modules
are also acceptable and simpler for separate compilation.
For practical complex math without an ISO-conformance claim:
DEFINITION MODULE Complex;
TYPE
T = RECORD
re, im : REAL
END;
PROCEDURE Add (a, b : T) : T;
PROCEDURE Sub (a, b : T) : T;
PROCEDURE Mul (a, b : T) : T;
PROCEDURE Div (a, b : T) : T;
PROCEDURE Conj (z : T) : T;
PROCEDURE Abs (z : T) : REAL;
PROCEDURE Arg (z : T) : REAL;
END Complex.
Advantages: value semantics, no allocation, simple ABI, easy to test.
Limitations: call syntax only; no operator or literal support; does not
satisfy ISO COMPLEX.
Do not call this type COMPLEX or reuse the existing COMPLEX*
aliases, because that would falsely suggest ISO support.
Implement COMPLEX/LONGCOMPLEX as compiler-known value types, with
arithmetic lowered to runtime helpers:
COMPLEX = two REAL values
a + b -> __m2_complex_add(a, b)
a * b -> __m2_complex_mul(a, b)
CMPLX -> compiler builtin
RE/IM -> compiler builtins
Then implement ComplexMath and LongComplexMath as libraries on top.
This avoids requiring QBE to understand a native complex type while still
providing ISO syntax and semantics.
Work items:
COMPLEX is 16 bytes with the current 8-byte
REAL; COMPLEX32 cannot be 8 bytes until a real 4-byte SHORTREAL
exists);CMPLX, RE, IM and constant expressions;abs, arg, conj, sqrt, exp, ln,
trigonometric functions and polarToComplex;For C interop, do not assume the C99 _Complex ABI without checking;
prefer small C wrappers where necessary.
Complex library first, with no classes and no fake COMPLEX names.COMPLEX32/64/128 REAL aliases so programs cannot silently get
wrong-size, non-complex values.V3 has no general pragma facility. It currently parses and ignores two
gm2 declaration pragmas (__BUILTIN__ and __ATTRIBUTE__
__BUILTIN__ (( name ))) after PROCEDURE, and its TopSpeed sidecar
lexes (*# ... *) as an ordinary comment. The local EXCEPTIONS.def
therefore omits <* noreturn *>.
There are two separate design questions:
The recommendation is to separate them: use the portable, scoped model as the native design, and add a narrow TopSpeed compatibility mode.
TopSpeed Modula-2 pragmas are a special form of comment:
(*# save *)
(*# check(range => on, index => off, overflow => off) *)
PROCEDURE P (...);
(*# restore *)
They are stateful compiler directives:
save / restore maintain a stack of compiler settings;They are grouped into pragma classes:
check(...) — range, index, overflow, NIL, stack checks;call(...) — calling convention, register parameters, interrupt
handlers, open-array passing;data(...) — segments, packing, volatile, enum size, stack/heap size;optimize(...) — CSE, register allocation, peephole, CPU choice;module(...) — initialization code, implementation-object rules;name(...) — external symbol naming;debug(...) — debug information and procedure tracing;option(...) — language extensions such as bitwise operations on
cardinals;save / restore — compiler-option stack;link, project, define — project-system integration.Older $ compiler directives are also accepted by TopSpeed for
compatibility.
Strengths
(*# ... *).Problems for V3
reg_param, iopl, memory models, overlays, CPU and
coprocessor selection. These do not map to V3's amd64/QBE target.(*# ... *) is a comment to other compilers, ignoring it
silently changes checks, layout or ABI without any diagnostic.TopSpeed pragmas are therefore useful as a compatibility adapter, not as V3's native pragma model.
The portable proposal (used by Modula-2 R10/BSK) uses <* ... *>:
PROCEDURE Reboot <*NORETURN*>;
TYPE Header = RECORD <*PACKED*>
...
END;
VAR Port <*VOLATILE*> : BYTE;
It defines:
<*v3.SomeOption=...*>;PRESETS / UNSET for external pragma settings instead of a
TopSpeed-style global save / restore;DEPRECATED, FFI, FFIDENT, INLINE and
PRIVATETO, plus ENCODING where Unicode is supported.The full proposal is much larger: ABI, ADDR, ALIGN, PACKED,
PADBITS, LEADBITS, VOLATILE, NORETURN, PURE, WEAK, IN,
OUT, SINGLEASSIGN, ARC, GC, OWNER, BORROWER, RETAIN,
RELEASE, MSG, TELL, TICKET and others. R10 itself omits some of
these (RANGE, RELEASE, RETAIN).
Strengths
<* ... *> aligns with ISO-style/gm2 pragma syntax.Problems
PURE/WEAK/DETM need effect checking, IN/OUT need
parameter-write analysis, NORETURN needs control-flow checks and
SINGLEASSIGN needs data-flow checks.ARC, GC, OWNER, BORROWER,
RETAIN, RELEASE; RESIDENT, CONTIGUOUS and LOWLATENCY are
mostly OS/runtime hints; ENCODING needs scanner/Unicode work.ADDR, ALIGN, PACKED, PADBITS and LEADBITS require real
layout support.| Aspect | TopSpeed V3 | Benjamin / portable |
|---|---|---|
| Status | Vendor dialect | Proposed portable specification, used by R10/BSK |
| Delimiters | (*# ... *) |
<* ... *> |
| Form | class(name => value, ...), save, restore |
PRAGMA, PRAGMA=expr, clause sequences |
| Scope | Stateful from point forward | Attached to explicit syntactic scopes |
| ABI/layout | Many 16-bit-specific directives | Portable ABI/layout annotations |
| Unknown pragmas | Often ignored or comment-like | Standard versus implementation-defined, diagnosable |
| Separate compilation | Needs external project-state discipline | Naturally attachable to symbols and object stamps |
| V3 fit | Compatibility adapter | Native pragma design |
TopSpeed is not a standard; the <* ... *> syntax is the better
long-term direction because it is already used by ISO-style/gm2
compilers and by the portable proposal.
Use <* ... *> with explicit scope attachment as the native pragma
syntax. Do not make (*# ... *) the default; handle it only in the
TopSpeed sidecar or behind an explicit compatibility flag.
Phase 1 — infrastructure and low-risk semantics
<* ... *> and adjacent blocks;DEPRECATED;NORETURN;NOINLINE;INLINE as advisory, with a warning if not honoured;MSG / TICKET diagnostics if desired;PRIVATETO.NORETURN is especially valuable immediately: it lets V3 consume
standard ISO/gm2 definition files without stripping pragmas.
Phase 2 — layout and ABI
PACKED;ALIGN / MEMALIGN;FFI / FFIDENT;ADDR only once absolute variables and address arithmetic are
designed;PADBITS / LEADBITS only after packed-record layout exists.Every ABI/layout pragma must participate in:
M2make --separate.Otherwise two objects can disagree about record layout or symbol names and still link.
Phase 3 — contracts
PURE, WEAK, DETM;IN / OUT;SINGLEASSIGN.These should not be accepted as no-ops. If V3 cannot verify them, it should reject them or emit a clear "accepted but not enforced" warning. Silently pretending to enforce a promise is worse than not supporting it.
Do not implement initially
ARC, GC, OWNER, BORROWER, RETAIN, RELEASE;RESIDENT, CONTIGUOUS, LOWLATENCY;PRESETS / pragma-file machinery;ENCODING.These need runtime support V3 does not have, or are platform hints with little value on the current target.
Keep TopSpeed pragmas out of the default V3 grammar. Handle them in the
existing TopSpeed-V3-M2.atg sidecar, or add a
--topspeed-pragmas mode to the main compiler.
Map only the meaningful subset:
check(range/index/overflow/nil/stack);data(packed/volatile);module(init_code);call(c_conv);save / restore.Reject or warn about:
near_call, far, segments, seg_name, ds_*;reg_param, reg_saved, reg_return;interrupt, iopl;cpu, copro;TopSpeed stateful pragmas can be translated by keeping an option stack during parsing and snapshotting the current state onto each declaration. That is an adapter, not the native model.
v3.* pragma should be a warning or informational message.Otherwise V3 will compile code with different checks or ABI and call it success.
trijezdci/M2-Pragmas wiki, especially Pragma Specification,
Pragma Placement and Pragma Scopes.V3 has good language-level Unicode support but only a thin library layer. The architecture is "UTF-8 at the edges, codepoints internally", not "UTF-8 strings everywhere".
UCHAR — 32-bit code point.UString — codepoint-based literals and ARRAY OF UCHAR values.U'...' / U"..." literals with strict RFC 3629 decoding at
compile time (error 234 on malformed input).UCHR, CHR8, UORD.UString whole-string assignment to a fixed ARRAY OF UCHAR
(count-clamped, zero-codepoint terminated).+ concatenation for UString literals and single-codepoint
operands, plus LEN/LENGTH/HIGH for literals.runtime/syslib/Utf8 — scalar codec:
Decode (one sequence from a byte array to UCHAR and next) and
Encode (one UCHAR to bytes). MaxUTF8Length = 4,
MaxCodePoint = 1114111.lib/iso/TextIO — UTF-8 output:
UWriteChar, UWriteString, UWriteLn.utf8_prog, utext_prog, t_ustrcat, t_ustrassign,
t_unicode, plus malformed-literal rejection tests.Utf8 lives in runtime/syslib and is always shared. TextIO is in
lib/iso, so its U* output helpers are ISO-dialect library API; other
dialects can import Utf8 manually.
| Area | Current state |
|---|---|
| UTF-8 codec | One codepoint at a time only |
| Whole-string validation | Manual Decode loop only |
| UTF-8 input | None |
| UTF-8 output | TextIO.U* only |
| UTF-8 length/index/slice | None |
| UString library | None |
| UString comparison/search | None |
| Dynamic UString | None |
| Unicode case conversion | None |
| Unicode character classes | CharClass is ASCII-only |
| Normalization / collation / grapheme clusters | None |
| Locale/bidi support | None |
lib/common/Strings is byte-oriented:
Length counts bytes, not codepoints;Slice, Pos, Insert, Replace and Delete use byte positions;Capitalize is explicitly ASCII;lib/common/CharClass is ASCII-only; there is no UCHAR version and
no Unicode category lookup.
Input is byte/line oriented. SysIO, STextIO, IOChan, Files and
StreamFile have no UTF-8 decoding or validation policy.
A U"..." literal is a descriptor { count, codepoints... }. A
declared ARRAY OF UCHAR is a fixed-length array. As a result:
LEN/HIGH on a fixed array refer to declared bounds, not the number
of codepoints actually assigned;TextIO.UWriteString writes 0 .. HIGH(u), not just through a zero
terminator;UString concatenation is bounded and transient. The shim uses a static
4096-codepoint buffer, silently truncates beyond that, and the result is
valid only until the next m2ustrcat call. Single-codepoint conversion
uses a small rotating pool (16 descriptors). This is adequate for
literal expressions, not for a general string runtime.
Utf8.Encode documents that it returns 0 for a non-scalar value, but
its implementation does not reject the surrogate range D800..DFFF.
Those values are currently encoded as three-byte UTF-8 sequences, which
is invalid UTF-8. Add the surrogate check and a regression test.
Keep byte strings and codepoint strings as separate layers. Do not make
Strings Unicode-aware in place; existing code expects bytes.
1. UTF-8 byte-string module
For interoperability with files, sockets, C APIs and existing V3
ARRAY OF CHAR strings:
ValidateLength (codepoint count)ByteLengthIndex (codepoint index to byte offset)Next / PrevSliceCompareFindToUStringFromUStringThis is the most practical "UTF-8 string" library for V3 because CHAR
arrays are already the byte-oriented representation.
2. UString codepoint module
For operations over ARRAY OF UCHAR:
First resolve the length model: either consistently use a zero terminator, or introduce an explicit descriptor with separate length and capacity. Do not mix both.
3. Input/output
Add UTF-8 input alongside output:
UReadChar / UReadString (or equivalent);UCHAR/UString values.4. Unicode properties and case
This is a later layer:
These need generated Unicode tables, not hand-written ranges.
Utf8.Encode surrogate rejection and add a test.Strings/CharClass byte/ASCII semantics and add separate
Unicode modules rather than changing existing behaviour.V3 has no collections library. What exists is only infrastructure:
lib/common/Storage — ALLOCATE, DEALLOCATE, REALLOCATElib/gm2/SysStorage — raw allocationlib/gm2/DynamicStrings — heap stringscompiler/tests/showcase23lib.def — DList(T)compiler/tests/showcase24stack.def — GStack(T)Those test modules are proof of concept, not production libraries: they
have no Done/Clear, no ownership policy, and use one NEW per node.
The MyWork/Strings/DataStructures FIFO/LIFO/dynamic-array files are
incomplete and are not part of V3.
The real toolkit is installed with the TS-V3 tree. Useful references:
$HOME/bin/Dos/M2/TS-V3/INCLUDE/BTREE.DEF
$HOME/bin/Dos/M2/TS-V3/SRC/BTREE.MOD
$HOME/bin/Dos/M2/TS-V3/DOC/BTREE.DOC
$HOME/bin/Dos/M2/TS-V3/Manuals/B-Tree toolkit.odt
It is a disk-based B-tree/ISAM toolkit, not an in-memory collections library. Its architecture is:
FHandle)IHandle) in that physical fileAdd/DeleteCompareFunction and KeyFunctionFind/Search (exact and greater-or-equal), Next/Prev/Reset
cursorsAdd, Delete, Change, Open, OpenData, OpenIndex, Close,
Flush, LastError, LastFErrorPack/APACK2The current definition also includes AccessMode values such as
FixSize, Size16, Size32, NoDealoc and Compress.
The source is 16-bit DOS/OS/2 code: inline x86 assembly, segment
arithmetic, TopSpeed pragmas, FIOx, CoreMem, and raw
ADDRESS/ARRAY OF BYTE interfaces. It is a design reference, not a
portable V3 implementation.
Find, Search, Next, Prev, Reset) rather
than exposing pages.These map naturally to V3 generics:
Compare : PROCEDURE(a, b : K) : INTEGER;
Hash : PROCEDURE(k : K) : CARDINAL;
Equal : PROCEDURE(a, b : K) : BOOLEAN;
ADDRESS/ARRAY OF BYTE public interfaces: provide typed
wrappers.SHARE/FIOx locking: use V3 file APIs and POSIX locking later.Keep in-memory collections and the persistent B-tree separate.
In-memory containers
Vector(T) dynamic array
Deque(T) FIFO / double-ended queue
Stack(T) LIFO
HashMap(K,V) hash table with Hash/Equal callbacks
BTreeMap(K,V) optional ordered map/set with Compare
Vector is the foundation. Stack and Deque should reuse it or a
ring-buffer implementation. Avoid one allocation per element unless
stable references are required.
HashMap should use open addressing with tombstones or separate
chaining, a load factor around 0.7, and explicit resize/rehash. Add a
StringMap(V) convenience wrapper with owned string keys.
BTreeMap is the in-memory ordered structure: comparator callback,
first/last/next/prev, range scan, optional duplicate keys, no pager.
Persistent B-tree
BTreeFile — disk-based index, toolkit-inspired
This is a separate library, not part of Vector/HashMap. It needs:
The TopSpeed toolkit is the checklist for this stage.
Init/Done/Clear.Storage.REALLOCATE exists, but arbitrary pointer arithmetic does
not. TopSpeed uses AddAddr/IncAddr/DecAddr assembly helpers;
V3 cannot rely on that.Vector(T) needs one of:
Suggested error model:
TYPE Status = (Ok, Empty, NotFound, InvalidIndex,
OutOfMemory, DuplicateKey);
If enum handling is awkward in a given context, use integer status codes
or BOOLEAN plus LastError.
Vector(T)Deque(T) / Stack(T)HashMap(K,V)StringMap(V)BTreeMap(K,V)BTreeFile based on the TopSpeed architectureThe TopSpeed B-tree Toolkit should not delay steps 1–3.
TBITSIZE (small, standard-facing).ADDADR/SUBADR/DIFADR (small, ISO SYSTEM).largeset5).COMPLEX* aliases.<* ... *> parser/IR plus a low-risk semantic
subset; later add the TopSpeed adapter and ABI/layout pragmas.Utf8.Encode, add byte-string
and UString modules, then UTF-8 input.Vector, Deque/Stack, HashMap, then optional
BTreeMap; persistent BTreeFile last.COROUTINES subset, then interrupt/protection layer.SET CONST and deep composite copies.External assembly needs no compiler work; document it as the supported low-level escape hatch.