Просмотр исходного кода

newLang: check expression types and returns (E03-S04)

types.s gives every expression a type and applies the spec 3.4/8
conversions, operator table, constant folding and diagnostic spellings.
parser.s records implements lists and init rows in pass 1, carries types
through member chains, groups, calls and indexing in pass 2, and checks
assignments, arguments, returns, conditions, for, print, delete and member
access. A method that declares a result must end in return; a missing one
is E150 at end, and a program return value is E151.

Three older parser fixtures gain true diagnostics. The lexer's host
reference formatter now spells bytes such as {0D} correctly, and the demo
tolerates a missing ERR file.

Verified in x128: 37 parser fixtures, 10 lexer fixtures, the 67-entry
diagnostic catalog, and the Vim-to-N8CHECK demo (good.n8 clean, bad.n8's
four diagnostics).

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
main
Daniel Covington 5 дней назад
Родитель
Сommit
5b94b01210
28 измененных файлов: 2229 добавлений и 180 удалений
  1. +7
    -1
      newLang/docs/architecture.md
  2. +4
    -0
      newLang/docs/decisions.md
  3. +16
    -0
      newLang/docs/diagnostics.md
  4. +109
    -0
      newLang/planning/claims/E03-S04.md
  5. +10
    -2
      newLang/scripts/build-parser-test.ps1
  6. +5
    -1
      newLang/scripts/demo-write-check.ps1
  7. +21
    -6
      newLang/scripts/verify-parser-test.ps1
  8. +16
    -0
      newLang/src/common/diagcat.inc
  9. +33
    -19
      newLang/src/common/parser.inc
  10. +720
    -141
      newLang/src/compiler/parser.s
  11. +853
    -0
      newLang/src/compiler/types.s
  12. +1
    -1
      newLang/tests/lexer/reference-lexer.ps1
  13. +5
    -4
      newLang/tests/parser/forms.n8
  14. +8
    -2
      newLang/tests/parser/parser-test.s
  15. +5
    -1
      newLang/tests/parser/recover.expect
  16. +3
    -1
      newLang/tests/parser/syntax.expect
  17. +3
    -1
      newLang/tests/parser/unknown.expect
  18. +18
    -0
      newLang/tests/semantic/calls.expect
  19. +63
    -0
      newLang/tests/semantic/calls.n8
  20. +21
    -0
      newLang/tests/semantic/convert.expect
  21. +26
    -0
      newLang/tests/semantic/convert.n8
  22. +19
    -0
      newLang/tests/semantic/ops.expect
  23. +29
    -0
      newLang/tests/semantic/ops.n8
  24. +16
    -0
      newLang/tests/semantic/refs.expect
  25. +45
    -0
      newLang/tests/semantic/refs.n8
  26. +115
    -0
      newLang/tests/semantic/types_ok.n8
  27. +21
    -0
      newLang/tests/semantic/uses.expect
  28. +37
    -0
      newLang/tests/semantic/uses.n8

+ 7
- 1
newLang/docs/architecture.md Просмотреть файл

@@ -196,7 +196,13 @@ member, and access lookups from E03-S02 run from the parser in pass 2. Each
expression level is counted, and more than 16 is E225, which bounds hardware
stack use (the deepest fixture leaves SP at `$A7`). For tests, pass 1 can
record every expression in postfix order (`ex_trace_on`). Operator and
conversion types remain E03-S04.
E03-S04 adds `types.s`: every expression carries a type in pass 2, and
`ty_assign`, `ty_binary`, and `ty_unary` apply spec 3.4 and 8 (D124-D127).
Assignments, call arguments and results, returns, conditions, `for`, `print`,
`delete`, indexing, and member chains are checked; a method that declares a
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);
arrays are never whole values (E088).

### Pass 1 — declarations and layout



+ 4
- 0
newLang/docs/decisions.md Просмотреть файл

@@ -50,6 +50,10 @@ documented default. `Proposed` means a later change still needs approval.
| D121 | A method name may be any identifier or reserved word, as in spec 5.1's `method print()`. | Spec 5.1 and the accepted fixtures declare `method print()` although `print` is a statement keyword. The parser accepts a keyword after `method`; E03-S03 must accept the same member names after `.`. |
| D122 | `init` returns no value (`as type` is E151), like `destroy`. | Spec 6.3 says `new Class(args)` yields the object, so an `init` result could never be observed. Spec 6.3 already gives `destroy` no parameters and no result (E152, E151). Implemented by E03-S02. |
| D123 | Inside a method an unqualified name is a parameter or local, else a field of the enclosing class; methods are always called through an expression such as `self.tick()`. | Spec 9 lets a local "shadow a field", which implies fields are in scope unqualified, but no fixture used one. Spec 7 defines calls only as `expression.method(arguments)`, so a bare `tick()` is an unknown name. Implemented by E03-S02. |
| D124 | Numeric literals are constants that adopt the type of the place or operand they meet when the value fits, and literal-only arithmetic folds at compile time. `and`/`or` take booleans; `+ - * /` take byte, word, or integer; `< <= > >=` take numbers or chars; `= <>` also take booleans and references (identity). `byte` widens to `word`/`integer`, but `word` and `integer` do not mix. | Spec 3.4 names the conversions but not literal types or the operator table. Out-of-range folded results are E084, division by zero E086, and a constant that does not fit E085. Implemented by `types.s`; changing it changes only that module and the semantic goldens. |
| D125 | An array index may be an integer or a value assignable to word; a constant index is also checked against the declared length. | Spec 3.3 gives arrays but not the index type. Byte indexes widen; an out-of-range constant is E089 at compile time. Implemented by `check_index` in `parser.s`. |
| D126 | Arrays are fields or locals only: never parameters, results, or whole values. A bare array is E088, and a parameter or result declaring one is E094. | Keeps array storage and copying out of the ABI (D108); indexing is the only way to read or write one. Implemented by `no_array_sig` and `ty_assign`. |
| D127 | `return` with a value in a program is E151. A method that declares a result must end every path in `return`; an `if` satisfies this only when both branches do, a `while` never does, and a missing return is reported at `end` as E150. | Spec 7 permits `return` but does not say what a program return means; a program has no caller or result. Deterministic and observable, and lets E04 emit a single exit path. Implemented by `close_block` and `set_ret`. |

## Deferred



+ 16
- 0
newLang/docs/diagnostics.md Просмотреть файл

@@ -71,6 +71,20 @@ After 20 errors the next one is replaced by E221 and compilation stops.
|---|---|---|
| E080 | `TYP_MISMATCH` | % EXPECTED; % FOUND |
| E081 | `TYP_CONDITION_NOT_BOOLEAN` | CONDITION MUST BE BOOLEAN |
| E082 | `TYP_BAD_OPERAND` | % NOT ALLOWED ON % |
| E083 | `TYP_OPERANDS_DIFFER` | % AND % DO NOT MIX |
| E084 | `TYP_NUMBER_RANGE` | NUMBER OUT OF RANGE |
| E085 | `TYP_NUMBER_DOES_NOT_FIT` | % DOES NOT FIT % |
| E086 | `TYP_DIVIDE_BY_ZERO` | DIVISION BY ZERO |
| E087 | `TYP_NOT_ARRAY` | % IS NOT AN ARRAY |
| E088 | `TYP_ARRAY_NEEDS_INDEX` | % NEEDS AN INDEX |
| E089 | `TYP_INDEX_RANGE` | INDEX OUT OF RANGE |
| E090 | `TYP_NO_MEMBERS` | % HAS NO MEMBERS |
| E091 | `TYP_CANNOT_PRINT` | % CANNOT BE PRINTED |
| E092 | `TYP_CANNOT_DELETE` | % CANNOT BE DELETED |
| E093 | `TYP_NOT_CLASS` | % IS NOT A CLASS |
| E094 | `TYP_ARRAY_SIGNATURE` | ARRAYS CANNOT BE PARAMETERS OR RESULTS |
| E095 | `TYP_ARRAY_LENGTH` | ARRAY LENGTH MUST NOT BE ZERO |

## Access (E120-E129)

@@ -92,6 +106,7 @@ After 20 errors the next one is replaced by E221 and compilation stops.
| E150 | `SEM_MISSING_RETURN` | MISSING RETURN VALUE |
| E151 | `SEM_LIFECYCLE_RETURN` | % CANNOT RETURN A VALUE |
| E152 | `SEM_LIFECYCLE_PARAMS` | % TAKES NO PARAMETERS |
| E153 | `SEM_ARG_COUNT` | % TAKES % ARGUMENTS |

