Number interface methods in source order and check each class against every implemented interface, reporting missing, private, mismatched, conflicting, and non-interface implementations at the class declaration. Generated with Codebuff 🤖 Co-Authored-By: Codebuff <noreply@codebuff.com>main
| @@ -211,6 +211,18 @@ 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 | 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. | whose instance would exceed 65535 bytes reports E096 at its declaration line. | ||||
| E03-S06 adds `interfaces.s`. After the declaration pass it numbers every | |||||
| interface method in source order (`C_MEM_SLOT`), giving later dispatch tables | |||||
| a stable slot order, and checks each class against every interface in its | |||||
| implements list. A satisfying method is public and has the same parameter | |||||
| count, parameter types, and return type; one method may satisfy identical | |||||
| requirements from several interfaces. Failures report at the class declaration | |||||
| line, column 1: E130 for a missing or private method, E131 for a signature | |||||
| mismatch, E132 when a same-named requirement of another implemented interface | |||||
| conflicts with the class method's signature, and E133 when an implements entry | |||||
| names a class or program rather than an interface. An implements entry that | |||||
| names no declared type is left to pass 2's unknown-name diagnostic. | |||||
| ### Pass 1 — declarations and layout | ### Pass 1 — declarations and layout | ||||
| - tokenize the complete source; | - tokenize the complete source; | ||||
| @@ -99,6 +99,8 @@ After 20 errors the next one is replaced by E221 and compilation stops. | |||||
| |---|---|---| | |---|---|---| | ||||
| | E130 | `IFC_METHOD_MISSING` | % IS NOT IMPLEMENTED | | | E130 | `IFC_METHOD_MISSING` | % IS NOT IMPLEMENTED | | ||||
| | E131 | `IFC_SIGNATURE_MISMATCH` | % DIFFERS FROM INTERFACE | | | E131 | `IFC_SIGNATURE_MISMATCH` | % DIFFERS FROM INTERFACE | | ||||
| | E132 | `IFC_SIGNATURE_CONFLICT` | % HAS CONFLICTING SIGNATURES | | |||||
| | E133 | `IFC_NOT_INTERFACE` | % IS NOT AN INTERFACE | | |||||
| ## Methods and returns (E150-E169) | ## Methods and returns (E150-E169) | ||||
| @@ -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` | 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) | | |||||
| | `parser.s` | 6,912 | 27 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, interface slot; E03-S02, E03-S05, E03-S06) and 255 parameter-type rows | | |||||
| 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,122 @@ | |||||
| --- | |||||
| story: E03-S06 | |||||
| owner: e03_iface | |||||
| status: done | |||||
| started: 2026-11-09 | |||||
| depends-on: [E03-S02, E03-S04] | |||||
| --- | |||||
| # E03-S06 Claim | |||||
| ## Intended change | |||||
| Add `src/compiler/interfaces.s`, which runs once after the declaration pass. | |||||
| It numbers each interface method in source order in a new `C_MEM_SLOT` member | |||||
| column, then checks every class against every interface in its implements | |||||
| list. A satisfying method is public and has the same parameter count, | |||||
| parameter types, and return type; one method may satisfy identical | |||||
| requirements from several interfaces. Missing and private methods report | |||||
| E130, signature mismatches E131, a class method that matches a same-named | |||||
| requirement of another implemented interface E132, and an implements entry | |||||
| that names a class or program E133. All are reported at the class declaration | |||||
| line, column 1. The parser harness emits an `$F9` slot record so a fixture can | |||||
| verify source order. | |||||
| ## File area | |||||
| - `src/compiler/interfaces.s` (new), `src/compiler/parser.s` | |||||
| - `src/common/parser.inc`, `src/common/diagcat.inc`, `docs/diagnostics.md` | |||||
| - `tests/parser/parser-test.s`, `scripts/build-parser-test.ps1`, | |||||
| `scripts/verify-parser-test.ps1`, `scripts/demo-write-check.ps1` | |||||
| - `tests/semantic/interfaces.n8`, `interfaces.expect`, `iface_bad.n8`, | |||||
| `iface_bad.expect`, `tests/parser/unknown.expect` | |||||
| - `docs/architecture.md`, `docs/memory-map.md` | |||||
| - `planning/claims/E03-S06.md` | |||||
| - generated files under `build/` | |||||
| ## Acceptance evidence | |||||
| - [x] Interface method slots follow interface source order: | |||||
| `interfaces.n8` declares Shape (`area`, `scale`, `name`), Named (`label`, | |||||
| `show`), and Display (`show`, declared after the class); the harness's | |||||
| `$F9` record and `interfaces.expect` list them as shape 0/1/2, named 0/1, | |||||
| display 0. `assign_slots` writes that order into `C_MEM_SLOT`. | |||||
| - [x] A public exact-signature method satisfies a requirement: Box's five | |||||
| public methods match all five interface signatures exactly, and | |||||
| `interfaces.n8` is accepted with no ERR file in the parser run. | |||||
| - [x] One method may satisfy identical requirements from multiple interfaces: | |||||
| Box's single public `show()` satisfies both `Named.show()` and | |||||
| `Display.show()`; no duplicate or conflict diagnostic is produced. | |||||
| - [x] Same-name incompatible requirements are rejected: `iface_bad.n8` has | |||||
| Left (`show()`) and Right (`show(x as byte)`) implemented by one class whose | |||||
| `show()` matches Left; Right reports E132 at 49:1, | |||||
| `SHOW HAS CONFLICTING SIGNATURES`. | |||||
| - [x] Missing, private, parameter-mismatched, and return-mismatched methods | |||||
| each have fixture coverage: `iface_bad.n8` expects E130 at 28:1 (`AREA`), | |||||
| E130 at 31:1 (`LABEL`, private), E131 at 37:1 (`SCALE`, word vs byte), and | |||||
| E131 at 43:1 (`OK`, word vs boolean). E133 at 54:1 covers a class used as an | |||||
| interface. All six exact diagnostics match their messages. | |||||
| ## Coordination notes | |||||
| - No other claim is active. E03-S05 is complete (`42a26c9`). | |||||
| - Conformance legitimately adds E130 to `tests/parser/unknown.n8` (Worker | |||||
| implements Printable without `print`); its exact expectation gained that | |||||
| first pass-1 line and still passes with six diagnostics. | |||||
| - E132 and E133 extend the approved E130-E149 interface category; they do not | |||||
| change source syntax or the interface ABI. Slot order is spec 5.2's source | |||||
| order, and C_MEM_SLOT is parser metadata for later dispatch/MAP work. | |||||
| ## Work log | |||||
| - `interfaces.s`: `assign_slots` numbers interface methods by declaration; | |||||
| `check_interfaces` walks classes and implements lists in source order and | |||||
| reports at the class line. `find_method` ignores fields; a private method is | |||||
| treated as not implemented. `same_sig` compares result type, parameter count, | |||||
| and all parameter types. `conflicting` decides E132 by looking for a | |||||
| same-named requirement in another implemented interface whose signature the | |||||
| class method does satisfy. `iface_err` spells the argument from `name_out` | |||||
| and reports through `diag_report`, returning carry when the error limit | |||||
| stops compilation. | |||||
| - `parser.s` exports `tb_set`, calls `assign_slots` then `check_interfaces` | |||||
| after `layout_classes`, and returns at once if the diagnostic limit is hit. | |||||
| - `parser.inc`: `C_MEM_SLOT = 26`, `TABLE_COLUMNS = 27`; the parser tables are | |||||
| now 6,912 bytes in `docs/memory-map.md`. | |||||
| - `parser-test.s`: `dump_ifaces` emits `$F9`, each interface name, member | |||||
| count, and each slot/name pair. Fixtures 40-41 added; `FIXTURE_COUNT = 41`. | |||||
| - `verify-parser-test.ps1`: decodes `$F9` before `$FA`, stops the type and | |||||
| member loops at it, treats the new `iface` group like an accepted fixture | |||||
| for diagnostics/member checks, and compares slot lines to | |||||
| `interfaces.expect`. | |||||
| - `diagcat.inc`: E132 `% HAS CONFLICTING SIGNATURES`, E133 | |||||
| `% IS NOT AN INTERFACE`; catalog is 70 entries and docs are regenerated. | |||||
| - First build failed on seven long `bcs @stop`/`beq @inext` branches in the | |||||
| 6502 code; each became a short branch plus `jmp` trampoline. | |||||
| ## Completion | |||||
| - Tests run: `scripts/build-parser-test.ps1`, | |||||
| `scripts/verify-parser-test.ps1 -Port 6530`, | |||||
| `scripts/build-diag-test.ps1 -UpdateDocs`, | |||||
| `scripts/verify-diag-test.ps1 -Port 6530`, | |||||
| `scripts/verify-lexer-test.ps1 -Port 6530`, | |||||
| `scripts/demo-write-check.ps1` (good.n8 and bad.n8), | |||||
| `scripts/demo-write-check.ps1 -Programs bad.n8`. | |||||
| - Results: all 41 parser fixtures pass (lowest expression SP `$A7`); | |||||
| `interfaces.n8` reports 6 interface slots and 11 members that match the | |||||
| source oracle, and `ifacebad.n8` matches all six exact diagnostics. The | |||||
| diagnostics catalog has 70 entries and its screen/ERR expectations and docs | |||||
| are in sync. The lexer still matches 10 fixtures / 6,733 bytes. The demo | |||||
| rebuilt and linked `interfaces.s`; good.n8 completed its Vim save and C128 | |||||
| check in the full run, and the bad.n8 rerun saved 258 PETSCII bytes | |||||
| identically and reported E151, E152, E120, and E061. | |||||
| - Files changed: as listed under File area. | |||||
| - Remaining risks: conformance positions are class-level (line, column 1) | |||||
| because member rows do not store a source column; the message names the | |||||
| method. E132 is anchored on a class method that matches another requirement; | |||||
| a class with no method at all still rejects each interface with E130. An | |||||
| implements entry naming no declared type is left to pass 2's unknown-name | |||||
| diagnostic, so E133 only covers declared classes/programs. The VICE verifier | |||||
| defaults on this machine (6532/6534/6535) can produce an empty monitor reply; | |||||
| all runs used `-Port 6530`. Interface dispatch tables and their MAP output | |||||
| remain E04/E05 work; C_MEM_SLOT is the stable input for them. | |||||
| @@ -53,6 +53,8 @@ $script:ParserFixtures = [ordered]@{ | |||||
| "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") | "layout.n8" = @("$t\layout.n8", "layout", "$t\layout.expect") | ||||
| "toobig.n8" = @("$t\too_big.n8", "rejected", "$t\too_big.expect") | "toobig.n8" = @("$t\too_big.n8", "rejected", "$t\too_big.expect") | ||||
| "interfaces.n8" = @("$t\interfaces.n8", "iface", "$t\interfaces.expect") | |||||
| "ifacebad.n8" = @("$t\iface_bad.n8", "exact", "$t\iface_bad.expect") | |||||
| } | } | ||||
| $link = Invoke-Nova128Link -Name "parser-test" -BuildDir $out ` | $link = Invoke-Nova128Link -Name "parser-test" -BuildDir $out ` | ||||
| @@ -64,6 +66,7 @@ $link = Invoke-Nova128Link -Name "parser-test" -BuildDir $out ` | |||||
| (Join-Path $project "src\compiler\names.s"), | (Join-Path $project "src\compiler\names.s"), | ||||
| (Join-Path $project "src\compiler\diagnostics.s"), | (Join-Path $project "src\compiler\diagnostics.s"), | ||||
| (Join-Path $project "src\compiler\parser.s"), | (Join-Path $project "src\compiler\parser.s"), | ||||
| (Join-Path $project "src\compiler\interfaces.s"), | |||||
| (Join-Path $project "src\compiler\types.s"), | (Join-Path $project "src\compiler\types.s"), | ||||
| (Join-Path $project "tests\parser\parser-test.s")) | (Join-Path $project "tests\parser\parser-test.s")) | ||||
| if ((Get-Nova128Label $link.Labels "test_entry") -ne 0x1C0D) { throw "test_entry is not at `$1C0D" } | if ((Get-Nova128Label $link.Labels "test_entry") -ne 0x1C0D) { throw "test_entry is not at `$1C0D" } | ||||
| @@ -39,6 +39,7 @@ $link = Invoke-Nova128Link -Name "n8check" -BuildDir $out ` | |||||
| (Join-Path $project "src\compiler\names.s"), | (Join-Path $project "src\compiler\names.s"), | ||||
| (Join-Path $project "src\compiler\diagnostics.s"), | (Join-Path $project "src\compiler\diagnostics.s"), | ||||
| (Join-Path $project "src\compiler\parser.s"), | (Join-Path $project "src\compiler\parser.s"), | ||||
| (Join-Path $project "src\compiler\interfaces.s"), | |||||
| (Join-Path $project "src\compiler\types.s"), | (Join-Path $project "src\compiler\types.s"), | ||||
| (Join-Path $project "demo\n8check.s")) | (Join-Path $project "demo\n8check.s")) | ||||
| @@ -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 = @(); Layout = @(); Trace = $null } | |||||
| MinSp = $dump[$p + 4]; Types = @(); Members = @(); Ifaces = @(); Layout = @(); Trace = $null } | |||||
| $p += 5 | $p += 5 | ||||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFC -and $dump[$p] -ne 0xFB -and $dump[$p] -ne 0xFA) { | |||||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFC -and $dump[$p] -ne 0xFB -and $dump[$p] -ne 0xFA -and $dump[$p] -ne 0xF9) { | |||||
| $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 -and $dump[$p] -ne 0xFA) { | |||||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFB -and $dump[$p] -ne 0xFA -and $dump[$p] -ne 0xF9) { | |||||
| $owner = Read-DumpName | $owner = Read-DumpName | ||||
| $name = Read-DumpName | $name = Read-DumpName | ||||
| $flags = $dump[$p]; $p++ | $flags = $dump[$p]; $p++ | ||||
| @@ -74,6 +74,18 @@ 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 0xF9) { | |||||
| $p++ | |||||
| while ($dump[$p] -ne 0xFA -and $dump[$p] -ne 0xFB -and $dump[$p] -ne 0xFD) { | |||||
| $owner = Read-DumpName | |||||
| $count = $dump[$p]; $p++ | |||||
| for ($k = 0; $k -lt $count; $k++) { | |||||
| $slot = $dump[$p]; $p++ | |||||
| $name = Read-DumpName | |||||
| $r.Ifaces += ("{0} slot {1} {2}" -f $owner, $slot, $name) | |||||
| } | |||||
| } | |||||
| } | |||||
| if ($dump[$p] -eq 0xFA) { | if ($dump[$p] -eq 0xFA) { | ||||
| $p++ | $p++ | ||||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFB) { | while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFB) { | ||||
| @@ -195,7 +207,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) { | ||||
| { $_ -eq "accepted" -or $_ -eq "layout" } { | |||||
| { $_ -eq "accepted" -or $_ -eq "layout" -or $_ -eq "iface" } { | |||||
| 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 = @() | ||||
| @@ -245,7 +257,7 @@ foreach ($name in $ParserFixtures.Keys) { | |||||
| $detail = "{0} diagnostics" -f $got.Count | $detail = "{0} diagnostics" -f $got.Count | ||||
| } | } | ||||
| } | } | ||||
| if ($group -in @("accepted", "layout") -or $memberChecked -contains $name) { | |||||
| if ($group -in @("accepted", "layout", "iface") -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 '; ')]" | ||||
| @@ -270,6 +282,13 @@ foreach ($name in $ParserFixtures.Keys) { | |||||
| } | } | ||||
| $detail += ", {0} layout lines" -f $r.Layout.Count | $detail += ", {0} layout lines" -f $r.Layout.Count | ||||
| } | } | ||||
| if ($group -eq "iface") { | |||||
| $want = @(Get-Content -LiteralPath $expect | Where-Object { $_ -and -not $_.StartsWith("//") }) | |||||
| if (($r.Ifaces -join ';') -ne ($want -join ';')) { | |||||
| $problems += "slots [$($r.Ifaces -join '; ')] expected [$($want -join '; ')]" | |||||
| } | |||||
| $detail += ", {0} interface slots" -f $r.Ifaces.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) | ||||
| @@ -82,6 +82,8 @@ | |||||
| ; ------------------------------------------------------------- interfaces | ; ------------------------------------------------------------- interfaces | ||||
| DIAGNOSTIC IFC_METHOD_MISSING, 130, "% IS NOT IMPLEMENTED" | DIAGNOSTIC IFC_METHOD_MISSING, 130, "% IS NOT IMPLEMENTED" | ||||
| DIAGNOSTIC IFC_SIGNATURE_MISMATCH, 131, "% DIFFERS FROM INTERFACE" | DIAGNOSTIC IFC_SIGNATURE_MISMATCH, 131, "% DIFFERS FROM INTERFACE" | ||||
| DIAGNOSTIC IFC_SIGNATURE_CONFLICT, 132, "% HAS CONFLICTING SIGNATURES" | |||||
| DIAGNOSTIC IFC_NOT_INTERFACE, 133, "% IS NOT AN INTERFACE" | |||||
| ; ---------------------------------------------------- methods and returns | ; ---------------------------------------------------- methods and returns | ||||
| DIAGNOSTIC SEM_MISSING_RETURN, 150, "MISSING RETURN VALUE" | DIAGNOSTIC SEM_MISSING_RETURN, 150, "MISSING RETURN VALUE" | ||||
| @@ -1,4 +1,4 @@ | |||||
| ; parser.inc - parser tables and limits (E03-S01..E03-S04). | |||||
| ; parser.inc - parser tables and limits (E03-S01..E03-S06). | |||||
| .ifndef PARSER_INC | .ifndef PARSER_INC | ||||
| PARSER_INC = 1 | PARSER_INC = 1 | ||||
| @@ -79,7 +79,8 @@ C_TYPE_SIZE_LO = 22 ; class instance size (descriptor + fields) | |||||
| C_TYPE_SIZE_HI = 23 | C_TYPE_SIZE_HI = 23 | ||||
| C_MEM_OFFSET_LO = 24 ; field byte offset | C_MEM_OFFSET_LO = 24 ; field byte offset | ||||
| C_MEM_OFFSET_HI = 25 | C_MEM_OFFSET_HI = 25 | ||||
| TABLE_COLUMNS = 26 | |||||
| C_MEM_SLOT = 26 ; interface method slot (source order) | |||||
| TABLE_COLUMNS = 27 | |||||
| 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 | ||||
| @@ -0,0 +1,452 @@ | |||||
| ; interfaces.s - interface slot order and class conformance (E03-S06). | |||||
| ; | |||||
| ; Runs once after pass 1, when every interface, class, member, parameter, and | |||||
| ; implements row is recorded: | |||||
| ; assign_slots numbers each interface method in declaration order and | |||||
| ; stores it in C_MEM_SLOT, so later method tables and the MAP | |||||
| ; writer can use a stable slot without re-reading the source; | |||||
| ; check_interfaces | |||||
| ; verifies each class against every interface it implements. | |||||
| ; A satisfying method is public and has the same parameter | |||||
| ; count, parameter types, and return type. One method may | |||||
| ; satisfy identical requirements from several interfaces. | |||||
| ; | |||||
| ; Diagnostics are reported at the class declaration line, column 1: | |||||
| ; E130 no public method with a required name (missing, field, or private) | |||||
| ; E131 a public method with that name differs in its signature | |||||
| ; E132 the differing method matches the same-named requirement of another | |||||
| ; implemented interface, so the interfaces conflict | |||||
| ; E133 an implements entry names a class or program, not an interface | |||||
| ; An implements entry that names no declared type is left to pass 2, which | |||||
| ; already reports the unknown name at the source position. | |||||
| ; | |||||
| ; Everything is read through tb_get/tb_set, so this module runs in the normal | |||||
| ; bank-0 compiler state. | |||||
| .setcpu "6502" | |||||
| .include "../common/errors.inc" | |||||
| .include "../common/parser.inc" | |||||
| .import tb_get, tb_set, names_count | |||||
| .import diag_report, diag_line, diag_col, diag_arg1, diag_arg1_len | |||||
| .import name_get, name_out, name_out_len | |||||
| .export assign_slots, check_interfaces | |||||
| .segment "CODE" | |||||
| ; ------------------------------------------------------------- slot order | |||||
| ; Every interface method gets its declaration index: 0, 1, ... in source | |||||
| ; order. Interfaces cannot declare fields, so member rows are signatures. | |||||
| assign_slots: | |||||
| lda #$01 | |||||
| sta as_id | |||||
| @next: lda as_id | |||||
| cmp names_count | |||||
| bcc :+ | |||||
| rts | |||||
| : ldy as_id | |||||
| lda #C_TYPE_KIND | |||||
| jsr tb_get | |||||
| cmp #TYPE_INTERFACE | |||||
| beq :+ | |||||
| jmp @skip | |||||
| : ldy as_id | |||||
| lda #C_TYPE_MFIRST | |||||
| jsr tb_get | |||||
| sta as_row | |||||
| ldy as_id | |||||
| lda #C_TYPE_MCOUNT | |||||
| jsr tb_get | |||||
| sta as_n | |||||
| lda #$00 | |||||
| sta as_slot | |||||
| @slot: lda as_n | |||||
| beq @skip | |||||
| ldy as_row | |||||
| ldx as_slot | |||||
| lda #C_MEM_SLOT | |||||
| jsr tb_set | |||||
| inc as_row | |||||
| inc as_slot | |||||
| dec as_n | |||||
| bne @slot | |||||
| @skip: inc as_id | |||||
| bne @next | |||||
| rts | |||||
| ; ----------------------------------------------------------- conformance | |||||
| ; For each class in declaration order, walk its implements list in source | |||||
| ; order and each interface's signatures in slot order. | |||||
| check_interfaces: | |||||
| lda #$01 | |||||
| sta cf_class | |||||
| @class: lda cf_class | |||||
| cmp names_count | |||||
| bcc :+ | |||||
| jmp @done | |||||
| : ldy cf_class | |||||
| lda #C_TYPE_KIND | |||||
| jsr tb_get | |||||
| cmp #TYPE_CLASS | |||||
| beq :+ | |||||
| jmp @cnext | |||||
| : ldy cf_class | |||||
| lda #C_TYPE_ICOUNT | |||||
| jsr tb_get | |||||
| bne :+ | |||||
| jmp @cnext ; no implements list | |||||
| : sta cf_count | |||||
| ldy cf_class | |||||
| lda #C_TYPE_IFIRST | |||||
| jsr tb_get | |||||
| sta cf_row | |||||
| lda #$00 | |||||
| sta cf_i | |||||
| @impl: lda cf_i | |||||
| cmp cf_count | |||||
| bcc :+ | |||||
| jmp @cnext | |||||
| : ldy cf_row | |||||
| lda #C_IMPL_IFACE | |||||
| jsr tb_get | |||||
| sta cf_iface | |||||
| ldy cf_iface | |||||
| lda #C_TYPE_KIND | |||||
| jsr tb_get | |||||
| cmp #TYPE_INTERFACE | |||||
| beq @conform | |||||
| cmp #$00 | |||||
| bne :+ ; unknown: pass 2 reports it | |||||
| jmp @inext | |||||
| : | |||||
| ldx cf_iface | |||||
| lda #E_IFC_NOT_INTERFACE | |||||
| jsr iface_err | |||||
| bcc :+ | |||||
| jmp @stop | |||||
| : | |||||
| jmp @inext | |||||
| @conform: | |||||
| ldy cf_iface | |||||
| lda #C_TYPE_MFIRST | |||||
| jsr tb_get | |||||
| sta cf_req | |||||
| ldy cf_iface | |||||
| lda #C_TYPE_MCOUNT | |||||
| jsr tb_get | |||||
| sta cf_n | |||||
| @req: lda cf_n | |||||
| bne :+ | |||||
| jmp @inext | |||||
| : ldy cf_req | |||||
| lda #C_MEM_NAME | |||||
| jsr tb_get | |||||
| sta cf_name | |||||
| lda cf_class | |||||
| ldx cf_name | |||||
| jsr find_method | |||||
| bcc @missing | |||||
| sty cf_method | |||||
| lda #C_MEM_FLAGS | |||||
| jsr tb_get | |||||
| and #MF_PRIVATE | |||||
| beq @sig | |||||
| jmp @missing ; private methods do not implement | |||||
| @sig: lda cf_req | |||||
| ldx cf_method | |||||
| jsr same_sig | |||||
| bcs @satisfied | |||||
| lda cf_method | |||||
| ldx cf_name | |||||
| jsr conflicting | |||||
| bcs @conflict | |||||
| ldx cf_name | |||||
| lda #E_IFC_SIGNATURE_MISMATCH | |||||
| jsr iface_err | |||||
| bcc :+ | |||||
| jmp @stop | |||||
| : | |||||
| jmp @satisfied | |||||
| @conflict: | |||||
| ldx cf_name | |||||
| lda #E_IFC_SIGNATURE_CONFLICT | |||||
| jsr iface_err | |||||
| bcc :+ | |||||
| jmp @stop | |||||
| : | |||||
| jmp @satisfied | |||||
| @missing: | |||||
| ldx cf_name | |||||
| lda #E_IFC_METHOD_MISSING | |||||
| jsr iface_err | |||||
| bcc :+ | |||||
| jmp @stop | |||||
| : | |||||
| @satisfied: | |||||
| inc cf_req | |||||
| dec cf_n | |||||
| jmp @req | |||||
| @inext: inc cf_row | |||||
| inc cf_i | |||||
| jmp @impl | |||||
| @cnext: inc cf_class | |||||
| beq @done | |||||
| jmp @class | |||||
| @stop: sec | |||||
| rts | |||||
| @done: clc | |||||
| rts | |||||
| ; A = class ID, X = method name ID. C=1 and Y=row when the class declares a | |||||
| ; method with that name; C=0 otherwise. A field with the name does not count. | |||||
| find_method: | |||||
| sta fmc_class | |||||
| stx fmc_name | |||||
| ldy fmc_class | |||||
| lda #C_TYPE_MFIRST | |||||
| jsr tb_get | |||||
| sta fmc_row | |||||
| ldy fmc_class | |||||
| lda #C_TYPE_MCOUNT | |||||
| jsr tb_get | |||||
| sta fmc_n | |||||
| @row: lda fmc_n | |||||
| beq @no | |||||
| ldy fmc_row | |||||
| lda #C_MEM_FLAGS | |||||
| jsr tb_get | |||||
| and #MF_METHOD | |||||
| beq @next | |||||
| ldy fmc_row | |||||
| lda #C_MEM_NAME | |||||
| jsr tb_get | |||||
| cmp fmc_name | |||||
| beq @yes | |||||
| @next: inc fmc_row | |||||
| dec fmc_n | |||||
| bne @row | |||||
| @no: clc | |||||
| rts | |||||
| @yes: ldy fmc_row | |||||
| sec | |||||
| rts | |||||
| ; C=1 when the class method A matches a same-named requirement (X) of an | |||||
| ; implemented interface other than the one being checked. That means the | |||||
| ; interfaces themselves disagree, not that the class method is wrong. | |||||
| conflicting: | |||||
| sta cn_method | |||||
| stx cn_name | |||||
| ldy cf_class | |||||
| lda #C_TYPE_IFIRST | |||||
| jsr tb_get | |||||
| sta cn_row | |||||
| ldy cf_class | |||||
| lda #C_TYPE_ICOUNT | |||||
| jsr tb_get | |||||
| sta cn_n | |||||
| @iface: lda cn_n | |||||
| beq @no | |||||
| ldy cn_row | |||||
| lda #C_IMPL_IFACE | |||||
| jsr tb_get | |||||
| sta cn_iface | |||||
| cmp cf_iface | |||||
| beq @next_iface | |||||
| ldy cn_iface | |||||
| lda #C_TYPE_KIND | |||||
| jsr tb_get | |||||
| cmp #TYPE_INTERFACE | |||||
| bne @next_iface | |||||
| ldy cn_iface | |||||
| lda #C_TYPE_MFIRST | |||||
| jsr tb_get | |||||
| sta cn_req | |||||
| ldy cn_iface | |||||
| lda #C_TYPE_MCOUNT | |||||
| jsr tb_get | |||||
| sta cn_req_n | |||||
| @req: lda cn_req_n | |||||
| beq @next_iface | |||||
| ldy cn_req | |||||
| lda #C_MEM_NAME | |||||
| jsr tb_get | |||||
| cmp cn_name | |||||
| bne @next_req | |||||
| lda cn_method | |||||
| ldx cn_req | |||||
| jsr same_sig | |||||
| bcs @yes | |||||
| @next_req: | |||||
| inc cn_req | |||||
| dec cn_req_n | |||||
| bne @req | |||||
| @next_iface: | |||||
| inc cn_row | |||||
| dec cn_n | |||||
| bne @iface | |||||
| @no: clc | |||||
| rts | |||||
| @yes: sec | |||||
| rts | |||||
| ; C=1 when member rows A and X have equal signatures: result type, parameter | |||||
| ; count, and each parameter type. | |||||
| same_sig: | |||||
| sta sg_a | |||||
| stx sg_b | |||||
| ldy sg_a | |||||
| lda #C_MEM_PCOUNT | |||||
| jsr tb_get | |||||
| sta sg_n | |||||
| ldy sg_b | |||||
| lda #C_MEM_PCOUNT | |||||
| jsr tb_get | |||||
| cmp sg_n | |||||
| bne @no | |||||
| lda sg_a | |||||
| ldx sg_b | |||||
| jsr same_result | |||||
| bcc @no | |||||
| ldy sg_a | |||||
| lda #C_MEM_PFIRST | |||||
| jsr tb_get | |||||
| sta sg_pa | |||||
| ldy sg_b | |||||
| lda #C_MEM_PFIRST | |||||
| jsr tb_get | |||||
| sta sg_pb | |||||
| lda #$00 | |||||
| sta sg_i | |||||
| @param: lda sg_i | |||||
| cmp sg_n | |||||
| beq @yes | |||||
| lda sg_pa | |||||
| clc | |||||
| adc sg_i | |||||
| sta sg_ra | |||||
| lda sg_pb | |||||
| clc | |||||
| adc sg_i | |||||
| sta sg_rb | |||||
| lda sg_ra | |||||
| ldx sg_rb | |||||
| jsr same_param | |||||
| bcc @no | |||||
| inc sg_i | |||||
| bne @param | |||||
| @yes: sec | |||||
| rts | |||||
| @no: clc | |||||
| rts | |||||
| same_result: | |||||
| ldy #C_MEM_TBASE | |||||
| jmp same_cells | |||||
| same_param: | |||||
| ldy #C_PAR_TBASE | |||||
| jmp same_cells | |||||
| ; C=1 when four consecutive type columns starting at Y are equal for rows A | |||||
| ; and X (base, name, length lo, length hi). | |||||
| same_cells: | |||||
| sta sg_a | |||||
| stx sg_b | |||||
| sty sg_col | |||||
| lda #$00 | |||||
| sta sg_ci | |||||
| @col: ldy sg_a | |||||
| lda sg_col | |||||
| clc | |||||
| adc sg_ci | |||||
| jsr tb_get | |||||
| sta sg_v | |||||
| ldy sg_b | |||||
| lda sg_col | |||||
| clc | |||||
| adc sg_ci | |||||
| jsr tb_get | |||||
| cmp sg_v | |||||
| bne @no | |||||
| inc sg_ci | |||||
| lda sg_ci | |||||
| cmp #$04 | |||||
| bne @col | |||||
| sec | |||||
| rts | |||||
| @no: clc | |||||
| rts | |||||
| ; Report A at class cf_class's declaration line, column 1, with name X as | |||||
| ; argument 1. C=1 when the diagnostic limit stopped compilation. | |||||
| iface_err: | |||||
| sta ie_code | |||||
| stx ie_name | |||||
| ldy cf_class | |||||
| lda #C_TYPE_LINE_LO | |||||
| jsr tb_get | |||||
| sta diag_line | |||||
| ldy cf_class | |||||
| lda #C_TYPE_LINE_HI | |||||
| jsr tb_get | |||||
| sta diag_line+1 | |||||
| lda #$01 | |||||
| sta diag_col | |||||
| lda ie_name | |||||
| jsr name_get | |||||
| lda #<name_out | |||||
| sta diag_arg1 | |||||
| lda #>name_out | |||||
| sta diag_arg1+1 | |||||
| lda name_out_len | |||||
| sta diag_arg1_len | |||||
| lda ie_code | |||||
| jsr diag_report | |||||
| bcc @ok | |||||
| sec | |||||
| rts | |||||
| @ok: clc | |||||
| rts | |||||
| .segment "BSS" | |||||
| ; slot assignment | |||||
| as_id: .res 1 | |||||
| as_row: .res 1 | |||||
| as_n: .res 1 | |||||
| as_slot: .res 1 | |||||
| ; conformance | |||||
| cf_class: .res 1 | |||||
| cf_iface: .res 1 | |||||
| cf_count: .res 1 | |||||
| cf_row: .res 1 | |||||
| cf_i: .res 1 | |||||
| cf_req: .res 1 | |||||
| cf_n: .res 1 | |||||
| cf_name: .res 1 | |||||
| cf_method: .res 1 | |||||
| fmc_class: .res 1 | |||||
| fmc_name: .res 1 | |||||
| fmc_row: .res 1 | |||||
| fmc_n: .res 1 | |||||
| cn_method: .res 1 | |||||
| cn_name: .res 1 | |||||
| cn_row: .res 1 | |||||
| cn_n: .res 1 | |||||
| cn_iface: .res 1 | |||||
| cn_req: .res 1 | |||||
| cn_req_n: .res 1 | |||||
| sg_a: .res 1 | |||||
| sg_b: .res 1 | |||||
| sg_n: .res 1 | |||||
| sg_i: .res 1 | |||||
| sg_pa: .res 1 | |||||
| sg_pb: .res 1 | |||||
| sg_col: .res 1 | |||||
| sg_ci: .res 1 | |||||
| sg_v: .res 1 | |||||
| sg_ra: .res 1 | |||||
| sg_rb: .res 1 | |||||
| ie_code: .res 1 | |||||
| ie_name: .res 1 | |||||
| @@ -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-S05). | |||||
| ; type checking (E03-S01..E03-S06). | |||||
| ; | ; | ||||
| ; 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): | ||||
| @@ -9,7 +9,8 @@ | |||||
| ; 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, | ; local-placement errors; records each class's implements list, | ||||
| ; init row, instance size, and field offsets (E03-S05). | |||||
| ; init row, instance size, and field offsets (E03-S05); assigns | |||||
| ; interface slots and checks class conformance (E03-S06). | |||||
| ; 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 | ||||
| @@ -54,8 +55,9 @@ | |||||
| .import tt_base, tt_name, tt_len | .import tt_base, tt_name, tt_len | ||||
| .import ty_assign, ty_binary, ty_unary | .import ty_assign, ty_binary, ty_unary | ||||
| .import ty_arg_sc1, ty_arg_sc2, ty_arg_name1, ty_arg_num2 | .import ty_arg_sc1, ty_arg_sc2, ty_arg_name1, ty_arg_num2 | ||||
| .import assign_slots, check_interfaces | |||||
| .export parse_file, tb_get, mem_count, par_count | |||||
| .export parse_file, tb_get, tb_set, mem_count, par_count | |||||
| .export ex_trace_on, ex_min_sp | .export ex_trace_on, ex_min_sp | ||||
| .exportzp ex_tp | .exportzp ex_tp | ||||
| .export type_kind, type_line_lo, type_line_hi | .export type_kind, type_line_lo, type_line_hi | ||||
| @@ -99,7 +101,11 @@ parse_file: | |||||
| sta pass | sta pass | ||||
| jsr parse_pass | jsr parse_pass | ||||
| jsr layout_classes | jsr layout_classes | ||||
| lda #$02 | |||||
| jsr assign_slots | |||||
| jsr check_interfaces | |||||
| bcc :+ | |||||
| rts | |||||
| : lda #$02 | |||||
| sta pass | sta pass | ||||
| jsr parse_pass | jsr parse_pass | ||||
| rts | rts | ||||
| @@ -11,6 +11,10 @@ | |||||
| ; per member row: owner name, member name, flags, type, parameter count, | ; per member row: owner name, member name, flags, type, parameter count, | ||||
| ; then each parameter's type. A name is a length and its spelling (length | ; then each parameter's type. A name is a length and its spelling (length | ||||
| ; 0 = none); a type is base, name, length lo, length hi. | ; 0 = none); a type is base, name, length lo, length hi. | ||||
| ; $F9 | |||||
| ; per interface: name, slot count (member count), then each slot's number | |||||
| ; and method name in source order (E03-S06) | |||||
| ; $FA, per class: name, instance size, field count, fields and offsets | |||||
| ; for the TRACE_INDEX fixture only: $FB, length lo/hi, the expression | ; for the TRACE_INDEX fixture only: $FB, length lo/hi, the expression | ||||
| ; trace (parser.inc TR_*) | ; trace (parser.inc TR_*) | ||||
| ; $FD | ; $FD | ||||
| @@ -141,6 +145,7 @@ test_entry: | |||||
| lda #$FC | lda #$FC | ||||
| jsr emit | jsr emit | ||||
| jsr dump_members | jsr dump_members | ||||
| jsr dump_ifaces | |||||
| jsr dump_layout | jsr dump_layout | ||||
| lda fx_index | lda fx_index | ||||
| cmp #TRACE_INDEX | cmp #TRACE_INDEX | ||||
| @@ -289,6 +294,52 @@ dump_members: | |||||
| jmp @next | jmp @next | ||||
| @done: rts | @done: rts | ||||
| ; Emit $F9, then each interface: name, member count, and each member's slot | |||||
| ; number and method name in declaration order. | |||||
| dump_ifaces: | |||||
| lda #$F9 | |||||
| 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_INTERFACE | |||||
| beq :+ | |||||
| jmp @adv | |||||
| : lda id | |||||
| jsr emit_name | |||||
| ldy id | |||||
| lda #C_TYPE_MCOUNT | |||||
| jsr tb_get | |||||
| sta lk_n | |||||
| jsr emit | |||||
| ldy id | |||||
| lda #C_TYPE_MFIRST | |||||
| jsr tb_get | |||||
| sta lk_row | |||||
| @slot: lda lk_n | |||||
| beq @adv | |||||
| ldy lk_row | |||||
| lda #C_MEM_SLOT | |||||
| jsr tb_get | |||||
| jsr emit | |||||
| ldy lk_row | |||||
| lda #C_MEM_NAME | |||||
| jsr tb_get | |||||
| jsr emit_name | |||||
| inc lk_row | |||||
| dec lk_n | |||||
| jmp @slot | |||||
| @adv: inc id | |||||
| beq @done | |||||
| jmp @next | |||||
| @done: rts | |||||
| ; Emit $FA, then each class: name, instance size lo/hi, field count, and | ; Emit $FA, then each class: name, instance size lo/hi, field count, and | ||||
| ; every field's name and offset lo/hi in declaration order. | ; every field's name and offset lo/hi in declaration order. | ||||
| dump_layout: | dump_layout: | ||||
| @@ -519,7 +570,9 @@ fixtures: | |||||
| FIXTURE "CALLS.N8" | FIXTURE "CALLS.N8" | ||||
| FIXTURE "LAYOUT.N8" | FIXTURE "LAYOUT.N8" | ||||
| FIXTURE "TOOBIG.N8" | FIXTURE "TOOBIG.N8" | ||||
| FIXTURE_COUNT = 39 | |||||
| FIXTURE "INTERFACES.N8" | |||||
| FIXTURE "IFACEBAD.N8" | |||||
| FIXTURE_COUNT = 41 | |||||
| TRACE_INDEX = 28 ; PREC.N8 | TRACE_INDEX = 28 ; PREC.N8 | ||||
| messages: | messages: | ||||
| @@ -1,5 +1,7 @@ | |||||
| // pass 2: Printable and Worker are forward references; the rest are unknown. | // pass 2: Printable and Worker are forward references; the rest are unknown. | ||||
| // run() declares a result but its body has no return (E150). | |||||
| // run() declares a result but its body has no return (E150). Worker also does | |||||
| // not implement Printable.print (E130, pass 1). | |||||
| IFC_METHOD_MISSING 1 1 PRINT IS NOT IMPLEMENTED | |||||
| SEM_UNKNOWN_NAME 1 25 | SEM_UNKNOWN_NAME 1 25 | ||||
| SEM_UNKNOWN_NAME 2 11 | SEM_UNKNOWN_NAME 2 11 | ||||
| SEM_UNKNOWN_NAME 3 28 | SEM_UNKNOWN_NAME 3 28 | ||||
| @@ -0,0 +1,10 @@ | |||||
| // E03-S06: conformance failures are reported at the class declaration line, | |||||
| // column 1. Missing, private, parameter-mismatched, return-mismatched, | |||||
| // conflicting same-name requirements, and a class used as an interface. | |||||
| // SYMBOL line column message (message compared case-insensitively) | |||||
| IFC_METHOD_MISSING 28 1 AREA IS NOT IMPLEMENTED | |||||
| IFC_METHOD_MISSING 31 1 LABEL IS NOT IMPLEMENTED | |||||
| IFC_SIGNATURE_MISMATCH 37 1 SCALE DIFFERS FROM INTERFACE | |||||
| IFC_SIGNATURE_MISMATCH 43 1 OK DIFFERS FROM INTERFACE | |||||
| IFC_SIGNATURE_CONFLICT 49 1 SHOW HAS CONFLICTING SIGNATURES | |||||
| IFC_NOT_INTERFACE 54 1 NOTIFACE IS NOT AN INTERFACE | |||||
| @@ -0,0 +1,58 @@ | |||||
| interface Shape | |||||
| method area() as word | |||||
| end interface | |||||
| interface Named | |||||
| method label() as char | |||||
| end interface | |||||
| interface Scaled | |||||
| method scale(f as byte) as word | |||||
| end interface | |||||
| interface Boolish | |||||
| method ok() as boolean | |||||
| end interface | |||||
| interface Left | |||||
| method show() | |||||
| end interface | |||||
| interface Right | |||||
| method show(x as byte) | |||||
| end interface | |||||
| class NotIface | |||||
| end class | |||||
| class Missing implements Shape | |||||
| end class | |||||
| class Hider implements Named | |||||
| private method label() as char | |||||
| return 'h' | |||||
| end method | |||||
| end class | |||||
| class WrongParam implements Scaled | |||||
| public method scale(f as word) as word | |||||
| return f | |||||
| end method | |||||
| end class | |||||
| class WrongReturn implements Boolish | |||||
| public method ok() as word | |||||
| return 0 | |||||
| end method | |||||
| end class | |||||
| class Conflict implements Left, Right | |||||
| public method show() | |||||
| end method | |||||
| end class | |||||
| class BadIface implements NotIface | |||||
| end class | |||||
| program Main | |||||
| end program | |||||
| @@ -0,0 +1,9 @@ | |||||
| // E03-S06: interface slots follow the interface's source declaration order, | |||||
| // not alphabetical or class order. Box satisfies Named.show and Display.show | |||||
| // with its single public show() method. | |||||
| shape slot 0 area | |||||
| shape slot 1 scale | |||||
| shape slot 2 name | |||||
| named slot 0 label | |||||
| named slot 1 show | |||||
| display slot 0 show | |||||
| @@ -0,0 +1,34 @@ | |||||
| interface Shape | |||||
| method area() as word | |||||
| method scale(f as byte) as word | |||||
| method name() as char | |||||
| end interface | |||||
| interface Named | |||||
| method label() as char | |||||
| method show() | |||||
| end interface | |||||
| class Box implements Shape, Named, Display | |||||
| public method area() as word | |||||
| return 1 | |||||
| end method | |||||
| public method scale(f as byte) as word | |||||
| return f | |||||
| end method | |||||
| public method name() as char | |||||
| return 'b' | |||||
| end method | |||||
| public method label() as char | |||||
| return 'l' | |||||
| end method | |||||
| public method show() | |||||
| end method | |||||
| end class | |||||
| interface Display | |||||
| method show() | |||||
| end interface | |||||
| program Main | |||||
| end program | |||||
Powered by TurnKey Linux.