Browse Source

docs: session summary and resume point (declarations, code window, string subscripts)

Consolidates the three milestones of this session -- v-TP3-MULTIDECL,
v-TP3-CODEWINDOW and v-TP3-STRINDEX -- into one write-up that records what
changed, the bugs found (GetWord without Skip, ParseAtom clobbering boff, the
silently unenforced 4 KiB window), the scope decisions, and the concrete next
work (the rest of the string language and item 2), pinned against TPSRC
sources and the fixture/gate numbers.

Commit only the summary; the working tree's other changes are user files.
Eric Streit 1 day ago
parent
commit
d822158ac0
1 changed files with 181 additions and 0 deletions
  1. 181 0
      v-TP3-DECL-WINDOW-STRIDX.md

+ 181 - 0
v-TP3-DECL-WINDOW-STRIDX.md

@@ -0,0 +1,181 @@
+v-TP3-DECL-WINDOW-STRIDX — Session summary
+==========================================
+
+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.
+
+What changed
+------------
+
+1. Multi-name declarations, both spellings (`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.
+
+2. The 4 KiB code window is enforced (`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.
+
+3. String subscripts `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).
+
+Bugs found and fixed
+--------------------
+
+1. **`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`.
+
+2. **`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.
+
+3. **The 4 KiB window was documented but not enforced.**  Not a crash but a
+   silent corruption: an over-long program overwrote its own data and linked
+   to a well-formed image with no error.  This is the shape of failure the
+   project treats as a bug.  `t43_codewindow` is red-as-`OK` (i.e. compiles
+   wrongly) with the gate disabled.
+
+Scope decisions and findings
+----------------------------
+
+- The declaration change is a strict SUPERSET of TP3: it accepts TP3's
+  `;`-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.
+- A `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.
+- The string subscript uses `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`.
+- Constant index only.  A variable index needs the runtime address arithmetic
+  TP3 does in `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.
+
+Fixtures added
+--------------
+
+    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')
+
+Next work
+---------
+
+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.
+
+How to verify this session's work
+---------------------------------
+
+    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.