## Disk (E200-E219)

@@ -110,3 +125,4 @@ After 20 errors the next one is replaced by E221 and compilation stops.
| E223 | `RES_TOO_MANY_MEMBERS` | MORE THAN 255 MEMBERS |
| E224 | `RES_TOO_MANY_PARAMS` | MORE THAN 255 PARAMETERS |
| E225 | `RES_EXPR_TOO_DEEP` | EXPRESSION NESTED TOO DEEPLY |
| E226 | `RES_TOO_MANY_IMPLEMENTS` | MORE THAN 255 IMPLEMENTED INTERFACES |

+ 109
- 0
newLang/planning/claims/E03-S04.md Просмотреть файл

@@ -0,0 +1,109 @@
---
story: E03-S04
owner: e03_types
status: done
started: 2026-11-08
depends-on: [E03-S03]
---

# E03-S04 Claim

## Intended change

Give every expression a type and check it in pass 2. A new module
`src/compiler/types.s` holds the type registers, assignability (spec 3.4),
operator typing (spec 8), numeric-literal constants with folding, and the
spelling of types in diagnostics. The parser records each class's
`implements` list and `init` row, finds a method's own member row again in
pass 2, carries full types through member chains, groups, calls, indexing,
and operators, and checks assignments, call arguments (count and type),
returns, conditions, `for`, `print`, `delete`, indexing, and member access.
Missing returns are found by tracking whether each block ends in `return`
(an `if` counts only with an `else` whose branches both do).

## File area

- `src/compiler/types.s` (new), `src/compiler/parser.s`,
`src/common/parser.inc`
- `src/common/diagcat.inc` (appended entries), `docs/diagnostics.md`
- `tests/semantic/` (new fixtures), `tests/parser/` (harness, expectations
that gain true type diagnostics, `forms.n8`)
- `scripts/build-parser-test.ps1`, `scripts/verify-parser-test.ps1`,
`scripts/demo-write-check.ps1` (links the parser)
- `docs/architecture.md`, `docs/decisions.md`
- `tests/lexer/reference-lexer.ps1` (host spelling fix found by the
regression run)
- `planning/claims/E03-S04.md`
- generated files under `build/`

## Acceptance evidence

- [x] Represents primitives, arrays, class references, interface references,
and null: `types_ok.n8` passes with five top-level types and eleven
members; its member table matches the source independently. `convert.n8`
and `refs.n8` cover every illegal conversion, arrays included.
- [x] Implements exactly the specified implicit conversions: `convert.n8`
matches all 19 unsafe primitive assignments and constant misfits;
`refs.n8` matches all 13 illegal reference and array cases, including
class-to-interface, unrelated references, and identity rules.
- [x] Checks calls, assignments, returns, conditions, indexing, and
operators: `calls.n8` (16 diagnostics), `uses.n8` (18), `ops.n8` (16),
`convert.n8` (19), and `refs.n8` (13) match their goldens; `types_ok.n8`
exercises the accepted forms without a diagnostic.
- [x] Reports incompatible interface assignments and missing returns:
`refs.n8` expects E080 for every unrelated reference; `calls.n8` expects
E150 at three `end method` sites (plain body, `if` without `else`,
`while` only) and E151 for a program `return` value.

## Coordination notes

- No other claim is active. D124-D127 are recorded as proposed: expression
typing and constant folding, array indexing, arrays as fields/locals only,
and `return` in a program plus the missing-return rule.

## Work log

- `types.s` holds the `sc_*`/`tt_*` type registers, `ty_assign`,
`ty_binary`, `ty_unary`, constant folding (16x16 multiply, restoring
divide), and the diagnostic spellings (BYTE, WORD[4], a class name, a
constant, NO VALUE).
- `parser.s` records `implements` lists and each class's `init` row in pass 1,
then in pass 2 re-finds the current member row, carries types through
member chains, groups, calls, and indexing, and checks every place a value
is used. Missing returns are reported at the `end` token (E150).
- The parser test harness dumps types, constant values, and members; the
semantic fixtures are compared against golden ERR lines by
`verify-parser-test.ps1`.
- Three older fixtures legitimately gained diagnostics: `unknown.n8` (E150
for an empty result method), `recover.n8` (E081 for `while x`/`if x` on a
word), and `syntax.n8` (E151 for `return a` in a program). Their
expectations were updated; `recover.n8` records the conditions in the
order the blocks close.
- The lexer golden check exposed a host-only bug: `Format-PetsciiText` used
`"{{{0:X2}}}"`, which Windows PowerShell renders as `{X2}`. It now builds
the braces explicitly and matches the goldens (`{0D}`, `{22}`, `{5C}`).
- `verify-parser-test.ps1` uses port 6535; on this machine the first run's
monitor reply was empty. Port 6530 works and is the port the other suites
already use; the final runs used `-Port 6530`.
- The demo's ERR read ran under `$ErrorActionPreference = "Stop"`, so a
missing `good.err` (the success case) made `c1541`'s stderr terminate the
script. It now checks the disk listing first and redirects stderr.

## Completion

- Tests run: `scripts/build-parser-test.ps1`,
`scripts/verify-parser-test.ps1 -Port 6530` (37 fixtures),
`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`
(`good.n8` and `bad.n8`).
- Results: all 37 parser fixtures pass (lowest expression SP `$A7`); the
lexer matches 6,733 bytes over 10 fixtures; the 67-entry diagnostic catalog
matches its screen/ERR expectations and `docs/diagnostics.md` is in sync;
the demo saves both programs from Vim byte-for-byte, `good.n8` reports no
errors, and `bad.n8` reports E151, E152, E120, and E061.
- Files changed: as listed under File area.
- Remaining risks: D124-D127 await approval; a change to D124 means
revisiting `types.s` and the semantic goldens. The type registers are
statically allocated, so nested type checks reuse them deliberately.
`for` step typing follows the same operator table.

+ 10
- 2
newLang/scripts/build-parser-test.ps1 Просмотреть файл

@@ -1,5 +1,5 @@
# E03-S01..E03-S03: build the parser test PRG and a fresh D81 holding every grammar and
# parser fixture. The fixture order must match tests/parser/parser-test.s.
# E03-S01..E03-S04: build the parser test PRG and a fresh D81 holding every grammar,
# parser, and semantic fixture. The fixture order must match tests/parser/parser-test.s.
[CmdletBinding()]
param()

@@ -11,6 +11,7 @@ New-Item -ItemType Directory -Force -Path $out | Out-Null

