Compute sizes and offsets after the declaration pass so forward references have a kind, report E096 for instances over 65535 bytes, and record the layout in the parser harness so declaration order is fixture-checked. Generated with Codebuff 🤖 Co-Authored-By: Codebuff <noreply@codebuff.com>main
| @@ -204,6 +204,13 @@ result must end in `return` (an `if` only when both branches do), reported at | |||||
| `end` as E150. Numeric literals are typed by context and folded (E084/E086); | `end` as E150. Numeric literals are typed by context and folded (E084/E086); | ||||
| arrays are never whole values (E088). | arrays are never whole values (E088). | ||||
| E03-S05 computes each class's instance size and field offsets after the | |||||
| declaration pass, once every forward type reference has a kind. Fields keep | |||||
| declaration order after the two-byte descriptor pointer with no padding; ABI | |||||
| sizes are 1 for boolean/byte/char, 2 for integer/word/class reference, 4 for | |||||
| an interface reference, and element size times length for an array. A class | |||||
| whose instance would exceed 65535 bytes reports E096 at its declaration line. | |||||
| ### Pass 1 — declarations and layout | ### Pass 1 — declarations and layout | ||||
| - tokenize the complete source; | - tokenize the complete source; | ||||
| @@ -85,6 +85,7 @@ After 20 errors the next one is replaced by E221 and compilation stops. | |||||
| | E093 | `TYP_NOT_CLASS` | % IS NOT A CLASS | | | E093 | `TYP_NOT_CLASS` | % IS NOT A CLASS | | ||||
| | E094 | `TYP_ARRAY_SIGNATURE` | ARRAYS CANNOT BE PARAMETERS OR RESULTS | | | E094 | `TYP_ARRAY_SIGNATURE` | ARRAYS CANNOT BE PARAMETERS OR RESULTS | | ||||
| | E095 | `TYP_ARRAY_LENGTH` | ARRAY LENGTH MUST NOT BE ZERO | | | E095 | `TYP_ARRAY_LENGTH` | ARRAY LENGTH MUST NOT BE ZERO | | ||||
| | E096 | `TYP_INSTANCE_TOO_LARGE` | CLASS INSTANCE TOO LARGE | | |||||
| ## Access (E120-E129) | ## Access (E120-E129) | ||||
| @@ -51,7 +51,7 @@ COMMON, DISKBUF, private ZP, SCRATCH, and MAILBOX are visible from bank 1. | |||||
| | Owner | Bytes | Contents | | | Owner | Bytes | Contents | | ||||
| |---|---|---| | |---|---|---| | ||||
| | `names.s` | 5,164 | 64 hash-chain heads + 255 name records of 20 bytes (D120) | | | `names.s` | 5,164 | 64 hash-chain heads + 255 name records of 20 bytes (D120) | | ||||
| | `parser.s` | 4,608 | 18 columns of 256 bytes (`src/common/parser.inc`): type table by name ID (kind, declaration line, member range; E03-S01/E03-S02), 255 member rows (owner, name, flags, type, parameter range) and 255 parameter-type rows (E03-S02) | | |||||
| | `parser.s` | 6,656 | 26 columns of 256 bytes (`src/common/parser.inc`): type table by name ID (kind, declaration line, member and implements ranges, instance size; E03-S01, E03-S05), 255 member rows (owner, name, flags, type, parameter range, field offset) and 255 parameter-type rows (E03-S02, E03-S05) | | |||||
| BSS is not zeroed at load: every table must be cleared by its owner before | BSS is not zeroed at load: every table must be cleared by its owner before | ||||
| use (`names_init`, `parser.s` `tt_clear`). The member and parameter rows are | use (`names_init`, `parser.s` `tt_clear`). The member and parameter rows are | ||||
| @@ -0,0 +1,107 @@ | |||||
| --- | |||||
| story: E03-S05 | |||||
| owner: e03_layout | |||||
| status: done | |||||
| started: 2026-11-09 | |||||
| depends-on: [E03-S02, E03-S04] | |||||
| --- | |||||
| # E03-S05 Claim | |||||
| ## Intended change | |||||
| Compute every class's instance size and every field's byte offset after pass 1, | |||||
| when forward type references have a kind at last, and report E096 when an | |||||
| instance would exceed 65535 bytes. Instances begin with the two-byte class | |||||
| descriptor pointer and keep fields in declaration order with no padding. | |||||
| Add the size and offset columns to the bank-1 type and member tables, and make | |||||
| the parser harness emit a deterministic layout record so a fixture can list | |||||
| each class's size and its fields' offsets in declaration order. | |||||
| ## File area | |||||
| - `src/common/parser.inc`, `src/compiler/parser.s` | |||||
| - `src/common/diagcat.inc`, `docs/diagnostics.md` (E096) | |||||
| - `tests/parser/parser-test.s`, `scripts/build-parser-test.ps1`, | |||||
| `scripts/verify-parser-test.ps1` | |||||
| - `tests/semantic/layout.n8`, `layout.expect`, `too_big.n8`, `too_big.expect` | |||||
| - `docs/architecture.md`, `docs/memory-map.md` | |||||
| - `planning/claims/E03-S05.md` | |||||
| - generated files under `build/` | |||||
| ## Acceptance evidence | |||||
| - [x] Field offsets begin after the class-descriptor pointer and are | |||||
| deterministic: `layout.n8` declares one field of every ABI size and two | |||||
| arrays; `layout.expect` records `box size 35` with offsets 2, 3, 4, 5, 7, 9, | |||||
| 15, 17, 21, 23, and 31 in declaration order, and `empty size 2`. The | |||||
| verifier compares the whole `$FA` layout list; all 39 parser fixtures pass. | |||||
| - [x] Instance size overflow/capacity is diagnosed: `too_big.n8` | |||||
| (`word[40000]` = 80,000 bytes) is rejected with E096 at 1:1, matching | |||||
| `too_big.expect`; the size columns saturate at `$FFFF` rather than wrapping. | |||||
| - [x] Field default privacy and method default publicity match the | |||||
| specification: `layout.n8` uses bare fields and one bare `public method`; | |||||
| the independent source oracle in `verify-parser-test.ps1` derives every | |||||
| member's access (fields private, methods public) and compares it against the | |||||
| parser's 13 member rows for the fixture. The same check covers all accepted | |||||
| fixtures. | |||||
| - [x] MAP fixture lists sizes and offsets in declaration order: | |||||
| `layout.n8`/`layout.expect` is that listing. The harness's `$FA` record is | |||||
| the deterministic layout listing the future `NAME.MAP` writer will consume; | |||||
| the MAP writer itself belongs to E05-S02 (`output.s`), so no `NAME.MAP` file | |||||
| exists to compare yet. This is recorded under remaining risks. | |||||
| ## Coordination notes | |||||
| - No other claim is active. No new decision: the sizes are the approved | |||||
| architecture ABI (1 for boolean/byte/char, 2 for integer/word/class | |||||
| reference, 4 for interface reference, element size times length for arrays) | |||||
| and object layout is the approved two-byte descriptor followed by fields. | |||||
| D124-D127 from E03-S04 remain proposed. | |||||
| - The layout pass runs between pass 1 and pass 2, so it only uses recorded | |||||
| table rows and never depends on source order or pass-2 typing. | |||||
| ## Work log | |||||
| - `parser.inc`: added `C_TYPE_SIZE_LO/HI` (22/23) and | |||||
| `C_MEM_OFFSET_LO/HI` (24/25); `TABLE_COLUMNS` is now 26. `docs/memory-map.md` | |||||
| records the parser tables as 6,656 bytes. | |||||
| - `parser.s`: `parse_file` calls `layout_classes` after pass 1. Helpers | |||||
| `field_size_mem`, `field_size`, and `elem_size` compute a member's size from | |||||
| its recorded type, and `store_field_offset` writes the current offset into | |||||
| the member row. A class starts at 2; methods are skipped; overflow sets the | |||||
| size to `$FFFF`, records the failure, and reports E096 once at the class's | |||||
| declaration line and column 1. The pass loops over name IDs and member rows, | |||||
| so classes and fields are laid out in declaration order. | |||||
| - `parser-test.s`: `dump_layout` emits `$FA`, then per class its name, size | |||||
| lo/hi, field count, and each field's name and offset lo/hi. `count_fields` | |||||
| counts non-method member rows owned by the class. `FIXTURE_COUNT` is 39. | |||||
| - `verify-parser-test.ps1`: decodes `$FA` until `$FD`/`$FB` and compares the | |||||
| layout lines against `layout.expect`; the member loop now also stops at | |||||
| `$FA`, because `dump_layout` always emits its marker before `$FD`. | |||||
| - `diagcat.inc`: appended `TYP_INSTANCE_TOO_LARGE, 96`; the catalog now has 68 | |||||
| entries and `docs/diagnostics.md` was regenerated. | |||||
| - First build exposed four 6502 branch-range errors in `layout_classes` and | |||||
| three in `dump_layout`; they are now short branches to nearby `jmp` | |||||
| trampolines. The verifier edit had also joined `$p += 5` and `while` on one | |||||
| line; fixed. | |||||
| ## Completion | |||||
| - Tests run: `scripts/build-parser-test.ps1`, | |||||
| `scripts/verify-parser-test.ps1 -Port 6530`, | |||||
| `scripts/build-lexer-test.ps1`, `scripts/verify-lexer-test.ps1`, | |||||
| `scripts/build-diag-test.ps1 -UpdateDocs`, | |||||
| `scripts/verify-diag-test.ps1`, `scripts/demo-write-check.ps1`. | |||||
| - Results: all 39 parser fixtures pass (lowest expression SP `$A7`); | |||||
| `layout.n8` matches 13 layout lines and `too_big.n8` reports E096 at 1:1. | |||||
| The lexer matches 10 fixtures / 6,733 dump bytes; the 68-entry diagnostic | |||||
| catalog and its screen/ERR expectations match with docs in sync; the demo | |||||
| saves `good.n8` and `bad.n8` byte-for-byte, `good.n8` reports no errors, and | |||||
| `bad.n8` reports E151, E152, E120, and E061. `vim/work.d81` was not touched. | |||||
| - Files changed: as listed under File area. | |||||
| - Remaining risks: the `NAME.MAP` text writer is E05-S02, so the declaration | |||||
| order and layout listing are verified through the harness record rather than | |||||
| a real `.map` file. E096 reports the instance's saturated `$FFFF` size in the | |||||
| table; consumers must treat that as "too large". Any future padding or | |||||
| alignment rule would change `layout.expect` and the offsets. | |||||
| @@ -51,6 +51,8 @@ $script:ParserFixtures = [ordered]@{ | |||||
| "ops.n8" = @("$t\ops.n8", "exact", "$t\ops.expect") | "ops.n8" = @("$t\ops.n8", "exact", "$t\ops.expect") | ||||
| "uses.n8" = @("$t\uses.n8", "exact", "$t\uses.expect") | "uses.n8" = @("$t\uses.n8", "exact", "$t\uses.expect") | ||||
| "calls.n8" = @("$t\calls.n8", "exact", "$t\calls.expect") | "calls.n8" = @("$t\calls.n8", "exact", "$t\calls.expect") | ||||
| "layout.n8" = @("$t\layout.n8", "layout", "$t\layout.expect") | |||||
| "toobig.n8" = @("$t\too_big.n8", "rejected", "$t\too_big.expect") | |||||
| } | } | ||||
| $link = Invoke-Nova128Link -Name "parser-test" -BuildDir $out ` | $link = Invoke-Nova128Link -Name "parser-test" -BuildDir $out ` | ||||
| @@ -53,9 +53,9 @@ $records = @(); $script:p = 0 | |||||
| while ($dump[$p] -ne 0xFE) { | while ($dump[$p] -ne 0xFE) { | ||||
| if ($dump[$p] -ne 0xFF) { throw "dump out of sync at $p" } | if ($dump[$p] -ne 0xFF) { throw "dump out of sync at $p" } | ||||
| $r = [pscustomobject]@{ Index = $dump[$p + 1]; Load = $dump[$p + 2]; Count = $dump[$p + 3] | $r = [pscustomobject]@{ Index = $dump[$p + 1]; Load = $dump[$p + 2]; Count = $dump[$p + 3] | ||||
| MinSp = $dump[$p + 4]; Types = @(); Members = @(); Trace = $null } | |||||
| MinSp = $dump[$p + 4]; Types = @(); Members = @(); Layout = @(); Trace = $null } | |||||
| $p += 5 | $p += 5 | ||||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFC) { | |||||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFC -and $dump[$p] -ne 0xFB -and $dump[$p] -ne 0xFA) { | |||||
| $len = $dump[$p + 3] | $len = $dump[$p + 3] | ||||
| $spell = ConvertFrom-Spelling $dump[($p + 4)..($p + 3 + $len)] | $spell = ConvertFrom-Spelling $dump[($p + 4)..($p + 3 + $len)] | ||||
| $r.Types += ("{0} {1} {2}" -f @("", "interface", "class", "program")[$dump[$p]], $spell, ($dump[$p + 1] + 256 * $dump[$p + 2])) | $r.Types += ("{0} {1} {2}" -f @("", "interface", "class", "program")[$dump[$p]], $spell, ($dump[$p + 1] + 256 * $dump[$p + 2])) | ||||
| @@ -63,7 +63,7 @@ while ($dump[$p] -ne 0xFE) { | |||||
| } | } | ||||
| if ($dump[$p] -eq 0xFC) { | if ($dump[$p] -eq 0xFC) { | ||||
| $p++ | $p++ | ||||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFB) { | |||||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFB -and $dump[$p] -ne 0xFA) { | |||||
| $owner = Read-DumpName | $owner = Read-DumpName | ||||
| $name = Read-DumpName | $name = Read-DumpName | ||||
| $flags = $dump[$p]; $p++ | $flags = $dump[$p]; $p++ | ||||
| @@ -74,6 +74,20 @@ while ($dump[$p] -ne 0xFE) { | |||||
| $(if ($flags -band 1) { "method" } else { "field" }), | $(if ($flags -band 1) { "method" } else { "field" }), | ||||
| $(if ($flags -band 2) { "private" } else { "public" }), $type, ($params -join ",")) | $(if ($flags -band 2) { "private" } else { "public" }), $type, ($params -join ",")) | ||||
| } | } | ||||
| if ($dump[$p] -eq 0xFA) { | |||||
| $p++ | |||||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFB) { | |||||
| $owner = Read-DumpName | |||||
| $size = $dump[$p] + 256 * $dump[$p + 1]; $p += 2 | |||||
| $count = $dump[$p]; $p++ | |||||
| $r.Layout += ("{0} size {1}" -f $owner, $size) | |||||
| for ($k = 0; $k -lt $count; $k++) { | |||||
| $fname = Read-DumpName | |||||
| $off = $dump[$p] + 256 * $dump[$p + 1]; $p += 2 | |||||
| $r.Layout += ("{0}.{1} offset {2}" -f $owner, $fname, $off) | |||||
| } | |||||
| } | |||||
| } | |||||
| if ($dump[$p] -eq 0xFB) { | if ($dump[$p] -eq 0xFB) { | ||||
| $n = $dump[$p + 1] + 256 * $dump[$p + 2] | $n = $dump[$p + 1] + 256 * $dump[$p + 2] | ||||
| $r.Trace = if ($n) { [byte[]]$dump[($p + 3)..($p + 2 + $n)] } else { [byte[]]@() } | $r.Trace = if ($n) { [byte[]]$dump[($p + 3)..($p + 2 + $n)] } else { [byte[]]@() } | ||||
| @@ -181,7 +195,7 @@ foreach ($name in $ParserFixtures.Keys) { | |||||
| $problems = @() | $problems = @() | ||||
| if ($r.Load -ne 0) { $problems += "src_load returned $($r.Load)" } | if ($r.Load -ne 0) { $problems += "src_load returned $($r.Load)" } | ||||
| switch ($group) { | switch ($group) { | ||||
| "accepted" { | |||||
| { $_ -eq "accepted" -or $_ -eq "layout" } { | |||||
| if ($r.Count -ne 0) { $problems += "$($r.Count) diagnostic(s): $($errLines -join ' | ')" } | if ($r.Count -ne 0) { $problems += "$($r.Count) diagnostic(s): $($errLines -join ' | ')" } | ||||
| if ($null -ne $errLines) { $problems += "an ERR file exists" } | if ($null -ne $errLines) { $problems += "an ERR file exists" } | ||||
| $want = @() | $want = @() | ||||
| @@ -231,7 +245,7 @@ foreach ($name in $ParserFixtures.Keys) { | |||||
| $detail = "{0} diagnostics" -f $got.Count | $detail = "{0} diagnostics" -f $got.Count | ||||
| } | } | ||||
| } | } | ||||
| if ($group -eq "accepted" -or $memberChecked -contains $name) { | |||||
| if ($group -in @("accepted", "layout") -or $memberChecked -contains $name) { | |||||
| $wantMembers = Get-SourceMembers $src | $wantMembers = Get-SourceMembers $src | ||||
| if (($r.Members -join ';') -ne ($wantMembers -join ';')) { | if (($r.Members -join ';') -ne ($wantMembers -join ';')) { | ||||
| $problems += "members [$($r.Members -join '; ')] expected [$($wantMembers -join '; ')]" | $problems += "members [$($r.Members -join '; ')] expected [$($wantMembers -join '; ')]" | ||||
| @@ -249,6 +263,13 @@ foreach ($name in $ParserFixtures.Keys) { | |||||
| $detail += ", {0} traced statements" -f $got.Count | $detail += ", {0} traced statements" -f $got.Count | ||||
| } | } | ||||
| } | } | ||||
| if ($group -eq "layout") { | |||||
| $want = @(Get-Content -LiteralPath $expect | Where-Object { $_ -and -not $_.StartsWith("//") }) | |||||
| if (($r.Layout -join ';') -ne ($want -join ';')) { | |||||
| $problems += "layout [$($r.Layout -join '; ')] expected [$($want -join '; ')]" | |||||
| } | |||||
| $detail += ", {0} layout lines" -f $r.Layout.Count | |||||
| } | |||||
| if ($r.MinSp -ne 0xFF -and $r.MinSp -lt $minSp) { $minSp = $r.MinSp } | if ($r.MinSp -ne 0xFF -and $r.MinSp -lt $minSp) { $minSp = $r.MinSp } | ||||
| $status = if ($problems) { "FAIL" } else { "ok" } | $status = if ($problems) { "FAIL" } else { "ok" } | ||||
| Write-Host (" {0,-13} {1,-9} {2} {3}" -f $name, $group, $status, $detail) | Write-Host (" {0,-13} {1,-9} {2} {3}" -f $name, $group, $status, $detail) | ||||
| @@ -74,6 +74,7 @@ | |||||
| DIAGNOSTIC TYP_NOT_CLASS, 93, "% IS NOT A CLASS" | DIAGNOSTIC TYP_NOT_CLASS, 93, "% IS NOT A CLASS" | ||||
| DIAGNOSTIC TYP_ARRAY_SIGNATURE, 94, "ARRAYS CANNOT BE PARAMETERS OR RESULTS" | DIAGNOSTIC TYP_ARRAY_SIGNATURE, 94, "ARRAYS CANNOT BE PARAMETERS OR RESULTS" | ||||
| DIAGNOSTIC TYP_ARRAY_LENGTH, 95, "ARRAY LENGTH MUST NOT BE ZERO" | DIAGNOSTIC TYP_ARRAY_LENGTH, 95, "ARRAY LENGTH MUST NOT BE ZERO" | ||||
| DIAGNOSTIC TYP_INSTANCE_TOO_LARGE, 96, "CLASS INSTANCE TOO LARGE" | |||||
| ; ----------------------------------------------------------------- access | ; ----------------------------------------------------------------- access | ||||
| DIAGNOSTIC ACC_PRIVATE_MEMBER, 120, "% IS PRIVATE" | DIAGNOSTIC ACC_PRIVATE_MEMBER, 120, "% IS PRIVATE" | ||||
| @@ -75,7 +75,11 @@ C_PAR_TNAME = 18 | |||||
| C_PAR_TLEN_LO = 19 | C_PAR_TLEN_LO = 19 | ||||
| C_PAR_TLEN_HI = 20 | C_PAR_TLEN_HI = 20 | ||||
| C_IMPL_IFACE = 21 ; implemented interface name ID | C_IMPL_IFACE = 21 ; implemented interface name ID | ||||
| TABLE_COLUMNS = 22 | |||||
| C_TYPE_SIZE_LO = 22 ; class instance size (descriptor + fields) | |||||
| C_TYPE_SIZE_HI = 23 | |||||
| C_MEM_OFFSET_LO = 24 ; field byte offset | |||||
| C_MEM_OFFSET_HI = 25 | |||||
| TABLE_COLUMNS = 26 | |||||
| C_TYPE_LAST = C_TYPE_INIT ; type columns 0..this are cleared per run | C_TYPE_LAST = C_TYPE_INIT ; type columns 0..this are cleared per run | ||||
| .endif | .endif | ||||
| @@ -1,5 +1,5 @@ | |||||
| ; parser.s - declarations, members, scopes, statements, expressions, and | ; parser.s - declarations, members, scopes, statements, expressions, and | ||||
| ; type checking (E03-S01..E03-S04). | |||||
| ; type checking (E03-S01..E03-S05). | |||||
| ; | ; | ||||
| ; parse_file runs two passes over the loaded source (call after src_load, | ; parse_file runs two passes over the loaded source (call after src_load, | ||||
| ; diag_init, and names_init): | ; diag_init, and names_init): | ||||
| @@ -8,8 +8,8 @@ | |||||
| ; the bank-1 type table and each field and method signature in the | ; the bank-1 type table and each field and method signature in the | ||||
| ; member and parameter tables; parses every statement; reports | ; member and parameter tables; parses every statement; reports | ||||
| ; syntax, ordering, duplicate, init/destroy shape, and | ; syntax, ordering, duplicate, init/destroy shape, and | ||||
| ; local-placement errors; records each class's implements list | |||||
| ; and init row. | |||||
| ; local-placement errors; records each class's implements list, | |||||
| ; init row, instance size, and field offsets (E03-S05). | |||||
| ; pass 2 replays the source, parsing everything again, and reports type | ; pass 2 replays the source, parsing everything again, and reports type | ||||
| ; references that do not name a declared interface or class | ; references that do not name a declared interface or class | ||||
| ; (forward references are therefore legal), name, member, and | ; (forward references are therefore legal), name, member, and | ||||
| @@ -45,6 +45,7 @@ | |||||
| .import tok_kind, tok_line, tok_col, tok_len, tok_value, tok_text, tok_text_len, tok_err | .import tok_kind, tok_line, tok_col, tok_len, tok_value, tok_text, tok_text_len, tok_err | ||||
| .import src_line | .import src_line | ||||
| .import name_intern, name_find, name_declare | .import name_intern, name_find, name_declare | ||||
| .import names_count | |||||
| .import diag_report, diag_line, diag_col, diag_arg1, diag_arg1_len | .import diag_report, diag_line, diag_col, diag_arg1, diag_arg1_len | ||||
| .import mmu_peek1, mmu_poke1 | .import mmu_peek1, mmu_poke1 | ||||
| .importzp mmu_ptr | .importzp mmu_ptr | ||||
| @@ -97,6 +98,7 @@ parse_file: | |||||
| lda #$01 | lda #$01 | ||||
| sta pass | sta pass | ||||
| jsr parse_pass | jsr parse_pass | ||||
| jsr layout_classes | |||||
| lda #$02 | lda #$02 | ||||
| sta pass | sta pass | ||||
| jsr parse_pass | jsr parse_pass | ||||
| @@ -544,6 +546,219 @@ no_array_sig: | |||||
| jmp err1_col | jmp err1_col | ||||
| @ok: rts | @ok: rts | ||||
| ; ------------------------------------------------------------- layout | |||||
| ; Pass 1, after all declarations: instance sizes and field offsets. A class | |||||
| ; starts with its two-byte descriptor pointer; fields follow in declaration | |||||
| ; order with no padding. ABI sizes: boolean/byte/char 1, integer/word/class | |||||
| ; reference 2, interface reference 4, array = element size * length. E096 is | |||||
| ; reported at the class line when the total exceeds 65535. | |||||
| layout_classes: | |||||
| lda #$01 | |||||
| sta lk_id | |||||
| @class: lda lk_id | |||||
| cmp names_count | |||||
| bcc :+ | |||||
| jmp @done | |||||
| : ldy lk_id | |||||
| lda #C_TYPE_KIND | |||||
| jsr tb_get | |||||
| cmp #TYPE_CLASS | |||||
| beq :+ | |||||
| jmp @next | |||||
| : lda #$02 | |||||
| sta cls_size | |||||
| lda #$00 | |||||
| sta cls_size+1 | |||||
| sta cls_bad | |||||
| sta lk_row | |||||
| @row: lda lk_row | |||||
| cmp mem_count | |||||
| bcs @class_done | |||||
| ldy lk_row | |||||
| lda #C_MEM_OWNER | |||||
| jsr tb_get | |||||
| cmp lk_id | |||||
| bne @rnext | |||||
| ldy lk_row | |||||
| lda #C_MEM_FLAGS | |||||
| jsr tb_get | |||||
| and #MF_METHOD | |||||
| bne @rnext | |||||
| lda cls_bad | |||||
| bne @saturated | |||||
| jsr field_size_mem | |||||
| bcs @overflow | |||||
| jsr store_field_offset | |||||
| clc | |||||
| lda cls_size | |||||
| adc fsize | |||||
| sta cls_size | |||||
| lda cls_size+1 | |||||
| adc fsize+1 | |||||
| sta cls_size+1 | |||||
| bcc @rnext | |||||
| @overflow: | |||||
| jsr store_field_offset | |||||
| lda #$01 | |||||
| sta cls_bad | |||||
| lda #$FF | |||||
| sta cls_size | |||||
| sta cls_size+1 | |||||
| jmp @rnext | |||||
| @saturated: | |||||
| jsr store_field_offset | |||||
| @rnext: inc lk_row | |||||
| jmp @row | |||||
| @class_done: | |||||
| ldy lk_id | |||||
| ldx cls_size | |||||
| lda #C_TYPE_SIZE_LO | |||||
| jsr tb_set | |||||
| ldx cls_size+1 | |||||
| lda #C_TYPE_SIZE_HI | |||||
| jsr tb_set | |||||
| lda cls_bad | |||||
| bne :+ | |||||
| jmp @next | |||||
| : ldy lk_id | |||||
| lda #C_TYPE_LINE_LO | |||||
| jsr tb_get | |||||
| sta diag_line | |||||
| ldy lk_id | |||||
| lda #C_TYPE_LINE_HI | |||||
| jsr tb_get | |||||
| sta diag_line+1 | |||||
| lda #$01 | |||||
| sta diag_col | |||||
| lda #E_TYP_INSTANCE_TOO_LARGE | |||||
| jsr diag_report | |||||
| bcs :+ | |||||
| jmp @next | |||||
| : jmp stop_parse | |||||
| @next: inc lk_id | |||||
| beq @done | |||||
| jmp @class | |||||
| @done: rts | |||||
| ; fsize = the type of member row lk_row; C=1 when it exceeds 65535. | |||||
| field_size_mem: | |||||
| ldy lk_row | |||||
| lda #C_MEM_TBASE | |||||
| jsr tb_get | |||||
| sta ty_base | |||||
| ldy lk_row | |||||
| lda #C_MEM_TNAME | |||||
| jsr tb_get | |||||
| sta ty_name | |||||
| ldy lk_row | |||||
| lda #C_MEM_TLEN_LO | |||||
| jsr tb_get | |||||
| sta ty_len | |||||
| ldy lk_row | |||||
| lda #C_MEM_TLEN_HI | |||||
| jsr tb_get | |||||
| sta ty_len+1 | |||||
| jsr field_size | |||||
| rts | |||||
| ; member row lk_row's offset = cls_size. | |||||
| store_field_offset: | |||||
| ldy lk_row | |||||
| lda #C_MEM_OFFSET_LO | |||||
| ldx cls_size | |||||
| jsr tb_set | |||||
| lda #C_MEM_OFFSET_HI | |||||
| ldx cls_size+1 | |||||
| jsr tb_set | |||||
| rts | |||||
| ; fsize = size of the type in ty_*; C=1 when the array is too large. | |||||
| field_size: | |||||
| lda ty_len | |||||
| ora ty_len+1 | |||||
| beq @scalar | |||||
| jsr elem_size | |||||
| beq @zero | |||||
| cmp #$01 | |||||
| beq @one | |||||
| cmp #$02 | |||||
| beq @two | |||||
| lda ty_len | |||||
| sta fsize | |||||
| lda ty_len+1 | |||||
| sta fsize+1 | |||||
| asl fsize | |||||
| rol fsize+1 | |||||
| bcs @over | |||||
| asl fsize | |||||
| rol fsize+1 | |||||
| bcs @over | |||||
| clc | |||||
| rts | |||||
| @one: lda ty_len | |||||
| sta fsize | |||||
| lda ty_len+1 | |||||
| sta fsize+1 | |||||
| clc | |||||
| rts | |||||
| @two: lda ty_len | |||||
| sta fsize | |||||
| lda ty_len+1 | |||||
| sta fsize+1 | |||||
| asl fsize | |||||
| rol fsize+1 | |||||
| bcs @over | |||||
| clc | |||||
| rts | |||||
| @zero: lda #$00 | |||||
| sta fsize | |||||
| sta fsize+1 | |||||
| clc | |||||
| rts | |||||
| @scalar: | |||||
| jsr elem_size | |||||
| sta fsize | |||||
| lda #$00 | |||||
| sta fsize+1 | |||||
| clc | |||||
| rts | |||||
| @over: lda #$FF | |||||
| sta fsize | |||||
| sta fsize+1 | |||||
| sec | |||||
| rts | |||||
| ; A = bytes in one element of ty_*: 1, 2, 4, or 0 for an unknown type. | |||||
| elem_size: | |||||
| lda ty_base | |||||
| cmp #TY_BOOLEAN | |||||
| beq @one | |||||
| cmp #TY_BYTE | |||||
| beq @one | |||||
| cmp #TY_CHAR | |||||
| beq @one | |||||
| cmp #TY_INTEGER | |||||
| beq @two | |||||
| cmp #TY_WORD | |||||
| beq @two | |||||
| cmp #TY_REF | |||||
| bne @none | |||||
| ldy ty_name | |||||
| lda #C_TYPE_KIND | |||||
| jsr tb_get | |||||
| cmp #TYPE_INTERFACE | |||||
| beq @four | |||||
| lda #$02 | |||||
| rts | |||||
| @one: lda #$01 | |||||
| rts | |||||
| @two: lda #$02 | |||||
| rts | |||||
| @four: lda #$04 | |||||
| rts | |||||
| @none: lda #$00 | |||||
| rts | |||||
| ; Pass 1: a class's init records its member row (+1) for `new` checks. | ; Pass 1: a class's init records its member row (+1) for `new` checks. | ||||
| note_init: | note_init: | ||||
| lda pass | lda pass | ||||
| @@ -2508,3 +2723,9 @@ ret_kind: .res 1 ; RET_* | |||||
| ret_row: .res 1 ; member row of the method (RET_VALUE) | ret_row: .res 1 ; member row of the method (RET_VALUE) | ||||
| body_nm: .res 1 ; method or program name ID (E151) | body_nm: .res 1 ; method or program name ID (E151) | ||||
| end_col: .res 1 ; column of `end` (E150) | end_col: .res 1 ; column of `end` (E150) | ||||
| ; class layout (E03-S05) | |||||
| cls_size: .res 2 ; instance size being accumulated | |||||
| cls_bad: .res 1 ; E096 reported for this class | |||||
| fsize: .res 2 ; one field's size | |||||
| lk_id: .res 1 ; layout: class name ID | |||||
| lk_row: .res 1 ; layout: member row | |||||
| @@ -141,6 +141,7 @@ test_entry: | |||||
| lda #$FC | lda #$FC | ||||
| jsr emit | jsr emit | ||||
| jsr dump_members | jsr dump_members | ||||
| jsr dump_layout | |||||
| lda fx_index | lda fx_index | ||||
| cmp #TRACE_INDEX | cmp #TRACE_INDEX | ||||
| bne @advance | bne @advance | ||||
| @@ -288,6 +289,97 @@ dump_members: | |||||
| jmp @next | jmp @next | ||||
| @done: rts | @done: rts | ||||
| ; Emit $FA, then each class: name, instance size lo/hi, field count, and | |||||
| ; every field's name and offset lo/hi in declaration order. | |||||
| dump_layout: | |||||
| lda #$FA | |||||
| jsr emit | |||||
| lda #$01 | |||||
| sta id | |||||
| @next: lda id | |||||
| cmp names_count | |||||
| bcc :+ | |||||
| jmp @done | |||||
| : ldy id | |||||
| lda #C_TYPE_KIND | |||||
| jsr tb_get | |||||
| cmp #TYPE_CLASS | |||||
| bne @not_class | |||||
| lda id | |||||
| jsr emit_name | |||||
| ldy id | |||||
| lda #C_TYPE_SIZE_LO | |||||
| jsr tb_get | |||||
| jsr emit | |||||
| ldy id | |||||
| lda #C_TYPE_SIZE_HI | |||||
| jsr tb_get | |||||
| jsr emit | |||||
| lda id | |||||
| jsr count_fields | |||||
| jsr emit | |||||
| lda #$00 | |||||
| sta lk_row | |||||
| @field_loop: | |||||
| lda lk_row | |||||
| cmp mem_count | |||||
| bcs @fields_done | |||||
| ldy lk_row | |||||
| lda #C_MEM_OWNER | |||||
| jsr tb_get | |||||
| cmp id | |||||
| bne @fnext | |||||
| ldy lk_row | |||||
| lda #C_MEM_FLAGS | |||||
| jsr tb_get | |||||
| and #MF_METHOD | |||||
| bne @fnext | |||||
| ldy lk_row | |||||
| lda #C_MEM_NAME | |||||
| jsr tb_get | |||||
| jsr emit_name | |||||
| ldy lk_row | |||||
| lda #C_MEM_OFFSET_LO | |||||
| jsr tb_get | |||||
| jsr emit | |||||
| ldy lk_row | |||||
| lda #C_MEM_OFFSET_HI | |||||
| jsr tb_get | |||||
| jsr emit | |||||
| @fnext: inc lk_row | |||||
| jmp @field_loop | |||||
| @fields_done: | |||||
| @not_class: | |||||
| inc id | |||||
| beq @done | |||||
| jmp @next | |||||
| @done: rts | |||||
| ; A = class name ID; A = number of field rows it owns. | |||||
| count_fields: | |||||
| sta lk_owner | |||||
| lda #$00 | |||||
| sta lk_n | |||||
| sta lk_row | |||||
| @loop: lda lk_row | |||||
| cmp mem_count | |||||
| bcs @done | |||||
| ldy lk_row | |||||
| lda #C_MEM_OWNER | |||||
| jsr tb_get | |||||
| cmp lk_owner | |||||
| bne @next | |||||
| ldy lk_row | |||||
| lda #C_MEM_FLAGS | |||||
| jsr tb_get | |||||
| and #MF_METHOD | |||||
| bne @next | |||||
| inc lk_n | |||||
| @next: inc lk_row | |||||
| jmp @loop | |||||
| @done: lda lk_n | |||||
| rts | |||||
| ; Emit $FB, the trace length, and the trace bytes. | ; Emit $FB, the trace length, and the trace bytes. | ||||
| dump_trace: | dump_trace: | ||||
| lda #$FB | lda #$FB | ||||
| @@ -425,7 +517,9 @@ fixtures: | |||||
| FIXTURE "OPS.N8" | FIXTURE "OPS.N8" | ||||
| FIXTURE "USES.N8" | FIXTURE "USES.N8" | ||||
| FIXTURE "CALLS.N8" | FIXTURE "CALLS.N8" | ||||
| FIXTURE_COUNT = 37 | |||||
| FIXTURE "LAYOUT.N8" | |||||
| FIXTURE "TOOBIG.N8" | |||||
| FIXTURE_COUNT = 39 | |||||
| TRACE_INDEX = 28 ; PREC.N8 | TRACE_INDEX = 28 ; PREC.N8 | ||||
| messages: | messages: | ||||
| @@ -448,6 +542,9 @@ prow: .res 1 | |||||
| pleft: .res 1 | pleft: .res 1 | ||||
| col: .res 1 | col: .res 1 | ||||
| trow: .res 1 | trow: .res 1 | ||||
| lk_owner: .res 1 | |||||
| lk_row: .res 1 | |||||
| lk_n: .res 1 | |||||
| trace_len: .res 2 | trace_len: .res 2 | ||||
| trace_buf: .res 1024 | trace_buf: .res 1024 | ||||
| dump: .res DUMP_SIZE | dump: .res DUMP_SIZE | ||||
| @@ -0,0 +1,17 @@ | |||||
| // E03-S05: instance size and field offsets. A class starts with its | |||||
| // two-byte descriptor pointer; fields follow in declaration order with no | |||||
| // padding. boolean/byte/char are 1 byte, integer/word/class reference 2, | |||||
| // interface reference 4, and an array is element size times length. | |||||
| box size 35 | |||||
| box.b offset 2 | |||||
| box.ok offset 3 | |||||
| box.c offset 4 | |||||
| box.w offset 5 | |||||
| box.i offset 7 | |||||
| box.nums offset 9 | |||||
| box.flags offset 15 | |||||
| box.s offset 17 | |||||
| box.next offset 21 | |||||
| box.many offset 23 | |||||
| box.kids offset 31 | |||||
| empty size 2 | |||||
| @@ -0,0 +1,27 @@ | |||||
| interface Shape | |||||
| method area() as word | |||||
| end interface | |||||
| class Box implements Shape | |||||
| byte b | |||||
| boolean ok | |||||
| char c | |||||
| word w | |||||
| integer i | |||||
| word[3] nums | |||||
| byte[2] flags | |||||
| Shape s | |||||
| Box next | |||||
| Shape[2] many | |||||
| Box[2] kids | |||||
| public method area() as word | |||||
| return 0 | |||||
| end method | |||||
| end class | |||||
| class Empty | |||||
| end class | |||||
| program Main | |||||
| end program | |||||
| @@ -0,0 +1,4 @@ | |||||
| diagnostic: TYP_INSTANCE_TOO_LARGE | |||||
| line: 1 | |||||
| column: 1 | |||||
| reason: the field array makes the class instance exceed 65535 bytes | |||||
| @@ -0,0 +1,6 @@ | |||||
| class Huge | |||||
| word[40000] data | |||||
| end class | |||||
| program Main | |||||
| end program | |||||
Powered by TurnKey Linux.