Tag: v-TP3-DECL-WINDOW-STRIDX Commit: (the commit that adds this file) Covers: v-TP3-MULTIDECL, v-TP3-CODEWINDOW, v-TP3-STRINDEX
This is the resume point for the session that did "Next steps" items 3 and 4 in
full and the first slice of item 1. It is written so the next session can pick
up without re-deriving anything: every claim below is pinned by a fixture in
shell/tests/fixtures/expected.tsv and by the gate suite.
v-TP3-MULTIDECL).TP3 reads a whole comma-separated list of NAMES and then ONE : type for
the list, groups separated by ;:
TPSRC7:744 vardef
TPSRC7:1267-1275 rdvarlst / rdvloop (`CALL ccomma / JZ rdvloop',
then ecolon/rdvartp)
TPSRC7:891-925 prfcnt / ccomma (formal parameters)
So var a, b, c : integer ; and
procedure p (x, y : integer ; z : integer) ; are legal, and the older
this-compiler-only spelling a : integer, b : integer is NOT what TP3
accepts. Both are accepted here now, because both reach the same symbols.
DefVar creates a placeholder per name (NewSym (nm, KVar, TNone, ...))
and patches the group's one cls/size/elem into every one after
ParseType; ProcFunc does the same and advances parmOff once per name.
The names-then-type order is what makes the placeholder-then-patch shape
necessary: the type is not known when the names are read.
v-TP3-CODEWINDOW).dataBase is rtSz + 1000H: code and data share one 64 KiB .COM segment,
so program code may not grow into the data area. Ebyte checked only
MaxCode (24000), so code past dataBase wrote over the globals and
produced a plausible, wrong image with no diagnostic. Ebyte now reports
TP3's error 98 ("Memory overflow" = EMemOvf, the number DefVar already
uses when dc overflows), gated on a new imgReady flag because dataBase
is 0 until Compile measures the runtime and lays out the data area.
s[i], constant index (v-TP3-STRINDEX).A string [n] is a length byte at s[0] followed by the characters, so
s[i] is the single byte at off+i. ParseSub folds a constant index
into boff (the field an array already uses) and drops r.cls to TChar;
then LoadAtom loads one byte (A0 off / 8A 46 disp) and zeroes AH,
Assignment stores one byte when the symbol is TString but r.cls is
TChar, IoCall already classified on arg.cls so writeln (s[i]) picked
wrchar, and the reader now classifies read (s[i]) the same way and
pushes off+boff. Only a constant index is accepted; a variable index is
ENoLib, exactly as it already was for an array.
No new runtime entry: a constant index is pure compile-time offset arithmetic, so the runtime image is unchanged (616 bytes, 18 entries, 51 branches).
GetWord does not Skip, so every name after the first in a multi-name
VAR group was wrong. After MatchDelim (',') the cursor sits on the
space, and GetWord reads CurCh as the first character, so the second
name became " B" (leading space). The first name parsed (its Skip came
from the group header) which is why "one name works, two do not". t42 is
red before the added Skip, green after. Same class as the nine Skip
sites fixed in v-TP3-PARSER-FIXES.
ParseAtom clobbered every subscript. Its variable arm ended with
ParseVar (r) ; r.boff := 0 ; (* discards the subscript ParseSub just folded in *)
ParseVar already sets boff := 0 before calling ParseSub, so the reset
was pure loss: s[i] addressed off+0, the length byte, while still
reporting itself as a char. Latent for a long time because an array
cannot be declared yet, so no fixture had ever exercised the fold.
t44 reads the length byte with the reset restored and the right bytes
without it.
t43_codewindow is red-as-OK (i.e. compiles
wrongly) with the gate disabled.;-separated groups AND the older ,-between-decls spelling. Refusing the
latter would turn working programs red for no behavioural reason, and both
reach the same symbols.GetWord after a ,/; must be preceded by Skip (). This is the same
trap as the existing "Alpha test without Skip" class and is worth remembering
for any new comma-loop.r.cls = TChar while the SYMBOL's class stays
TString as the discriminator between "the element" and "the whole string".
That is the only thing that lets s := t, writeln (s) and s[i] share one
symbol without a new field on ERes.indstr/ialoop (TPSRC9), and is the natural next step.tpshell clean-build size is unchanged at 191104 bytes across items 3+4 and
the string index. This is real, not a stale build: the two tpshell
binaries differ by md5 and Compiler.o grows 75064 -> 75568 bytes, but the
linked size lands on the same boundary. make was verified to rebuild
Compiler.o before linking.t42_multidecl OK 304 12 both declaration spellings
t43_codewindow ERR 98 4978 code past dataBase, TP3 error 98
t44_strindex OK 258 16 s[i] read + write, global + local
t45_strindex_read OK 134 15 readln (s[i]) (input: 'X' then 'Y')
Item 1 (the rest of the string language), in the order that keeps each step self-contained:
a. Variable index s [i]. TP3 computes the address at run time:
`indstr` / `ialoop` / `ianopop` (TPSRC9:543 `xindchk`). A clean shape for
this compiler is `LEA BX,[base]` (new `EmLeaBx`; the address forms already
exist as `EmPushVarAddr`'s three encodings, minus the push), then
`LoadAtom (index)` into AX (which must not touch BX), `ADD BX,AX` (`01 C3'),
then `MOV AL,[BX]` / `MOV [BX],AL`. `ParseSub` must park the index `ERes`
on `r` until `LoadAtom`/`Assignment` runs, so `ERes` needs somewhere to
keep a nested index (a side array indexed by a small handle, or the
kind/imm/idx/boff of the index copied across).
b. String comparison = <> < <= > >= (cmpstr, TPSRC2:890-924, and
`vcompare`, TPSRC9:133). A new runtime entry taking two addresses,
leaving flags set, is enough because `EmSetcc` reads live flags; the open
question is the SIGNEDNESS and the prefix rule (is "ab" < "abc"?) — read
it off `cmpstr` before trusting the `SETL`/`SETG` opcodes. This adds a
runtime entry, so `runtime.golden`, `check_runtime.py`, `rt_exec.py`
(next selector is 18) and the branch count all move with it.
c. Concatenation + (xconcat, TPSRC2; addstr -> xconcat,
TPSRC9:194). Hardest: TP3 pushes strings inline on the stack, this model
addresses them, so a result needs a scratch/result string and a length
rule (TP3 caps at 255).
Item 2 (the large one) is untouched: nested procedures / recursion, var
parameters (the SEG:OFF push, RESUME-TP3.md 3.11), range/index checks
(TU_RANGE_CHECK, TU_INDEX_CHECK), typed constants (3.14), array at its
point of use (t14), case with subrange labels.
Also still open from before this session:
d. Harden the program-header parameter loop against non-advancing input
(`program p(1;)`) with a `BOOLEAN` flag — NOT `EXIT`, which ICEs gm2.
e. FreeDOS (freedos.qcow2, FD14-LiveCD) is still untried; it is the only
third opinion on the `INT 21h` shim.
cd shell && make clean && make # tpshell 191104 bytes, rc=0
cd shell && tests/run_all.sh # expect: OVERALL: ALL PASS
At this tag the suite reports:
compile matrix: 46 passed, 0 failed (of 46)
independent .COM check: 41 checked, 0 failed
execution: 41 passed, 0 failed (of 41)
exec86: 41 passed, 0 failed (of 41), cross-checked against qemu
runtime entries: 51 passed, 0 failed (of 51)
check_8086: 41 of 41 fixtures swept end to end, 4963 bytes
runtime: 616 bytes, 18 entries, 51 branches
OVERALL: ALL PASS
Each new behaviour was proven red before green: the multiply-name read
(t42) fails without the Skip, the subscript read (t44) reads the length
byte without the ParseAtom fix, and the code-window report (t43) turns
into a silent OK with the Ebyte gate disabled.