$g = Join-Path $project "tests\grammar"
$p = Join-Path $project "tests\parser"
$t = Join-Path $project "tests\semantic"
# disk name -> @(source, group, expect file)
$script:ParserFixtures = [ordered]@{
"minimal.n8" = @("$g\accepted\minimal.n8", "accepted", $null)
@@ -44,6 +45,12 @@ $script:ParserFixtures = [ordered]@{
"prec.n8" = @("$p\prec.n8", "accepted", "$p\prec.expect")
"forms.n8" = @("$p\forms.n8", "accepted", $null)
"syntax.n8" = @("$p\syntax.n8", "exact", "$p\syntax.expect")
"typesok.n8" = @("$t\types_ok.n8", "accepted", $null)
"convert.n8" = @("$t\convert.n8", "exact", "$t\convert.expect")
"refs.n8" = @("$t\refs.n8", "exact", "$t\refs.expect")
"ops.n8" = @("$t\ops.n8", "exact", "$t\ops.expect")
"uses.n8" = @("$t\uses.n8", "exact", "$t\uses.expect")
"calls.n8" = @("$t\calls.n8", "exact", "$t\calls.expect")
}

$link = Invoke-Nova128Link -Name "parser-test" -BuildDir $out `
@@ -55,6 +62,7 @@ $link = Invoke-Nova128Link -Name "parser-test" -BuildDir $out `
(Join-Path $project "src\compiler\names.s"),
(Join-Path $project "src\compiler\diagnostics.s"),
(Join-Path $project "src\compiler\parser.s"),
(Join-Path $project "src\compiler\types.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" }



+ 5
- 1
newLang/scripts/demo-write-check.ps1 Просмотреть файл

@@ -39,6 +39,7 @@ $link = Invoke-Nova128Link -Name "n8check" -BuildDir $out `
(Join-Path $project "src\compiler\names.s"),
(Join-Path $project "src\compiler\diagnostics.s"),
(Join-Path $project "src\compiler\parser.s"),
(Join-Path $project "src\compiler\types.s"),
(Join-Path $project "demo\n8check.s"))

# A fresh copy: the Vim work disk itself is never written.
@@ -124,7 +125,10 @@ foreach ($name in $Programs) {
# 4. results
$err = Join-Path $out "$base.err.txt"
if (Test-Path $err) { Remove-Item $err -Force }
& $c1541 $disk -read "$base.err,s" $err 2>&1 | Out-Null
$listing = (& $c1541 -attach $disk -list 2>$null) | Out-String
if ($listing -match ('"' + [regex]::Escape("$base.err") + '"')) {
& $c1541 $disk -read "$base.err,s" $err 2>$null | Out-Null
}
if (Test-Path $err) {
Write-Host " $base.err:"
foreach ($line in ([Text.Encoding]::ASCII.GetString([IO.File]::ReadAllBytes($err)).TrimEnd("`r") -split "`r")) {


+ 21
- 6
newLang/scripts/verify-parser-test.ps1 Просмотреть файл

@@ -159,11 +159,16 @@ function Get-ErrLines([string]$base) {
if ($listing -notmatch ('"' + [regex]::Escape("$base.err") + '"')) { return $null }
& $c1541 -attach $disk -read "$base.err,s" $dest 2>&1 | Out-Null
# The leading comma keeps a one-line file an array (PowerShell unrolls it).
return ,@(([Text.Encoding]::ASCII.GetString([IO.File]::ReadAllBytes($dest)).TrimEnd("`r")) -split "`r")
# Names keep their source spelling: shifted PETSCII letters ($C1-$DA) are
# capitals; messages are compared case-insensitively.
$text = -join ([IO.File]::ReadAllBytes($dest) | ForEach-Object {
if ($_ -ge 0xC1 -and $_ -le 0xDA) { [char]($_ - 0x80) } else { [char]$_ } })
return ,@(($text.TrimEnd("`r")) -split "`r")
}
function Parse-Err([string]$line) {
$m = [regex]::Match($line, '^E(\d{3}) \S+ (\d+) (\d+) ')
return [pscustomobject]@{ Code = [int]$m.Groups[1].Value; Line = [int]$m.Groups[2].Value; Col = [int]$m.Groups[3].Value }
$m = [regex]::Match($line, '^E(\d{3}) \S+ (\d+) (\d+) (.*)$')
return [pscustomobject]@{ Code = [int]$m.Groups[1].Value; Line = [int]$m.Groups[2].Value
Col = [int]$m.Groups[3].Value; Message = $m.Groups[4].Value }
}

# ---------------------------------------------------------------- check
@@ -207,11 +212,21 @@ foreach ($name in $ParserFixtures.Keys) {
$detail = if ($errLines) { $errLines[0] } else { "" }
}
"exact" {
$want = @(Get-Content -LiteralPath $expect | Where-Object { $_ -and -not $_.StartsWith("//") } | ForEach-Object {
$f = $_ -split ' '; "{0} {1} {2}" -f $codes[$f[0]], $f[1], $f[2] })
$got = @($errLines | Where-Object { $_ } | ForEach-Object { $e = Parse-Err $_; "{0} {1} {2}" -f $e.Code, $e.Line, $e.Col })
# SYMBOL line column [message]; a message is compared when given.
$rows = @(Get-Content -LiteralPath $expect | Where-Object { $_ -and -not $_.StartsWith("//") } | ForEach-Object {
$f = $_ -split ' ', 4
[pscustomobject]@{ Key = "{0} {1} {2}" -f $codes[$f[0]], $f[1], $f[2]; Message = $f[3] } })
$want = @($rows | ForEach-Object { $_.Key })
$errs = @($errLines | Where-Object { $_ } | ForEach-Object { Parse-Err $_ })
$got = @($errs | ForEach-Object { "{0} {1} {2}" -f $_.Code, $_.Line, $_.Col })
if (($got -join ';') -ne ($want -join ';')) {
$problems += "diagnostics [$($got -join '; ')] expected [$($want -join '; ')]"
} else {
for ($k = 0; $k -lt $rows.Count; $k++) {
if ($rows[$k].Message -and $rows[$k].Message.ToUpper() -ne $errs[$k].Message.ToUpper()) {
$problems += "E$($errs[$k].Code) at $($errs[$k].Line):$($errs[$k].Col) says '$($errs[$k].Message)', expected '$($rows[$k].Message)'"
}
}
}
$detail = "{0} diagnostics" -f $got.Count
}


+ 16
- 0
newLang/src/common/diagcat.inc Просмотреть файл

@@ -60,6 +60,20 @@
; ------------------------------------------------------------------ types
DIAGNOSTIC TYP_MISMATCH, 80, "% EXPECTED; % FOUND"
DIAGNOSTIC TYP_CONDITION_NOT_BOOLEAN, 81, "CONDITION MUST BE BOOLEAN"
DIAGNOSTIC TYP_BAD_OPERAND, 82, "% NOT ALLOWED ON %"
DIAGNOSTIC TYP_OPERANDS_DIFFER, 83, "% AND % DO NOT MIX"
DIAGNOSTIC TYP_NUMBER_RANGE, 84, "NUMBER OUT OF RANGE"
DIAGNOSTIC TYP_NUMBER_DOES_NOT_FIT, 85, "% DOES NOT FIT %"
DIAGNOSTIC TYP_DIVIDE_BY_ZERO, 86, "DIVISION BY ZERO"
DIAGNOSTIC TYP_NOT_ARRAY, 87, "% IS NOT AN ARRAY"
DIAGNOSTIC TYP_ARRAY_NEEDS_INDEX, 88, "% NEEDS AN INDEX"
DIAGNOSTIC TYP_INDEX_RANGE, 89, "INDEX OUT OF RANGE"
DIAGNOSTIC TYP_NO_MEMBERS, 90, "% HAS NO MEMBERS"
DIAGNOSTIC TYP_CANNOT_PRINT, 91, "% CANNOT BE PRINTED"
DIAGNOSTIC TYP_CANNOT_DELETE, 92, "% CANNOT BE DELETED"
DIAGNOSTIC TYP_NOT_CLASS, 93, "% IS NOT A CLASS"
DIAGNOSTIC TYP_ARRAY_SIGNATURE, 94, "ARRAYS CANNOT BE PARAMETERS OR RESULTS"
DIAGNOSTIC TYP_ARRAY_LENGTH, 95, "ARRAY LENGTH MUST NOT BE ZERO"

; ----------------------------------------------------------------- access
DIAGNOSTIC ACC_PRIVATE_MEMBER, 120, "% IS PRIVATE"
@@ -72,6 +86,7 @@
DIAGNOSTIC SEM_MISSING_RETURN, 150, "MISSING RETURN VALUE"
DIAGNOSTIC SEM_LIFECYCLE_RETURN, 151, "% CANNOT RETURN A VALUE"
DIAGNOSTIC SEM_LIFECYCLE_PARAMS, 152, "% TAKES NO PARAMETERS"
DIAGNOSTIC SEM_ARG_COUNT, 153, "% TAKES % ARGUMENTS"

; ------------------------------------------------------------------- disk
DIAGNOSTIC DSK_SOURCE_UNREADABLE, 200, "CANNOT READ SOURCE; DOS %"
@@ -84,3 +99,4 @@
DIAGNOSTIC RES_TOO_MANY_MEMBERS, 223, "MORE THAN 255 MEMBERS"
DIAGNOSTIC RES_TOO_MANY_PARAMS, 224, "MORE THAN 255 PARAMETERS"
DIAGNOSTIC RES_EXPR_TOO_DEEP, 225, "EXPRESSION NESTED TOO DEEPLY"
DIAGNOSTIC RES_TOO_MANY_IMPLEMENTS, 226, "MORE THAN 255 IMPLEMENTED INTERFACES"

+ 33
- 19
newLang/src/common/parser.inc Просмотреть файл

@@ -1,4 +1,4 @@
; parser.inc - parser tables and limits (E03-S01, E03-S02).
; parser.inc - parser tables and limits (E03-S01..E03-S04).

.ifndef PARSER_INC
PARSER_INC = 1
@@ -23,17 +23,25 @@ TR_NEG = $F0 ; unary -
TR_NOT = $F1 ; unary not
TR_LINE = $FF ; statement start, then line lo/hi

; Declared-type descriptor (provisional until E03-S04): base, name, length.
; base TY_*; TY_NONE for a method without a result
; A type is base, name, length (E03-S04, types.s). Declared types use the
; first four bytes; an expression's type adds a constant's value and sign.
; base TY_*
; name class/interface name ID when base = TY_REF, else 0
; length array length (2 bytes); 0 = not an array
TY_NONE = 0
TY_BOOLEAN = 1
; value TY_CONST only: magnitude 0..65535 (2 bytes)
; neg TY_CONST only: 1 = negative (magnitude 1..32768)
TY_NONE = 0 ; declared: no result; expression: unknown
TY_BOOLEAN = 1 ; (an error was already reported: no checks)
TY_BYTE = 2
TY_CHAR = 3
TY_INTEGER = 4
TY_WORD = 5
TY_REF = 6
TY_NULL = 7 ; expressions only: the null literal
TY_STRING = 8 ; a string literal (print only)
TY_CONST = 9 ; a number literal or constant expression
TY_VOID = 10 ; the result of a method without `as type`
TV_SIZE = 7 ; bytes in a type value (sc_*, tt_*)

; Member flags
MF_METHOD = $01 ; clear = field
@@ -41,6 +49,7 @@ MF_PRIVATE = $02 ; clear = public

MEMBERS_MAX = 255 ; rows 0..254, all classes and interfaces
PARAMS_MAX = 255 ; rows 0..254, all method signatures
IMPLS_MAX = 255 ; rows 0..254, all implements lists

; Bank-1 parser tables: 256-byte columns, consecutive from type_kind.
; tb_get/tb_set take A = column and Y = row (a name ID for type columns).
@@ -49,19 +58,24 @@ C_TYPE_LINE_LO = 1 ; declaration line
C_TYPE_LINE_HI = 2
C_TYPE_MFIRST = 3 ; first member row
C_TYPE_MCOUNT = 4 ; member rows (contiguous, declaration order)
C_MEM_OWNER = 5 ; owning class/interface name ID
C_MEM_NAME = 6 ; member name ID
C_MEM_FLAGS = 7 ; MF_*
C_MEM_TBASE = 8 ; field type / method result type
C_MEM_TNAME = 9
C_MEM_TLEN_LO = 10
C_MEM_TLEN_HI = 11
C_MEM_PFIRST = 12 ; first parameter row
C_MEM_PCOUNT = 13 ; parameter rows (declaration order)
C_PAR_TBASE = 14 ; parameter type
C_PAR_TNAME = 15
C_PAR_TLEN_LO = 16
C_PAR_TLEN_HI = 17
TABLE_COLUMNS = 18
C_TYPE_IFIRST = 5 ; class: first implements row
C_TYPE_ICOUNT = 6 ; class: implements rows (source order)
C_TYPE_INIT = 7 ; class: init member row + 1, 0 = none
C_MEM_OWNER = 8 ; owning class/interface name ID
C_MEM_NAME = 9 ; member name ID
C_MEM_FLAGS = 10 ; MF_*
C_MEM_TBASE = 11 ; field type / method result type
C_MEM_TNAME = 12
C_MEM_TLEN_LO = 13
C_MEM_TLEN_HI = 14
C_MEM_PFIRST = 15 ; first parameter row
C_MEM_PCOUNT = 16 ; parameter rows (declaration order)
C_PAR_TBASE = 17 ; parameter type
C_PAR_TNAME = 18
C_PAR_TLEN_LO = 19
C_PAR_TLEN_HI = 20
C_IMPL_IFACE = 21 ; implemented interface name ID
TABLE_COLUMNS = 22
C_TYPE_LAST = C_TYPE_INIT ; type columns 0..this are cleared per run

.endif

+ 720
- 141
newLang/src/compiler/parser.s
Разница между файлами не показана из-за своего большого размера
Просмотреть файл


+ 853
- 0
newLang/src/compiler/types.s Просмотреть файл

@@ -0,0 +1,853 @@
; types.s - expression types, conversions, operators, and constants (E03-S04).
;
; Two type registers hold a type value each (parser.inc, TV_SIZE bytes):
; sc_* the value just parsed (the source of a conversion, the right
; operand, the operand of a unary operator, the result)
; tt_* the target of a conversion or the left operand
; They are contiguous, sc first, so a routine can address either with X = 0
; (sc) or X = TV_SIZE (tt).
;
; Rules (spec 3.4 and 8, decisions D124-D126):
; ty_assign may sc be stored in a place of type tt? byte widens to word
; and integer; a constant fits the target's range; null goes
; to any reference; a class goes to an interface it
; implements; nothing else converts. Arrays are not values.
; ty_binary sc = tt <op> sc. and/or take booleans. + - * / take numbers
; and yield the operand type after widening (a constant adopts
; the other operand's type; two constants fold). < <= > >=
; take numbers or chars, = <> also booleans and references
; (identity: same type, null, or a class and an interface it
; implements); they yield boolean.
; ty_unary not takes a boolean; - takes integer, byte (giving integer),
; or a constant (folded).
; Each returns A = 0 (Z=1) or an E_ code (Z=0) whose arguments have been
; spelled into diag_arg1/diag_arg2. TY_NONE and references to undeclared
; types are "vague": an error was already reported, so nothing is checked.
; For E_TYP_BAD_OPERAND only argument 2 (the type) is set; the caller
; supplies argument 1 (the operator). After an operator error sc is TY_NONE.
;
; Constants: magnitude 0..65535 plus a sign; negative magnitudes are 1..32768.
; A folded result outside -32768..65535 is E_TYP_NUMBER_RANGE.

.setcpu "6502"
.include "../common/tokens.inc"
.include "../common/errors.inc"
.include "../common/parser.inc"

.import tb_get
.import name_get, name_out, name_out_len
.import diag_arg1, diag_arg1_len, diag_arg2, diag_arg2_len

.export sc_base, sc_name, sc_len, sc_val, sc_neg
.export tt_base, tt_name, tt_len, tt_val, tt_neg
.export ty_assign, ty_binary, ty_unary
.export ty_arg_sc1, ty_arg_sc2, ty_arg_tt1, ty_arg_name1, ty_arg_num2

ARG_MAX = 16 ; spelled arguments (diagnostics rule)

; operand classes (bit masks)
CL_BAD = $00
CL_NUM = $01
CL_BOOL = $02
CL_CHAR = $04
CL_REF = $08

; operator groups
G_LOGIC = 0
G_ARITH = 1
G_ORDER = 2
G_EQUAL = 3

; offsets within a type value
V_BASE = 0
V_NAME = 1
V_LEN = 2
V_VAL = 4
V_NEG = 6

.segment "CODE"

; ------------------------------------------------------------ assignment
ty_assign:
ldx #$00
jsr vague
bcs @ok
ldx #TV_SIZE
jsr vague
bcs @ok
lda sc_len
ora sc_len+1
beq :+
lda #E_TYP_ARRAY_NEEDS_INDEX
jmp err_sc1
: lda tt_len ; an array place takes no value
ora tt_len+1
bne @bad
lda sc_base
cmp #TY_CONST
bne @typed
lda tt_base
jsr is_numeric
bcc @bad
ldx #$00
lda tt_base
jsr fits
bcs @ok
lda #E_TYP_NUMBER_DOES_NOT_FIT
jmp err_sc1_tt2
@typed: cmp tt_base
bne @diff
cmp #TY_REF
bne @ok
lda sc_name
cmp tt_name
beq @ok
lda sc_name ; class -> implemented interface
sta cv_class
lda tt_name
sta cv_iface
jsr converts
bcs @ok
bcc @bad
@diff: cmp #TY_BYTE ; byte widens to word and integer
bne @null
lda tt_base
cmp #TY_WORD
beq @ok
cmp #TY_INTEGER
beq @ok
bne @bad
@null: cmp #TY_NULL
bne @bad
lda tt_base
cmp #TY_REF
beq @ok
@bad: lda #E_TYP_MISMATCH
jmp err_tt1_sc2
@ok: lda #$00
rts

; ------------------------------------------------------------- operators
; A = operator token; tt = left operand, sc = right operand.
ty_binary:
sta op
ldx #$00
jsr vague
bcs @vague
ldx #TV_SIZE
jsr vague
bcc :+
@vague: jmp to_none_ok
: ldx #G_LOGIC ; group and the operand classes it takes
lda op
cmp #KW_AND
beq @group
cmp #KW_OR
beq @group
ldx #G_EQUAL
cmp #TOK_EQ
beq @group
cmp #TOK_NE
beq @group
ldx #G_ARITH
cmp #TOK_PLUS
bcc @order
cmp #TOK_SLASH + 1
bcc @group
@order: ldx #G_ORDER
@group: stx group
lda group_allow,x
sta allow
ldx #TV_SIZE ; the left operand first
jsr opclass
sta lcls
and allow
bne :+
jsr ty_arg_tt2
jmp bad_operand
: ldx #$00
jsr opclass
sta rcls
and allow
bne :+
jsr ty_arg_sc2
jmp bad_operand
: lda lcls
cmp rcls
bne @mix
cmp #CL_NUM
beq @num
cmp #CL_REF
beq @refs
jmp to_boolean ; two booleans or two chars
@num: jsr unify ; sc = the operand type, or an error
bne @err
lda group
cmp #G_ARITH
bne to_boolean
lda sc_base
cmp #TY_CONST
bne @done
lda tt_base
cmp #TY_CONST
bne @done
jmp fold
@done: lda #$00
rts
@err: jmp to_none
@refs: lda tt_base ; identity comparison
cmp #TY_NULL
beq to_boolean
lda sc_base
cmp #TY_NULL
beq to_boolean
lda tt_name
cmp sc_name
beq to_boolean
sta cv_class ; left class, right interface
lda sc_name
sta cv_iface
jsr converts
bcs to_boolean
lda sc_name ; right class, left interface
sta cv_class
lda tt_name
sta cv_iface
jsr converts
bcs to_boolean
@mix: lda #E_TYP_OPERANDS_DIFFER
jsr err_tt1_sc2
jmp to_none

to_boolean:
lda #TY_BOOLEAN
sta sc_base
lda #$00
sta sc_name
sta sc_len
sta sc_len+1
rts

bad_operand:
lda #E_TYP_BAD_OPERAND
jmp to_none

; A = operator token; sc = operand.
ty_unary:
sta op
ldx #$00
jsr vague
bcs to_none_ok
lda sc_len
ora sc_len+1
bne @bad
lda sc_base
ldx op
cpx #KW_NOT
bne @neg
cmp #TY_BOOLEAN
beq @ok
bne @bad
@neg: cmp #TY_INTEGER
beq @ok
cmp #TY_BYTE
bne :+
lda #TY_INTEGER ; byte widens to integer for negation
sta sc_base
bne @ok
: cmp #TY_CONST
bne @bad
jsr negate
bcc @ok
lda #E_TYP_NUMBER_RANGE
jmp to_none
@ok: lda #$00
rts
@bad: jsr ty_arg_sc2
jmp bad_operand

; sc = TY_NONE; A = 0 (Z=1) from to_none_ok, else A preserved.
to_none_ok:
lda #$00
to_none:
pha
lda #TY_NONE
sta sc_base
sta sc_name
sta sc_len
sta sc_len+1
pla
rts

; Both operands numeric (tt, sc): sc = the common type. A = 0 or E_ code.
unify: lda tt_base
cmp #TY_CONST
bne @ltyped
lda sc_base
cmp #TY_CONST
beq @ok ; two constants: the caller folds
ldx #TV_SIZE ; the left constant must fit the right type
jsr fits
bcs @ok
lda #E_TYP_NUMBER_DOES_NOT_FIT
jmp err_tt1_sc2
@ltyped:
lda sc_base
cmp #TY_CONST
bne @both
ldx #$00 ; the right constant must fit the left type
lda tt_base
jsr fits
bcc :+
lda tt_base
sta sc_base
bne @ok
: lda #E_TYP_NUMBER_DOES_NOT_FIT
jmp err_sc1_tt2
@both: cmp tt_base
beq @ok
lda tt_base ; byte widens to the other operand's type
cmp #TY_BYTE
beq @ok
lda sc_base
cmp #TY_BYTE
bne @mix
lda tt_base
sta sc_base
@ok: lda #$00
rts
@mix: lda #E_TYP_OPERANDS_DIFFER
jmp err_tt1_sc2

; A = the operand class of the type at X (CL_*).
opclass:
lda tv+V_LEN,x
ora tv+V_LEN+1,x
bne @bad
ldy tv+V_BASE,x
lda base_class,y
rts
@bad: lda #CL_BAD
rts

; C=1 when A is byte, word, or integer.
is_numeric:
cmp #TY_BYTE
beq @yes
cmp #TY_WORD
beq @yes
cmp #TY_INTEGER
beq @yes
clc
rts
@yes: sec
rts

; C=1 when the constant at X fits primitive A (byte, word, or integer).
fits: cmp #TY_BYTE
beq @byte
cmp #TY_WORD
beq @word
lda tv+V_NEG,x ; integer: -32768..32767
bne @yes
lda tv+V_VAL+1,x
cmp #$80
bcc @yes
bcs @no
@byte: lda tv+V_NEG,x
bne @no
lda tv+V_VAL+1,x
bne @no
beq @yes
@word: lda tv+V_NEG,x
bne @no
@yes: sec
rts
@no: clc
rts

; C=1 when the type at X is TY_NONE or names no declared class/interface.
vague: lda tv+V_BASE,x
beq @yes
cmp #TY_REF
bne @no
ldy tv+V_NAME,x
lda #C_TYPE_KIND
jsr tb_get
cmp #TYPE_INTERFACE
beq @no
cmp #TYPE_CLASS
beq @no
@yes: sec
rts
@no: clc
rts

; C=1 when cv_class is a class whose implements list names interface cv_iface.
converts:
ldy cv_iface
lda #C_TYPE_KIND
jsr tb_get
cmp #TYPE_INTERFACE
bne @no
ldy cv_class
lda #C_TYPE_KIND
jsr tb_get
cmp #TYPE_CLASS
bne @no
lda #C_TYPE_ICOUNT
jsr tb_get
beq @no
sta cv_left
lda #C_TYPE_IFIRST
jsr tb_get
tay
@scan: lda #C_IMPL_IFACE
jsr tb_get
cmp cv_iface
beq @yes
iny
dec cv_left
bne @scan
@no: clc
rts
@yes: sec
rts

; ------------------------------------------------------------- constants
; sc = -sc. C=1 when the result is below -32768.
negate: lda sc_val
ora sc_val+1
beq @ok ; -0 = 0
lda sc_neg
bne @flip
lda sc_val+1 ; 1..32768 can become negative
cmp #$80
bcc @flip
bne @range
lda sc_val
bne @range
@flip: lda sc_neg
eor #$01
sta sc_neg
@ok: clc
rts
@range: sec
rts

; sc = tt op sc, both constants. A = 0 or E_ code (sc = TY_NONE).
fold: lda op
cmp #TOK_STAR
bne :+
jmp @mul
: lda op
cmp #TOK_SLASH
bne :+
jmp @div
: ldx #TV_SIZE ; + and - in 24-bit two's complement
ldy #0
jsr to24
ldx #$00
ldy #3
jsr to24
lda op
cmp #TOK_MINUS
beq @sub
clc
lda w24
adc w24+3
sta w24
lda w24+1
adc w24+4
sta w24+1
lda w24+2
adc w24+5
sta w24+2
jmp @back
@sub: sec
lda w24
sbc w24+3
sta w24
lda w24+1
sbc w24+4
sta w24+1
lda w24+2
sbc w24+5
sta w24+2
@back: lda w24+2 ; -32768..65535 back to magnitude and sign
beq @pos
cmp #$FF
bne @range
lda w24+1
bpl @range
sec
lda #$00
sbc w24
sta sc_val
lda #$00
sbc w24+1
sta sc_val+1
lda #$01
sta sc_neg
lda #$00
rts
@pos: lda w24
sta sc_val
lda w24+1
sta sc_val+1
lda #$00
sta sc_neg
rts
@range: lda #E_TYP_NUMBER_RANGE
jmp to_none

@mul: lda tt_val ; 16 x 16 -> 32 unsigned
sta mc
lda tt_val+1
sta mc+1
lda sc_val
sta mr
lda sc_val+1
sta mr+1
lda #$00
sta prod+2
ldx #16
@mloop: lsr mr+1
ror mr
bcc @mshift
tay
clc
lda mc
adc prod+2
sta prod+2
tya
adc mc+1
@mshift:
ror a
ror prod+2
ror prod+1
ror prod
dex
bne @mloop
ora prod+2 ; above 65535
bne @range
lda prod
sta sc_val
lda prod+1
sta sc_val+1
jmp @sign

@div: lda sc_val
ora sc_val+1
bne :+
lda #E_TYP_DIVIDE_BY_ZERO
jmp to_none
: lda tt_val ; 16 / 16 unsigned, quotient in mr
sta mr
lda tt_val+1
sta mr+1
lda #$00
sta prod
sta prod+1
ldx #16
@dloop: asl mr
rol mr+1
rol prod
rol prod+1
sec
lda prod
sbc sc_val
tay
lda prod+1
sbc sc_val+1
bcc @dnext
sta prod+1
sty prod
inc mr
@dnext: dex
bne @dloop
lda mr
sta sc_val
lda mr+1
sta sc_val+1
; sign of a product or quotient; -0 = 0; a negative magnitude <= 32768
@sign: lda tt_neg
eor sc_neg
sta sc_neg
lda sc_val
ora sc_val+1
bne :+
sta sc_neg
: lda sc_neg
beq @fine
lda sc_val+1
cmp #$80
bcc @fine
bne @big
lda sc_val
bne @big
@fine: lda #$00
rts
@big: lda #E_TYP_NUMBER_RANGE
jmp to_none

; w24[Y..Y+2] = the constant at X as a signed 24-bit number.
to24: lda tv+V_VAL,x
sta w24,y
lda tv+V_VAL+1,x
sta w24+1,y
lda #$00
sta w24+2,y
lda tv+V_NEG,x
beq @done
sec
lda #$00
sbc w24,y
sta w24,y
lda #$00
sbc w24+1,y
sta w24+1,y
lda #$00
sbc w24+2,y
sta w24+2,y
@done: rts

; ------------------------------------------------------------- spelling
; Error exits: spell the operands into the arguments, return A = code.
err_sc1:
pha
jsr ty_arg_sc1
pla
rts
err_tt1_sc2:
pha
jsr ty_arg_tt1
jsr ty_arg_sc2
pla
rts
err_sc1_tt2:
pha
jsr ty_arg_sc1
jsr ty_arg_tt2
pla
rts

ty_arg_sc1:
ldx #$00
ldy #$00
beq spell_from
ty_arg_sc2:
ldx #$00
ldy #$01
bne spell_from
ty_arg_tt1:
ldx #TV_SIZE
ldy #$00
beq spell_from
ty_arg_tt2:
ldx #TV_SIZE
ldy #$01
spell_from:
sty sp_which
ldy #$00
: lda tv,x
sta sp_base,y
inx
iny
cpy #TV_SIZE
bne :-
jmp spell

; Argument 1 = the spelling of name ID A.
ty_arg_name1:
sta sp_name
lda #TY_REF
ldy #$00
beq simple
; Argument 2 = the decimal number A.
ty_arg_num2:
sta sp_val
lda #TY_CONST
ldy #$01
simple: sta sp_base
sty sp_which
lda #$00
sta sp_len
sta sp_len+1
sta sp_val+1
sta sp_neg
; fall through

; Spell sp_* into argument buffer sp_which and point the argument at it:
; BYTE, WORD, ... the class/interface name, NULL, STRING, a constant's
; value, NO VALUE; an array adds [length]. Clipped to ARG_MAX bytes.
spell: lda #$00
sta sp_n
lda sp_base
cmp #TY_REF
beq @ref
cmp #TY_CONST
beq @const
tax
lda word_offs,x
tax
@word: lda words,x
beq @array
jsr sp_put
inx
bne @word
@ref: lda sp_name
beq @array
jsr name_get
ldx #$00
: cpx name_out_len
beq @array
lda name_out,x
jsr sp_put
inx
bne :-
@const: lda sp_neg
beq :+
lda #'-'
jsr sp_put
: lda sp_val
ldy sp_val+1
jsr sp_dec
@array: lda sp_len
ora sp_len+1
beq @point
lda #'['
jsr sp_put
lda sp_len
ldy sp_len+1
jsr sp_dec
lda #']'
jsr sp_put
@point: lda sp_which
bne @two
lda #<arg1_buf
sta diag_arg1
lda #>arg1_buf
sta diag_arg1+1
lda sp_n
sta diag_arg1_len
rts
@two: lda #<arg2_buf
sta diag_arg2
lda #>arg2_buf
sta diag_arg2+1
lda sp_n
sta diag_arg2_len
rts

; Append A to the current argument buffer. Preserves X and Y.
sp_put: stx sp_x
sty sp_y
ldy sp_n
cpy #ARG_MAX
bcs @out
ldx sp_which
bne @two
sta arg1_buf,y
jmp @inc
@two: sta arg2_buf,y
@inc: inc sp_n
@out: ldx sp_x
ldy sp_y
rts

; Append the unsigned 16-bit number A (low) / Y (high) in decimal.
sp_dec: sta sp_num
sty sp_num+1
lda #$00
sta sp_started
ldx #4 ; 10000, 1000, 100, 10, 1
@digit: lda #'0'
sta sp_digit
@sub: sec
lda sp_num
sbc pow_lo,x
tay
lda sp_num+1
sbc pow_hi,x
bcc @emit
sta sp_num+1
sty sp_num
inc sp_digit
bne @sub
@emit: lda sp_digit
cpx #$00 ; always the units digit
beq @out
cmp #'0'
bne @out
ldy sp_started
beq @skip
@out: sta sp_started
jsr sp_put
@skip: dex
bpl @digit
rts

.segment "RODATA"
group_allow: ; G_LOGIC, G_ARITH, G_ORDER, G_EQUAL
.byte CL_BOOL, CL_NUM, CL_NUM | CL_CHAR
.byte CL_NUM | CL_CHAR | CL_BOOL | CL_REF
base_class: ; by TY_*
.byte CL_BAD, CL_BOOL, CL_NUM, CL_CHAR, CL_NUM, CL_NUM, CL_REF
.byte CL_REF, CL_BAD, CL_NUM, CL_BAD
word_offs: ; by TY_*: offset into words
.byte w_none - words, w_boolean - words, w_byte - words
.byte w_char - words, w_integer - words, w_word - words
.byte w_none - words, w_null - words, w_string - words
.byte w_none - words, w_void - words
words:
w_none: .byte "?", 0
w_boolean: .byte "BOOLEAN", 0
w_byte: .byte "BYTE", 0
w_char: .byte "CHAR", 0
w_integer: .byte "INTEGER", 0
w_word: .byte "WORD", 0
w_null: .byte "NULL", 0
w_string: .byte "STRING", 0
w_void: .byte "NO VALUE", 0
pow_lo: .byte <1, <10, <100, <1000, <10000
pow_hi: .byte >1, >10, >100, >1000, >10000

.segment "BSS"
tv: ; sc, then tt (TV_SIZE bytes each)
sc_base: .res 1
sc_name: .res 1
sc_len: .res 2
sc_val: .res 2
sc_neg: .res 1
tt_base: .res 1
tt_name: .res 1
tt_len: .res 2
tt_val: .res 2
tt_neg: .res 1
sp_base: .res 1 ; the type being spelled
sp_name: .res 1
sp_len: .res 2
sp_val: .res 2
sp_neg: .res 1
sp_which: .res 1 ; 0 = argument 1, 1 = argument 2
sp_n: .res 1
sp_x: .res 1
sp_y: .res 1
sp_num: .res 2
sp_digit: .res 1
sp_started: .res 1
arg1_buf: .res ARG_MAX
arg2_buf: .res ARG_MAX
op: .res 1
group: .res 1
allow: .res 1
lcls: .res 1
rcls: .res 1
cv_class: .res 1
cv_iface: .res 1
cv_left: .res 1
w24: .res 6
mc: .res 2
mr: .res 2
prod: .res 4

+ 1
- 1
newLang/tests/lexer/reference-lexer.ps1 Просмотреть файл

@@ -52,7 +52,7 @@ function Format-PetsciiText([byte[]]$bytes) {
if ($b -ge 0x41 -and $b -le 0x5A) { [void]$sb.Append([char]($b + 0x20)) } # a-z
elseif ($b -ge 0xC1 -and $b -le 0xDA) { [void]$sb.Append([char]($b - 0x80)) } # A-Z
elseif ($b -ge 0x20 -and $b -le 0x3F -and $b -ne 0x22) { [void]$sb.Append([char]$b) }
else { [void]$sb.Append(("{{{0:X2}}}" -f $b)) }
else { [void]$sb.Append(('{' + $b.ToString("X2") + '}')) }
}
return '"' + $sb.ToString() + '"'
}


+ 5
- 4
newLang/tests/parser/forms.n8 Просмотреть файл

@@ -21,15 +21,16 @@ class Counter implements Source

public method fill(n as word)
var i as word
var k as integer
for i = 0 to 3 step 1
seen[i] = self.next()
end for
for i = 3 to 0 step -1
if seen[i] = 0
for k = 3 to 0 step -1
if seen[k] = 0
return
else
if i > n and not (i = 2)
seen[i] = seen[i - 1]
if k > 1 and not (k = 2)
seen[k] = seen[k - 1]
end if
end if
end for


+ 8
- 2
newLang/tests/parser/parser-test.s Просмотреть файл

@@ -1,4 +1,4 @@
; E03-S01..E03-S03: parser test in x128.
; E03-S01..E03-S04: parser and type-checker test in x128.
;
; For each fixture: src_load, names_init, diag_init (screen off), parse_file,
; diag_finish. Rejected fixtures leave NAME.ERR on the disk for the host to
@@ -419,7 +419,13 @@ fixtures:
FIXTURE "PREC.N8"
FIXTURE "FORMS.N8"
FIXTURE "SYNTAX.N8"
FIXTURE_COUNT = 31
FIXTURE "TYPESOK.N8"
FIXTURE "CONVERT.N8"
FIXTURE "REFS.N8"
FIXTURE "OPS.N8"
FIXTURE "USES.N8"
FIXTURE "CALLS.N8"
FIXTURE_COUNT = 37
TRACE_INDEX = 28 ; PREC.N8

messages:


+ 5
- 1
newLang/tests/parser/recover.expect Просмотреть файл

@@ -1,6 +1,10 @@
// five independent errors; each line after an error still parses
// seven independent errors; each line after an error still parses. The
// duplicate x is a word, so both conditions are type errors too; they are
// checked when their blocks close, after the missing `end if` at line 16.
SYN_EXPECTED_IDENTIFIER 1 10
SYN_EXPECTED_INTERFACE_NAME 5 21
SYN_EXPECTED_IDENTIFIER 7 22
SEM_DUPLICATE_NAME 13 7
SYN_EXPECTED_END_IF 16 7
TYP_CONDITION_NOT_BOOLEAN 14 9
TYP_CONDITION_NOT_BOOLEAN 15 8

+ 3
- 1
newLang/tests/parser/syntax.expect Просмотреть файл

@@ -16,5 +16,7 @@ SYN_UNEXPECTED_TOKEN 20 3
SYN_UNEXPECTED_TOKEN 22 7
LEX_BAD_CHAR_LITERAL 23 7
RES_EXPR_TOO_DEEP 24 23
// pass 2: name errors are still found after all of the above
// pass 2: name errors are still found after all of the above, and a program
// cannot return a value (E151)
SEM_UNKNOWN_NAME 26 3
SEM_LIFECYCLE_RETURN 27 10

+ 3
- 1
newLang/tests/parser/unknown.expect Просмотреть файл

@@ -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).
SEM_UNKNOWN_NAME 1 25
SEM_UNKNOWN_NAME 2 11
SEM_UNKNOWN_NAME 3 28
SEM_MISSING_RETURN 4 3
SEM_UNKNOWN_NAME 13 12

+ 18
- 0
newLang/tests/semantic/calls.expect Просмотреть файл

@@ -0,0 +1,18 @@
// calls, new, and returns. Missing returns are reported at `end method`
// SYMBOL line column message (message compared case-insensitively)
SEM_MISSING_RETURN 20 3 MISSING RETURN VALUE
SEM_MISSING_RETURN 26 3 MISSING RETURN VALUE
SEM_MISSING_RETURN 32 7 MISSING RETURN VALUE
SEM_LIFECYCLE_RETURN 37 12 NONE CANNOT RETURN A VALUE
TYP_MISMATCH 41 12 BOOLEAN EXPECTED; WORD FOUND
SEM_ARG_COUNT 52 16 BOX TAKES 2 ARGUMENTS
SEM_ARG_COUNT 53 21 BOX TAKES 2 ARGUMENTS
TYP_MISMATCH 54 18 BYTE EXPECTED; BOOLEAN FOUND
TYP_NUMBER_DOES_NOT_FIT 55 15 -1 DOES NOT FIT WORD
SEM_ARG_COUNT 56 15 SCALE TAKES 1 ARGUMENTS
SEM_ARG_COUNT 57 18 SCALE TAKES 1 ARGUMENTS
SEM_ARG_COUNT 58 14 AREA TAKES 0 ARGUMENTS
TYP_NOT_CLASS 59 11 SHAPE IS NOT A CLASS
SEM_ARG_COUNT 60 16 BARE TAKES 0 ARGUMENTS
TYP_MISMATCH 60 7 SHAPE EXPECTED; BARE FOUND
SEM_LIFECYCLE_RETURN 61 10 MAIN CANNOT RETURN A VALUE

+ 63
- 0
newLang/tests/semantic/calls.n8 Просмотреть файл

@@ -0,0 +1,63 @@
interface Shape
method area() as word
end interface

class Box implements Shape
private word side

public method init(s as word, t as byte)
side = s + t
end method

public method area() as word
return side
end method

public method scale(f as byte) as word
if f = 0
return 0
end if
end method

public method pick(f as boolean) as word
while f
return 1
end while
end method

public method both(f as boolean) as word
if f
return 1
else
return
end if
end method

public method none()
return 5
end method

public method wrong() as boolean
return side
end method
end class

class Bare
end class

program Main
var b as Box
var s as Shape
var w as word
b = new Box(1)
b = new Box(1, 2, 3, 4)
b = new Box(1, true)
b = new Box(-1, 2)
w = b.scale()
w = b.scale(1, 2)
w = s.area(5)
s = new Shape()
s = new Bare(1)
return 1
return
end program

+ 21
- 0
newLang/tests/semantic/convert.expect Просмотреть файл

@@ -0,0 +1,21 @@
// every illegal primitive conversion and constant: one per line
// SYMBOL line column message (message compared case-insensitively)
TYP_MISMATCH 7 7 INTEGER EXPECTED; WORD FOUND
TYP_MISMATCH 8 7 WORD EXPECTED; INTEGER FOUND
TYP_MISMATCH 9 7 BYTE EXPECTED; WORD FOUND
TYP_MISMATCH 10 7 BYTE EXPECTED; INTEGER FOUND
TYP_MISMATCH 11 7 WORD EXPECTED; BOOLEAN FOUND
TYP_MISMATCH 12 8 BOOLEAN EXPECTED; BYTE FOUND
TYP_MISMATCH 13 7 BYTE EXPECTED; CHAR FOUND
TYP_MISMATCH 14 7 CHAR EXPECTED; BYTE FOUND
TYP_MISMATCH 15 7 CHAR EXPECTED; 65 FOUND
TYP_NUMBER_DOES_NOT_FIT 16 7 256 DOES NOT FIT BYTE
TYP_NUMBER_DOES_NOT_FIT 17 7 -1 DOES NOT FIT WORD
TYP_NUMBER_DOES_NOT_FIT 18 7 32768 DOES NOT FIT INTEGER
TYP_NUMBER_RANGE 19 7 NUMBER OUT OF RANGE
TYP_MISMATCH 20 7 WORD EXPECTED; STRING FOUND
TYP_NUMBER_DOES_NOT_FIT 21 7 300 DOES NOT FIT BYTE
TYP_NUMBER_RANGE 22 11 NUMBER OUT OF RANGE
TYP_DIVIDE_BY_ZERO 23 9 DIVISION BY ZERO
TYP_NUMBER_RANGE 24 9 NUMBER OUT OF RANGE
TYP_NUMBER_DOES_NOT_FIT 25 7 -1 DOES NOT FIT WORD

+ 26
- 0
newLang/tests/semantic/convert.n8 Просмотреть файл

@@ -0,0 +1,26 @@
program Main
var x as byte
var w as word
var i as integer
var c as char
var ok as boolean
i = w
w = i
x = w
x = i
w = ok
ok = x
x = c
c = x
c = 65
x = 256
w = -1
i = 32768
i = -32769
w = "text"
x = 200 + 100
w = 300 * 300
w = 1 / 0
i = 0 - 32769
w = 1 - 2
end program

+ 19
- 0
newLang/tests/semantic/ops.expect Просмотреть файл

@@ -0,0 +1,19 @@
// operator operands, reported at the operator; line 27: the error in the
// group is not repeated for the outer *
// SYMBOL line column message (message compared case-insensitively)
TYP_BAD_OPERAND 13 9 + NOT ALLOWED ON BOOLEAN
TYP_BAD_OPERAND 14 11 + NOT ALLOWED ON BOOLEAN
TYP_BAD_OPERAND 15 10 AND NOT ALLOWED ON WORD
TYP_BAD_OPERAND 16 8 NOT NOT ALLOWED ON WORD
TYP_BAD_OPERAND 17 7 - NOT ALLOWED ON WORD
TYP_OPERANDS_DIFFER 18 9 WORD AND INTEGER DO NOT MIX
TYP_OPERANDS_DIFFER 19 10 CHAR AND BYTE DO NOT MIX
TYP_BAD_OPERAND 20 11 < NOT ALLOWED ON BOOLEAN
TYP_BAD_OPERAND 21 11 + NOT ALLOWED ON STRING
TYP_BAD_OPERAND 22 9 + NOT ALLOWED ON BOX
TYP_OPERANDS_DIFFER 23 10 BOX AND 5 DO NOT MIX
TYP_BAD_OPERAND 24 11 + NOT ALLOWED ON BYTE[3]
TYP_NUMBER_DOES_NOT_FIT 25 9 300 DOES NOT FIT BYTE
TYP_OPERANDS_DIFFER 26 10 5 AND CHAR DO NOT MIX
TYP_BAD_OPERAND 27 10 + NOT ALLOWED ON BOOLEAN
TYP_BAD_OPERAND 28 8 - NOT ALLOWED ON BOOLEAN

+ 29
- 0
newLang/tests/semantic/ops.n8 Просмотреть файл

@@ -0,0 +1,29 @@
class Box
public word size
end class

program Main
var x as byte
var w as word
var i as integer
var c as char
var ok as boolean
var b as Box
var arr as byte[3]
w = w + ok
ok = ok + ok
ok = w and ok
ok = not w
w = -w
i = w + i
ok = c < x
ok = ok < ok
w = "a" + 1
w = b + 1
ok = b = 5
w = arr + 1
x = x + 300
ok = 5 = c
w = (w + ok) * 2
ok = - ok
end program

+ 16
- 0
newLang/tests/semantic/refs.expect Просмотреть файл

@@ -0,0 +1,16 @@
// references: only null, the same type, or a class to an interface it
// implements convert; arrays are not values; identity needs related types
// SYMBOL line column message (message compared case-insensitively)
TYP_MISMATCH 29 8 BOOLEAN EXPECTED; NULL FOUND
TYP_MISMATCH 30 7 BOX EXPECTED; SHAPE FOUND
TYP_MISMATCH 31 7 NAMED EXPECTED; SHAPE FOUND
TYP_MISMATCH 32 7 NAMED EXPECTED; BOX FOUND
TYP_MISMATCH 33 7 CAN EXPECTED; BOX FOUND
TYP_MISMATCH 34 7 SHAPE EXPECTED; CAN FOUND
TYP_MISMATCH 35 9 WORD[4] EXPECTED; 5 FOUND
TYP_ARRAY_NEEDS_INDEX 36 7 WORD[4] NEEDS AN INDEX
TYP_ARRAY_NEEDS_INDEX 37 9 WORD[4] NEEDS AN INDEX
TYP_MISMATCH 38 7 WORD EXPECTED; NO VALUE FOUND
TYP_OPERANDS_DIFFER 42 10 BOX AND CAN DO NOT MIX
TYP_OPERANDS_DIFFER 43 10 SHAPE AND NAMED DO NOT MIX
TYP_OPERANDS_DIFFER 44 10 NAMED AND BOX DO NOT MIX

+ 45
- 0
newLang/tests/semantic/refs.n8 Просмотреть файл

@@ -0,0 +1,45 @@
interface Shape
method area() as word
end interface

interface Named
method id() as byte
end interface

class Box implements Shape
public method area() as word
return 1
end method
public method reset()
end method
end class

class Can
end class

program Main
var w as word
var ok as boolean
var b as Box
var k as Can
var s as Shape
var n as Named
var arr as word[4]
var arr2 as word[4]
ok = null
b = s
n = s
n = b
k = b
s = k
arr = 5
w = arr
arr = arr2
w = b.reset()
b = null
s = b
n = null
ok = b = k
ok = s = n
ok = n = b
end program

+ 115
- 0
newLang/tests/semantic/types_ok.n8 Просмотреть файл

@@ -0,0 +1,115 @@
interface Shape
method area() as word
end interface

interface Named
method id() as byte
end interface

class Box implements Shape, Named
private word side
private byte[3] tags
public Box[2] kids

public method init(s as byte)
var w as word
w = s
self.side = w + s
tags[0] = 255
tags[2] = s + 1
side = (self).side
end method

public method area() as word
return self.side * side
end method

public method id() as byte
return tags[1]
end method

public method same(other as Shape) as boolean
var me as Shape
me = self
return me = other or self = other and not (other = null)
end method

public method sign(n as integer) as char
if n < 0
return '-'
else
if n = 0
return '0'
end if
end if
while n > 100
n = n / 2
end while
return '+'
end method

public method kid(i as integer) as Box
return kids[i]
end method
end class

class Plain
end class

program Main
var b as Box
var s as Shape
var t as Named
var p as Plain
var x as byte
var w as word
var i as integer
var c as char
var ok as boolean
b = new Box(3)
p = new Plain()
s = b
t = b
s = null
s = b.kid(0)
ok = s = b or t <> null and b <> null
x = 255
w = 65535
i = -32768
i = 32767
w = $FFFF
x = %1010
w = (1 + 2) * 3
i = -(5 - 10)
w = 300 * 200
w = 100 / 3
i = -1 - 32767
x = x + 1
w = x + w
i = x + i
i = -x
w = w * 2 + 1000
ok = c < 'z' and c = c and ok = true
ok = w <= 10 or i >= -1
for x = 0 to 9
print x
end for
for i = 3 to -3 step -1
print i
end for
for w = x to 1000 step x
print w
end for
w = b.kids[1].area() + s.area()
x = t.id()
c = b.sign(i)
ok = b.same(b)
print "done"
print c
print ok
print -5
print 60000 / 2
delete b
delete t
delete null
end program

+ 21
- 0
newLang/tests/semantic/uses.expect Просмотреть файл

@@ -0,0 +1,21 @@
// conditions, members, indexing, print, delete, for, and array signatures
// (pass 1); lines 35-36 use the bad signatures without cascades
// SYMBOL line column message (message compared case-insensitively)
TYP_ARRAY_LENGTH 3 16 ARRAY LENGTH MUST NOT BE ZERO
TYP_ARRAY_SIGNATURE 4 27 ARRAYS CANNOT BE PARAMETERS OR RESULTS
TYP_ARRAY_SIGNATURE 6 26 ARRAYS CANNOT BE PARAMETERS OR RESULTS
TYP_CONDITION_NOT_BOOLEAN 16 6 CONDITION MUST BE BOOLEAN
TYP_CONDITION_NOT_BOOLEAN 18 9 CONDITION MUST BE BOOLEAN
TYP_NO_MEMBERS 20 9 5 HAS NO MEMBERS
TYP_NO_MEMBERS 21 9 BYTE HAS NO MEMBERS
TYP_NO_MEMBERS 22 11 BYTE[3] HAS NO MEMBERS
TYP_NOT_ARRAY 23 8 WORD IS NOT AN ARRAY
TYP_MISMATCH 24 11 WORD EXPECTED; BOOLEAN FOUND
TYP_INDEX_RANGE 25 11 INDEX OUT OF RANGE
TYP_NUMBER_DOES_NOT_FIT 26 11 -1 DOES NOT FIT WORD
TYP_CANNOT_PRINT 27 9 BOX CANNOT BE PRINTED
TYP_CANNOT_PRINT 28 9 BYTE[3] CANNOT BE PRINTED
TYP_CANNOT_PRINT 29 9 NULL CANNOT BE PRINTED
TYP_CANNOT_DELETE 30 10 WORD CANNOT BE DELETED
TYP_BAD_OPERAND 31 7 FOR NOT ALLOWED ON BOOLEAN
TYP_NUMBER_DOES_NOT_FIT 33 16 300 DOES NOT FIT BYTE

+ 37
- 0
newLang/tests/semantic/uses.n8 Просмотреть файл

@@ -0,0 +1,37 @@
class Box
public word size
private byte[0] none
public method fill(a as byte[4])
end method
public method get() as word[2]
end method
end class

program Main
var x as byte
var w as word
var ok as boolean
var b as Box
var arr as byte[3]
if w
end if
while 1
end while
x = 5.size
x = x.size
x = arr.size
x = w[0]
x = arr[ok]
x = arr[3]
x = arr[-1]
print b
print arr
print null
delete w
for ok = 1 to 2
end for
for x = 0 to 300
end for
b.fill(arr)
w = b.get()[0]
end program

Загрузка…
Отмена
Сохранить

Powered by TurnKey Linux.