summary_step5.md 4.2 KB

Step 5 — Open arrays, copy, strings, I/O, NIL trap (uncommitted)

99/99 tests green (74 regressions + 15 run + 10 rejection), mc64 suite green.

Goal

Phase 4, first half: 1D open arrays (ARRAY OF T, HIGH, VAR + value, fixed actuals only), whole array/record copy (copy_block), fixed CHAR-array =/#/</<=/>/>= (string_comp), WriteInt / WriteString (SYSTEM 1 + print helper), NIL-trap (VM Fatal, no static check). Deferred to step 6: single-file modules, VAR tails (233), value composites (230), open-to-open (233).

What was built

  • SymTab: NewOpen/IsOpen (tOpen flag), open params take 2 slots (EnterParam: addr+len, parCnt+=2, Leave drops slots, arity still counts formals).
  • MGen: open actuals (ActValue: addr + ArrayLen const → 2 temps; ActEnd: reload len then addr so addr lands at base), PushAddr treats open value params as indirect, PrintNum/CallPrint (print helper), SysCall (0C3), Swap/LoadByte/StoreByte already from step 4.
  • M2c.atg: ArrayType allows ARRAY OF (no index → NewOpen; LL(1) via OF lookahead), record fields reject open (230), HIGH(...) builtin (fixed ArrayHi, open len-1 via LoadLocal or display FrameAddr+LoadIndir; non-array 217), whole copy (pushedDst for all whole Array/Record vars + pushedFld fields: string Str→Array with length check 210, else Assignable? copy : 210; no more 230 for same-type copy), fixed CHAR-array compare (StrComp + Or/Not/Swap/Drop, no RealEq cmp), WriteInt (IsIntFamily? CallPrint : 210), WriteString (Str or Array-of-CHAR? PushInt(1) + SysCall : 210).
  • mc64/Interpreter: string_comp gt/lt swap fix + src-NUL length fix (dst non-NUL → gt); NIL trap on 41H/51H/60H-6FH/70H-7FH (p=0 → Fatal, exit 1). mkdtest field_mask 252 unchanged (exclusive VM kept; M2c ranges inclusive via hi+1 from step 4).

Tests — 99/99 (74 + 15 + 10)

15 run: o_open 150 (VAR open+HIGH), c_copy 18 (whole array), s_streq 21 (=/#/<), o_val 14 (value open), o_high 14 (HIGH fixed 5+9), c_rec 17 (whole record), w_io hi42 (WriteString+WriteInt), o_open2 286 (two lengths, runtime HIGH), c_copy2 9 (CHAR copy), s_cmp2 10 (>/>=/<=), r_copy2 15 (nested record copy), a_subopen 66 (2D subarray actual), w_str2 ok8 (WriteString var), p_newarr 66 (NEW array + p^[i]), o_char 4 (open CHAR+HIGH). 10 rejections, each exactly one error: o_bad_open 233 (open-to-open), o_bad_high 217, c_bad_len 210, s_bad_cmp 213, v_bad_var 233 (VAR a[i]), w_bad_int 210, w_bad_str 210, r_bad_open 230, p_bad_dispose 219, s_bad_op 211.

Bugs found and fixed

  1. string_comp gt/lt swapped (src>dst gave gt, should be lt)
    • src-NUL ignored longer dst (gave equal, should be gt).
  2. String =/# used RealEq/RealNe (extra OPrCmp on gt/lt — reinterprets bits as REAL) — use Or/Not/Swap/Drop only.
  3. Whole a:=x (array:=int) gave 230 hiding 210 — check Assignable before 230 (same for records/fields).
  4. t_bad_array (a:=b same-type) became valid after whole-copy lift — repurposed to open-var 230, added c_copy run (18).
  5. Empty "" lexed as CHAR (len 2 ≤ 3 → CharOrd('"')=34) — CHAR iff len = 3; len 2/≥4 is Str (empty via EmitString NUL only).
  6. Single-slot [1..1] CHAR arrays took scalar StoreFinish (stored pointer, no length check) — pushedDst for any Array-CHAR (not just slots>1); s_bad_long ("abcdefghij" → 11 > 8) now 210.
  7. Design unreachable after WITH lift (Coco error, step 4 loop) — removed dead Design (step-4 build already fixed; kept).

Known limits (deferred with rejection tests)

Open-to-open actuals 233, VAR designators with tails (a[i]/p^/ fields as VAR) 233, value composite params/returns 230 (a_bad_val kept), open string compare 213→230? (currently 213 via Array/Record guard; open CHAR = gives 213 — acceptable, no test), HIGH on open needs same/descendant frame (display walk implemented, untested beyond one level), WriteInt prints unsigned (like ExitCode; negatives wrap), modules single-file untouched (step 6).