test: prove ordinary import binding modes
This commit is contained in:
@@ -4159,7 +4159,7 @@ Hex encoding keeps arbitrary legal filesystem bytes inside one comment. The
|
|||||||
metadata makes ordinary-to-vendor changes and vendor symlink retargeting
|
metadata makes ordinary-to-vendor changes and vendor symlink retargeting
|
||||||
invalidate the importer even when its source bytes and both already-warm export
|
invalidate the importer even when its source bytes and both already-warm export
|
||||||
bytes happen to match. It contains no dependency body and does not alter source
|
bytes happen to match. It contains no dependency body and does not alter source
|
||||||
positions. The current workdir formats are build 14 and test 13. An equivalent
|
positions. The current workdir formats are build 15 and test 14. An equivalent
|
||||||
warm request remains a package-production no-op; an export change propagates
|
warm request remains a package-production no-op; an export change propagates
|
||||||
only through ordinary direct-export comparison.
|
only through ordinary direct-export comparison.
|
||||||
|
|
||||||
@@ -4509,15 +4509,16 @@ source-file binding is named `wire`. The path leaf `codec` is not installed as
|
|||||||
another qualifier, and a sibling source file receives no `wire` binding unless
|
another qualifier, and a sibling source file receives no `wire` binding unless
|
||||||
that file has its own import.
|
that file has its own import.
|
||||||
|
|
||||||
The implemented representation keeps five facts distinct:
|
The implemented representation keeps six facts distinct:
|
||||||
|
|
||||||
1. source import spelling, including its source file, line, and column;
|
1. source import spelling, including its source file, line, and column;
|
||||||
2. contextually expanded canonical import identity;
|
2. contextually expanded canonical import identity;
|
||||||
3. canonical physical directory;
|
3. canonical physical directory;
|
||||||
4. the one declared package name read from eligible source clauses and `.wwi`
|
4. the one declared package name read from eligible source clauses and `.wwi`
|
||||||
package markers; and
|
package markers;
|
||||||
5. the source-file-local default qualifier binding from that declaration to
|
5. the optional explicit alias written at that import occurrence; and
|
||||||
the canonical target.
|
6. the effective source-file-local qualifier, selected from the explicit alias
|
||||||
|
when present and otherwise from the imported declaration.
|
||||||
|
|
||||||
Both drivers retain one dynamically allocated `sepbind` occurrence for every
|
Both drivers retain one dynamically allocated `sepbind` occurrence for every
|
||||||
real import site. The occurrence stores the source spelling and position plus a
|
real import site. The occurrence stores the source spelling and position plus a
|
||||||
@@ -4541,14 +4542,15 @@ declared name are no longer collapsed into one token.
|
|||||||
`w6c` and `w6c_ww` validate every direct export's leading canonical owner
|
`w6c` and `w6c_ww` validate every direct export's leading canonical owner
|
||||||
against its paired `--import` path. After all direct exports are parsed, they
|
against its paired `--import` path. After all direct exports are parsed, they
|
||||||
build canonical-path-to-declared-name metadata from those interfaces, apply any
|
build canonical-path-to-declared-name metadata from those interfaces, apply any
|
||||||
vendor `--import-map` only to canonical identity, and bind the imported
|
vendor `--import-map` only to canonical identity, and bind each primary source
|
||||||
declaration as the default qualifier of each primary source import. The
|
import through its explicit alias or, when absent, the imported declaration.
|
||||||
checkers and code generators select bindings by source-section ID and canonical
|
The checkers and code generators select bindings by source-section ID and
|
||||||
owner. Qualified and retained bare-import lookups mark only that file's binding
|
canonical owner. Only a qualified lookup through that effective binding marks
|
||||||
used. Two files may consequently bind the same name to different canonical
|
the owning file's occurrence used; imported declarations are never a bare-name
|
||||||
|
fallback. Two files may consequently bind the same name to different canonical
|
||||||
packages, while the graph still contains one edge/action for each target.
|
packages, while the graph still contains one edge/action for each target.
|
||||||
|
|
||||||
Within one file, two imports that produce the same default qualifier are a
|
Within one file, two imports that produce the same effective qualifier are a
|
||||||
redeclared binding; the later unused binding is also reported. An unused import
|
redeclared binding; the later unused binding is also reported. An unused import
|
||||||
is reported at its own import position even if a sibling file uses the same
|
is reported at its own import position even if a sibling file uses the same
|
||||||
qualifier or canonical dependency. A package-scope declaration collides with
|
qualifier or canonical dependency. A package-scope declaration collides with
|
||||||
@@ -4590,30 +4592,32 @@ identities and archive closures. A generated dispatcher privately binds its
|
|||||||
tested target through
|
tested target through
|
||||||
`--test-target-package <canonical-path>` so a command variant declared `main`
|
`--test-target-package <canonical-path>` so a command variant declared `main`
|
||||||
does not collide with the dispatcher's own synthesized `main`; this is
|
does not collide with the dispatcher's own synthesized `main`; this is
|
||||||
compiler-generated wiring, not source alias syntax. Zero-test dispatchers mark
|
compiler-generated wiring distinct from ordinary source aliases. Zero-test
|
||||||
their compiler-owned target/support metadata imports consumed. That private
|
dispatchers mark their compiler-owned target/support metadata imports consumed.
|
||||||
binding is installed or marked consumed only in the generated dispatcher's
|
That private binding is installed or marked consumed only in the generated
|
||||||
source section; an import from an earlier test-file section neither supplies
|
dispatcher's source section; an import from an earlier test-file section
|
||||||
nor satisfies it.
|
neither supplies nor satisfies it.
|
||||||
|
|
||||||
Vendor expansion changes only canonical identity and physical selection. A
|
Vendor expansion changes only canonical identity and physical selection. A
|
||||||
source spelling such as `lib.codec` can resolve to
|
source spelling such as `lib.codec` can resolve to
|
||||||
`domain.app.vendor.lib.codec`, while the vendored package's declaration, for
|
`domain.app.vendor.lib.codec`, while the vendored package's declaration, for
|
||||||
example `package wire`, supplies the file-local qualifier. The driver emits one
|
example `package wire`, supplies the default file-local qualifier. An explicit
|
||||||
sorted semantic `--import-map lib.codec domain.app.vendor.lib.codec` and one
|
alias overrides only that qualifier. The driver emits one sorted semantic
|
||||||
direct export input despite repeated import occurrences in separate files.
|
`--import-map lib.codec domain.app.vendor.lib.codec` and one direct export input
|
||||||
The expanded identity continues to own symbols, `.wwi`, archive, voucher, and
|
despite repeated import occurrences in separate files. The expanded identity
|
||||||
link inputs.
|
continues to own symbols, `.wwi`, archive, voucher, and link inputs.
|
||||||
|
|
||||||
Canonical action interning remains the directory/path/variant model of sections
|
Canonical action interning remains the directory/path/variant model of sections
|
||||||
11.7 and 11.14. Independent file bindings never clone an action, and a declared
|
11.7 and 11.14. Independent file bindings never clone an action, and a declared
|
||||||
name never enters an artifact basename or storage locator. The name is semantic
|
name or source alias never enters an artifact basename or storage locator.
|
||||||
content in the owner unit and export. Changing only a dependency's declaration
|
Changing a dependency's declaration keeps the same action identity and causes
|
||||||
therefore keeps the same action identity but changes its export bytes, causes
|
each direct importer to be reconsidered. A default-bound importer may then fail
|
||||||
each direct importer to be reconsidered, and stops propagation after an
|
because its old qualifier disappeared; an explicitly aliased importer keeps its
|
||||||
importer's regenerated export is unchanged. An identical warm request remains
|
binding, regenerates a canonical semantic export, and stops reverse propagation
|
||||||
a producer no-op. Build workdir format 14 and test format 13 prevent reuse of
|
when those bytes are unchanged. An alias-only source edit rebuilds its owner but
|
||||||
older vouchers that lack these semantics.
|
likewise leaves canonical export identity unchanged. An identical warm request
|
||||||
|
remains a producer no-op. Build workdir format 15 and test format 14 prevent
|
||||||
|
reuse of older vouchers that lack these semantics.
|
||||||
|
|
||||||
Compiler argv still contains exactly the sorted, deduplicated `.wwi` exports of
|
Compiler argv still contains exactly the sorted, deduplicated `.wwi` exports of
|
||||||
direct canonical dependencies; no transitive `.wwi` and no qualifier-derived
|
direct canonical dependencies; no transitive `.wwi` and no qualifier-derived
|
||||||
@@ -4634,10 +4638,11 @@ Responsibility is intentionally split as follows:
|
|||||||
publication. Their storage, diagnostics, call positions, and allocation
|
publication. Their storage, diagnostics, call positions, and allocation
|
||||||
failures are isomorphic.
|
failures are isomorphic.
|
||||||
- `cmd/w6c`/`cmd/wcc` and `selfhost/cmd/w6c`/`selfhost/cmd/wcc` own export
|
- `cmd/w6c`/`cmd/wcc` and `selfhost/cmd/w6c`/`selfhost/cmd/wcc` own export
|
||||||
owner/name reading and writing, file-local binding installation, collision
|
owner/name reading and writing, ordinary/aliased parse facts, file-local
|
||||||
and unused-import diagnostics, name/type lookup, canonical symbol ownership,
|
binding installation, collision and unused-import diagnostics, name/type
|
||||||
and generated-dispatcher private qualification. Cstage and WWstage emit
|
lookup, canonical symbol ownership, and generated-dispatcher private
|
||||||
byte-identical applicable interfaces, assembly, archives, and binaries.
|
qualification. Cstage and WWstage emit byte-identical applicable interfaces,
|
||||||
|
assembly, archives, and binaries.
|
||||||
- `cmd/wwtest` remains only the test-command dispatcher.
|
- `cmd/wwtest` remains only the test-command dispatcher.
|
||||||
|
|
||||||
The behavior follows pinned official Go 1.26.5 source at commit
|
The behavior follows pinned official Go 1.26.5 source at commit
|
||||||
@@ -4684,11 +4689,9 @@ The behavior follows pinned official Go 1.26.5 source at commit
|
|||||||
[lines 628–659](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L659),
|
[lines 628–659](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L659),
|
||||||
[`cmd/go/internal/test/test.go`, lines 1133–1226](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1133-L1226)).
|
[`cmd/go/internal/test/test.go`, lines 1133–1226](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1133-L1226)).
|
||||||
|
|
||||||
The deliberately retained source grammar is `import dotted.path;`. Quoted Go
|
Section 11.19 completes ordinary explicit aliases while retaining dotted,
|
||||||
imports, explicit aliases, dot imports, and blank imports are not implemented.
|
unquoted paths. Grouped, quoted, dot, and blank imports remain deliberately
|
||||||
Because bindings already retain source ownership and position separately from
|
unimplemented.
|
||||||
canonical identity and declared name, adding explicit aliases later will not
|
|
||||||
require another package-wide namespace redesign.
|
|
||||||
|
|
||||||
The focused native observer
|
The focused native observer
|
||||||
`declared_name_identity_and_file_import_scope` generates every tree
|
`declared_name_identity_and_file_import_scope` generates every tree
|
||||||
@@ -4700,6 +4703,204 @@ exports and archive-only links, warm no-op behavior, declared-name invalidation,
|
|||||||
rejection-state preservation, normalized argv, artifact/binary identity,
|
rejection-state preservation, normalized argv, artifact/binary identity,
|
||||||
allocation-bearing runtime behavior, and request/product-order independence.
|
allocation-bearing runtime behavior, and request/product-order independence.
|
||||||
|
|
||||||
|
### 11.19 Implemented ordinary and explicitly aliased file-scoped imports
|
||||||
|
|
||||||
|
WW now implements the two ordinary binding modes for its local dotted import
|
||||||
|
model:
|
||||||
|
|
||||||
|
```ww
|
||||||
|
import acme.codec; // effective qualifier is the declared package name
|
||||||
|
import stable acme.codec; // effective qualifier is exactly stable
|
||||||
|
```
|
||||||
|
|
||||||
|
If canonical package `acme.codec` declares `package wire`, the first form
|
||||||
|
exposes only `wire.Name`; the second exposes only `stable.Name`. Neither form
|
||||||
|
also exposes `codec.Name`, the unused alternative qualifier, or bare `Name`.
|
||||||
|
WW has no dot-import form, so an ordinary import never inserts the dependency's
|
||||||
|
exported declarations into unqualified lookup. Builtins, lexical declarations,
|
||||||
|
and same-package declarations retain ordinary bare lookup.
|
||||||
|
|
||||||
|
This is the dotted-path counterpart of Go's independent local name and quoted
|
||||||
|
path, without adopting quoted paths. The syntax AST stores the optional source
|
||||||
|
alias independently from the original dotted spelling, canonical expanded
|
||||||
|
identity, imported declared name, effective qualifier, owning source section,
|
||||||
|
and source position. `Sym.use_alias` remains the older checker coexistence bit
|
||||||
|
for a declaration that shares a leaf with a package qualifier; it is not the
|
||||||
|
source-language alias fact.
|
||||||
|
|
||||||
|
#### Pinned Go evidence
|
||||||
|
|
||||||
|
The reference is official Go 1.26.5 source at commit
|
||||||
|
`c19862e5f8415b4f24b189d065ed739517c548ba`:
|
||||||
|
|
||||||
|
- `go/ast.ImportSpec` stores `Name` and `Path` independently, and `Pos` selects
|
||||||
|
the alias position when one exists
|
||||||
|
([`go/ast/ast.go`, lines 908–915 and 939–955](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/ast/ast.go#L908-L955)).
|
||||||
|
`parseImportSpec` parses the optional local name separately from the path
|
||||||
|
([`go/parser/parser.go`, lines 2509–2546](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/parser/parser.go#L2509-L2546)).
|
||||||
|
- `go/types` creates one child scope per source file, chooses an explicit alias
|
||||||
|
when present and the imported package's declared name otherwise, inserts one
|
||||||
|
ordinary package-name object, and inserts bare exports only for an explicit
|
||||||
|
dot import
|
||||||
|
([`go/types/resolver.go`, lines 237–350](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L237-L350)).
|
||||||
|
Package/file collisions and unused occurrences are handled at
|
||||||
|
[`resolver.go`, lines 463–480](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L463-L480)
|
||||||
|
and
|
||||||
|
[`resolver.go`, lines 701–735](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L701-L735).
|
||||||
|
The production compiler mirrors those rules at
|
||||||
|
[`cmd/compile/internal/types2/resolver.go`, lines 223–335](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L223-L335),
|
||||||
|
[lines 472–489](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L472-L489),
|
||||||
|
and
|
||||||
|
[lines 706–740](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L706-L740).
|
||||||
|
- Qualified selection marks the exact file-local package-name object used;
|
||||||
|
bare imported declarations are associated only with the dot-import table
|
||||||
|
([`go/types/call.go`, lines 682–693](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/call.go#L682-L693),
|
||||||
|
[`go/types/typexpr.go`, lines 20–31 and 79–86](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/typexpr.go#L20-L86)).
|
||||||
|
Compiler diagnostics are stably sorted by source position before printing
|
||||||
|
([`cmd/compile/internal/base/print.go`, lines 70–92](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/base/print.go#L70-L92)).
|
||||||
|
- Official testdata permits one path under distinct names, including default
|
||||||
|
plus aliases, while keeping every occurrence independently subject to unused
|
||||||
|
checking
|
||||||
|
([`internal/types/testdata/check/importdecl0/importdecl0a.go`, lines 29–52](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/importdecl0/importdecl0a.go#L29-L52),
|
||||||
|
[`test/import.go`, lines 7–23](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/import.go#L7-L23),
|
||||||
|
[`test/import1.go`, lines 7–18](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/import1.go#L7-L18)).
|
||||||
|
- Canonical package path and declared name are independent in
|
||||||
|
[`go/types/package.go`, lines 26–40](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/package.go#L26-L40).
|
||||||
|
Unified export writes and restores canonical path and name independently
|
||||||
|
([`cmd/compile/internal/noder/writer.go`, lines 430–465](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/writer.go#L430-L465),
|
||||||
|
[`cmd/compile/internal/importer/ureader.go`, lines 157–196](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/importer/ureader.go#L157-L196),
|
||||||
|
[`cmd/compile/internal/noder/reader.go`, lines 342–376](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/reader.go#L342-L376)).
|
||||||
|
- `go/build` keeps production, internal-test, and external-test files/imports
|
||||||
|
separate and constructs their source import sets
|
||||||
|
([`go/build/build.go`, lines 436–505](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L436-L505),
|
||||||
|
[lines 939–1040](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L939-L1040),
|
||||||
|
[lines 1061–1063](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L1061-L1063)).
|
||||||
|
`cmd/go` interns by canonical import path, performs internal/vendor checks at
|
||||||
|
every real site, rejects imported `main`, and builds distinct test variants
|
||||||
|
([`cmd/go/internal/load/pkg.go`, lines 633–636](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L633-L636),
|
||||||
|
[lines 757–806](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L757-L806),
|
||||||
|
[lines 2024–2047](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L2024-L2047),
|
||||||
|
[`cmd/go/internal/load/test.go`, lines 175–293](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L175-L293),
|
||||||
|
[lines 421–484](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L421-L484)).
|
||||||
|
- Build actions are keyed by operation and canonical package object, consume
|
||||||
|
canonical direct dependencies, and link the reachable canonical closure
|
||||||
|
([`cmd/go/internal/work/action.go`, lines 202–206](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L202-L206),
|
||||||
|
[lines 437–447](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L437-L447),
|
||||||
|
[lines 628–708](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L708),
|
||||||
|
[lines 919–968](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L919-L968),
|
||||||
|
[lines 1034–1068](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L1034-L1068)).
|
||||||
|
Root construction and test execution retain those canonical production and
|
||||||
|
generated-test objects
|
||||||
|
([`cmd/go/internal/work/build.go`, lines 495–558](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L495-L558),
|
||||||
|
[`cmd/go/internal/test/test.go`, lines 1133–1226 and 1297–1366](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1133-L1366)).
|
||||||
|
|
||||||
|
Go's dot-import branch is negative evidence only: it demonstrates that bare
|
||||||
|
foreign declarations require a distinct explicit mode. WW does not implement
|
||||||
|
that mode.
|
||||||
|
|
||||||
|
#### Scope, duplicate, collision, and usage rules
|
||||||
|
|
||||||
|
Every import occurrence owns its spelling, optional alias, position, source
|
||||||
|
section, and used bit. The effective qualifier is installed only in that
|
||||||
|
section. A qualified type, value, function, def, const, or variable lookup maps
|
||||||
|
the effective qualifier to canonical identity and marks that exact occurrence
|
||||||
|
used. A failed bare lookup marks nothing. A sibling file cannot use or satisfy
|
||||||
|
the occurrence, while two files may independently reuse one alias for different
|
||||||
|
canonical packages.
|
||||||
|
|
||||||
|
In one source file, equal effective qualifiers are duplicate bindings. The
|
||||||
|
later occurrence remains independently unused; at one source position the
|
||||||
|
duplicate diagnostic precedes its unused diagnostic. A later bare undefined
|
||||||
|
name is printed after the earlier unused-import diagnostic. Equal canonical
|
||||||
|
paths are otherwise not a conflict: distinct aliases, or default plus explicit
|
||||||
|
alias, are accepted when their effective names differ and each occurrence is
|
||||||
|
used. A package-scope declaration colliding with a file import is rejected in
|
||||||
|
the existing deterministic reconciliation pass. The unused wording follows
|
||||||
|
Go's leaf comparison: a binding equal to the path leaf says `imported and not
|
||||||
|
used`; any other binding, including an unusual default declared name, says
|
||||||
|
`imported as <name> and not used`.
|
||||||
|
|
||||||
|
`_` is rejected at parse time as `blank import alias _ is not implemented`.
|
||||||
|
Grouped imports, quoted paths, dot imports, and blank imports are deliberately
|
||||||
|
deferred rather than partially implemented.
|
||||||
|
|
||||||
|
#### Graph, export, artifact, and persistence identity
|
||||||
|
|
||||||
|
The imports-only parser and full parser share one import-spec routine and retain
|
||||||
|
alias and dotted path separately. Both drivers sort and resolve occurrences by
|
||||||
|
the dotted spelling, perform contextual internal and nearest-first vendor checks
|
||||||
|
at every real site, and intern the expanded canonical action. `sepbind` and
|
||||||
|
`--import-map` continue to mean source dotted spelling to expanded vendor
|
||||||
|
identity; neither contains the alias. Repeated occurrences remain separate
|
||||||
|
scope facts but form one sorted canonical edge/action.
|
||||||
|
|
||||||
|
The compiler independently reads the direct dependency's declared name. It
|
||||||
|
installs the explicit alias when present or that declared name otherwise, while
|
||||||
|
keeping the canonical owner on declarations, symbols, and code generation. A
|
||||||
|
source alias cannot bypass imported-`main` rejection. Production, internal-test,
|
||||||
|
external-test, support, and generated-main identities remain isolated, and an
|
||||||
|
import found only in a test file reaches only its corresponding test variant.
|
||||||
|
The coordinator remains responsible only for package/test classification and
|
||||||
|
submitting those roots; it does not parse or rewrite imports.
|
||||||
|
|
||||||
|
`.wwi` data never exports a local alias as package identity. Qualified exported
|
||||||
|
type and constant references are normalized to a deterministic compiler-private
|
||||||
|
qualifier `__wwi_` followed by the lowercase hexadecimal bytes of the canonical
|
||||||
|
path. Matching import records still name the canonical path, and transitive fact
|
||||||
|
sections carry the same canonical private spelling. The reader restores the
|
||||||
|
real declared name from the direct owner's metadata while treating those
|
||||||
|
private names as semantic placeholders. Thus two source aliases for the same
|
||||||
|
canonical type produce the same interface bytes, even if two dependencies have
|
||||||
|
the same declared name.
|
||||||
|
|
||||||
|
The source/import and interface protocol change advances persistent build
|
||||||
|
workdirs to format 15 and test workdirs to format 14. Older unit vouchers are
|
||||||
|
invalidated before reuse, so an interface written with historical implicit-dot
|
||||||
|
or declared-name spelling cannot preserve a stale qualifier under the new
|
||||||
|
checker.
|
||||||
|
|
||||||
|
Compiler argv remains exactly one sorted `--import <canonical-path> <direct.wwi>`
|
||||||
|
pair per direct dependency plus the exact required vendor import maps. No
|
||||||
|
transitive `.wwi` is passed. Symbols, objects, archives, vouchers, stamps,
|
||||||
|
persistent directories, and product basenames remain canonical-action owned.
|
||||||
|
Linker argv remains root plus reachable archives and native/runtime inputs only;
|
||||||
|
it contains neither `.wwi` files nor alias-derived archive names.
|
||||||
|
|
||||||
|
A dependency declared-name change invalidates its semantic export and
|
||||||
|
reconsiders every direct importer. Default-bound unchanged source loses the old
|
||||||
|
qualifier and is rejected cleanly. Explicitly aliased source stays valid; after
|
||||||
|
its canonical interface regenerates unchanged, invalidation stops before
|
||||||
|
unaffected reverse dependencies. An alias-only source edit rebuilds the edited
|
||||||
|
owner but likewise cannot rename symbols or alter canonical export identity, so
|
||||||
|
unchanged semantic bytes stop reverse rebuilding.
|
||||||
|
|
||||||
|
Parser, identity, alias, declaration, binding, and scope failures occur before
|
||||||
|
publication. Compiler-owned failures may start the compiler, but staged unit,
|
||||||
|
interface, assembly, object, archive, voucher, stamp, status, and product state
|
||||||
|
is discarded or invalidated under section 11.18's partial-commit protocol.
|
||||||
|
|
||||||
|
#### Stage and observer ownership
|
||||||
|
|
||||||
|
The C and WW parsers retain identical alias/path/position facts. The Cstage and
|
||||||
|
WWstage drivers resolve only the dotted path; the compiler mains restore the
|
||||||
|
declared name and choose the effective binding; the checkers own duplicate,
|
||||||
|
collision, usage, and file-scope lookup; the interface writers own canonical
|
||||||
|
normalization; and code generators consume checker-stamped canonical types and
|
||||||
|
module ownership. WWstage resolves all top-level function signatures in their
|
||||||
|
declaring package before checking bodies, matching Cstage and preventing a
|
||||||
|
consumer from reinterpreting a later declaration's bare same-package types.
|
||||||
|
|
||||||
|
The native `explicit_import_alias_binding_modes` observer owns the focused
|
||||||
|
ordinary/alias/bare-negative, duplicate, repeated-path, file-scope, canonical
|
||||||
|
artifact, `.wwi`, and rejection-state matrix. The existing
|
||||||
|
`declared_name_identity_and_file_import_scope` observer owns default-versus-
|
||||||
|
stable-alias invalidation and propagation. Existing directory, command-test,
|
||||||
|
vendor, recursive, exact-argv, link-closure, persistent-workdir, and
|
||||||
|
partial-commit observers retain their broader variant and action ownership.
|
||||||
|
Every applicable proof runs Cstage and WWstage from independent cold roots and
|
||||||
|
compares diagnostics, normalized tool arguments, artifacts, binaries, and
|
||||||
|
runtime output.
|
||||||
|
|
||||||
## 12. Candidate architectures and hard-gate decision
|
## 12. Candidate architectures and hard-gate decision
|
||||||
|
|
||||||
Five candidates were developed as coherent systems, not as feature bins.
|
Five candidates were developed as coherent systems, not as feature bins.
|
||||||
|
|||||||
17
docs/spec.md
17
docs/spec.md
@@ -246,7 +246,8 @@ does not return (e.g. a call to `abort`).
|
|||||||
```
|
```
|
||||||
SourceFile = PackageClause { ImportDecl } { TopDecl } .
|
SourceFile = PackageClause { ImportDecl } { TopDecl } .
|
||||||
PackageClause = "package" ident ";" .
|
PackageClause = "package" ident ";" .
|
||||||
ImportDecl = "import" ident ";" .
|
ImportDecl = "import" ( ImportPath | ident ImportPath ) ";" .
|
||||||
|
ImportPath = ident { "." ident } .
|
||||||
```
|
```
|
||||||
|
|
||||||
- Every source file begins with a package clause. A directory of eligible
|
- Every source file begins with a package clause. A directory of eligible
|
||||||
@@ -255,8 +256,13 @@ ImportDecl = "import" ident ";" .
|
|||||||
canonical import identity.
|
canonical import identity.
|
||||||
- `import acme.codec;` loads the canonical package `acme.codec`. If that
|
- `import acme.codec;` loads the canonical package `acme.codec`. If that
|
||||||
package declares `package wire;`, the importing file sees its exported names
|
package declares `package wire;`, the importing file sees its exported names
|
||||||
as `wire.Name`; `codec.Name` is not an additional binding. The import binding
|
as `wire.Name`; `codec.Name` is not an additional binding. An explicit alias
|
||||||
is scoped to that source file. A sibling file must declare its own import.
|
replaces only that visible qualifier: `import stable acme.codec;` exposes
|
||||||
|
`stable.Name`, not `wire.Name` or `codec.Name`. Both kinds of binding are
|
||||||
|
scoped to that source file. A sibling file must declare its own import.
|
||||||
|
Neither form exposes an imported declaration as a bare `Name`; ordinary
|
||||||
|
unqualified lookup remains limited to lexical, builtin, and same-package
|
||||||
|
declarations.
|
||||||
The package dependency graph is the sorted, deduplicated union of the real
|
The package dependency graph is the sorted, deduplicated union of the real
|
||||||
imports in all eligible files. Self-import is rejected.
|
imports in all eligible files. Self-import is rejected.
|
||||||
- An executable package is one declared `package main` and containing a
|
- An executable package is one declared `package main` and containing a
|
||||||
@@ -265,8 +271,9 @@ ImportDecl = "import" ident ";" .
|
|||||||
colocated external-test wiring.
|
colocated external-test wiring.
|
||||||
- Only names marked `export` (§5) are visible across module boundaries.
|
- Only names marked `export` (§5) are visible across module boundaries.
|
||||||
|
|
||||||
The implemented grammar remains dotted and unaliased. Quoted import paths,
|
Import paths remain unquoted and dotted. Grouped imports, quoted import paths,
|
||||||
explicit aliases, dot imports, and blank imports are not currently accepted.
|
dot imports, and blank imports are not implemented. In particular, `_` is
|
||||||
|
rejected as an alias rather than being treated as a blank import.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -228,17 +228,26 @@ Separate compilation is the only driver build path; no compatibility mode
|
|||||||
switch remains.
|
switch remains.
|
||||||
|
|
||||||
`test/package/package_test.ww` also owns the dual-stage
|
`test/package/package_test.ww` also owns the dual-stage
|
||||||
`declared_name_identity_and_file_import_scope` observer. It generates temporary
|
`declared_name_identity_and_file_import_scope` and
|
||||||
|
`explicit_import_alias_binding_modes` observers. They generate temporary
|
||||||
directory trees proving that canonical import identity, physical directory,
|
directory trees proving that canonical import identity, physical directory,
|
||||||
declared package name, and source-file default qualifier remain distinct;
|
declared package name, optional source alias, and effective source-file
|
||||||
imports are file-scoped while dependency edges are the package-wide sorted
|
qualifier remain distinct. The alias matrix covers default and explicit-only
|
||||||
union; command and production/internal/external/generated-main variants retain
|
qualification, bare function/type/def/const/variable rejection with exact
|
||||||
canonical action ownership; vendor expansion changes identity but not the
|
unused-before-undefined diagnostics, same-file duplicate bindings, accepted
|
||||||
declared qualifier; compiler argv contains only direct `.wwi` inputs; and its
|
same-path imports under distinct bindings, alias reuse across files, sibling
|
||||||
named rejected actions leave neither committed nor staged action artifacts or a
|
scope isolation, package-declaration collisions, unused aliases, and explicit
|
||||||
published binary. It also forces a staged multi-artifact commit to fail after
|
blank-alias rejection. It also pins canonical `.wwi` spelling, archive/action
|
||||||
the interface rename, then proves that both stages invalidate old unit vouchers
|
ownership, stage-equal diagnostics and binaries, and clean rejection state.
|
||||||
and reconsider the importer rather than accepting a mixed warm generation. A
|
The declared-name observer proves imports are file-scoped while dependency
|
||||||
|
edges are the package-wide sorted union; command and
|
||||||
|
production/internal/external/generated-main variants retain canonical action
|
||||||
|
ownership; vendor expansion changes identity but not the effective qualifier;
|
||||||
|
compiler argv contains only direct `.wwi` inputs; and its named rejected
|
||||||
|
actions leave neither committed nor staged action artifacts or a published
|
||||||
|
binary. It also forces a staged multi-artifact commit to fail after the
|
||||||
|
interface rename, then proves that both stages invalidate old unit vouchers and
|
||||||
|
reconsider the importer rather than accepting a mixed warm generation. A
|
||||||
separate injected stamp-removal failure proves the pre-commit gate leaves all
|
separate injected stamp-removal failure proves the pre-commit gate leaves all
|
||||||
previously committed artifacts byte-identical.
|
previously committed artifacts byte-identical.
|
||||||
Together with the existing directory, recursive, vendor,
|
Together with the existing directory, recursive, vendor,
|
||||||
|
|||||||
@@ -17,19 +17,19 @@ import path;
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < 4) {
|
for (i < 4) {
|
||||||
let got: i32 = 0;
|
let got: i32 = 0;
|
||||||
if (abs(ins[i])) { got = 1; };
|
if (path.abs(ins[i])) { got = 1; };
|
||||||
assert(!(got != want[i]));
|
assert(!(got != want[i]));
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
// buffer-arm: &buf ptr-ident-widens into the union.
|
// buffer-arm: &buf ptr-ident-widens into the union.
|
||||||
let buf = buffer { ... };
|
let buf: path.buffer;
|
||||||
assert(!(abs(&buf))); // empty → false
|
assert(!(path.abs(&buf))); // empty → false
|
||||||
assert(!(push(&buf, "/foo")! != "/foo"));
|
assert(!(path.push(&buf, "/foo")! != "/foo"));
|
||||||
assert(!(!abs(&buf))); // "/foo" → true
|
assert(!(!path.abs(&buf))); // "/foo" → true
|
||||||
buf.end = 0;
|
buf.end = 0;
|
||||||
assert(!(push(&buf, "foo")! != "foo"));
|
assert(!(path.push(&buf, "foo")! != "foo"));
|
||||||
assert(!(abs(&buf))); // "foo" → false
|
assert(!(path.abs(&buf))); // "foo" → false
|
||||||
};
|
};
|
||||||
|
|
||||||
// ref/hare/path/buffer.ha:44-51. Both arms: str (== sepstr) and *buffer
|
// ref/hare/path/buffer.ha:44-51. Both arms: str (== sepstr) and *buffer
|
||||||
@@ -37,24 +37,24 @@ import path;
|
|||||||
// inline; str-arm rows table-driven.
|
// inline; str-arm rows table-driven.
|
||||||
|
|
||||||
@test fn isroot_cases() void = {
|
@test fn isroot_cases() void = {
|
||||||
let buf = buffer { ... };
|
let buf: path.buffer;
|
||||||
// empty buffer (end 0)
|
// empty buffer (end 0)
|
||||||
assert(!(isroot(&buf)));
|
assert(!(path.isroot(&buf)));
|
||||||
|
|
||||||
// "/" — root. local() is c3, so seed the SEP byte directly.
|
// "/" — root. local() is c3, so seed the SEP byte directly.
|
||||||
buf.buf[0] = SEP; buf.end = 1;
|
buf.buf[0] = path.SEP; buf.end = 1;
|
||||||
assert(!(!isroot(&buf)));
|
assert(!(!path.isroot(&buf)));
|
||||||
assert(!(string(&buf) != "/"));
|
assert(!(path.string(&buf) != "/"));
|
||||||
|
|
||||||
// "foo" — relative, not root.
|
// "foo" — relative, not root.
|
||||||
buf.end = 0;
|
buf.end = 0;
|
||||||
assert(!(push(&buf, "foo")! != "foo"));
|
assert(!(path.push(&buf, "foo")! != "foo"));
|
||||||
assert(!(isroot(&buf)));
|
assert(!(path.isroot(&buf)));
|
||||||
|
|
||||||
// "/foo" — absolute but not root.
|
// "/foo" — absolute but not root.
|
||||||
buf.buf[0] = SEP; buf.end = 1;
|
buf.buf[0] = path.SEP; buf.end = 1;
|
||||||
assert(!(push(&buf, "foo")! != "/foo"));
|
assert(!(path.push(&buf, "foo")! != "/foo"));
|
||||||
assert(!(isroot(&buf)));
|
assert(!(path.isroot(&buf)));
|
||||||
|
|
||||||
// str-arm (buffer.ha:47): only the bare separator is root.
|
// str-arm (buffer.ha:47): only the bare separator is root.
|
||||||
let ins: [4]str;
|
let ins: [4]str;
|
||||||
@@ -66,7 +66,7 @@ import path;
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < 4) {
|
for (i < 4) {
|
||||||
let got: i32 = 0;
|
let got: i32 = 0;
|
||||||
if (isroot(ins[i])) { got = 1; };
|
if (path.isroot(ins[i])) { got = 1; };
|
||||||
assert(!(got != want[i]));
|
assert(!(got != want[i]));
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
@@ -76,10 +76,10 @@ import path;
|
|||||||
// buffer views the byte prefix.
|
// buffer views the byte prefix.
|
||||||
|
|
||||||
@test fn string_cases() void = {
|
@test fn string_cases() void = {
|
||||||
let buf = buffer { ... };
|
let buf: path.buffer;
|
||||||
assert(!(string(&buf) != "."));
|
assert(!(path.string(&buf) != "."));
|
||||||
assert(!(push(&buf, "foo")! != "foo"));
|
assert(!(path.push(&buf, "foo")! != "foo"));
|
||||||
assert(!(string(&buf) != "foo"));
|
assert(!(path.string(&buf) != "foo"));
|
||||||
};
|
};
|
||||||
|
|
||||||
// ref/hare/path/buffer.ha:55-77. Rewrites '/'→SEP into a static buffer;
|
// ref/hare/path/buffer.ha:55-77. Rewrites '/'→SEP into a static buffer;
|
||||||
@@ -95,7 +95,7 @@ import path;
|
|||||||
ins[3]=""; want[3]="";
|
ins[3]=""; want[3]="";
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < 4) {
|
for (i < 4) {
|
||||||
assert(!(local(ins[i])! != want[i]));
|
assert(!(path.local(ins[i])! != want[i]));
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ import path;
|
|||||||
inputs[9]="/home//dwc//test"; wantdir[9]="/home//dwc"; wantbase[9]="test";
|
inputs[9]="/home//dwc//test"; wantdir[9]="/home//dwc"; wantbase[9]="test";
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < 10) {
|
for (i < 10) {
|
||||||
assert(!(dirname(inputs[i]) != wantdir[i]));
|
assert(!(path.dirname(inputs[i]) != wantdir[i]));
|
||||||
assert(!(basename(inputs[i]) != wantbase[i]));
|
assert(!(path.basename(inputs[i]) != wantbase[i]));
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import path;
|
|||||||
// appendnorm @test is needed.
|
// appendnorm @test is needed.
|
||||||
|
|
||||||
@test fn push_cases() void = {
|
@test fn push_cases() void = {
|
||||||
let buf = buffer { ... };
|
let buf: path.buffer;
|
||||||
assert(!(string(&buf) != "."));
|
assert(!(path.string(&buf) != "."));
|
||||||
|
|
||||||
// current-dir + parent-dir invariants (stack.ha:82-88). Single
|
// current-dir + parent-dir invariants (stack.ha:82-88). Single
|
||||||
// segment per row → table-driven sequential apply.
|
// segment per row → table-driven sequential apply.
|
||||||
@@ -33,13 +33,13 @@ import path;
|
|||||||
segs[4]="."; wants[4]="..";
|
segs[4]="."; wants[4]="..";
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < 5) {
|
for (i < 5) {
|
||||||
assert(!(push(&buf, segs[i])! != wants[i]));
|
assert(!(path.push(&buf, segs[i])! != wants[i]));
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
// set(&buf) resets to "." (stack.ha:91). set forwards an EMPTY
|
// set(&buf) resets to "." (stack.ha:91). set forwards an EMPTY
|
||||||
// variadic into push (KEN-oracle flag 1: zero-arg gather+forward).
|
// variadic into push (KEN-oracle flag 1: zero-arg gather+forward).
|
||||||
assert(!(set(&buf)! != "."));
|
assert(!(path.set(&buf)! != "."));
|
||||||
|
|
||||||
// regular path + parent (stack.ha:101-104, minus the local() row).
|
// regular path + parent (stack.ha:101-104, minus the local() row).
|
||||||
let segs2: [3]str;
|
let segs2: [3]str;
|
||||||
@@ -49,25 +49,25 @@ import path;
|
|||||||
segs2[2]=".."; wants2[2]=".";
|
segs2[2]=".."; wants2[2]=".";
|
||||||
let k: i32 = 0;
|
let k: i32 = 0;
|
||||||
for (k < 3) {
|
for (k < 3) {
|
||||||
assert(!(push(&buf, segs2[k])! != wants2[k]));
|
assert(!(path.push(&buf, segs2[k])! != wants2[k]));
|
||||||
k += 1;
|
k += 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
// multi-segment (stack.ha:107-111). Variadic arity varies (1 or 2)
|
// multi-segment (stack.ha:107-111). Variadic arity varies (1 or 2)
|
||||||
// → inline.
|
// → inline.
|
||||||
assert(!(push(&buf, "a", "b")! != "a/b"));
|
assert(!(path.push(&buf, "a", "b")! != "a/b"));
|
||||||
assert(!(push(&buf, "..", "c")! != "a/c"));
|
assert(!(path.push(&buf, "..", "c")! != "a/c"));
|
||||||
assert(!(push(&buf, "..")! != "a"));
|
assert(!(path.push(&buf, "..")! != "a"));
|
||||||
// stack.ha:110; local()→literal, SEP='/' on Linux, proper local() rides c3
|
// stack.ha:110; local()→literal, SEP='/' on Linux, proper local() rides c3
|
||||||
assert(!(push(&buf, "/d")! != "a/d"));
|
assert(!(path.push(&buf, "/d")! != "a/d"));
|
||||||
assert(!(push(&buf, "..", "..")! != ".")); // stack.ha:111
|
assert(!(path.push(&buf, "..", "..")! != ".")); // stack.ha:111
|
||||||
|
|
||||||
// leading-SEP segment into an EMPTY buffer exercises push's
|
// leading-SEP segment into an EMPTY buffer exercises push's
|
||||||
// `j==0 && buf.end==0` root-seed arm (stack.ha:18-20), unreached
|
// `j==0 && buf.end==0` root-seed arm (stack.ha:18-20), unreached
|
||||||
// by the relative rows above. Hare covers it via local("/")-seeded
|
// by the relative rows above. Hare covers it via local("/")-seeded
|
||||||
// rows that defer to c3; literal "/foo" stands in (SEP='/' on Linux).
|
// rows that defer to c3; literal "/foo" stands in (SEP='/' on Linux).
|
||||||
assert(!(set(&buf)! != "."));
|
assert(!(path.set(&buf)! != "."));
|
||||||
assert(!(push(&buf, "/foo")! != "/foo"));
|
assert(!(path.push(&buf, "/foo")! != "/foo"));
|
||||||
};
|
};
|
||||||
|
|
||||||
// ref/hare/path/stack.ha:172-180. Parent dir; root → root; empty → "..".
|
// ref/hare/path/stack.ha:172-180. Parent dir; root → root; empty → "..".
|
||||||
@@ -76,7 +76,7 @@ import path;
|
|||||||
// not "") can share the table.
|
// not "") can share the table.
|
||||||
|
|
||||||
@test fn parent_cases() void = {
|
@test fn parent_cases() void = {
|
||||||
let buf = buffer { ... };
|
let buf: path.buffer;
|
||||||
let ins: [5]str;
|
let ins: [5]str;
|
||||||
let want: [5]str;
|
let want: [5]str;
|
||||||
ins[0]="/usr/lib"; want[0]="/usr";
|
ins[0]="/usr/lib"; want[0]="/usr";
|
||||||
@@ -87,8 +87,8 @@ import path;
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < 5) {
|
for (i < 5) {
|
||||||
buf.end = 0;
|
buf.end = 0;
|
||||||
push(&buf, ins[i])!;
|
path.push(&buf, ins[i])!;
|
||||||
assert(!(parent(&buf)! != want[i]));
|
assert(!(path.parent(&buf)! != want[i]));
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -99,30 +99,30 @@ import path;
|
|||||||
// #48 respelling didn't smuggle in pop's buf.end mutation.
|
// #48 respelling didn't smuggle in pop's buf.end mutation.
|
||||||
|
|
||||||
@test fn popsplit_cases() void = {
|
@test fn popsplit_cases() void = {
|
||||||
let buf = buffer { ... };
|
let buf: path.buffer;
|
||||||
|
|
||||||
// empty
|
// empty
|
||||||
assert(!(!(pop(&buf) is void)));
|
assert(!(!(path.pop(&buf) is void)));
|
||||||
assert(!(string(&buf) != "."));
|
assert(!(path.string(&buf) != "."));
|
||||||
|
|
||||||
// root dir
|
// root dir
|
||||||
buf.end = 0;
|
buf.end = 0;
|
||||||
assert(!(push(&buf, "/")! != "/"));
|
assert(!(path.push(&buf, "/")! != "/"));
|
||||||
assert(!(!(pop(&buf) is void)));
|
assert(!(!(path.pop(&buf) is void)));
|
||||||
assert(!(string(&buf) != "/"));
|
assert(!(path.string(&buf) != "/"));
|
||||||
|
|
||||||
// relative file — peek is NON-mutating
|
// relative file — peek is NON-mutating
|
||||||
buf.end = 0;
|
buf.end = 0;
|
||||||
assert(!(push(&buf, "foo")! != "foo"));
|
assert(!(path.push(&buf, "foo")! != "foo"));
|
||||||
assert(!(peek(&buf) as str != "foo"));
|
assert(!(path.peek(&buf) as str != "foo"));
|
||||||
assert(!(string(&buf) != "foo"));
|
assert(!(path.string(&buf) != "foo"));
|
||||||
assert(!(pop(&buf) as str != "foo"));
|
assert(!(path.pop(&buf) as str != "foo"));
|
||||||
assert(!(string(&buf) != "."));
|
assert(!(path.string(&buf) != "."));
|
||||||
|
|
||||||
// absolute file
|
// absolute file
|
||||||
buf.end = 0;
|
buf.end = 0;
|
||||||
assert(!(push(&buf, "/foo")! != "/foo"));
|
assert(!(path.push(&buf, "/foo")! != "/foo"));
|
||||||
assert(!(peek(&buf) as str != "foo"));
|
assert(!(path.peek(&buf) as str != "foo"));
|
||||||
assert(!(pop(&buf) as str != "foo"));
|
assert(!(path.pop(&buf) as str != "foo"));
|
||||||
assert(!(string(&buf) != "/"));
|
assert(!(path.string(&buf) != "/"));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,12 +21,10 @@
|
|||||||
// the regex/capture struct shapes, and finish(). A failing row aborts
|
// the regex/capture struct shapes, and finish(). A failing row aborts
|
||||||
// via the assert/abort builtin (task #5 @test conversion).
|
// via the assert/abort builtin (task #5 @test conversion).
|
||||||
//
|
//
|
||||||
// Struct literals below name the type UNQUALIFIED (`inst_charset { … }`,
|
// The parser rejects a qualified name in struct-literal position (#29), so
|
||||||
// not `regex.inst_charset { … }`): the parser rejects a module-qualified
|
// the few struct payload rows below initialize a qualified typed local before
|
||||||
// name in struct-literal position (#29), and the imported EXPORTED type
|
// injecting it into the exported union. This stays distinct from the
|
||||||
// is in scope unqualified. These reach only exported types — the #29
|
// white-box privacy boundary handled by the whitebox_test.ww split above.
|
||||||
// axis, distinct from the white-box privacy boundary handled by the
|
|
||||||
// whitebox_test.ww split above.
|
|
||||||
package regex_test;
|
package regex_test;
|
||||||
|
|
||||||
import regex;
|
import regex;
|
||||||
@@ -107,7 +105,10 @@ import strings;
|
|||||||
// inst_charset carries a struct payload; its fields survive the union
|
// inst_charset carries a struct payload; its fields survive the union
|
||||||
// round-trip.
|
// round-trip.
|
||||||
@test fn charset_payload() void = {
|
@test fn charset_payload() void = {
|
||||||
let c: regex.inst = (inst_charset { idx = 3, is_positive = true });
|
let value: regex.inst_charset;
|
||||||
|
value.idx = 3;
|
||||||
|
value.is_positive = true;
|
||||||
|
let c: regex.inst = value;
|
||||||
match (c) {
|
match (c) {
|
||||||
case let cs: regex.inst_charset => {
|
case let cs: regex.inst_charset => {
|
||||||
assert(!(cs.idx != (3: size)));
|
assert(!(cs.idx != (3: size)));
|
||||||
@@ -123,9 +124,12 @@ import strings;
|
|||||||
// diverges cs≠ww (#26 — the wwstage frames it wider), so
|
// diverges cs≠ww (#26 — the wwstage frames it wider), so
|
||||||
// asserting the bounds here would seed a rule-10-divergent fixture.
|
// asserting the bounds here would seed a rule-10-divergent fixture.
|
||||||
@test fn repeat_payload() void = {
|
@test fn repeat_payload() void = {
|
||||||
let r: regex.inst = (inst_repeat {
|
let value: regex.inst_repeat;
|
||||||
id = 1, origin = 4, min = (2: size), max = void,
|
value.id = 1;
|
||||||
});
|
value.origin = 4;
|
||||||
|
value.min = (2: size);
|
||||||
|
value.max = void;
|
||||||
|
let r: regex.inst = value;
|
||||||
match (r) {
|
match (r) {
|
||||||
case let rp: regex.inst_repeat => {
|
case let rp: regex.inst_repeat => {
|
||||||
assert(!(rp.id != (1: size)));
|
assert(!(rp.id != (1: size)));
|
||||||
@@ -138,13 +142,12 @@ import strings;
|
|||||||
// The regex/capture structs hold their fields; finish() is a no-op
|
// The regex/capture structs hold their fields; finish() is a no-op
|
||||||
// (no-free runtime) and must accept a built regex.
|
// (no-free runtime) and must accept a built regex.
|
||||||
@test fn struct_shapes_and_finish() void = {
|
@test fn struct_shapes_and_finish() void = {
|
||||||
let cap: regex.capture = capture {
|
let cap: regex.capture;
|
||||||
content = "abc",
|
cap.content = "abc";
|
||||||
start = 0,
|
cap.start = 0;
|
||||||
start_bytesize = 0,
|
cap.start_bytesize = 0;
|
||||||
end = 3,
|
cap.end = 3;
|
||||||
end_bytesize = 3,
|
cap.end_bytesize = 3;
|
||||||
};
|
|
||||||
assert(!(cap.content.len != 3));
|
assert(!(cap.content.len != 3));
|
||||||
assert(!(cap.end != (3: size)));
|
assert(!(cap.end != (3: size)));
|
||||||
|
|
||||||
@@ -256,15 +259,18 @@ import strings;
|
|||||||
// the documented no-op (no-free runtime), so the header must stay
|
// the documented no-op (no-free runtime), so the header must stay
|
||||||
// readable after — a future real free changes this row consciously.
|
// readable after — a future real free changes this row consciously.
|
||||||
// The local is spelled []regex.capture, not the regex.result alias:
|
// The local is spelled []regex.capture, not the regex.result alias:
|
||||||
// wwstage falsely loud-bails appending a struct literal onto an
|
// wwstage falsely loud-bails appending a capture value onto an alias-typed
|
||||||
// alias-typed dst (#20); the alias + signature stay exercised by the
|
// dst (#20); the alias + signature stay exercised by result_free itself.
|
||||||
// result_free call itself. Reverts to `regex.result` when #20 lands.
|
// Reverts to `regex.result` when #20 lands.
|
||||||
@test fn result_free_noop() void = {
|
@test fn result_free_noop() void = {
|
||||||
let res: []regex.capture;
|
let res: []regex.capture;
|
||||||
append(res, capture {
|
let value: regex.capture;
|
||||||
content = "x", start = 0, start_bytesize = 0,
|
value.content = "x";
|
||||||
end = 1, end_bytesize = 1,
|
value.start = 0;
|
||||||
});
|
value.start_bytesize = 0;
|
||||||
|
value.end = 1;
|
||||||
|
value.end_bytesize = 1;
|
||||||
|
append(res, value);
|
||||||
regex.result_free(res);
|
regex.result_free(res);
|
||||||
assert(!(len(res) != 1));
|
assert(!(len(res) != 1));
|
||||||
assert(!(res[0].end != (1: size)));
|
assert(!(res[0].end != (1: size)));
|
||||||
@@ -872,16 +878,16 @@ type cerow = struct {
|
|||||||
// class items as -2 (presence sentinel — fn-ptr address not stable).
|
// class items as -2 (presence sentinel — fn-ptr address not stable).
|
||||||
// The element binds structurally, not via the `charset` alias — an
|
// The element binds structurally, not via the `charset` alias — an
|
||||||
// alias-typed slice local's index read mis-scales in wwstage (#68).
|
// alias-typed slice local's index read mis-scales in wwstage (#68).
|
||||||
fn cssig(cs: [](charset_lit_item | charset_range_item |
|
fn cssig(cs: [](regex.charset_lit_item | regex.charset_range_item |
|
||||||
charset_class_item), k: size) i64 = {
|
regex.charset_class_item), k: size) i64 = {
|
||||||
let cur: (charset_lit_item | charset_range_item |
|
let cur: (regex.charset_lit_item | regex.charset_range_item |
|
||||||
charset_class_item) = cs[k];
|
regex.charset_class_item) = cs[k];
|
||||||
match (cur) {
|
match (cur) {
|
||||||
case let l: charset_lit_item =>
|
case let l: regex.charset_lit_item =>
|
||||||
return 1000000 + ((l: rune): i64);
|
return 1000000 + ((l: rune): i64);
|
||||||
case let range: charset_range_item =>
|
case let range: regex.charset_range_item =>
|
||||||
return (range.0: i64) * 10000 + (range.1: i64);
|
return (range.0: i64) * 10000 + (range.1: i64);
|
||||||
case charset_class_item => return -2; // fn-ptr address not stable
|
case regex.charset_class_item => return -2; // fn-ptr address not stable
|
||||||
case => return -1;
|
case => return -1;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -928,8 +934,8 @@ type cscase = struct {
|
|||||||
match (c) {
|
match (c) {
|
||||||
case let re: regex.regex => {
|
case let re: regex.regex => {
|
||||||
assert(!(re.charsets.len != 1));
|
assert(!(re.charsets.len != 1));
|
||||||
let cs0: [](charset_lit_item | charset_range_item |
|
let cs0: [](regex.charset_lit_item | regex.charset_range_item |
|
||||||
charset_class_item) = re.charsets[0];
|
regex.charset_class_item) = re.charsets[0];
|
||||||
assert(!((len(cs0): i32) != rows[i].ecnt));
|
assert(!((len(cs0): i32) != rows[i].ecnt));
|
||||||
let k: i32 = 0;
|
let k: i32 = 0;
|
||||||
for (k < rows[i].ecnt) {
|
for (k < rows[i].ecnt) {
|
||||||
@@ -1715,8 +1721,8 @@ type smcase = struct {
|
|||||||
match (c) {
|
match (c) {
|
||||||
case let re: regex.regex => {
|
case let re: regex.regex => {
|
||||||
assert(!(re.charsets.len != 1));
|
assert(!(re.charsets.len != 1));
|
||||||
let cs0: [](charset_lit_item | charset_range_item |
|
let cs0: [](regex.charset_lit_item | regex.charset_range_item |
|
||||||
charset_class_item) = re.charsets[0];
|
regex.charset_class_item) = re.charsets[0];
|
||||||
assert(!((len(cs0): i32) != 1));
|
assert(!((len(cs0): i32) != 1));
|
||||||
assert(!(cssig(cs0, 0) != -2));
|
assert(!(cssig(cs0, 0) != -2));
|
||||||
regex.finish(&re);
|
regex.finish(&re);
|
||||||
|
|||||||
@@ -28,25 +28,25 @@ fn fstreq(a: str, b: str) bool = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn chk(n: f64, want: str) bool = {
|
fn chk(n: f64, want: str) bool = {
|
||||||
return fstreq(f64tos(n), want);
|
return fstreq(strconv.f64tos(n), want);
|
||||||
};
|
};
|
||||||
|
|
||||||
// rt64 — bit-exact round-trip: render then parse back must reproduce the
|
// rt64 — bit-exact round-trip: render then parse back must reproduce the
|
||||||
// exact bits. The parse-back re-runs strconv's decimal slow path.
|
// exact bits. The parse-back re-runs strconv's decimal slow path.
|
||||||
fn rt64(x: f64) bool = {
|
fn rt64(x: f64) bool = {
|
||||||
match (stof64(f64tos(x), base.DEC)) {
|
match (strconv.stof64(strconv.f64tos(x), strconv.base.DEC)) {
|
||||||
case let v: f64 => { return math.f64bits(v) == math.f64bits(x); };
|
case let v: f64 => { return math.f64bits(v) == math.f64bits(x); };
|
||||||
case let e: invalid => { return false; };
|
case let e: strconv.invalid => { return false; };
|
||||||
case let e: overflow => { return false; };
|
case let e: strconv.overflow => { return false; };
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn rt32(x: f32) bool = {
|
fn rt32(x: f32) bool = {
|
||||||
match (stof32(f32tos(x), base.DEC)) {
|
match (strconv.stof32(strconv.f32tos(x), strconv.base.DEC)) {
|
||||||
case let v: f32 => { return math.f32bits(v) == math.f32bits(x); };
|
case let v: f32 => { return math.f32bits(v) == math.f32bits(x); };
|
||||||
case let e: invalid => { return false; };
|
case let e: strconv.invalid => { return false; };
|
||||||
case let e: overflow => { return false; };
|
case let e: strconv.overflow => { return false; };
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@@ -96,7 +96,7 @@ fn rt32(x: f32) bool = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn chkf32(n: f32, want: str) bool = {
|
fn chkf32(n: f32, want: str) bool = {
|
||||||
return fstreq(f32tos(n), want);
|
return fstreq(strconv.f32tos(n), want);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ftos_test.ha tcs G/void rows (the "pass for both f32 and f64" set) —
|
// ftos_test.ha tcs G/void rows (the "pass for both f32 and f64" set) —
|
||||||
|
|||||||
@@ -14,177 +14,177 @@ package strconv_test;
|
|||||||
import strconv;
|
import strconv;
|
||||||
|
|
||||||
|
|
||||||
fn cki(id: i32, s: str, b: base, want: i64) void = {
|
fn cki(id: i32, s: str, b: strconv.base, want: i64) void = {
|
||||||
match (stoi64(s, b)) {
|
match (strconv.stoi64(s, b)) {
|
||||||
case let v: i64 => assert(!(v != want));
|
case let v: i64 => assert(!(v != want));
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn cki_inv(id: i32, s: str, b: base, idx: i32) void = {
|
fn cki_inv(id: i32, s: str, b: strconv.base, idx: i32) void = {
|
||||||
match (stoi64(s, b)) {
|
match (strconv.stoi64(s, b)) {
|
||||||
case let v: i64 => abort();
|
case let v: i64 => abort();
|
||||||
case let e: invalid => assert(!(e: i32 != idx));
|
case let e: strconv.invalid => assert(!(e: i32 != idx));
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn cki_ovf(id: i32, s: str, b: base) void = {
|
fn cki_ovf(id: i32, s: str, b: strconv.base) void = {
|
||||||
match (stoi64(s, b)) {
|
match (strconv.stoi64(s, b)) {
|
||||||
case let v: i64 => abort();
|
case let v: i64 => abort();
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => { };
|
case let e: strconv.overflow => { };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn cku(id: i32, s: str, b: base, want: u64) void = {
|
fn cku(id: i32, s: str, b: strconv.base, want: u64) void = {
|
||||||
match (stou64(s, b)) {
|
match (strconv.stou64(s, b)) {
|
||||||
case let v: u64 => assert(!(v != want));
|
case let v: u64 => assert(!(v != want));
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn cku_inv(id: i32, s: str, b: base, idx: i32) void = {
|
fn cku_inv(id: i32, s: str, b: strconv.base, idx: i32) void = {
|
||||||
match (stou64(s, b)) {
|
match (strconv.stou64(s, b)) {
|
||||||
case let v: u64 => abort();
|
case let v: u64 => abort();
|
||||||
case let e: invalid => assert(!(e: i32 != idx));
|
case let e: strconv.invalid => assert(!(e: i32 != idx));
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn cku_ovf(id: i32, s: str, b: base) void = {
|
fn cku_ovf(id: i32, s: str, b: strconv.base) void = {
|
||||||
match (stou64(s, b)) {
|
match (strconv.stou64(s, b)) {
|
||||||
case let v: u64 => abort();
|
case let v: u64 => abort();
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => { };
|
case let e: strconv.overflow => { };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn cki32_ovf(id: i32, s: str, b: base) void = {
|
fn cki32_ovf(id: i32, s: str, b: strconv.base) void = {
|
||||||
match (stoi32(s, b)) {
|
match (strconv.stoi32(s, b)) {
|
||||||
case let v: i32 => abort();
|
case let v: i32 => abort();
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => { };
|
case let e: strconv.overflow => { };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn cki32(id: i32, s: str, b: base, want: i32) void = {
|
fn cki32(id: i32, s: str, b: strconv.base, want: i32) void = {
|
||||||
match (stoi32(s, b)) {
|
match (strconv.stoi32(s, b)) {
|
||||||
case let v: i32 => assert(!(v != want));
|
case let v: i32 => assert(!(v != want));
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn ck_int(id: i32, s: str, b: base, want: int) void = {
|
fn ck_int(id: i32, s: str, b: strconv.base, want: int) void = {
|
||||||
match (stoi(s, b)) {
|
match (strconv.stoi(s, b)) {
|
||||||
case let v: int => assert(!(v != want));
|
case let v: int => assert(!(v != want));
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn ck_uint(id: i32, s: str, b: base, want: uint) void = {
|
fn ck_uint(id: i32, s: str, b: strconv.base, want: uint) void = {
|
||||||
match (stou(s, b)) {
|
match (strconv.stou(s, b)) {
|
||||||
case let v: uint => assert(!(v != want));
|
case let v: uint => assert(!(v != want));
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn ck_size(id: i32, s: str, b: base, want: size) void = {
|
fn ck_size(id: i32, s: str, b: strconv.base, want: size) void = {
|
||||||
match (stoz(s, b)) {
|
match (strconv.stoz(s, b)) {
|
||||||
case let v: size => assert(!(v != want));
|
case let v: size => assert(!(v != want));
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn ck_int_ovf(id: i32, s: str, b: base) void = {
|
fn ck_int_ovf(id: i32, s: str, b: strconv.base) void = {
|
||||||
match (stoi(s, b)) {
|
match (strconv.stoi(s, b)) {
|
||||||
case let v: int => abort();
|
case let v: int => abort();
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => { };
|
case let e: strconv.overflow => { };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
fn ck_uint_ovf(id: i32, s: str, b: base) void = {
|
fn ck_uint_ovf(id: i32, s: str, b: strconv.base) void = {
|
||||||
match (stou(s, b)) {
|
match (strconv.stou(s, b)) {
|
||||||
case let v: uint => abort();
|
case let v: uint => abort();
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => { };
|
case let e: strconv.overflow => { };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// ref/hare/strconv/stoi.ha:56-79.
|
// ref/hare/strconv/stoi.ha:56-79.
|
||||||
@test fn test_stoi64() void = {
|
@test fn test_stoi64() void = {
|
||||||
cki_inv(1, "", base.DEC, 0);
|
cki_inv(1, "", strconv.base.DEC, 0);
|
||||||
cki_inv(2, "abc", base.DEC, 0);
|
cki_inv(2, "abc", strconv.base.DEC, 0);
|
||||||
cki_inv(3, "1a", base.DEC, 1);
|
cki_inv(3, "1a", strconv.base.DEC, 1);
|
||||||
cki_inv(4, "+", base.DEC, 1);
|
cki_inv(4, "+", strconv.base.DEC, 1);
|
||||||
cki_inv(5, "-+", base.DEC, 1);
|
cki_inv(5, "-+", strconv.base.DEC, 1);
|
||||||
cki_inv(6, "-z", base.DEC, 1);
|
cki_inv(6, "-z", strconv.base.DEC, 1);
|
||||||
|
|
||||||
cki_ovf(7, "9223372036854775808", base.DEC);
|
cki_ovf(7, "9223372036854775808", strconv.base.DEC);
|
||||||
cki_ovf(8, "-9223372036854775809", base.DEC);
|
cki_ovf(8, "-9223372036854775809", strconv.base.DEC);
|
||||||
// The last multiply wraps to a value larger than its prefix, so a
|
// The last multiply wraps to a value larger than its prefix, so a
|
||||||
// post-arithmetic `n < old` check misses this overflow.
|
// post-arithmetic `n < old` check misses this overflow.
|
||||||
cki_ovf(19, "21000000000000000000", base.DEC);
|
cki_ovf(19, "21000000000000000000", strconv.base.DEC);
|
||||||
|
|
||||||
cki(9, "0", base.DEC, 0);
|
cki(9, "0", strconv.base.DEC, 0);
|
||||||
cki(10, "1", base.DEC, 1);
|
cki(10, "1", strconv.base.DEC, 1);
|
||||||
cki(11, "+1", base.DEC, 1);
|
cki(11, "+1", strconv.base.DEC, 1);
|
||||||
cki(12, "-1", base.DEC, -1);
|
cki(12, "-1", strconv.base.DEC, -1);
|
||||||
cki(13, "9223372036854775807", base.DEC, 9223372036854775807i64);
|
cki(13, "9223372036854775807", strconv.base.DEC, 9223372036854775807i64);
|
||||||
// I64_MIN. Spelled -I64_MAX-1 (Hare's own two's-complement identity,
|
// I64_MIN. Spelled -I64_MAX-1 (Hare's own two's-complement identity,
|
||||||
// stoi64 comment in stoi.ha:11) because the wwstage mis-lexes the
|
// stoi64 comment in stoi.ha:11) because the wwstage mis-lexes the
|
||||||
// direct literal -9223372036854775808 (and types.I64_MIN) to 0 —
|
// direct literal -9223372036854775808 (and types.I64_MIN) to 0 —
|
||||||
// proj #245. The INPUT string is unaffected; stoi64 parses it to the
|
// proj #245. The INPUT string is unaffected; stoi64 parses it to the
|
||||||
// correct value on both stages. This isolates the parse test from #245.
|
// correct value on both stages. This isolates the parse test from #245.
|
||||||
cki(14, "-9223372036854775808", base.DEC, -9223372036854775807i64 - 1i64);
|
cki(14, "-9223372036854775808", strconv.base.DEC, -9223372036854775807i64 - 1i64);
|
||||||
|
|
||||||
// width wrapper boundaries (ref/hare/strconv/stoi.ha:74-78).
|
// width wrapper boundaries (ref/hare/strconv/stoi.ha:74-78).
|
||||||
cki32_ovf(15, "2147483648", base.DEC);
|
cki32_ovf(15, "2147483648", strconv.base.DEC);
|
||||||
cki32_ovf(16, "-2147483649", base.DEC);
|
cki32_ovf(16, "-2147483649", strconv.base.DEC);
|
||||||
cki32(17, "2147483647", base.DEC, 2147483647i32);
|
cki32(17, "2147483647", strconv.base.DEC, 2147483647i32);
|
||||||
cki32(18, "-2147483648", base.DEC, -2147483648i32);
|
cki32(18, "-2147483648", strconv.base.DEC, -2147483648i32);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ref/hare/strconv/stoi.ha:81-86.
|
// ref/hare/strconv/stoi.ha:81-86.
|
||||||
@test fn test_stoi64_bases() void = {
|
@test fn test_stoi64_bases() void = {
|
||||||
cki(20, "-7f", base.HEX, -127i64); // -0x7f
|
cki(20, "-7f", strconv.base.HEX, -127i64); // -0x7f
|
||||||
cki(21, "7F", base.HEX, 127i64); // 0x7f
|
cki(21, "7F", strconv.base.HEX, 127i64); // 0x7f
|
||||||
cki(22, "37", base.OCT, 31i64); // 0o37
|
cki(22, "37", strconv.base.OCT, 31i64); // 0o37
|
||||||
cki(23, "-110101", base.BIN, -53i64); // -0b110101
|
cki(23, "-110101", strconv.base.BIN, -53i64); // -0b110101
|
||||||
};
|
};
|
||||||
|
|
||||||
// ref/hare/strconv/stou.ha:116-130.
|
// ref/hare/strconv/stou.ha:116-130.
|
||||||
@test fn test_stou64() void = {
|
@test fn test_stou64() void = {
|
||||||
cku_inv(30, "", base.DEC, 0);
|
cku_inv(30, "", strconv.base.DEC, 0);
|
||||||
cku_inv(31, "+", base.DEC, 1);
|
cku_inv(31, "+", strconv.base.DEC, 1);
|
||||||
cku_inv(32, "+a", base.DEC, 1);
|
cku_inv(32, "+a", strconv.base.DEC, 1);
|
||||||
cku_inv(33, "abc", base.DEC, 0);
|
cku_inv(33, "abc", strconv.base.DEC, 0);
|
||||||
cku_inv(34, "1a", base.DEC, 1);
|
cku_inv(34, "1a", strconv.base.DEC, 1);
|
||||||
|
|
||||||
cku_ovf(35, "18446744073709551616", base.DEC);
|
cku_ovf(35, "18446744073709551616", strconv.base.DEC);
|
||||||
cku_ovf(36, "184467440737095516150", base.DEC);
|
cku_ovf(36, "184467440737095516150", strconv.base.DEC);
|
||||||
cku_ovf(37, "-1", base.DEC);
|
cku_ovf(37, "-1", strconv.base.DEC);
|
||||||
cku_ovf(46, "21000000000000000000", base.DEC);
|
cku_ovf(46, "21000000000000000000", strconv.base.DEC);
|
||||||
|
|
||||||
cku(38, "0", base.DEC, 0u64);
|
cku(38, "0", strconv.base.DEC, 0u64);
|
||||||
cku(39, "1", base.DEC, 1u64);
|
cku(39, "1", strconv.base.DEC, 1u64);
|
||||||
cku(40, "18446744073709551615", base.DEC, 18446744073709551615u64);
|
cku(40, "18446744073709551615", strconv.base.DEC, 18446744073709551615u64);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ref/hare/strconv/stou.ha:132-138.
|
// ref/hare/strconv/stou.ha:132-138.
|
||||||
@test fn test_stou64_bases() void = {
|
@test fn test_stou64_bases() void = {
|
||||||
cku(41, "f", base.HEX_LOWER, 15u64); // 0xf
|
cku(41, "f", strconv.base.HEX_LOWER, 15u64); // 0xf
|
||||||
cku(42, "7f", base.HEX, 127u64); // 0x7f
|
cku(42, "7f", strconv.base.HEX, 127u64); // 0x7f
|
||||||
cku(43, "7F", base.HEX, 127u64); // 0x7f
|
cku(43, "7F", strconv.base.HEX, 127u64); // 0x7f
|
||||||
cku(44, "37", base.OCT, 31u64); // 0o37
|
cku(44, "37", strconv.base.OCT, 31u64); // 0o37
|
||||||
cku(45, "110101", base.BIN, 53u64); // 0b110101
|
cku(45, "110101", strconv.base.BIN, 53u64); // 0b110101
|
||||||
};
|
};
|
||||||
|
|
||||||
// stoi / stou / stoz — int/uint/size machine-word wrappers
|
// stoi / stou / stoz — int/uint/size machine-word wrappers
|
||||||
@@ -192,102 +192,102 @@ fn ck_uint_ovf(id: i32, s: str, b: base) void = {
|
|||||||
// 8B, so the iN/uN clamp is a no-op: the full i64/u64 range parses with
|
// 8B, so the iN/uN clamp is a no-op: the full i64/u64 range parses with
|
||||||
// no spurious overflow. That no-clamp fidelity is what these check.
|
// no spurious overflow. That no-clamp fidelity is what these check.
|
||||||
@test fn test_stoi_stou_stoz() void = {
|
@test fn test_stoi_stou_stoz() void = {
|
||||||
ck_int(50, "0", base.DEC, 0);
|
ck_int(50, "0", strconv.base.DEC, 0);
|
||||||
ck_int(51, "-1", base.DEC, -1);
|
ck_int(51, "-1", strconv.base.DEC, -1);
|
||||||
ck_int(52, "9223372036854775807", base.DEC, 9223372036854775807i64: int); // I64_MAX fits int
|
ck_int(52, "9223372036854775807", strconv.base.DEC, 9223372036854775807i64: int); // I64_MAX fits int
|
||||||
ck_int_ovf(53, "9223372036854775808", base.DEC);
|
ck_int_ovf(53, "9223372036854775808", strconv.base.DEC);
|
||||||
ck_int_ovf(54, "-9223372036854775809", base.DEC);
|
ck_int_ovf(54, "-9223372036854775809", strconv.base.DEC);
|
||||||
|
|
||||||
ck_uint(55, "0", base.DEC, 0u64: uint);
|
ck_uint(55, "0", strconv.base.DEC, 0u64: uint);
|
||||||
ck_uint(56, "18446744073709551615", base.DEC, 18446744073709551615u64: uint); // U64_MAX fits uint
|
ck_uint(56, "18446744073709551615", strconv.base.DEC, 18446744073709551615u64: uint); // U64_MAX fits uint
|
||||||
ck_uint_ovf(57, "18446744073709551616", base.DEC);
|
ck_uint_ovf(57, "18446744073709551616", strconv.base.DEC);
|
||||||
ck_uint_ovf(58, "-1", base.DEC);
|
ck_uint_ovf(58, "-1", strconv.base.DEC);
|
||||||
|
|
||||||
ck_size(59, "0", base.DEC, 0u64: size);
|
ck_size(59, "0", strconv.base.DEC, 0u64: size);
|
||||||
ck_size(60, "18446744073709551615", base.DEC, 18446744073709551615u64: size); // U64_MAX fits size
|
ck_size(60, "18446744073709551615", strconv.base.DEC, 18446744073709551615u64: size); // U64_MAX fits size
|
||||||
|
|
||||||
// bases route through the same parseint core.
|
// bases route through the same parseint core.
|
||||||
ck_int(61, "-7f", base.HEX, -127i64: int); // -0x7f
|
ck_int(61, "-7f", strconv.base.HEX, -127i64: int); // -0x7f
|
||||||
ck_uint(62, "110101", base.BIN, 53u64: uint); // 0b110101
|
ck_uint(62, "110101", strconv.base.BIN, 53u64: uint); // 0b110101
|
||||||
};
|
};
|
||||||
|
|
||||||
@test fn test_narrow_parse_boundaries() void = {
|
@test fn test_narrow_parse_boundaries() void = {
|
||||||
match (stoi16("32767", base.DEC)) {
|
match (strconv.stoi16("32767", strconv.base.DEC)) {
|
||||||
case let v: i16 => assert(v == 32767i16);
|
case let v: i16 => assert(v == 32767i16);
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
match (stoi16("-32768", base.DEC)) {
|
match (strconv.stoi16("-32768", strconv.base.DEC)) {
|
||||||
case let v: i16 => assert(v == -32768i16);
|
case let v: i16 => assert(v == -32768i16);
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
match (stoi16("32768", base.DEC)) {
|
match (strconv.stoi16("32768", strconv.base.DEC)) {
|
||||||
case let v: i16 => abort();
|
case let v: i16 => abort();
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => { };
|
case let e: strconv.overflow => { };
|
||||||
};
|
};
|
||||||
match (stoi16("-32769", base.DEC)) {
|
match (strconv.stoi16("-32769", strconv.base.DEC)) {
|
||||||
case let v: i16 => abort();
|
case let v: i16 => abort();
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => { };
|
case let e: strconv.overflow => { };
|
||||||
};
|
};
|
||||||
|
|
||||||
match (stoi8("127", base.DEC)) {
|
match (strconv.stoi8("127", strconv.base.DEC)) {
|
||||||
case let v: i8 => assert(v == 127i8);
|
case let v: i8 => assert(v == 127i8);
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
match (stoi8("-128", base.DEC)) {
|
match (strconv.stoi8("-128", strconv.base.DEC)) {
|
||||||
case let v: i8 => assert(v == -128i8);
|
case let v: i8 => assert(v == -128i8);
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
match (stoi8("128", base.DEC)) {
|
match (strconv.stoi8("128", strconv.base.DEC)) {
|
||||||
case let v: i8 => abort();
|
case let v: i8 => abort();
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => { };
|
case let e: strconv.overflow => { };
|
||||||
};
|
};
|
||||||
match (stoi8("-129", base.DEC)) {
|
match (strconv.stoi8("-129", strconv.base.DEC)) {
|
||||||
case let v: i8 => abort();
|
case let v: i8 => abort();
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => { };
|
case let e: strconv.overflow => { };
|
||||||
};
|
};
|
||||||
|
|
||||||
match (stou16("65535", base.DEC)) {
|
match (strconv.stou16("65535", strconv.base.DEC)) {
|
||||||
case let v: u16 => assert(v == 65535u16);
|
case let v: u16 => assert(v == 65535u16);
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
match (stou16("65536", base.DEC)) {
|
match (strconv.stou16("65536", strconv.base.DEC)) {
|
||||||
case let v: u16 => abort();
|
case let v: u16 => abort();
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => { };
|
case let e: strconv.overflow => { };
|
||||||
};
|
};
|
||||||
match (stou8("255", base.DEC)) {
|
match (strconv.stou8("255", strconv.base.DEC)) {
|
||||||
case let v: u8 => assert(v == 255u8);
|
case let v: u8 => assert(v == 255u8);
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => abort();
|
case let e: strconv.overflow => abort();
|
||||||
};
|
};
|
||||||
match (stou8("256", base.DEC)) {
|
match (strconv.stou8("256", strconv.base.DEC)) {
|
||||||
case let v: u8 => abort();
|
case let v: u8 => abort();
|
||||||
case let e: invalid => abort();
|
case let e: strconv.invalid => abort();
|
||||||
case let e: overflow => { };
|
case let e: strconv.overflow => { };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@test fn test_strerror_static() void = {
|
@test fn test_strerror_static() void = {
|
||||||
let inv: invalid = 0: invalid;
|
let inv: strconv.invalid = 0: strconv.invalid;
|
||||||
let ie: error = inv;
|
let ie: strconv.error = inv;
|
||||||
let ia: str = strerror(ie);
|
let ia: str = strconv.strerror(ie);
|
||||||
let ib: str = strerror(ie);
|
let ib: str = strconv.strerror(ie);
|
||||||
assert(ia.ptr == ib.ptr);
|
assert(ia.ptr == ib.ptr);
|
||||||
assert(streq(ia, "input is not a valid number"));
|
assert(streq(ia, "input is not a valid number"));
|
||||||
|
|
||||||
let ov: overflow;
|
let ov: strconv.overflow;
|
||||||
let oe: error = ov;
|
let oe: strconv.error = ov;
|
||||||
let oa: str = strerror(oe);
|
let oa: str = strconv.strerror(oe);
|
||||||
let ob: str = strerror(oe);
|
let ob: str = strconv.strerror(oe);
|
||||||
assert(oa.ptr == ob.ptr);
|
assert(oa.ptr == ob.ptr);
|
||||||
assert(streq(oa, "input number doesn't fit target type"));
|
assert(streq(oa, "input number doesn't fit target type"));
|
||||||
};
|
};
|
||||||
@@ -314,48 +314,48 @@ fn cks(id: i32, got: str, want: str) void = {
|
|||||||
|
|
||||||
// ref/hare/strconv/utos.ha:74-83.
|
// ref/hare/strconv/utos.ha:74-83.
|
||||||
@test fn test_u64tos_bases() void = {
|
@test fn test_u64tos_bases() void = {
|
||||||
cks(70, u64tos(26u64, base.BIN), "11010"); // 0b11010
|
cks(70, strconv.u64tos(26u64, strconv.base.BIN), "11010"); // 0b11010
|
||||||
cks(71, u64tos(342391u64, base.OCT), "1234567"); // 0o1234567
|
cks(71, strconv.u64tos(342391u64, strconv.base.OCT), "1234567"); // 0o1234567
|
||||||
cks(72, u64tos(123456789u64, base.DEC), "123456789");
|
cks(72, strconv.u64tos(123456789u64, strconv.base.DEC), "123456789");
|
||||||
cks(73, u64tos(81985529216486895u64, base.HEX), "123456789ABCDEF"); // 0x123456789ABCDEF
|
cks(73, strconv.u64tos(81985529216486895u64, strconv.base.HEX), "123456789ABCDEF"); // 0x123456789ABCDEF
|
||||||
cks(74, u64tos(81985529216486895u64, base.HEX_UPPER), "123456789ABCDEF");
|
cks(74, strconv.u64tos(81985529216486895u64, strconv.base.HEX_UPPER), "123456789ABCDEF");
|
||||||
cks(75, u64tos(81985529216486895u64, base.HEX_LOWER), "123456789abcdef");
|
cks(75, strconv.u64tos(81985529216486895u64, strconv.base.HEX_LOWER), "123456789abcdef");
|
||||||
cks(76, u64tos(18446744073709551615u64, base.BIN), // U64_MAX
|
cks(76, strconv.u64tos(18446744073709551615u64, strconv.base.BIN), // U64_MAX
|
||||||
"1111111111111111111111111111111111111111111111111111111111111111");
|
"1111111111111111111111111111111111111111111111111111111111111111");
|
||||||
};
|
};
|
||||||
|
|
||||||
// ref/hare/strconv/utos.ha:85-103.
|
// ref/hare/strconv/utos.ha:85-103.
|
||||||
@test fn test_u64tos() void = {
|
@test fn test_u64tos() void = {
|
||||||
cks(77, u64tos(1234u64, base.DEC), "1234");
|
cks(77, strconv.u64tos(1234u64, strconv.base.DEC), "1234");
|
||||||
cks(78, u64tos(4321u64, base.DEC), "4321");
|
cks(78, strconv.u64tos(4321u64, strconv.base.DEC), "4321");
|
||||||
cks(79, u64tos(0u64, base.DEC), "0"); // U64_MIN
|
cks(79, strconv.u64tos(0u64, strconv.base.DEC), "0"); // U64_MIN
|
||||||
cks(80, u64tos(18446744073709551615u64, base.DEC), "18446744073709551615"); // U64_MAX
|
cks(80, strconv.u64tos(18446744073709551615u64, strconv.base.DEC), "18446744073709551615"); // U64_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
// ref/hare/strconv/itos.ha:54-63.
|
// ref/hare/strconv/itos.ha:54-63.
|
||||||
@test fn test_i64tos_bases() void = {
|
@test fn test_i64tos_bases() void = {
|
||||||
cks(81, i64tos(26i64, base.BIN), "11010");
|
cks(81, strconv.i64tos(26i64, strconv.base.BIN), "11010");
|
||||||
cks(82, i64tos(342391i64, base.OCT), "1234567");
|
cks(82, strconv.i64tos(342391i64, strconv.base.OCT), "1234567");
|
||||||
cks(83, i64tos(123456789i64, base.DEC), "123456789");
|
cks(83, strconv.i64tos(123456789i64, strconv.base.DEC), "123456789");
|
||||||
cks(84, i64tos(81985529216486895i64, base.HEX), "123456789ABCDEF");
|
cks(84, strconv.i64tos(81985529216486895i64, strconv.base.HEX), "123456789ABCDEF");
|
||||||
cks(85, i64tos(81985529216486895i64, base.HEX_UPPER), "123456789ABCDEF");
|
cks(85, strconv.i64tos(81985529216486895i64, strconv.base.HEX_UPPER), "123456789ABCDEF");
|
||||||
cks(86, i64tos(81985529216486895i64, base.HEX_LOWER), "123456789abcdef");
|
cks(86, strconv.i64tos(81985529216486895i64, strconv.base.HEX_LOWER), "123456789abcdef");
|
||||||
// I64_MIN binary = '1' + 63 zeros, prefixed '-'. Spelled -I64_MAX-1
|
// I64_MIN binary = '1' + 63 zeros, prefixed '-'. Spelled -I64_MAX-1
|
||||||
// (proj #245: wwstage mis-lexes the direct 2^63 / types.I64_MIN literal).
|
// (proj #245: wwstage mis-lexes the direct 2^63 / types.I64_MIN literal).
|
||||||
cks(87, i64tos(-9223372036854775807i64 - 1i64, base.BIN),
|
cks(87, strconv.i64tos(-9223372036854775807i64 - 1i64, strconv.base.BIN),
|
||||||
"-1000000000000000000000000000000000000000000000000000000000000000");
|
"-1000000000000000000000000000000000000000000000000000000000000000");
|
||||||
};
|
};
|
||||||
|
|
||||||
// ref/hare/strconv/itos.ha:65-87.
|
// ref/hare/strconv/itos.ha:65-87.
|
||||||
@test fn test_i64tos() void = {
|
@test fn test_i64tos() void = {
|
||||||
cks(88, i64tos(1234i64, base.DEC), "1234");
|
cks(88, strconv.i64tos(1234i64, strconv.base.DEC), "1234");
|
||||||
cks(89, i64tos(4321i64, base.DEC), "4321");
|
cks(89, strconv.i64tos(4321i64, strconv.base.DEC), "4321");
|
||||||
cks(90, i64tos(-1337i64, base.DEC), "-1337");
|
cks(90, strconv.i64tos(-1337i64, strconv.base.DEC), "-1337");
|
||||||
cks(91, i64tos(0i64, base.DEC), "0");
|
cks(91, strconv.i64tos(0i64, strconv.base.DEC), "0");
|
||||||
cks(92, i64tos(9223372036854775807i64, base.DEC), "9223372036854775807"); // I64_MAX
|
cks(92, strconv.i64tos(9223372036854775807i64, strconv.base.DEC), "9223372036854775807"); // I64_MAX
|
||||||
// drew probe-1: NEG-of-I64_MIN + i64→u64 reinterpret preserves the
|
// drew probe-1: NEG-of-I64_MIN + i64→u64 reinterpret preserves the
|
||||||
// two's-complement bit pattern. I64_MIN spelled -I64_MAX-1 (proj #245).
|
// two's-complement bit pattern. I64_MIN spelled -I64_MAX-1 (proj #245).
|
||||||
cks(93, i64tos(-9223372036854775807i64 - 1i64, base.DEC),
|
cks(93, strconv.i64tos(-9223372036854775807i64 - 1i64, strconv.base.DEC),
|
||||||
"-9223372036854775808");
|
"-9223372036854775808");
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -363,11 +363,11 @@ fn cks(id: i32, got: str, want: str) void = {
|
|||||||
// all 8B, so the full i64/u64 range renders untruncated. ww-authored
|
// all 8B, so the full i64/u64 range renders untruncated. ww-authored
|
||||||
// (Hare's wrappers are trivial aliases): itos.ha:37-52, utos.ha:44-72.
|
// (Hare's wrappers are trivial aliases): itos.ha:37-52, utos.ha:44-72.
|
||||||
@test fn test_word_wrappers() void = {
|
@test fn test_word_wrappers() void = {
|
||||||
cks(94, itos(-1337, base.DEC), "-1337");
|
cks(94, strconv.itos(-1337, strconv.base.DEC), "-1337");
|
||||||
cks(95, itos(9223372036854775807i64: int, base.DEC), "9223372036854775807");
|
cks(95, strconv.itos(9223372036854775807i64: int, strconv.base.DEC), "9223372036854775807");
|
||||||
cks(96, utos(18446744073709551615u64: uint, base.DEC), "18446744073709551615");
|
cks(96, strconv.utos(18446744073709551615u64: uint, strconv.base.DEC), "18446744073709551615");
|
||||||
cks(97, ztos(255u64: size, base.HEX), "FF");
|
cks(97, strconv.ztos(255u64: size, strconv.base.HEX), "FF");
|
||||||
cks(98, uptrtos(4096u64: uintptr, base.DEC), "4096");
|
cks(98, strconv.uptrtos(4096u64: uintptr, strconv.base.DEC), "4096");
|
||||||
cks(99, i32tos(-2147483648i32, base.DEC), "-2147483648");
|
cks(99, strconv.i32tos(-2147483648i32, strconv.base.DEC), "-2147483648");
|
||||||
cks(100, u8tos(255u8, base.HEX_LOWER), "ff");
|
cks(100, strconv.u8tos(255u8, strconv.base.HEX_LOWER), "ff");
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,75 +12,75 @@ import strconv;
|
|||||||
import math;
|
import math;
|
||||||
|
|
||||||
|
|
||||||
fn chk64(s: str, b: base, want: f64) bool = {
|
fn chk64(s: str, b: strconv.base, want: f64) bool = {
|
||||||
match (stof64(s, b)) {
|
match (strconv.stof64(s, b)) {
|
||||||
case let v: f64 => { return math.f64bits(v) == math.f64bits(want); };
|
case let v: f64 => { return math.f64bits(v) == math.f64bits(want); };
|
||||||
case let e: invalid => { return false; };
|
case let e: strconv.invalid => { return false; };
|
||||||
case let e: overflow => { return false; };
|
case let e: strconv.overflow => { return false; };
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn chk32(s: str, b: base, want: f32) bool = {
|
fn chk32(s: str, b: strconv.base, want: f32) bool = {
|
||||||
match (stof32(s, b)) {
|
match (strconv.stof32(s, b)) {
|
||||||
case let v: f32 => { return math.f32bits(v) == math.f32bits(want); };
|
case let v: f32 => { return math.f32bits(v) == math.f32bits(want); };
|
||||||
case let e: invalid => { return false; };
|
case let e: strconv.invalid => { return false; };
|
||||||
case let e: overflow => { return false; };
|
case let e: strconv.overflow => { return false; };
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn ovf64(s: str, b: base) bool = {
|
fn ovf64(s: str, b: strconv.base) bool = {
|
||||||
match (stof64(s, b)) {
|
match (strconv.stof64(s, b)) {
|
||||||
case let v: f64 => { return false; };
|
case let v: f64 => { return false; };
|
||||||
case let e: invalid => { return false; };
|
case let e: strconv.invalid => { return false; };
|
||||||
case let e: overflow => { return true; };
|
case let e: strconv.overflow => { return true; };
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn ovf32(s: str, b: base) bool = {
|
fn ovf32(s: str, b: strconv.base) bool = {
|
||||||
match (stof32(s, b)) {
|
match (strconv.stof32(s, b)) {
|
||||||
case let v: f32 => { return false; };
|
case let v: f32 => { return false; };
|
||||||
case let e: invalid => { return false; };
|
case let e: strconv.invalid => { return false; };
|
||||||
case let e: overflow => { return true; };
|
case let e: strconv.overflow => { return true; };
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// inv64 — the invalid byte-index, or -1 if not invalid.
|
// inv64 — the invalid byte-index, or -1 if not invalid.
|
||||||
fn inv64(s: str) i32 = {
|
fn inv64(s: str) i32 = {
|
||||||
match (stof64(s, base.DEC)) {
|
match (strconv.stof64(s, strconv.base.DEC)) {
|
||||||
case let v: f64 => { return -1; };
|
case let v: f64 => { return -1; };
|
||||||
case let e: invalid => { return (e: i32); };
|
case let e: strconv.invalid => { return (e: i32); };
|
||||||
case let e: overflow => { return -1; };
|
case let e: strconv.overflow => { return -1; };
|
||||||
};
|
};
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn inv32(s: str) i32 = {
|
fn inv32(s: str) i32 = {
|
||||||
match (stof32(s, base.DEC)) {
|
match (strconv.stof32(s, strconv.base.DEC)) {
|
||||||
case let v: f32 => { return -1; };
|
case let v: f32 => { return -1; };
|
||||||
case let e: invalid => { return (e: i32); };
|
case let e: strconv.invalid => { return (e: i32); };
|
||||||
case let e: overflow => { return -1; };
|
case let e: strconv.overflow => { return -1; };
|
||||||
};
|
};
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn nan64(s: str) bool = {
|
fn nan64(s: str) bool = {
|
||||||
match (stof64(s, base.DEC)) {
|
match (strconv.stof64(s, strconv.base.DEC)) {
|
||||||
case let v: f64 => { return math.isnan(v); };
|
case let v: f64 => { return math.isnan(v); };
|
||||||
case let e: invalid => { return false; };
|
case let e: strconv.invalid => { return false; };
|
||||||
case let e: overflow => { return false; };
|
case let e: strconv.overflow => { return false; };
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn nan32(s: str) bool = {
|
fn nan32(s: str) bool = {
|
||||||
match (stof32(s, base.DEC)) {
|
match (strconv.stof32(s, strconv.base.DEC)) {
|
||||||
case let v: f32 => { return math.isnan((v: f64)); };
|
case let v: f32 => { return math.isnan((v: f64)); };
|
||||||
case let e: invalid => { return false; };
|
case let e: strconv.invalid => { return false; };
|
||||||
case let e: overflow => { return false; };
|
case let e: strconv.overflow => { return false; };
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@@ -93,20 +93,20 @@ fn nan32(s: str) bool = {
|
|||||||
let nzero: f64 = math.f64frombits(1u64 << 63u64); // -0.0 (1<<63 dodges the I64_MIN-literal emit bug #144)
|
let nzero: f64 = math.f64frombits(1u64 << 63u64); // -0.0 (1<<63 dodges the I64_MIN-literal emit bug #144)
|
||||||
let subn: f64 = math.f64frombits(0x0000000000000001u64); // 5e-324
|
let subn: f64 = math.f64frombits(0x0000000000000001u64); // 5e-324
|
||||||
|
|
||||||
assert(!(!chk64("0", base.DEC, 0.0)));
|
assert(!(!chk64("0", strconv.base.DEC, 0.0)));
|
||||||
assert(!(!chk64("200", base.DEC, 200.0)));
|
assert(!(!chk64("200", strconv.base.DEC, 200.0)));
|
||||||
assert(!(!chk64("12345", base.DEC, 12345.0)));
|
assert(!(!chk64("12345", strconv.base.DEC, 12345.0)));
|
||||||
assert(!(!chk64("+112233445566778899", base.DEC, 1.122334455667789e17)));
|
assert(!(!chk64("+112233445566778899", strconv.base.DEC, 1.122334455667789e17)));
|
||||||
assert(!(!chk64("3.14", base.DEC, 3.14)));
|
assert(!(!chk64("3.14", strconv.base.DEC, 3.14)));
|
||||||
assert(!(!chk64("2.99792458E+8", base.DEC, 299792458.0)));
|
assert(!(!chk64("2.99792458E+8", strconv.base.DEC, 299792458.0)));
|
||||||
assert(!(!chk64("6.022e23", base.DEC, 6.022e23)));
|
assert(!(!chk64("6.022e23", strconv.base.DEC, 6.022e23)));
|
||||||
assert(!(!ovf64("1e310", base.DEC)));
|
assert(!(!ovf64("1e310", strconv.base.DEC)));
|
||||||
assert(!(!chk64("9007199254740991", base.DEC, 9007199254740991.0)));
|
assert(!(!chk64("9007199254740991", strconv.base.DEC, 9007199254740991.0)));
|
||||||
assert(!(!chk64("90071992547409915", base.DEC, 90071992547409920.0)));
|
assert(!(!chk64("90071992547409915", strconv.base.DEC, 90071992547409920.0)));
|
||||||
assert(!(!chk64("90071992547409925", base.DEC, 90071992547409920.0)));
|
assert(!(!chk64("90071992547409925", strconv.base.DEC, 90071992547409920.0)));
|
||||||
assert(!(!chk64("2.2250738585072014e-308", base.DEC, 2.2250738585072014e-308)));
|
assert(!(!chk64("2.2250738585072014e-308", strconv.base.DEC, 2.2250738585072014e-308)));
|
||||||
assert(!(!chk64("-1e-324", base.DEC, nzero)));
|
assert(!(!chk64("-1e-324", strconv.base.DEC, nzero)));
|
||||||
assert(!(!chk64("5e-324", base.DEC, subn)));
|
assert(!(!chk64("5e-324", strconv.base.DEC, subn)));
|
||||||
|
|
||||||
// Decimal-engine edge probes migrated here when the white-box
|
// Decimal-engine edge probes migrated here when the white-box
|
||||||
// decimaltest.ww retired (#16): the external-test idiom can't reach
|
// decimaltest.ww retired (#16): the external-test idiom can't reach
|
||||||
@@ -117,20 +117,20 @@ fn nan32(s: str) bool = {
|
|||||||
// Micro-gap (rule-7, honest): decimal_shift's k=0 early-return no-op
|
// Micro-gap (rule-7, honest): decimal_shift's k=0 early-return no-op
|
||||||
// is a trivial guard, not reached transitively by any slow-path input
|
// is a trivial guard, not reached transitively by any slow-path input
|
||||||
// — consciously un-migrated, not silently dropped.
|
// — consciously un-migrated, not silently dropped.
|
||||||
assert(!(!chk64("9999999999999999", base.DEC, 1.0e16)));
|
assert(!(!chk64("9999999999999999", strconv.base.DEC, 1.0e16)));
|
||||||
assert(!(!chk64("100000000000000000001", base.DEC, 1.0e20)));
|
assert(!(!chk64("100000000000000000001", strconv.base.DEC, 1.0e20)));
|
||||||
|
|
||||||
assert(!(inv64("") != 0));
|
assert(!(inv64("") != 0));
|
||||||
assert(!(inv64("0ZO") != 1));
|
assert(!(inv64("0ZO") != 1));
|
||||||
assert(!(inv64("1.23ezz") != 5));
|
assert(!(inv64("1.23ezz") != 5));
|
||||||
|
|
||||||
assert(!(!chk64("Infinity", base.DEC, inf)));
|
assert(!(!chk64("Infinity", strconv.base.DEC, inf)));
|
||||||
assert(!(!chk64("+Infinity", base.DEC, inf)));
|
assert(!(!chk64("+Infinity", strconv.base.DEC, inf)));
|
||||||
assert(!(!chk64("-Infinity", base.DEC, ninf)));
|
assert(!(!chk64("-Infinity", strconv.base.DEC, ninf)));
|
||||||
assert(!(!chk64("infinity", base.DEC, inf)));
|
assert(!(!chk64("infinity", strconv.base.DEC, inf)));
|
||||||
assert(!(!chk64("inFinIty", base.DEC, inf)));
|
assert(!(!chk64("inFinIty", strconv.base.DEC, inf)));
|
||||||
assert(!(!chk64("-infinity", base.DEC, ninf)));
|
assert(!(!chk64("-infinity", strconv.base.DEC, ninf)));
|
||||||
assert(!(!chk64("-infiNity", base.DEC, ninf)));
|
assert(!(!chk64("-infiNity", strconv.base.DEC, ninf)));
|
||||||
assert(!(!nan64("NaN")));
|
assert(!(!nan64("NaN")));
|
||||||
assert(!(!nan64("nan")));
|
assert(!(!nan64("nan")));
|
||||||
assert(!(!nan64("naN")));
|
assert(!(!nan64("naN")));
|
||||||
@@ -144,34 +144,34 @@ fn nan32(s: str) bool = {
|
|||||||
let nzero: f32 = math.f32frombits(0x80000000u32); // -0.0
|
let nzero: f32 = math.f32frombits(0x80000000u32); // -0.0
|
||||||
let subn: f32 = math.f32frombits(0x00000001u32); // 1e-45
|
let subn: f32 = math.f32frombits(0x00000001u32); // 1e-45
|
||||||
|
|
||||||
assert(!(!chk32("0", base.DEC, 0.0f32)));
|
assert(!(!chk32("0", strconv.base.DEC, 0.0f32)));
|
||||||
assert(!(!chk32("1e10", base.DEC, 1.0e10f32)));
|
assert(!(!chk32("1e10", strconv.base.DEC, 1.0e10f32)));
|
||||||
assert(!(!chk32("299792458", base.DEC, 299792458.0f32)));
|
assert(!(!chk32("299792458", strconv.base.DEC, 299792458.0f32)));
|
||||||
assert(!(!chk32("6.022e23", base.DEC, 6.022e23f32)));
|
assert(!(!chk32("6.022e23", strconv.base.DEC, 6.022e23f32)));
|
||||||
assert(!(!ovf32("1e40", base.DEC)));
|
assert(!(!ovf32("1e40", strconv.base.DEC)));
|
||||||
assert(!(!chk32("16777215", base.DEC, 16777215.0f32)));
|
assert(!(!chk32("16777215", strconv.base.DEC, 16777215.0f32)));
|
||||||
assert(!(!chk32("167772155", base.DEC, 167772160.0f32)));
|
assert(!(!chk32("167772155", strconv.base.DEC, 167772160.0f32)));
|
||||||
assert(!(!chk32("167772145", base.DEC, 167772140.0f32)));
|
assert(!(!chk32("167772145", strconv.base.DEC, 167772140.0f32)));
|
||||||
assert(!(!chk32("6.62607015e-34", base.DEC, 6.62607015e-34f32)));
|
assert(!(!chk32("6.62607015e-34", strconv.base.DEC, 6.62607015e-34f32)));
|
||||||
assert(!(!chk32("1.1754944e-38", base.DEC, 1.1754944e-38f32)));
|
assert(!(!chk32("1.1754944e-38", strconv.base.DEC, 1.1754944e-38f32)));
|
||||||
assert(!(!chk32("-1e-50", base.DEC, nzero)));
|
assert(!(!chk32("-1e-50", strconv.base.DEC, nzero)));
|
||||||
assert(!(!chk32("1e-45", base.DEC, subn)));
|
assert(!(!chk32("1e-45", strconv.base.DEC, subn)));
|
||||||
|
|
||||||
assert(!(inv32("") != 0));
|
assert(!(inv32("") != 0));
|
||||||
assert(!(inv32("0ZO") != 1));
|
assert(!(inv32("0ZO") != 1));
|
||||||
assert(!(inv32("1.23e-zz") != 6));
|
assert(!(inv32("1.23e-zz") != 6));
|
||||||
|
|
||||||
assert(!(!chk32("Infinity", base.DEC, inf)));
|
assert(!(!chk32("Infinity", strconv.base.DEC, inf)));
|
||||||
assert(!(!chk32("+Infinity", base.DEC, inf)));
|
assert(!(!chk32("+Infinity", strconv.base.DEC, inf)));
|
||||||
assert(!(!chk32("-Infinity", base.DEC, ninf)));
|
assert(!(!chk32("-Infinity", strconv.base.DEC, ninf)));
|
||||||
assert(!(!chk32("infinity", base.DEC, inf)));
|
assert(!(!chk32("infinity", strconv.base.DEC, inf)));
|
||||||
assert(!(!chk32("inFinIty", base.DEC, inf)));
|
assert(!(!chk32("inFinIty", strconv.base.DEC, inf)));
|
||||||
assert(!(!chk32("-infinity", base.DEC, ninf)));
|
assert(!(!chk32("-infinity", strconv.base.DEC, ninf)));
|
||||||
assert(!(!chk32("-infiniTy", base.DEC, ninf)));
|
assert(!(!chk32("-infiniTy", strconv.base.DEC, ninf)));
|
||||||
assert(!(!nan32("NaN")));
|
assert(!(!nan32("NaN")));
|
||||||
assert(!(!nan32("nan")));
|
assert(!(!nan32("nan")));
|
||||||
assert(!(!nan32("naN")));
|
assert(!(!nan32("naN")));
|
||||||
assert(!(!chk32("9.19100241453305036800e+20", base.DEC, 9.19100241453305036800e+20f32)));
|
assert(!(!chk32("9.19100241453305036800e+20", strconv.base.DEC, 9.19100241453305036800e+20f32)));
|
||||||
};
|
};
|
||||||
|
|
||||||
// ref/hare/strconv/stof.ha:590. Hex-float surface-form literals (0x1.fp-2,
|
// ref/hare/strconv/stof.ha:590. Hex-float surface-form literals (0x1.fp-2,
|
||||||
@@ -179,36 +179,36 @@ fn nan32(s: str) bool = {
|
|||||||
// has no hex-float literal lexer / no F*_MAX_NORMAL math consts.
|
// has no hex-float literal lexer / no F*_MAX_NORMAL math consts.
|
||||||
|
|
||||||
@test fn stof64_hex() void = {
|
@test fn stof64_hex() void = {
|
||||||
assert(!(!chk64("0p0", base.HEX, 0.0)));
|
assert(!(!chk64("0p0", strconv.base.HEX, 0.0)));
|
||||||
assert(!(!chk64("1p0", base.HEX, 1.0)));
|
assert(!(!chk64("1p0", strconv.base.HEX, 1.0)));
|
||||||
assert(!(!chk64("-1p0", base.HEX_LOWER, -1.0)));
|
assert(!(!chk64("-1p0", strconv.base.HEX_LOWER, -1.0)));
|
||||||
// 0x1.fp-2 = 1.9375 * 2^-2 = 0.484375 (exact).
|
// 0x1.fp-2 = 1.9375 * 2^-2 = 0.484375 (exact).
|
||||||
assert(!(!chk64("1.fp-2", base.HEX, 0.484375)));
|
assert(!(!chk64("1.fp-2", strconv.base.HEX, 0.484375)));
|
||||||
// F64_MAX_NORMAL.
|
// F64_MAX_NORMAL.
|
||||||
if (!chk64("1.fffffffffffffp+1023", base.HEX,
|
if (!chk64("1.fffffffffffffp+1023", strconv.base.HEX,
|
||||||
math.f64frombits(0x7FEFFFFFFFFFFFFFu64))) { abort(); };
|
math.f64frombits(0x7FEFFFFFFFFFFFFFu64))) { abort(); };
|
||||||
// F64_MIN_NORMAL.
|
// F64_MIN_NORMAL.
|
||||||
if (!chk64("1.0000000000000p-1022", base.HEX,
|
if (!chk64("1.0000000000000p-1022", strconv.base.HEX,
|
||||||
math.f64frombits(0x0010000000000000u64))) { abort(); };
|
math.f64frombits(0x0010000000000000u64))) { abort(); };
|
||||||
// F64_MIN_SUBNORMAL.
|
// F64_MIN_SUBNORMAL.
|
||||||
if (!chk64("0.0000000000001p-1022", base.HEX,
|
if (!chk64("0.0000000000001p-1022", strconv.base.HEX,
|
||||||
math.f64frombits(0x0000000000000001u64))) { abort(); };
|
math.f64frombits(0x0000000000000001u64))) { abort(); };
|
||||||
assert(!(!ovf64("1p+1024", base.HEX)));
|
assert(!(!ovf64("1p+1024", strconv.base.HEX)));
|
||||||
assert(!(!chk64("0.00000000000001p-1022", base.HEX, 0.0)));
|
assert(!(!chk64("0.00000000000001p-1022", strconv.base.HEX, 0.0)));
|
||||||
};
|
};
|
||||||
|
|
||||||
@test fn stof32_hex() void = {
|
@test fn stof32_hex() void = {
|
||||||
assert(!(!chk32("0p0", base.HEX, 0.0f32)));
|
assert(!(!chk32("0p0", strconv.base.HEX, 0.0f32)));
|
||||||
assert(!(!chk32("1p0", base.HEX, 1.0f32)));
|
assert(!(!chk32("1p0", strconv.base.HEX, 1.0f32)));
|
||||||
assert(!(!chk32("-1p0", base.HEX, -1.0f32)));
|
assert(!(!chk32("-1p0", strconv.base.HEX, -1.0f32)));
|
||||||
assert(!(!chk32("1.fp-2", base.HEX, 0.484375f32)));
|
assert(!(!chk32("1.fp-2", strconv.base.HEX, 0.484375f32)));
|
||||||
// F32_MAX_NORMAL.
|
// F32_MAX_NORMAL.
|
||||||
if (!chk32("1.fffffd586b834p+127", base.HEX,
|
if (!chk32("1.fffffd586b834p+127", strconv.base.HEX,
|
||||||
math.f32frombits(0x7F7FFFFFu32))) { abort(); };
|
math.f32frombits(0x7F7FFFFFu32))) { abort(); };
|
||||||
// F32_MIN_NORMAL.
|
// F32_MIN_NORMAL.
|
||||||
assert(!(!chk32("1.0p-126", base.HEX, math.f32frombits(0x00800000u32))));
|
assert(!(!chk32("1.0p-126", strconv.base.HEX, math.f32frombits(0x00800000u32))));
|
||||||
// F32_MIN_SUBNORMAL.
|
// F32_MIN_SUBNORMAL.
|
||||||
assert(!(!chk32("1.6p-150", base.HEX, math.f32frombits(0x00000001u32))));
|
assert(!(!chk32("1.6p-150", strconv.base.HEX, math.f32frombits(0x00000001u32))));
|
||||||
assert(!(!ovf32("1.0p+128", base.HEX)));
|
assert(!(!ovf32("1.0p+128", strconv.base.HEX)));
|
||||||
assert(!(!chk32("1.0p-151", base.HEX, 0.0f32)));
|
assert(!(!chk32("1.0p-151", strconv.base.HEX, 0.0f32)));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,90 +10,90 @@ package syntax_test;
|
|||||||
import ww.syntax;
|
import ww.syntax;
|
||||||
|
|
||||||
|
|
||||||
fn checknkname(k: nkind, want: str) void = {
|
fn checknkname(k: syntax.nkind, want: str) void = {
|
||||||
assert(!(nkname(k) != want));
|
assert(!(syntax.nkname(k) != want));
|
||||||
};
|
};
|
||||||
|
|
||||||
// One row per nkind value — same string the if-ladder returned. The
|
// One row per nkind value — same string the if-ladder returned. The
|
||||||
// final row pins the unknown-kind fallback ("?").
|
// final row pins the unknown-kind fallback ("?").
|
||||||
@test fn nkname_cases() void = {
|
@test fn nkname_cases() void = {
|
||||||
checknkname(nkind.N_NONE, "none");
|
checknkname(syntax.nkind.N_NONE, "none");
|
||||||
checknkname(nkind.N_INTLIT, "int");
|
checknkname(syntax.nkind.N_INTLIT, "int");
|
||||||
checknkname(nkind.N_FLOATLIT, "float");
|
checknkname(syntax.nkind.N_FLOATLIT, "float");
|
||||||
checknkname(nkind.N_STRLIT, "str");
|
checknkname(syntax.nkind.N_STRLIT, "str");
|
||||||
checknkname(nkind.N_RUNELIT, "rune");
|
checknkname(syntax.nkind.N_RUNELIT, "rune");
|
||||||
checknkname(nkind.N_TRUE, "true");
|
checknkname(syntax.nkind.N_TRUE, "true");
|
||||||
checknkname(nkind.N_FALSE, "false");
|
checknkname(syntax.nkind.N_FALSE, "false");
|
||||||
checknkname(nkind.N_NIL, "nil");
|
checknkname(syntax.nkind.N_NIL, "nil");
|
||||||
checknkname(nkind.N_IDENT, "id");
|
checknkname(syntax.nkind.N_IDENT, "id");
|
||||||
|
|
||||||
checknkname(nkind.N_BIN, "bin");
|
checknkname(syntax.nkind.N_BIN, "bin");
|
||||||
checknkname(nkind.N_UN, "un");
|
checknkname(syntax.nkind.N_UN, "un");
|
||||||
checknkname(nkind.N_CALL, "call");
|
checknkname(syntax.nkind.N_CALL, "call");
|
||||||
checknkname(nkind.N_INDEX, "index");
|
checknkname(syntax.nkind.N_INDEX, "index");
|
||||||
checknkname(nkind.N_DOT, "dot");
|
checknkname(syntax.nkind.N_DOT, "dot");
|
||||||
checknkname(nkind.N_CAST, "cast");
|
checknkname(syntax.nkind.N_CAST, "cast");
|
||||||
checknkname(nkind.N_STRUCTLIT, "structlit");
|
checknkname(syntax.nkind.N_STRUCTLIT, "structlit");
|
||||||
checknkname(nkind.N_ARRLIT, "arrlit");
|
checknkname(syntax.nkind.N_ARRLIT, "arrlit");
|
||||||
checknkname(nkind.N_FIELD, "field");
|
checknkname(syntax.nkind.N_FIELD, "field");
|
||||||
checknkname(nkind.N_ASSIGN, "assign");
|
checknkname(syntax.nkind.N_ASSIGN, "assign");
|
||||||
checknkname(nkind.N_ALLOC, "alloc");
|
checknkname(syntax.nkind.N_ALLOC, "alloc");
|
||||||
checknkname(nkind.N_FREE, "free");
|
checknkname(syntax.nkind.N_FREE, "free");
|
||||||
checknkname(nkind.N_RECV, "recv");
|
checknkname(syntax.nkind.N_RECV, "recv");
|
||||||
checknkname(nkind.N_SLICE, "slice");
|
checknkname(syntax.nkind.N_SLICE, "slice");
|
||||||
checknkname(nkind.N_SPREAD, "spread");
|
checknkname(syntax.nkind.N_SPREAD, "spread");
|
||||||
|
|
||||||
checknkname(nkind.N_BLOCK, "block");
|
checknkname(syntax.nkind.N_BLOCK, "block");
|
||||||
checknkname(nkind.N_EXPRSTMT, "exprstmt");
|
checknkname(syntax.nkind.N_EXPRSTMT, "exprstmt");
|
||||||
checknkname(nkind.N_LET, "let");
|
checknkname(syntax.nkind.N_LET, "let");
|
||||||
checknkname(nkind.N_RETURN, "return");
|
checknkname(syntax.nkind.N_RETURN, "return");
|
||||||
checknkname(nkind.N_IF, "if");
|
checknkname(syntax.nkind.N_IF, "if");
|
||||||
checknkname(nkind.N_FOR, "for");
|
checknkname(syntax.nkind.N_FOR, "for");
|
||||||
checknkname(nkind.N_FORRANGE, "forrange");
|
checknkname(syntax.nkind.N_FORRANGE, "forrange");
|
||||||
checknkname(nkind.N_DEFER, "defer");
|
checknkname(syntax.nkind.N_DEFER, "defer");
|
||||||
checknkname(nkind.N_BREAK, "break");
|
checknkname(syntax.nkind.N_BREAK, "break");
|
||||||
checknkname(nkind.N_CONTINUE, "continue");
|
checknkname(syntax.nkind.N_CONTINUE, "continue");
|
||||||
checknkname(nkind.N_SWITCH, "switch");
|
checknkname(syntax.nkind.N_SWITCH, "switch");
|
||||||
checknkname(nkind.N_CASE, "case");
|
checknkname(syntax.nkind.N_CASE, "case");
|
||||||
|
|
||||||
checknkname(nkind.N_FILE, "file");
|
checknkname(syntax.nkind.N_FILE, "file");
|
||||||
checknkname(nkind.N_USE, "use");
|
checknkname(syntax.nkind.N_USE, "use");
|
||||||
checknkname(nkind.N_DEF, "def");
|
checknkname(syntax.nkind.N_DEF, "def");
|
||||||
checknkname(nkind.N_TYPEDECL, "typedecl");
|
checknkname(syntax.nkind.N_TYPEDECL, "typedecl");
|
||||||
checknkname(nkind.N_FNDECL, "fn");
|
checknkname(syntax.nkind.N_FNDECL, "fn");
|
||||||
checknkname(nkind.N_PARAM, "param");
|
checknkname(syntax.nkind.N_PARAM, "param");
|
||||||
|
|
||||||
checknkname(nkind.N_TNAME, "tname");
|
checknkname(syntax.nkind.N_TNAME, "tname");
|
||||||
checknkname(nkind.N_TPTR, "tptr");
|
checknkname(syntax.nkind.N_TPTR, "tptr");
|
||||||
checknkname(nkind.N_TSLICE, "tslice");
|
checknkname(syntax.nkind.N_TSLICE, "tslice");
|
||||||
checknkname(nkind.N_TARRAY, "tarray");
|
checknkname(syntax.nkind.N_TARRAY, "tarray");
|
||||||
checknkname(nkind.N_TFN, "tfn");
|
checknkname(syntax.nkind.N_TFN, "tfn");
|
||||||
checknkname(nkind.N_TSTRUCT, "tstruct");
|
checknkname(syntax.nkind.N_TSTRUCT, "tstruct");
|
||||||
checknkname(nkind.N_TFIELD, "tfield");
|
checknkname(syntax.nkind.N_TFIELD, "tfield");
|
||||||
checknkname(nkind.N_TCHAN, "tchan");
|
checknkname(syntax.nkind.N_TCHAN, "tchan");
|
||||||
|
|
||||||
checknkname(nkind.N_ATTR, "attr");
|
checknkname(syntax.nkind.N_ATTR, "attr");
|
||||||
checknkname(nkind.N_TTUPLE, "ttuple");
|
checknkname(syntax.nkind.N_TTUPLE, "ttuple");
|
||||||
checknkname(nkind.N_TTAGGED, "ttagged");
|
checknkname(syntax.nkind.N_TTAGGED, "ttagged");
|
||||||
checknkname(nkind.N_TUPLE, "tuple");
|
checknkname(syntax.nkind.N_TUPLE, "tuple");
|
||||||
checknkname(nkind.N_MATCH, "match");
|
checknkname(syntax.nkind.N_MATCH, "match");
|
||||||
checknkname(nkind.N_MCASE, "mcase");
|
checknkname(syntax.nkind.N_MCASE, "mcase");
|
||||||
checknkname(nkind.N_TRYPROP, "tryprop");
|
checknkname(syntax.nkind.N_TRYPROP, "tryprop");
|
||||||
checknkname(nkind.N_TRYUNW, "tryunw");
|
checknkname(syntax.nkind.N_TRYUNW, "tryunw");
|
||||||
checknkname(nkind.N_MLET, "mlet");
|
checknkname(syntax.nkind.N_MLET, "mlet");
|
||||||
checknkname(nkind.N_MASSIGN, "massign");
|
checknkname(syntax.nkind.N_MASSIGN, "massign");
|
||||||
|
|
||||||
checknkname(nkind.N_TYPETEST, "typetest");
|
checknkname(syntax.nkind.N_TYPETEST, "typetest");
|
||||||
checknkname(nkind.N_TYPEASSERT, "typeassert");
|
checknkname(syntax.nkind.N_TYPEASSERT, "typeassert");
|
||||||
checknkname(nkind.N_VOIDLIT, "voidlit");
|
checknkname(syntax.nkind.N_VOIDLIT, "voidlit");
|
||||||
checknkname(nkind.N_TBANG, "tbang");
|
checknkname(syntax.nkind.N_TBANG, "tbang");
|
||||||
checknkname(nkind.N_YIELD, "yield");
|
checknkname(syntax.nkind.N_YIELD, "yield");
|
||||||
checknkname(nkind.N_TENUM, "tenum");
|
checknkname(syntax.nkind.N_TENUM, "tenum");
|
||||||
checknkname(nkind.N_TENUMMEMBER, "tenummember");
|
checknkname(syntax.nkind.N_TENUMMEMBER, "tenummember");
|
||||||
checknkname(nkind.N_TPARAM, "tparam");
|
checknkname(syntax.nkind.N_TPARAM, "tparam");
|
||||||
checknkname(nkind.N_LAST, "last");
|
checknkname(syntax.nkind.N_LAST, "last");
|
||||||
|
|
||||||
// Unknown kind → the post-switch fallback. N_LAST is the highest
|
// Unknown kind → the post-switch fallback. N_LAST is the highest
|
||||||
// named value (68); 69 is out of band, exercising the "?" tail.
|
// named value (68); 69 is out of band, exercising the "?" tail.
|
||||||
checknkname(69: nkind, "?");
|
checknkname(69: syntax.nkind, "?");
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,34 +12,34 @@ package syntax_test;
|
|||||||
import ww.syntax;
|
import ww.syntax;
|
||||||
|
|
||||||
@test fn scope_define_lookup() void = {
|
@test fn scope_define_lookup() void = {
|
||||||
let s: *scope = newscope(nil);
|
let s: *syntax.scope = syntax.newscope(nil);
|
||||||
assert(!(s == nil));
|
assert(!(s == nil));
|
||||||
|
|
||||||
let r1: *sym = scopedefine(s, "foo", skind.SK_VAR, nil, nil);
|
let r1: *syntax.sym = syntax.scopedefine(s, "foo", syntax.skind.SK_VAR, nil, nil);
|
||||||
assert(!(r1 == nil));
|
assert(!(r1 == nil));
|
||||||
let r2: *sym = scopedefine(s, "bar", skind.SK_TYPE, nil, nil);
|
let r2: *syntax.sym = syntax.scopedefine(s, "bar", syntax.skind.SK_TYPE, nil, nil);
|
||||||
assert(!(r2 == nil));
|
assert(!(r2 == nil));
|
||||||
|
|
||||||
let l1: *sym = scopelookup(s, "foo");
|
let l1: *syntax.sym = syntax.scopelookup(s, "foo");
|
||||||
assert(!(l1 == nil));
|
assert(!(l1 == nil));
|
||||||
assert(!(l1.skind != skind.SK_VAR));
|
assert(!(l1.skind != syntax.skind.SK_VAR));
|
||||||
let l2: *sym = scopelookup(s, "bar");
|
let l2: *syntax.sym = syntax.scopelookup(s, "bar");
|
||||||
assert(!(l2 == nil));
|
assert(!(l2 == nil));
|
||||||
assert(!(l2.skind != skind.SK_TYPE));
|
assert(!(l2.skind != syntax.skind.SK_TYPE));
|
||||||
};
|
};
|
||||||
|
|
||||||
@test fn scope_duplicate_reject() void = {
|
@test fn scope_duplicate_reject() void = {
|
||||||
let s: *scope = newscope(nil);
|
let s: *syntax.scope = syntax.newscope(nil);
|
||||||
assert(!(s == nil));
|
assert(!(s == nil));
|
||||||
let r1: *sym = scopedefine(s, "foo", skind.SK_VAR, nil, nil);
|
let r1: *syntax.sym = syntax.scopedefine(s, "foo", syntax.skind.SK_VAR, nil, nil);
|
||||||
assert(!(r1 == nil));
|
assert(!(r1 == nil));
|
||||||
let r3: *sym = scopedefine(s, "foo", skind.SK_VAR, nil, nil);
|
let r3: *syntax.sym = syntax.scopedefine(s, "foo", syntax.skind.SK_VAR, nil, nil);
|
||||||
assert(!(r3 != nil));
|
assert(!(r3 != nil));
|
||||||
};
|
};
|
||||||
|
|
||||||
@test fn scope_notfound_nil() void = {
|
@test fn scope_notfound_nil() void = {
|
||||||
let s: *scope = newscope(nil);
|
let s: *syntax.scope = syntax.newscope(nil);
|
||||||
assert(!(s == nil));
|
assert(!(s == nil));
|
||||||
let l3: *sym = scopelookup(s, "baz");
|
let l3: *syntax.sym = syntax.scopelookup(s, "baz");
|
||||||
assert(!(l3 != nil));
|
assert(!(l3 != nil));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,120 +23,120 @@ import ww.syntax;
|
|||||||
import temp;
|
import temp;
|
||||||
|
|
||||||
|
|
||||||
fn checktokname(k: tkind, want: str) void = {
|
fn checktokname(k: syntax.tkind, want: str) void = {
|
||||||
assert(!(tokname(k) != want));
|
assert(!(syntax.tokname(k) != want));
|
||||||
};
|
};
|
||||||
|
|
||||||
// One row per tkind value — same string the if-ladder returned. The
|
// One row per tkind value — same string the if-ladder returned. The
|
||||||
// final row pins the unknown-kind fallback ("<?>").
|
// final row pins the unknown-kind fallback ("<?>").
|
||||||
@test fn tokname_cases() void = {
|
@test fn tokname_cases() void = {
|
||||||
checktokname(tkind.TK_NONE, "<none>");
|
checktokname(syntax.tkind.TK_NONE, "<none>");
|
||||||
checktokname(tkind.TK_EOF, "EOF");
|
checktokname(syntax.tkind.TK_EOF, "EOF");
|
||||||
checktokname(tkind.TK_ERR, "ERR");
|
checktokname(syntax.tkind.TK_ERR, "ERR");
|
||||||
checktokname(tkind.TK_IDENT, "IDENT");
|
checktokname(syntax.tkind.TK_IDENT, "IDENT");
|
||||||
checktokname(tkind.TK_INT, "INT");
|
checktokname(syntax.tkind.TK_INT, "INT");
|
||||||
checktokname(tkind.TK_FLOAT, "FLOAT");
|
checktokname(syntax.tkind.TK_FLOAT, "FLOAT");
|
||||||
checktokname(tkind.TK_RUNE, "RUNE");
|
checktokname(syntax.tkind.TK_RUNE, "RUNE");
|
||||||
checktokname(tkind.TK_STR, "STR");
|
checktokname(syntax.tkind.TK_STR, "STR");
|
||||||
|
|
||||||
checktokname(tkind.TK_FN, "fn");
|
checktokname(syntax.tkind.TK_FN, "fn");
|
||||||
checktokname(tkind.TK_LET, "let");
|
checktokname(syntax.tkind.TK_LET, "let");
|
||||||
checktokname(tkind.TK_DEF, "def");
|
checktokname(syntax.tkind.TK_DEF, "def");
|
||||||
checktokname(tkind.TK_IF, "if");
|
checktokname(syntax.tkind.TK_IF, "if");
|
||||||
checktokname(tkind.TK_ELSE, "else");
|
checktokname(syntax.tkind.TK_ELSE, "else");
|
||||||
checktokname(tkind.TK_FOR, "for");
|
checktokname(syntax.tkind.TK_FOR, "for");
|
||||||
checktokname(tkind.TK_SWITCH, "switch");
|
checktokname(syntax.tkind.TK_SWITCH, "switch");
|
||||||
checktokname(tkind.TK_CASE, "case");
|
checktokname(syntax.tkind.TK_CASE, "case");
|
||||||
checktokname(tkind.TK_RETURN, "return");
|
checktokname(syntax.tkind.TK_RETURN, "return");
|
||||||
checktokname(tkind.TK_USE, "import");
|
checktokname(syntax.tkind.TK_USE, "import");
|
||||||
checktokname(tkind.TK_TYPE, "type");
|
checktokname(syntax.tkind.TK_TYPE, "type");
|
||||||
checktokname(tkind.TK_STRUCT, "struct");
|
checktokname(syntax.tkind.TK_STRUCT, "struct");
|
||||||
checktokname(tkind.TK_DEFER, "defer");
|
checktokname(syntax.tkind.TK_DEFER, "defer");
|
||||||
checktokname(tkind.TK_BREAK, "break");
|
checktokname(syntax.tkind.TK_BREAK, "break");
|
||||||
checktokname(tkind.TK_CONTINUE, "continue");
|
checktokname(syntax.tkind.TK_CONTINUE, "continue");
|
||||||
checktokname(tkind.TK_EXPORT, "export");
|
checktokname(syntax.tkind.TK_EXPORT, "export");
|
||||||
checktokname(tkind.TK_PROC, "proc");
|
checktokname(syntax.tkind.TK_PROC, "proc");
|
||||||
checktokname(tkind.TK_CHAN, "chan");
|
checktokname(syntax.tkind.TK_CHAN, "chan");
|
||||||
checktokname(tkind.TK_NIL, "nil");
|
checktokname(syntax.tkind.TK_NIL, "nil");
|
||||||
checktokname(tkind.TK_TRUE, "true");
|
checktokname(syntax.tkind.TK_TRUE, "true");
|
||||||
checktokname(tkind.TK_FALSE, "false");
|
checktokname(syntax.tkind.TK_FALSE, "false");
|
||||||
checktokname(tkind.TK_AS, "as");
|
checktokname(syntax.tkind.TK_AS, "as");
|
||||||
checktokname(tkind.TK_IS, "is");
|
checktokname(syntax.tkind.TK_IS, "is");
|
||||||
checktokname(tkind.TK_VOID, "void");
|
checktokname(syntax.tkind.TK_VOID, "void");
|
||||||
checktokname(tkind.TK_YIELD, "yield");
|
checktokname(syntax.tkind.TK_YIELD, "yield");
|
||||||
checktokname(tkind.TK_STATIC, "static");
|
checktokname(syntax.tkind.TK_STATIC, "static");
|
||||||
checktokname(tkind.TK_MATCH, "match");
|
checktokname(syntax.tkind.TK_MATCH, "match");
|
||||||
checktokname(tkind.TK_CONST, "const");
|
checktokname(syntax.tkind.TK_CONST, "const");
|
||||||
checktokname(tkind.TK_UNDER, "_");
|
checktokname(syntax.tkind.TK_UNDER, "_");
|
||||||
checktokname(tkind.TK_ENUM, "enum");
|
checktokname(syntax.tkind.TK_ENUM, "enum");
|
||||||
checktokname(tkind.TK_MODULE, "package");
|
checktokname(syntax.tkind.TK_MODULE, "package");
|
||||||
|
|
||||||
checktokname(tkind.TK_LPAREN, "(");
|
checktokname(syntax.tkind.TK_LPAREN, "(");
|
||||||
checktokname(tkind.TK_RPAREN, ")");
|
checktokname(syntax.tkind.TK_RPAREN, ")");
|
||||||
checktokname(tkind.TK_LBRACE, "{");
|
checktokname(syntax.tkind.TK_LBRACE, "{");
|
||||||
checktokname(tkind.TK_RBRACE, "}");
|
checktokname(syntax.tkind.TK_RBRACE, "}");
|
||||||
checktokname(tkind.TK_LBRACK, "[");
|
checktokname(syntax.tkind.TK_LBRACK, "[");
|
||||||
checktokname(tkind.TK_RBRACK, "]");
|
checktokname(syntax.tkind.TK_RBRACK, "]");
|
||||||
checktokname(tkind.TK_COMMA, ",");
|
checktokname(syntax.tkind.TK_COMMA, ",");
|
||||||
checktokname(tkind.TK_SEMI, ";");
|
checktokname(syntax.tkind.TK_SEMI, ";");
|
||||||
checktokname(tkind.TK_COLON, ":");
|
checktokname(syntax.tkind.TK_COLON, ":");
|
||||||
checktokname(tkind.TK_DOT, ".");
|
checktokname(syntax.tkind.TK_DOT, ".");
|
||||||
checktokname(tkind.TK_ELLIPSIS, "...");
|
checktokname(syntax.tkind.TK_ELLIPSIS, "...");
|
||||||
checktokname(tkind.TK_DOTDOT, "..");
|
checktokname(syntax.tkind.TK_DOTDOT, "..");
|
||||||
checktokname(tkind.TK_AT, "@");
|
checktokname(syntax.tkind.TK_AT, "@");
|
||||||
checktokname(tkind.TK_QUESTION, "?");
|
checktokname(syntax.tkind.TK_QUESTION, "?");
|
||||||
|
|
||||||
checktokname(tkind.TK_ASSIGN, "=");
|
checktokname(syntax.tkind.TK_ASSIGN, "=");
|
||||||
checktokname(tkind.TK_PLUSEQ, "+=");
|
checktokname(syntax.tkind.TK_PLUSEQ, "+=");
|
||||||
checktokname(tkind.TK_MINUSEQ, "-=");
|
checktokname(syntax.tkind.TK_MINUSEQ, "-=");
|
||||||
checktokname(tkind.TK_STAREQ, "*=");
|
checktokname(syntax.tkind.TK_STAREQ, "*=");
|
||||||
checktokname(tkind.TK_SLASHEQ, "/=");
|
checktokname(syntax.tkind.TK_SLASHEQ, "/=");
|
||||||
checktokname(tkind.TK_PERCENTEQ, "%=");
|
checktokname(syntax.tkind.TK_PERCENTEQ, "%=");
|
||||||
checktokname(tkind.TK_AMPEQ, "&=");
|
checktokname(syntax.tkind.TK_AMPEQ, "&=");
|
||||||
checktokname(tkind.TK_PIPEEQ, "|=");
|
checktokname(syntax.tkind.TK_PIPEEQ, "|=");
|
||||||
checktokname(tkind.TK_CARETEQ, "^=");
|
checktokname(syntax.tkind.TK_CARETEQ, "^=");
|
||||||
checktokname(tkind.TK_LSHIFTEQ, "<<=");
|
checktokname(syntax.tkind.TK_LSHIFTEQ, "<<=");
|
||||||
checktokname(tkind.TK_RSHIFTEQ, ">>=");
|
checktokname(syntax.tkind.TK_RSHIFTEQ, ">>=");
|
||||||
|
|
||||||
checktokname(tkind.TK_PLUS, "+");
|
checktokname(syntax.tkind.TK_PLUS, "+");
|
||||||
checktokname(tkind.TK_MINUS, "-");
|
checktokname(syntax.tkind.TK_MINUS, "-");
|
||||||
checktokname(tkind.TK_STAR, "*");
|
checktokname(syntax.tkind.TK_STAR, "*");
|
||||||
checktokname(tkind.TK_SLASH, "/");
|
checktokname(syntax.tkind.TK_SLASH, "/");
|
||||||
checktokname(tkind.TK_PERCENT, "%");
|
checktokname(syntax.tkind.TK_PERCENT, "%");
|
||||||
checktokname(tkind.TK_AMP, "&");
|
checktokname(syntax.tkind.TK_AMP, "&");
|
||||||
checktokname(tkind.TK_PIPE, "|");
|
checktokname(syntax.tkind.TK_PIPE, "|");
|
||||||
checktokname(tkind.TK_CARET, "^");
|
checktokname(syntax.tkind.TK_CARET, "^");
|
||||||
checktokname(tkind.TK_TILDE, "~");
|
checktokname(syntax.tkind.TK_TILDE, "~");
|
||||||
checktokname(tkind.TK_LSHIFT, "<<");
|
checktokname(syntax.tkind.TK_LSHIFT, "<<");
|
||||||
checktokname(tkind.TK_RSHIFT, ">>");
|
checktokname(syntax.tkind.TK_RSHIFT, ">>");
|
||||||
|
|
||||||
checktokname(tkind.TK_EQ, "==");
|
checktokname(syntax.tkind.TK_EQ, "==");
|
||||||
checktokname(tkind.TK_NEQ, "!=");
|
checktokname(syntax.tkind.TK_NEQ, "!=");
|
||||||
checktokname(tkind.TK_LT, "<");
|
checktokname(syntax.tkind.TK_LT, "<");
|
||||||
checktokname(tkind.TK_LE, "<=");
|
checktokname(syntax.tkind.TK_LE, "<=");
|
||||||
checktokname(tkind.TK_GT, ">");
|
checktokname(syntax.tkind.TK_GT, ">");
|
||||||
checktokname(tkind.TK_GE, ">=");
|
checktokname(syntax.tkind.TK_GE, ">=");
|
||||||
|
|
||||||
checktokname(tkind.TK_AND, "&&");
|
checktokname(syntax.tkind.TK_AND, "&&");
|
||||||
checktokname(tkind.TK_OR, "||");
|
checktokname(syntax.tkind.TK_OR, "||");
|
||||||
checktokname(tkind.TK_NOT, "!");
|
checktokname(syntax.tkind.TK_NOT, "!");
|
||||||
|
|
||||||
checktokname(tkind.TK_LARROW, "<-");
|
checktokname(syntax.tkind.TK_LARROW, "<-");
|
||||||
checktokname(tkind.TK_ARROW, "->");
|
checktokname(syntax.tkind.TK_ARROW, "->");
|
||||||
checktokname(tkind.TK_FATARROW, "=>");
|
checktokname(syntax.tkind.TK_FATARROW, "=>");
|
||||||
|
|
||||||
checktokname(tkind.TK_MODRESET, "//ww:module-reset");
|
checktokname(syntax.tkind.TK_MODRESET, "//ww:module-reset");
|
||||||
checktokname(tkind.TK_MODPATH, "//ww:module");
|
checktokname(syntax.tkind.TK_MODPATH, "//ww:module");
|
||||||
checktokname(tkind.TK_LAST, "<last>");
|
checktokname(syntax.tkind.TK_LAST, "<last>");
|
||||||
|
|
||||||
// Unknown kind → the post-switch fallback. TK_LAST is the highest
|
// Unknown kind → the post-switch fallback. TK_LAST is the highest
|
||||||
// named value (89, after TK_MODPATH=88 landed); 90 is out of band,
|
// named value (89, after TK_MODPATH=88 landed); 90 is out of band,
|
||||||
// exercising the "<?>" tail.
|
// exercising the "<?>" tail.
|
||||||
checktokname(90: tkind, "<?>");
|
checktokname(90: syntax.tkind, "<?>");
|
||||||
};
|
};
|
||||||
|
|
||||||
fn checkkw(s: str, want: tkind) void = {
|
fn checkkw(s: str, want: syntax.tkind) void = {
|
||||||
assert(!(kwlookup(s.ptr, s.len) != want));
|
assert(!(syntax.kwlookup(s.ptr, s.len) != want));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Table-driven (parallel-array idiom; tuple rows blocked by #111). The
|
// Table-driven (parallel-array idiom; tuple rows blocked by #111). The
|
||||||
@@ -153,15 +153,15 @@ fn checkkw(s: str, want: tkind) void = {
|
|||||||
"import", "let", "match", "nil", "package", "proc", "return",
|
"import", "let", "match", "nil", "package", "proc", "return",
|
||||||
"static", "struct", "switch", "true", "type", "void", "yield",
|
"static", "struct", "switch", "true", "type", "void", "yield",
|
||||||
];
|
];
|
||||||
let kwexp: [30]tkind = [
|
let kwexp: [30]syntax.tkind = [
|
||||||
tkind.TK_AS, tkind.TK_BREAK, tkind.TK_CASE, tkind.TK_CHAN,
|
syntax.tkind.TK_AS, syntax.tkind.TK_BREAK, syntax.tkind.TK_CASE, syntax.tkind.TK_CHAN,
|
||||||
tkind.TK_CONST, tkind.TK_CONTINUE, tkind.TK_DEF, tkind.TK_DEFER,
|
syntax.tkind.TK_CONST, syntax.tkind.TK_CONTINUE, syntax.tkind.TK_DEF, syntax.tkind.TK_DEFER,
|
||||||
tkind.TK_ELSE, tkind.TK_ENUM, tkind.TK_EXPORT, tkind.TK_FALSE,
|
syntax.tkind.TK_ELSE, syntax.tkind.TK_ENUM, syntax.tkind.TK_EXPORT, syntax.tkind.TK_FALSE,
|
||||||
tkind.TK_FN, tkind.TK_FOR, tkind.TK_IF, tkind.TK_IS,
|
syntax.tkind.TK_FN, syntax.tkind.TK_FOR, syntax.tkind.TK_IF, syntax.tkind.TK_IS,
|
||||||
tkind.TK_USE, tkind.TK_LET, tkind.TK_MATCH, tkind.TK_NIL,
|
syntax.tkind.TK_USE, syntax.tkind.TK_LET, syntax.tkind.TK_MATCH, syntax.tkind.TK_NIL,
|
||||||
tkind.TK_MODULE, tkind.TK_PROC, tkind.TK_RETURN, tkind.TK_STATIC,
|
syntax.tkind.TK_MODULE, syntax.tkind.TK_PROC, syntax.tkind.TK_RETURN, syntax.tkind.TK_STATIC,
|
||||||
tkind.TK_STRUCT, tkind.TK_SWITCH, tkind.TK_TRUE, tkind.TK_TYPE,
|
syntax.tkind.TK_STRUCT, syntax.tkind.TK_SWITCH, syntax.tkind.TK_TRUE, syntax.tkind.TK_TYPE,
|
||||||
tkind.TK_VOID, tkind.TK_YIELD,
|
syntax.tkind.TK_VOID, syntax.tkind.TK_YIELD,
|
||||||
];
|
];
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < len(kwin)) {
|
for (i < len(kwin)) {
|
||||||
@@ -197,7 +197,7 @@ fn checkkw(s: str, want: tkind) void = {
|
|||||||
];
|
];
|
||||||
let j: i32 = 0;
|
let j: i32 = 0;
|
||||||
for (j < len(nk)) {
|
for (j < len(nk)) {
|
||||||
checkkw(nk[j], tkind.TK_NONE);
|
checkkw(nk[j], syntax.tkind.TK_NONE);
|
||||||
j += 1;
|
j += 1;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -205,9 +205,9 @@ fn checkkw(s: str, want: tkind) void = {
|
|||||||
// The fd is RDWR; we lseek to 0
|
// The fd is RDWR; we lseek to 0
|
||||||
// before each write so earlier (possibly longer) content past want.len
|
// before each write so earlier (possibly longer) content past want.len
|
||||||
// is irrelevant — only want.len bytes from offset 0 are compared.
|
// is irrelevant — only want.len bytes from offset 0 are compared.
|
||||||
fn checkprint(fd: i32, t: *tok, want: str) void = {
|
fn checkprint(fd: i32, t: *syntax.tok, want: str) void = {
|
||||||
assert(!(os.lseek(fd, 0i64, os.whence.SET) != 0i64));
|
assert(!(os.lseek(fd, 0i64, os.whence.SET) != 0i64));
|
||||||
tokprint(fd, t);
|
syntax.tokprint(fd, t);
|
||||||
assert(!(os.lseek(fd, 0i64, os.whence.SET) != 0i64));
|
assert(!(os.lseek(fd, 0i64, os.whence.SET) != 0i64));
|
||||||
let rbuf: [256]u8;
|
let rbuf: [256]u8;
|
||||||
let z: i32 = 0;
|
let z: i32 = 0;
|
||||||
@@ -233,33 +233,33 @@ fn checkprint(fd: i32, t: *tok, want: str) void = {
|
|||||||
};
|
};
|
||||||
assert(!(fd < 0));
|
assert(!(fd < 0));
|
||||||
|
|
||||||
let t: tok;
|
let t: syntax.tok;
|
||||||
t.file = "t";
|
t.file = "t";
|
||||||
t.line = 1;
|
t.line = 1;
|
||||||
t.col = 1;
|
t.col = 1;
|
||||||
|
|
||||||
t.kind = tkind.TK_STR;
|
t.kind = syntax.tkind.TK_STR;
|
||||||
t.text = "\\\"\n\t\r\x01\x7fA";
|
t.text = "\\\"\n\t\r\x01\x7fA";
|
||||||
checkprint(fd, &t, "t:1:1 STR \"\\\\\\\"\\n\\t\\r\\x01\\x7fA\"\n");
|
checkprint(fd, &t, "t:1:1 STR \"\\\\\\\"\\n\\t\\r\\x01\\x7fA\"\n");
|
||||||
|
|
||||||
t.kind = tkind.TK_IDENT;
|
t.kind = syntax.tkind.TK_IDENT;
|
||||||
t.text = "name";
|
t.text = "name";
|
||||||
checkprint(fd, &t, "t:1:1 IDENT \"name\"\n");
|
checkprint(fd, &t, "t:1:1 IDENT \"name\"\n");
|
||||||
|
|
||||||
t.kind = tkind.TK_ERR;
|
t.kind = syntax.tkind.TK_ERR;
|
||||||
t.text = "oops";
|
t.text = "oops";
|
||||||
checkprint(fd, &t, "t:1:1 ERR \"oops\"\n");
|
checkprint(fd, &t, "t:1:1 ERR \"oops\"\n");
|
||||||
|
|
||||||
t.kind = tkind.TK_INT;
|
t.kind = syntax.tkind.TK_INT;
|
||||||
t.uval = 42u64;
|
t.uval = 42u64;
|
||||||
checkprint(fd, &t, "t:1:1 INT 42\n");
|
checkprint(fd, &t, "t:1:1 INT 42\n");
|
||||||
|
|
||||||
t.kind = tkind.TK_RUNE;
|
t.kind = syntax.tkind.TK_RUNE;
|
||||||
t.uval = 65u64;
|
t.uval = 65u64;
|
||||||
checkprint(fd, &t, "t:1:1 RUNE 65\n");
|
checkprint(fd, &t, "t:1:1 RUNE 65\n");
|
||||||
|
|
||||||
// Default arm: a kind outside the switch appends no value.
|
// Default arm: a kind outside the switch appends no value.
|
||||||
t.kind = tkind.TK_NONE;
|
t.kind = syntax.tkind.TK_NONE;
|
||||||
checkprint(fd, &t, "t:1:1 <none>\n");
|
checkprint(fd, &t, "t:1:1 <none>\n");
|
||||||
|
|
||||||
assert(!(os.close(fd) != 0));
|
assert(!(os.close(fd) != 0));
|
||||||
@@ -267,11 +267,11 @@ fn checkprint(fd: i32, t: *tok, want: str) void = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn checkfloat(src: str, want: u64) void = {
|
fn checkfloat(src: str, want: u64) void = {
|
||||||
let l: lex;
|
let l: syntax.lex;
|
||||||
lexinit(&l, "t", src.ptr, src.len: u64);
|
syntax.lexinit(&l, "t", src.ptr, src.len: u64);
|
||||||
let t: tok;
|
let t: syntax.tok;
|
||||||
lexnext(&l, &t);
|
syntax.lexnext(&l, &t);
|
||||||
assert(!(t.kind != tkind.TK_FLOAT));
|
assert(!(t.kind != syntax.tkind.TK_FLOAT));
|
||||||
assert(!(t.uval != want));
|
assert(!(t.uval != want));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -312,29 +312,29 @@ fn checkfloat(src: str, want: u64) void = {
|
|||||||
|
|
||||||
@test fn intfinalband() void = {
|
@test fn intfinalband() void = {
|
||||||
let src: str = "18446744073709551616";
|
let src: str = "18446744073709551616";
|
||||||
let l: lex;
|
let l: syntax.lex;
|
||||||
lexinit(&l, "t", src.ptr, src.len: u64);
|
syntax.lexinit(&l, "t", src.ptr, src.len: u64);
|
||||||
let t: tok;
|
let t: syntax.tok;
|
||||||
lexnext(&l, &t);
|
syntax.lexnext(&l, &t);
|
||||||
assert(!(t.kind != tkind.TK_ERR));
|
assert(!(t.kind != syntax.tkind.TK_ERR));
|
||||||
assert(!(l.errs != 1));
|
assert(!(l.errs != 1));
|
||||||
};
|
};
|
||||||
|
|
||||||
@test fn suffixrewindcols() void = {
|
@test fn suffixrewindcols() void = {
|
||||||
let src: str = "1foo bar";
|
let src: str = "1foo bar";
|
||||||
let l: lex;
|
let l: syntax.lex;
|
||||||
lexinit(&l, "t", src.ptr, src.len: u64);
|
syntax.lexinit(&l, "t", src.ptr, src.len: u64);
|
||||||
let t: tok;
|
let t: syntax.tok;
|
||||||
lexnext(&l, &t);
|
syntax.lexnext(&l, &t);
|
||||||
assert(!(t.kind != tkind.TK_INT));
|
assert(!(t.kind != syntax.tkind.TK_INT));
|
||||||
assert(!(t.col != 1));
|
assert(!(t.col != 1));
|
||||||
assert(!(t.uval != 1u64));
|
assert(!(t.uval != 1u64));
|
||||||
lexnext(&l, &t);
|
syntax.lexnext(&l, &t);
|
||||||
assert(!(t.kind != tkind.TK_IDENT));
|
assert(!(t.kind != syntax.tkind.TK_IDENT));
|
||||||
assert(!(t.col != 2));
|
assert(!(t.col != 2));
|
||||||
assert(!(t.text != "foo"));
|
assert(!(t.text != "foo"));
|
||||||
lexnext(&l, &t);
|
syntax.lexnext(&l, &t);
|
||||||
assert(!(t.kind != tkind.TK_IDENT));
|
assert(!(t.kind != syntax.tkind.TK_IDENT));
|
||||||
assert(!(t.col != 6));
|
assert(!(t.col != 6));
|
||||||
assert(!(t.text != "bar"));
|
assert(!(t.text != "bar"));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -75,6 +75,31 @@ fn astrow(label: str, src: str, needles: []str) void = {
|
|||||||
testenv.clean(td);
|
testenv.clean(td);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn rejectrow(label: str, src: str, needle: str) void = {
|
||||||
|
let td: str = testenv.fresh();
|
||||||
|
testenv.writefile(strings.concat(td, "/a.ww"), src);
|
||||||
|
let tools: []str = ["wwdump", "wwdump_ww"];
|
||||||
|
let stages: []str = ["cstage", "wwstage"];
|
||||||
|
let errors: []str = ["", ""];
|
||||||
|
let i: i32 = 0;
|
||||||
|
for (i < tools.len) {
|
||||||
|
let av: []str = [testenv.driver(tools[i]), "-a",
|
||||||
|
strings.concat(td, "/a.ww")];
|
||||||
|
let co: testenv.commandout;
|
||||||
|
testenv.runcommand(td, td, stages[i], av, tmo(), &co);
|
||||||
|
if (co.termination != exec.termination.EXIT || co.code == 0
|
||||||
|
|| !testenv.has(co.stderr, needle)) {
|
||||||
|
fail(label, strings.concat(stages[i], " accepted rejected import"));
|
||||||
|
};
|
||||||
|
errors[i] = strings.dup(co.stderr);
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
if (!testenv.same(errors[0], errors[1])) {
|
||||||
|
fail(label, "cs/ww parse diagnostics DIFFER");
|
||||||
|
};
|
||||||
|
testenv.clean(td);
|
||||||
|
};
|
||||||
|
|
||||||
@test fn astproof() void = {
|
@test fn astproof() void = {
|
||||||
let trailing: []str = ["(dot \"x\"", "(structlit",
|
let trailing: []str = ["(dot \"x\"", "(structlit",
|
||||||
"(tname \"pkg.point\""];
|
"(tname \"pkg.point\""];
|
||||||
@@ -92,4 +117,19 @@ fn astrow(label: str, src: str, needles: []str) void = {
|
|||||||
"\tlet v: i64 = a.b.C { x = 1i64 }.x;\n",
|
"\tlet v: i64 = a.b.C { x = 1i64 }.x;\n",
|
||||||
"\treturn v;\n",
|
"\treturn v;\n",
|
||||||
"};\n"), multilevel);
|
"};\n"), multilevel);
|
||||||
|
let ordinary: []str = ["(use \"codec\""];
|
||||||
|
astrow("ordinary-import", strings.concat(
|
||||||
|
"package main;\n",
|
||||||
|
"import acme.codec;\n",
|
||||||
|
"fn main() i32 = { return 0; };\n"), ordinary);
|
||||||
|
let aliased: []str = ["(use \"stable\""];
|
||||||
|
astrow("aliased-import", strings.concat(
|
||||||
|
"package main;\n",
|
||||||
|
"import stable acme.codec;\n",
|
||||||
|
"fn main() i32 = { return 0; };\n"), aliased);
|
||||||
|
rejectrow("blank-alias", strings.concat(
|
||||||
|
"package main;\n",
|
||||||
|
"import _ acme.codec;\n",
|
||||||
|
"fn main() i32 = { return 0; };\n"),
|
||||||
|
"blank import alias _ is not implemented");
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7906,9 +7906,11 @@ fn runtimepath(relative: str) str = {
|
|||||||
|
|
||||||
let wirebody: str = strings.concat(
|
let wirebody: str = strings.concat(
|
||||||
"package wire;\n",
|
"package wire;\n",
|
||||||
|
"export type Thing = i32;\n",
|
||||||
"export fn value() i32 = { return 42; };\n");
|
"export fn value() i32 = { return 42; };\n");
|
||||||
let cablebody: str = strings.concat(
|
let cablebody: str = strings.concat(
|
||||||
"package cable;\n",
|
"package cable;\n",
|
||||||
|
"export type Thing = i32;\n",
|
||||||
"export fn value() i32 = { return 42; };\n");
|
"export fn value() i32 = { return 42; };\n");
|
||||||
let codecfile: str = strings.concat(codec, "/codec.ww");
|
let codecfile: str = strings.concat(codec, "/codec.ww");
|
||||||
writefile(codecfile, wirebody);
|
writefile(codecfile, wirebody);
|
||||||
@@ -7918,12 +7920,16 @@ fn runtimepath(relative: str) str = {
|
|||||||
writefile(commitappfile, strings.concat(
|
writefile(commitappfile, strings.concat(
|
||||||
"package main;\nimport commit.codec;\n",
|
"package main;\nimport commit.codec;\n",
|
||||||
"fn main() i32 = { return wire.value(); };\n"));
|
"fn main() i32 = { return wire.value(); };\n"));
|
||||||
writefile(strings.concat(bridge, "/bridge.ww"), strings.concat(
|
let bridgefile: str = strings.concat(bridge, "/bridge.ww");
|
||||||
"package bridge;\nimport acme.codec;\n",
|
let stablebridgebody: str = strings.concat(
|
||||||
// WW retains its direct-import bare-declaration convenience. It is
|
"package bridge;\nimport stable acme.codec;\n",
|
||||||
// useful here because changing only the dependency declaration leaves
|
"export type BridgedThing = stable.Thing;\n",
|
||||||
// this source unchanged while still forcing its action to reconsider.
|
"export fn bridged() i32 = { return stable.value(); };\n");
|
||||||
"export fn bridged() i32 = { return value(); };\n"));
|
let steadybridgebody: str = strings.concat(
|
||||||
|
"package bridge;\nimport steady acme.codec;\n",
|
||||||
|
"export type BridgedThing = steady.Thing;\n",
|
||||||
|
"export fn bridged() i32 = { return steady.value(); };\n");
|
||||||
|
writefile(bridgefile, stablebridgebody);
|
||||||
writefile(strings.concat(direct, "/main.ww"), strings.concat(
|
writefile(strings.concat(direct, "/main.ww"), strings.concat(
|
||||||
"package main;\nimport acme.codec;\n",
|
"package main;\nimport acme.codec;\n",
|
||||||
"fn main() i32 = { return wire.value(); };\n"));
|
"fn main() i32 = { return wire.value(); };\n"));
|
||||||
@@ -7936,17 +7942,17 @@ fn runtimepath(relative: str) str = {
|
|||||||
writefile(strings.concat(scopetwo, "/two.ww"),
|
writefile(strings.concat(scopetwo, "/two.ww"),
|
||||||
"package same;\nexport fn value() i32 = { return 22; };\n");
|
"package same;\nexport fn value() i32 = { return 22; };\n");
|
||||||
writefile(strings.concat(scoped, "/a.ww"), strings.concat(
|
writefile(strings.concat(scoped, "/a.ww"), strings.concat(
|
||||||
"package main;\nimport scope.one;\n",
|
"package main;\nimport shared scope.one;\n",
|
||||||
"fn left() i32 = { return same.value(); };\n"));
|
"fn left() i32 = { return shared.value(); };\n"));
|
||||||
writefile(strings.concat(scoped, "/b.ww"), strings.concat(
|
writefile(strings.concat(scoped, "/b.ww"), strings.concat(
|
||||||
"package main;\nimport scope.two;\n",
|
"package main;\nimport shared scope.two;\n",
|
||||||
"fn right() i32 = { return same.value(); };\n"));
|
"fn right() i32 = { return shared.value(); };\n"));
|
||||||
writefile(strings.concat(scoped, "/main.ww"),
|
writefile(strings.concat(scoped, "/main.ww"),
|
||||||
"package main;\nfn main() i32 = { return left() + right(); };\n");
|
"package main;\nfn main() i32 = { return left() + right(); };\n");
|
||||||
|
|
||||||
writefile(strings.concat(repeated, "/a.ww"), strings.concat(
|
writefile(strings.concat(repeated, "/a.ww"), strings.concat(
|
||||||
"package main;\nimport acme.codec;\n",
|
"package main;\nimport stable acme.codec;\n",
|
||||||
"fn first() i32 = { return wire.value(); };\n"));
|
"fn first() i32 = { return stable.value(); };\n"));
|
||||||
writefile(strings.concat(repeated, "/b.ww"), strings.concat(
|
writefile(strings.concat(repeated, "/b.ww"), strings.concat(
|
||||||
"package main;\nimport acme.codec;\n",
|
"package main;\nimport acme.codec;\n",
|
||||||
"fn second() i32 = { return wire.value(); };\n"));
|
"fn second() i32 = { return wire.value(); };\n"));
|
||||||
@@ -7990,26 +7996,26 @@ fn runtimepath(relative: str) str = {
|
|||||||
"package main;\nimport domain.main;\n",
|
"package main;\nimport domain.main;\n",
|
||||||
"fn main() i32 = { return utility.value(); };\n"));
|
"fn main() i32 = { return utility.value(); };\n"));
|
||||||
writefile(strings.concat(programuser, "/main.ww"), strings.concat(
|
writefile(strings.concat(programuser, "/main.ww"), strings.concat(
|
||||||
"package main;\nimport domain.program;\n",
|
"package main;\nimport stable domain.program;\n",
|
||||||
"fn main() i32 = { return main.value(); };\n"));
|
"fn main() i32 = { return stable.value(); };\n"));
|
||||||
|
|
||||||
writefile(strings.concat(testhelper, "/helper.ww"),
|
writefile(strings.concat(testhelper, "/helper.ww"),
|
||||||
"package helper;\nexport fn value() i32 = { return 1; };\n");
|
"package helper;\nexport fn value() i32 = { return 1; };\n");
|
||||||
writefile(strings.concat(testcodec, "/codec.ww"), wirebody);
|
writefile(strings.concat(testcodec, "/codec.ww"), wirebody);
|
||||||
writefile(strings.concat(testcodec, "/internal_test.ww"), strings.concat(
|
writefile(strings.concat(testcodec, "/internal_test.ww"), strings.concat(
|
||||||
"package wire;\nimport testonly.helper;\n",
|
"package wire;\nimport assist testonly.helper;\n",
|
||||||
"@test fn internal_name_uses_declaration() void = {\n",
|
"@test fn internal_name_uses_declaration() void = {\n",
|
||||||
" assert(value() + helper.value() == 43);\n};\n"));
|
" assert(value() + assist.value() == 43);\n};\n"));
|
||||||
writefile(strings.concat(testcodec, "/external_test.ww"), strings.concat(
|
writefile(strings.concat(testcodec, "/external_test.ww"), strings.concat(
|
||||||
"package wire_test;\nimport testpkg.codec;\n",
|
"package wire_test;\nimport prod testpkg.codec;\n",
|
||||||
"@test fn external_name_uses_declaration() void = {\n",
|
"@test fn external_name_uses_declaration() void = {\n",
|
||||||
" assert(wire.value() == 42);\n};\n"));
|
" assert(prod.value() == 42);\n};\n"));
|
||||||
|
|
||||||
writefile(strings.concat(vendored, "/codec.ww"),
|
writefile(strings.concat(vendored, "/codec.ww"),
|
||||||
"package cable;\nexport fn value() i32 = { return 13; };\n");
|
"package cable;\nexport fn value() i32 = { return 13; };\n");
|
||||||
writefile(strings.concat(vendorclient, "/a.ww"), strings.concat(
|
writefile(strings.concat(vendorclient, "/a.ww"), strings.concat(
|
||||||
"package main;\nimport short.codec;\n",
|
"package main;\nimport stable short.codec;\n",
|
||||||
"fn vendora() i32 = { return cable.value(); };\n"));
|
"fn vendora() i32 = { return stable.value(); };\n"));
|
||||||
writefile(strings.concat(vendorclient, "/b.ww"), strings.concat(
|
writefile(strings.concat(vendorclient, "/b.ww"), strings.concat(
|
||||||
"package main;\nimport short.codec;\n",
|
"package main;\nimport short.codec;\n",
|
||||||
"fn vendorb() i32 = { return cable.value(); };\n"));
|
"fn vendorb() i32 = { return cable.value(); };\n"));
|
||||||
@@ -8080,6 +8086,7 @@ fn runtimepath(relative: str) str = {
|
|||||||
let scopedtraceref: str = "";
|
let scopedtraceref: str = "";
|
||||||
let repeatedtraceref: str = "";
|
let repeatedtraceref: str = "";
|
||||||
let changedtraceref: str = "";
|
let changedtraceref: str = "";
|
||||||
|
let aliastraceref: str = "";
|
||||||
let commitfailref: str = "";
|
let commitfailref: str = "";
|
||||||
let commitpreflightref: str = "";
|
let commitpreflightref: str = "";
|
||||||
let commitbindingref: str = "";
|
let commitbindingref: str = "";
|
||||||
@@ -8090,6 +8097,7 @@ fn runtimepath(relative: str) str = {
|
|||||||
let si: i32 = 0;
|
let si: i32 = 0;
|
||||||
for (si < stages.len) {
|
for (si < stages.len) {
|
||||||
rewritefile(codecfile, wirebody);
|
rewritefile(codecfile, wirebody);
|
||||||
|
rewritefile(bridgefile, stablebridgebody);
|
||||||
rewritefile(commitcodecfile, wirebody);
|
rewritefile(commitcodecfile, wirebody);
|
||||||
rewritefile(commitappfile, strings.concat(
|
rewritefile(commitappfile, strings.concat(
|
||||||
"package main;\nimport commit.codec;\n",
|
"package main;\nimport commit.codec;\n",
|
||||||
@@ -8313,7 +8321,8 @@ fn runtimepath(relative: str) str = {
|
|||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
let repeatedunit: str = readfile(strings.concat(works[si],
|
let repeatedunit: str = readfile(strings.concat(works[si],
|
||||||
"/cmd.repeated.unit.ww"));
|
"/cmd.repeated.unit.ww"));
|
||||||
assert(occurrences(repeatedunit, "import acme.codec;") == 2);
|
assert(occurrences(repeatedunit, "import stable acme.codec;") == 1);
|
||||||
|
assert(occurrences(repeatedunit, "import acme.codec;") == 1);
|
||||||
let repeatedtrace: str = readfile(traces[si]);
|
let repeatedtrace: str = readfile(traces[si]);
|
||||||
let repeatedline: str = linecontaining(repeatedtrace,
|
let repeatedline: str = linecontaining(repeatedtrace,
|
||||||
"cmd.repeated.unit.new");
|
"cmd.repeated.unit.new");
|
||||||
@@ -8427,7 +8436,7 @@ fn runtimepath(relative: str) str = {
|
|||||||
let externalunit: str = readfile(strings.concat(testwork,
|
let externalunit: str = readfile(strings.concat(testwork,
|
||||||
"/testpkg.codec_test-external-test.unit.ww"));
|
"/testpkg.codec_test-external-test.unit.ww"));
|
||||||
assert(!has(productionunit, "testonly.helper"));
|
assert(!has(productionunit, "testonly.helper"));
|
||||||
assert(has(internalunit, "import testonly.helper;"));
|
assert(has(internalunit, "import assist testonly.helper;"));
|
||||||
assert(!has(externalunit, "testonly.helper"));
|
assert(!has(externalunit, "testonly.helper"));
|
||||||
assert(has(externalunit, "package wire_test;"));
|
assert(has(externalunit, "package wire_test;"));
|
||||||
assert(os.exists(strings.concat(testwork,
|
assert(os.exists(strings.concat(testwork,
|
||||||
@@ -8475,7 +8484,8 @@ fn runtimepath(relative: str) str = {
|
|||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
let vendorunit: str = readfile(strings.concat(works[si],
|
let vendorunit: str = readfile(strings.concat(works[si],
|
||||||
"/vend.client.unit.ww"));
|
"/vend.client.unit.ww"));
|
||||||
assert(occurrences(vendorunit, "import short.codec;") == 2);
|
assert(occurrences(vendorunit, "import stable short.codec;") == 1);
|
||||||
|
assert(occurrences(vendorunit, "import short.codec;") == 1);
|
||||||
assert(has(vendorunit, strings.concat(
|
assert(has(vendorunit, strings.concat(
|
||||||
"//ww:import-map short.codec vend.vendor.short.codec ",
|
"//ww:import-map short.codec vend.vendor.short.codec ",
|
||||||
hexbytes(vendored), "\n")));
|
hexbytes(vendored), "\n")));
|
||||||
@@ -8498,9 +8508,9 @@ fn runtimepath(relative: str) str = {
|
|||||||
vendorrun, (30i64 * (time.second: i64)): time.duration, &out);
|
vendorrun, (30i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 13);
|
expectexit(&out, 13);
|
||||||
|
|
||||||
// Build the bare-import bridge and its command, establish a no-op warm
|
// A stable explicit alias survives a dependency declared-name change.
|
||||||
// rebuild, then change only the dependency's declared name. The direct
|
// Canonicalized exported type references keep the bridge interface
|
||||||
// importer is reconsidered; its unchanged export stops propagation.
|
// unchanged, so reconsideration stops after the direct importer.
|
||||||
rewritefile(traces[si], "");
|
rewritefile(traces[si], "");
|
||||||
let appav: []str = [driver(stages[si]), "build", "-w", works[si],
|
let appav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||||||
"-I", source, "-o", appbins[si], app];
|
"-I", source, "-o", appbins[si], app];
|
||||||
@@ -8511,6 +8521,13 @@ fn runtimepath(relative: str) str = {
|
|||||||
runcommand(root, strings.concat("name-app-run-", stages[si]), apprun,
|
runcommand(root, strings.concat("name-app-run-", stages[si]), apprun,
|
||||||
(30i64 * (time.second: i64)): time.duration, &out);
|
(30i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 42);
|
expectexit(&out, 42);
|
||||||
|
let stablebridgewwi: str = readfile(strings.concat(works[si],
|
||||||
|
"/acme.bridge.wwi"));
|
||||||
|
assert(has(stablebridgewwi,
|
||||||
|
"import __wwi_61636d652e636f646563 acme.codec;\n"));
|
||||||
|
assert(has(stablebridgewwi,
|
||||||
|
"export type BridgedThing = __wwi_61636d652e636f646563.Thing;\n"));
|
||||||
|
assert(!has(stablebridgewwi, "stable.Thing"));
|
||||||
rewritefile(traces[si], "");
|
rewritefile(traces[si], "");
|
||||||
runcommandenv(root, strings.concat("name-app-warm-", stages[si]), appav,
|
runcommandenv(root, strings.concat("name-app-warm-", stages[si]), appav,
|
||||||
env, (120i64 * (time.second: i64)): time.duration, &out);
|
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
@@ -8534,10 +8551,34 @@ fn runtimepath(relative: str) str = {
|
|||||||
"/acme.codec.wwi"));
|
"/acme.codec.wwi"));
|
||||||
assert(has(renamedwwi, "//ww:module acme.codec\npackage cable;\n"));
|
assert(has(renamedwwi, "//ww:module acme.codec\npackage cable;\n"));
|
||||||
assert(!has(renamedwwi, "package wire;"));
|
assert(!has(renamedwwi, "package wire;"));
|
||||||
|
assert(same(stablebridgewwi, readfile(strings.concat(works[si],
|
||||||
|
"/acme.bridge.wwi"))));
|
||||||
runcommand(root, strings.concat("name-app-renamed-run-", stages[si]),
|
runcommand(root, strings.concat("name-app-renamed-run-", stages[si]),
|
||||||
apprun, (30i64 * (time.second: i64)): time.duration, &out);
|
apprun, (30i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 42);
|
expectexit(&out, 42);
|
||||||
|
|
||||||
|
// Editing only the local alias rebuilds its owning source action. It
|
||||||
|
// cannot rename canonical exports or force the downstream command.
|
||||||
|
rewritefile(bridgefile, steadybridgebody);
|
||||||
|
rewritefile(traces[si], "");
|
||||||
|
runcommandenv(root, strings.concat("name-app-alias-edit-", stages[si]),
|
||||||
|
appav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
let aliastrace: str = readfile(traces[si]);
|
||||||
|
assert(occurrences(aliastrace, "\n") == 1);
|
||||||
|
assert(occurrences(aliastrace, "/acme.bridge.unit.new") == 1);
|
||||||
|
assert(!has(aliastrace, "/acme.codec.unit.new"));
|
||||||
|
assert(!has(aliastrace, "/cmd.app.unit.new"));
|
||||||
|
assert(same(stablebridgewwi, readfile(strings.concat(works[si],
|
||||||
|
"/acme.bridge.wwi"))));
|
||||||
|
let nalias: str = normalizedtrace(aliastrace,
|
||||||
|
strings.concat(works[si], "/"), appbins[si]);
|
||||||
|
if (si == 0) { aliastraceref = strings.dup(nalias); }
|
||||||
|
else { assert(same(aliastraceref, nalias)); };
|
||||||
|
runcommand(root, strings.concat("name-app-alias-edit-run-", stages[si]),
|
||||||
|
apprun, (30i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 42);
|
||||||
|
|
||||||
// New semantic artifacts and binaries are byte-identical across the
|
// New semantic artifacts and binaries are byte-identical across the
|
||||||
// independently cold Cstage and WWstage work roots.
|
// independently cold Cstage and WWstage work roots.
|
||||||
ari = 0;
|
ari = 0;
|
||||||
@@ -8567,6 +8608,272 @@ fn runtimepath(relative: str) str = {
|
|||||||
clean(root);
|
clean(root);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@test fn explicit_import_alias_binding_modes() void = {
|
||||||
|
let root: str = fresh();
|
||||||
|
let source: str = strings.concat(root, "/source");
|
||||||
|
let one: str = strings.concat(source, "/pkg/one");
|
||||||
|
let two: str = strings.concat(source, "/pkg/two");
|
||||||
|
let positives: []str = ["default", "alias", "reuse", "repeatok",
|
||||||
|
"repeatedfiles"];
|
||||||
|
let negatives: []str = ["barefn", "baretype", "baredef", "bareconst",
|
||||||
|
"barevar", "aliasbad", "aliasleak", "repeatdup", "aliasdup",
|
||||||
|
"defaultalias", "declcollision", "unusedalias", "blankalias"];
|
||||||
|
let dirs: []str = [one, two];
|
||||||
|
let i: i32 = 0;
|
||||||
|
for (i < positives.len) {
|
||||||
|
append(dirs, strings.concat(source, "/cmd/", positives[i]));
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
i = 0;
|
||||||
|
for (i < negatives.len) {
|
||||||
|
append(dirs, strings.concat(source, "/cmd/", negatives[i]));
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
i = 0;
|
||||||
|
for (i < dirs.len) { mkdirall(dirs[i]); i += 1; };
|
||||||
|
|
||||||
|
writefile(strings.concat(one, "/one.ww"), strings.concat(
|
||||||
|
"package wire;\n",
|
||||||
|
"export fn value() i32 = { return 11; };\n",
|
||||||
|
"export type Thing = i32;\n",
|
||||||
|
"export def DEFINED: i32 = 13;\n",
|
||||||
|
"export const CONSTANT: i32 = 17;\n",
|
||||||
|
"export let VARIABLE: i32 = 19;\n"));
|
||||||
|
writefile(strings.concat(two, "/two.ww"),
|
||||||
|
"package cable;\nexport fn value() i32 = { return 29; };\n");
|
||||||
|
let defaultbody: str = strings.concat(
|
||||||
|
"package main;\nimport pkg.one;\n",
|
||||||
|
"fn typed(v: wire.Thing) i32 = { return v: i32; };\n",
|
||||||
|
"fn main() i32 = { return wire.value() + wire.DEFINED + ",
|
||||||
|
"wire.CONSTANT + wire.VARIABLE + typed(23); };\n");
|
||||||
|
writefile(strings.concat(source, "/cmd/default/main.ww"), defaultbody);
|
||||||
|
writefile(strings.concat(source, "/cmd/alias/main.ww"), strings.concat(
|
||||||
|
"package main;\nimport stable pkg.one;\n",
|
||||||
|
"fn typed(v: stable.Thing) i32 = { return v: i32; };\n",
|
||||||
|
"fn main() i32 = { return stable.value() + stable.DEFINED + ",
|
||||||
|
"stable.CONSTANT + stable.VARIABLE + typed(23); };\n"));
|
||||||
|
writefile(strings.concat(source, "/cmd/reuse/a.ww"), strings.concat(
|
||||||
|
"package main;\nimport shared pkg.one;\n",
|
||||||
|
"fn left() i32 = { return shared.value(); };\n"));
|
||||||
|
writefile(strings.concat(source, "/cmd/reuse/b.ww"), strings.concat(
|
||||||
|
"package main;\nimport shared pkg.two;\n",
|
||||||
|
"fn right() i32 = { return shared.value(); };\n"));
|
||||||
|
writefile(strings.concat(source, "/cmd/reuse/main.ww"),
|
||||||
|
"package main;\nfn main() i32 = { return left() + right(); };\n");
|
||||||
|
writefile(strings.concat(source, "/cmd/repeatok/main.ww"), strings.concat(
|
||||||
|
"package main;\nimport pkg.one;\nimport first pkg.one;\n",
|
||||||
|
"import second pkg.one;\n",
|
||||||
|
"fn main() i32 = { return wire.value() + first.value() + ",
|
||||||
|
"second.value(); };\n"));
|
||||||
|
writefile(strings.concat(source, "/cmd/repeatedfiles/a.ww"), strings.concat(
|
||||||
|
"package main;\nimport left pkg.one;\n",
|
||||||
|
"fn first() i32 = { return left.value(); };\n"));
|
||||||
|
writefile(strings.concat(source, "/cmd/repeatedfiles/b.ww"), strings.concat(
|
||||||
|
"package main;\nimport right pkg.one;\n",
|
||||||
|
"fn second() i32 = { return right.value(); };\n"));
|
||||||
|
writefile(strings.concat(source, "/cmd/repeatedfiles/main.ww"),
|
||||||
|
"package main;\nfn main() i32 = { return first() + second(); };\n");
|
||||||
|
|
||||||
|
let bareexprs: []str = ["value()", "take(7)", "DEFINED", "CONSTANT",
|
||||||
|
"VARIABLE"];
|
||||||
|
let barenames: []str = ["barefn", "baretype", "baredef", "bareconst",
|
||||||
|
"barevar"];
|
||||||
|
i = 0;
|
||||||
|
for (i < barenames.len) {
|
||||||
|
let pre: str = "";
|
||||||
|
if (i == 1) { pre = "fn take(v: Thing) i32 = { return v: i32; };\n"; };
|
||||||
|
writefile(strings.concat(source, "/cmd/", barenames[i], "/main.ww"),
|
||||||
|
strings.concat("package main;\nimport pkg.one;\n", pre,
|
||||||
|
"fn main() i32 = { return ", bareexprs[i], "; };\n"));
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
writefile(strings.concat(source, "/cmd/aliasbad/main.ww"), strings.concat(
|
||||||
|
"package main;\nimport stable pkg.one;\n",
|
||||||
|
"fn bad(v: wire.Thing) i32 = { return v: i32; };\n",
|
||||||
|
"fn declared() i32 = { return wire.value(); };\n",
|
||||||
|
"fn leaf() i32 = { return one.value(); };\n",
|
||||||
|
"fn bare() i32 = { return value(); };\n",
|
||||||
|
"fn main() i32 = { return stable.value(); };\n"));
|
||||||
|
writefile(strings.concat(source, "/cmd/aliasleak/a.ww"), strings.concat(
|
||||||
|
"package main;\nimport stable pkg.one;\n",
|
||||||
|
"fn anchor() i32 = { return stable.value(); };\n"));
|
||||||
|
writefile(strings.concat(source, "/cmd/aliasleak/b.ww"),
|
||||||
|
"package main;\nfn leaked() i32 = { return stable.value(); };\n");
|
||||||
|
writefile(strings.concat(source, "/cmd/aliasleak/main.ww"),
|
||||||
|
"package main;\nfn main() i32 = { return leaked(); };\n");
|
||||||
|
writefile(strings.concat(source, "/cmd/repeatdup/main.ww"), strings.concat(
|
||||||
|
"package main;\nimport pkg.one;\nimport pkg.one;\n",
|
||||||
|
"fn main() i32 = { return wire.value(); };\n"));
|
||||||
|
writefile(strings.concat(source, "/cmd/aliasdup/main.ww"), strings.concat(
|
||||||
|
"package main;\nimport stable pkg.one;\nimport stable pkg.two;\n",
|
||||||
|
"fn main() i32 = { return stable.value(); };\n"));
|
||||||
|
writefile(strings.concat(source, "/cmd/defaultalias/main.ww"), strings.concat(
|
||||||
|
"package main;\nimport pkg.one;\nimport wire pkg.two;\n",
|
||||||
|
"fn main() i32 = { return wire.value(); };\n"));
|
||||||
|
writefile(strings.concat(source, "/cmd/declcollision/main.ww"), strings.concat(
|
||||||
|
"package main;\nimport stable pkg.one;\n",
|
||||||
|
"fn stable() i32 = { return 0; };\n",
|
||||||
|
"fn main() i32 = { return stable(); };\n"));
|
||||||
|
writefile(strings.concat(source, "/cmd/unusedalias/main.ww"),
|
||||||
|
"package main;\nimport stable pkg.one;\nfn main() i32 = { return 0; };\n");
|
||||||
|
writefile(strings.concat(source, "/cmd/blankalias/main.ww"),
|
||||||
|
"package main;\nimport _ pkg.one;\nfn main() i32 = { return 0; };\n");
|
||||||
|
|
||||||
|
let stages: []str = ["ww", "ww_ww"];
|
||||||
|
let expected: []i32 = [83, 83, 40, 33, 22];
|
||||||
|
let binaryrefs: []str = ["", "", "", "", ""];
|
||||||
|
let diagrefs: []str = ["", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", ""];
|
||||||
|
let needles: []str = ["undefined: value", "unknown type 'Thing'",
|
||||||
|
"undefined: DEFINED", "undefined: CONSTANT", "undefined: VARIABLE",
|
||||||
|
"unknown type 'wire.Thing'", "package 'stable' is not directly imported",
|
||||||
|
"wire redeclared in this block", "stable redeclared in this block",
|
||||||
|
"wire redeclared in this block",
|
||||||
|
"stable already declared through import of package stable",
|
||||||
|
"\"pkg.one\" imported as stable and not used",
|
||||||
|
"blank import alias _ is not implemented"];
|
||||||
|
let exactdiags: []str = [
|
||||||
|
strings.concat(
|
||||||
|
"$WORK/cmd.barefn.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n",
|
||||||
|
"$WORK/cmd.barefn.unit.new:4:26: error: undefined: value\n",
|
||||||
|
"ww: w6c failed for cmd.barefn\n"),
|
||||||
|
strings.concat(
|
||||||
|
"$WORK/cmd.baretype.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n",
|
||||||
|
"$WORK/cmd.baretype.unit.new:4:12: error: unknown type 'Thing'\n",
|
||||||
|
"ww: w6c failed for cmd.baretype\n"),
|
||||||
|
strings.concat(
|
||||||
|
"$WORK/cmd.baredef.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n",
|
||||||
|
"$WORK/cmd.baredef.unit.new:4:26: error: undefined: DEFINED\n",
|
||||||
|
"ww: w6c failed for cmd.baredef\n"),
|
||||||
|
strings.concat(
|
||||||
|
"$WORK/cmd.bareconst.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n",
|
||||||
|
"$WORK/cmd.bareconst.unit.new:4:26: error: undefined: CONSTANT\n",
|
||||||
|
"ww: w6c failed for cmd.bareconst\n"),
|
||||||
|
strings.concat(
|
||||||
|
"$WORK/cmd.barevar.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n",
|
||||||
|
"$WORK/cmd.barevar.unit.new:4:26: error: undefined: VARIABLE\n",
|
||||||
|
"ww: w6c failed for cmd.barevar\n"),
|
||||||
|
"", "",
|
||||||
|
strings.concat(
|
||||||
|
"$WORK/cmd.repeatdup.unit.new:4:1: error: wire redeclared in this block\n",
|
||||||
|
"\t$WORK/cmd.repeatdup.unit.new:3:1: other declaration of wire\n",
|
||||||
|
"$WORK/cmd.repeatdup.unit.new:4:1: error: \"pkg.one\" imported as wire and not used\n",
|
||||||
|
"ww: w6c failed for cmd.repeatdup\n"),
|
||||||
|
strings.concat(
|
||||||
|
"$WORK/cmd.aliasdup.unit.new:4:1: error: stable redeclared in this block\n",
|
||||||
|
"\t$WORK/cmd.aliasdup.unit.new:3:1: other declaration of stable\n",
|
||||||
|
"$WORK/cmd.aliasdup.unit.new:4:1: error: \"pkg.two\" imported as stable and not used\n",
|
||||||
|
"ww: w6c failed for cmd.aliasdup\n"),
|
||||||
|
strings.concat(
|
||||||
|
"$WORK/cmd.defaultalias.unit.new:4:1: error: wire redeclared in this block\n",
|
||||||
|
"\t$WORK/cmd.defaultalias.unit.new:3:1: other declaration of wire\n",
|
||||||
|
"$WORK/cmd.defaultalias.unit.new:4:1: error: \"pkg.two\" imported as wire and not used\n",
|
||||||
|
"ww: w6c failed for cmd.defaultalias\n"),
|
||||||
|
strings.concat(
|
||||||
|
"$WORK/cmd.declcollision.unit.new:3:1: error: \"pkg.one\" imported as stable and not used\n",
|
||||||
|
"$WORK/cmd.declcollision.unit.new:4:1: error: stable already declared through import of package stable (\"pkg.one\")\n",
|
||||||
|
"\t$WORK/cmd.declcollision.unit.new:3:1: other declaration of stable\n",
|
||||||
|
"ww: w6c failed for cmd.declcollision\n"),
|
||||||
|
strings.concat(
|
||||||
|
"$WORK/cmd.unusedalias.unit.new:3:1: error: \"pkg.one\" imported as stable and not used\n",
|
||||||
|
"ww: w6c failed for cmd.unusedalias\n"),
|
||||||
|
strings.concat(source,
|
||||||
|
"/cmd/blankalias/main.ww:2:8: error: blank import alias _ is not implemented\n"),
|
||||||
|
];
|
||||||
|
let si: i32 = 0;
|
||||||
|
for (si < stages.len) {
|
||||||
|
let work: str = strings.concat(root, "/work-", stages[si]);
|
||||||
|
let rejectwork: str = strings.concat(root, "/reject-", stages[si]);
|
||||||
|
mkdirall(work); mkdirall(rejectwork);
|
||||||
|
i = 0;
|
||||||
|
for (i < positives.len) {
|
||||||
|
let target: str = strings.concat(source, "/cmd/", positives[i]);
|
||||||
|
let binary: str = strings.concat(root, "/", stages[si], "-",
|
||||||
|
positives[i]);
|
||||||
|
let av: []str = [driver(stages[si]), "build", "-w", work, "-I",
|
||||||
|
source, "-o", binary, target];
|
||||||
|
let out: commandout;
|
||||||
|
runcommand(root, strings.concat("alias-positive-", stages[si], "-",
|
||||||
|
positives[i]), av,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
let runav: []str = [binary];
|
||||||
|
runcommand(root, strings.concat("alias-positive-run-", stages[si],
|
||||||
|
"-", positives[i]), runav,
|
||||||
|
(30i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, expected[i]);
|
||||||
|
let bytes: str = readfile(binary);
|
||||||
|
if (si == 0) { binaryrefs[i] = strings.dup(bytes); }
|
||||||
|
else { assert(same(binaryrefs[i], bytes)); };
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
let aliasunit: str = readfile(strings.concat(work, "/cmd.alias.unit.ww"));
|
||||||
|
assert(has(aliasunit, "import stable pkg.one;"));
|
||||||
|
let aliaswwi: str = readfile(strings.concat(work, "/cmd.alias.wwi"));
|
||||||
|
assert(has(aliaswwi, "import __wwi_706b672e6f6e65 pkg.one;"));
|
||||||
|
assert(!has(aliaswwi, "import stable"));
|
||||||
|
assert(os.exists(strings.concat(work, "/pkg.one.a")));
|
||||||
|
assert(!os.exists(strings.concat(work, "/stable.a")));
|
||||||
|
let repeatunit: str = readfile(strings.concat(work,
|
||||||
|
"/cmd.repeatok.unit.ww"));
|
||||||
|
assert(occurrences(repeatunit, "pkg.one;") == 3);
|
||||||
|
let repeatedunit: str = readfile(strings.concat(work,
|
||||||
|
"/cmd.repeatedfiles.unit.ww"));
|
||||||
|
assert(occurrences(repeatedunit, "pkg.one;") == 2);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
for (i < negatives.len) {
|
||||||
|
let target: str = strings.concat(source, "/cmd/", negatives[i]);
|
||||||
|
let binary: str = strings.concat(root, "/reject-bin-", stages[si],
|
||||||
|
"-", negatives[i]);
|
||||||
|
let av: []str = [driver(stages[si]), "build", "-w", rejectwork,
|
||||||
|
"-I", source, "-o", binary, target];
|
||||||
|
let out: commandout;
|
||||||
|
runcommand(root, strings.concat("alias-negative-", stages[si], "-",
|
||||||
|
negatives[i]), av,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(has(out.stderr, needles[i]));
|
||||||
|
if (i == 5) {
|
||||||
|
assert(has(out.stderr, "undefined: wire"));
|
||||||
|
assert(has(out.stderr, "undefined: one"));
|
||||||
|
assert(has(out.stderr, "undefined: value"));
|
||||||
|
};
|
||||||
|
if (i < 5) {
|
||||||
|
let unusedpos: i32 = pos(out.stderr,
|
||||||
|
"\"pkg.one\" imported as wire and not used");
|
||||||
|
let namepos: i32 = pos(out.stderr, needles[i]);
|
||||||
|
assert(unusedpos >= 0 && namepos > unusedpos);
|
||||||
|
};
|
||||||
|
if (i == 7 || i == 8 || i == 9) {
|
||||||
|
let redeclared: i32 = pos(out.stderr, "redeclared in this block");
|
||||||
|
let unused: i32 = pos(out.stderr, "imported as");
|
||||||
|
assert(redeclared >= 0 && unused > redeclared);
|
||||||
|
};
|
||||||
|
let normalized: str = normalizedtrace(out.stderr,
|
||||||
|
strings.concat(rejectwork, "/"), binary);
|
||||||
|
if (exactdiags[i].len > 0) {
|
||||||
|
assert(same(normalized, exactdiags[i]));
|
||||||
|
};
|
||||||
|
if (si == 0) { diagrefs[i] = strings.dup(normalized); }
|
||||||
|
else { assert(same(diagrefs[i], normalized)); };
|
||||||
|
assert(!os.exists(binary));
|
||||||
|
let action: str = strings.concat("cmd.", negatives[i]);
|
||||||
|
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a",
|
||||||
|
".unit.new", ".wwi.new", ".s.new", ".o.new", ".a.new"];
|
||||||
|
let j: i32 = 0;
|
||||||
|
for (j < suffixes.len) {
|
||||||
|
assert(!os.exists(strings.concat(rejectwork, "/", action,
|
||||||
|
suffixes[j])));
|
||||||
|
j += 1;
|
||||||
|
};
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
si += 1;
|
||||||
|
};
|
||||||
|
clean(root);
|
||||||
|
};
|
||||||
|
|
||||||
@test fn compile_artifact_naming() void = {
|
@test fn compile_artifact_naming() void = {
|
||||||
let root: str = fresh();
|
let root: str = fresh();
|
||||||
let pdir: str = strings.concat(root, "/pkg");
|
let pdir: str = strings.concat(root, "/pkg");
|
||||||
|
|||||||
@@ -206,8 +206,8 @@ fn rejectstable(dir: str, label: str, target: str, needle: str) void = {
|
|||||||
"import dimensions;\n",
|
"import dimensions;\n",
|
||||||
"def PRIVATE_WIDTH: i32 = 3;\n",
|
"def PRIVATE_WIDTH: i32 = 3;\n",
|
||||||
"export type Buffer = [PRIVATE_WIDTH]u8;\n",
|
"export type Buffer = [PRIVATE_WIDTH]u8;\n",
|
||||||
"export type PublicBuffer = [WIDTH]u8;\n",
|
"export type PublicBuffer = [dimensions.WIDTH]u8;\n",
|
||||||
"export type CastBuffer = [(WIDTH: u64)]u8;\n",
|
"export type CastBuffer = [(dimensions.WIDTH: u64)]u8;\n",
|
||||||
"export type Detail = struct { code: i64, };\n",
|
"export type Detail = struct { code: i64, };\n",
|
||||||
"export type Member = struct { detail: Detail, payload: i64, };\n",
|
"export type Member = struct { detail: Detail, payload: i64, };\n",
|
||||||
"export type Unused = struct { noise: i32, };\n",
|
"export type Unused = struct { noise: i32, };\n",
|
||||||
@@ -223,7 +223,7 @@ fn rejectstable(dir: str, label: str, target: str, needle: str) void = {
|
|||||||
"export type Member = struct { local: i32, };\n",
|
"export type Member = struct { local: i32, };\n",
|
||||||
"export fn produce(v: i32) implementation.Member = { return implementation.construct(v); };\n",
|
"export fn produce(v: i32) implementation.Member = { return implementation.construct(v); };\n",
|
||||||
"export fn score(m: implementation.Member) i32 = { return (m.detail.code + m.payload): i32; };\n",
|
"export fn score(m: implementation.Member) i32 = { return (m.detail.code + m.payload): i32; };\n",
|
||||||
"export fn width_of(b: Buffer) i32 = { return len(b): i32; };\n",
|
"export fn width_of(b: implementation.Buffer) i32 = { return len(b): i32; };\n",
|
||||||
"export fn public_width_of(b: implementation.PublicBuffer) i32 = { return len(b): i32; };\n",
|
"export fn public_width_of(b: implementation.PublicBuffer) i32 = { return len(b): i32; };\n",
|
||||||
"export fn cast_width_of(b: implementation.CastBuffer) i32 = { return len(b): i32; };\n");
|
"export fn cast_width_of(b: implementation.CastBuffer) i32 = { return len(b): i32; };\n");
|
||||||
let mainsrc: str = strings.concat(
|
let mainsrc: str = strings.concat(
|
||||||
@@ -423,7 +423,8 @@ fn writediamond(td: str, reverse: bool) str = {
|
|||||||
"left.wwi"));
|
"left.wwi"));
|
||||||
let rightiface: str = testenv.readfile(strings.concat(scratch,
|
let rightiface: str = testenv.readfile(strings.concat(scratch,
|
||||||
"right.wwi"));
|
"right.wwi"));
|
||||||
if (testenv.occurrences(leftiface, "import shared;") != 1) {
|
if (testenv.occurrences(leftiface,
|
||||||
|
"import __wwi_736861726564 shared;") != 1) {
|
||||||
fail("diamond", "direct import is missing or duplicated in export data");
|
fail("diamond", "direct import is missing or duplicated in export data");
|
||||||
};
|
};
|
||||||
if (testenv.occurrences(leftiface, "export type token") != 1
|
if (testenv.occurrences(leftiface, "export type token") != 1
|
||||||
@@ -907,7 +908,7 @@ fn writediamond(td: str, reverse: bool) str = {
|
|||||||
"/.wwtool.w6a")), testenv.readfile(assembler))
|
"/.wwtool.w6a")), testenv.readfile(assembler))
|
||||||
|| !testenv.same(testenv.readfile(strings.concat(work,
|
|| !testenv.same(testenv.readfile(strings.concat(work,
|
||||||
"/.wwtool.stamp")),
|
"/.wwtool.stamp")),
|
||||||
"ww workdir fmt 14 mode build asm 0\n")) {
|
"ww workdir fmt 15 mode build asm 0\n")) {
|
||||||
fail("driver-identity", "persistent artifacts or identities are incomplete");
|
fail("driver-identity", "persistent artifacts or identities are incomplete");
|
||||||
};
|
};
|
||||||
let coldwwi: str = testenv.readfile(strings.concat(work, "/dep.wwi"));
|
let coldwwi: str = testenv.readfile(strings.concat(work, "/dep.wwi"));
|
||||||
@@ -1140,7 +1141,7 @@ fn writediamond(td: str, reverse: bool) str = {
|
|||||||
"package main;\nimport left;\n",
|
"package main;\nimport left;\n",
|
||||||
"fn main() i32 = { return shared.base(); };\n"));
|
"fn main() i32 = { return shared.base(); };\n"));
|
||||||
rejectstable(td, "transitive", strings.concat(td, "/transitive.ww"),
|
rejectstable(td, "transitive", strings.concat(td, "/transitive.ww"),
|
||||||
"package 'shared' is not directly imported");
|
"undefined: shared");
|
||||||
testenv.writefile(strings.concat(td, "/transitive-bare-value.ww"),
|
testenv.writefile(strings.concat(td, "/transitive-bare-value.ww"),
|
||||||
strings.concat("package main;\nimport left;\n",
|
strings.concat("package main;\nimport left;\n",
|
||||||
"fn main() i32 = { return base(); };\n"));
|
"fn main() i32 = { return base(); };\n"));
|
||||||
|
|||||||
@@ -100,6 +100,37 @@ imports_to_str(const char *src, int *errs, const char **pkg)
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
must_import_shape(const char *src, int imports_only, const char *binding,
|
||||||
|
const char *path, const char *alias)
|
||||||
|
{
|
||||||
|
Arena *a = newarena();
|
||||||
|
Lex l;
|
||||||
|
Parser p;
|
||||||
|
char *wrapped = imports_only ? strdup(src) : wwtest_wrap(src);
|
||||||
|
lexinit(&l, a, imports_only ? "imports.ww" : "<test>", wrapped,
|
||||||
|
strlen(wrapped));
|
||||||
|
parserinit(&p, a, &l);
|
||||||
|
Node *file = imports_only ? parseimports(&p) : parsefile(&p);
|
||||||
|
Node *u = file ? file->list : NULL;
|
||||||
|
while (u != NULL && u->kind != N_USE) u = u->next;
|
||||||
|
int ok = p.errs == 0 && l.errs == 0 && u != NULL
|
||||||
|
&& u->str != NULL && strcmp(u->str, binding) == 0
|
||||||
|
&& u->usesource != NULL && strcmp(u->usesource, path) == 0
|
||||||
|
&& u->usepath != NULL && strcmp(u->usepath, path) == 0
|
||||||
|
&& ((alias == NULL && u->usealias == NULL)
|
||||||
|
|| (alias != NULL && u->usealias != NULL
|
||||||
|
&& strcmp(u->usealias, alias) == 0))
|
||||||
|
&& u->usepkgname == NULL && u->pos.file != NULL
|
||||||
|
&& u->pos.line > 0 && u->pos.col > 0;
|
||||||
|
if (!ok)
|
||||||
|
fprintf(stderr, "import shape mismatch (%s): %s\n",
|
||||||
|
imports_only ? "imports-only" : "full", src);
|
||||||
|
free(wrapped);
|
||||||
|
freearena(a);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(void)
|
main(void)
|
||||||
{
|
{
|
||||||
@@ -108,6 +139,7 @@ main(void)
|
|||||||
const char *parses[] = {
|
const char *parses[] = {
|
||||||
"import io;",
|
"import io;",
|
||||||
"import io.bufio;",
|
"import io.bufio;",
|
||||||
|
"import stable io.bufio;",
|
||||||
"def MAX: i32 = 4096;",
|
"def MAX: i32 = 4096;",
|
||||||
"export def MAX: i32 = 4096;",
|
"export def MAX: i32 = 4096;",
|
||||||
"type point = struct { x: i32, y: i32 };",
|
"type point = struct { x: i32, y: i32 };",
|
||||||
@@ -193,6 +225,7 @@ main(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!must_contain("import io;", "(use \"io\"")) fail++;
|
if (!must_contain("import io;", "(use \"io\"")) fail++;
|
||||||
|
if (!must_contain("import stable io.bufio;", "(use \"stable\"")) fail++;
|
||||||
if (!must_contain("def N: i32 = 4;", "(def \"N\"")) fail++;
|
if (!must_contain("def N: i32 = 4;", "(def \"N\"")) fail++;
|
||||||
if (!must_contain("def N: i32 = 4;", "(int 4")) fail++;
|
if (!must_contain("def N: i32 = 4;", "(int 4")) fail++;
|
||||||
if (!must_contain("export fn f() void = {};", "(fn \"f\" export")) fail++;
|
if (!must_contain("export fn f() void = {};", "(fn \"f\" export")) fail++;
|
||||||
@@ -201,6 +234,14 @@ main(void)
|
|||||||
if (!must_contain("fn f() void = { x = 1; };", "(assign =")) fail++;
|
if (!must_contain("fn f() void = { x = 1; };", "(assign =")) fail++;
|
||||||
if (!must_contain("fn f() i32 = { return a + b; };", "(bin +")) fail++;
|
if (!must_contain("fn f() i32 = { return a + b; };", "(bin +")) fail++;
|
||||||
if (!must_contain("@symbol(\"x\") fn f() void;", "(attr \"symbol\""))fail++;
|
if (!must_contain("@symbol(\"x\") fn f() void;", "(attr \"symbol\""))fail++;
|
||||||
|
if (!must_import_shape("import io.bufio;", 0, "bufio", "io.bufio",
|
||||||
|
NULL)) fail++;
|
||||||
|
if (!must_import_shape("import stable io.bufio;", 0, "stable",
|
||||||
|
"io.bufio", "stable")) fail++;
|
||||||
|
if (!must_import_shape("package main;\nimport io.bufio;\n", 1,
|
||||||
|
"bufio", "io.bufio", NULL)) fail++;
|
||||||
|
if (!must_import_shape("package main;\nimport stable io.bufio;\n", 1,
|
||||||
|
"stable", "io.bufio", "stable")) fail++;
|
||||||
|
|
||||||
{
|
{
|
||||||
const char *src =
|
const char *src =
|
||||||
@@ -268,6 +309,6 @@ main(void)
|
|||||||
fprintf(stderr, "%d parse tests failed\n", fail);
|
fprintf(stderr, "%d parse tests failed\n", fail);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
printf("parse: %d/%d ok + 9 shape ok\n", n, n);
|
printf("parse: %d/%d ok + 14 shape ok\n", n, n);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ export fn assert(b: bool, msg: str) void = { };
|
|||||||
package main;
|
package main;
|
||||||
import m;
|
import m;
|
||||||
export fn main() i32 = {
|
export fn main() i32 = {
|
||||||
assert(false, "routed to m.assert");
|
m.assert(false, "routed to m.assert");
|
||||||
return 42;
|
return 42;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ export type gerr2 = !u8;
|
|||||||
export fn op() (i32 | gerr1 | gerr2) = { return 0; };
|
export fn op() (i32 | gerr1 | gerr2) = { return 0; };
|
||||||
package main;
|
package main;
|
||||||
import beta;
|
import beta;
|
||||||
fn main() int = { match (beta.user()) { case let n: i32 => return n: int; case berr => return 99; }; };
|
fn main() int = { match (beta.user()) { case let n: i32 => return n: int; case beta.berr => return 99; }; };
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ export type gerr1 = !void;
|
|||||||
export fn other() (i32 | gerr1) = { return 0; };
|
export fn other() (i32 | gerr1) = { return 0; };
|
||||||
package main;
|
package main;
|
||||||
import beta;
|
import beta;
|
||||||
fn main() int = { match (beta.user()) { case let n: i32 => return n: int; case berr => return 99; }; };
|
fn main() int = { match (beta.user()) { case let n: i32 => return n: int; case beta.berr => return 99; }; };
|
||||||
|
|||||||
Reference in New Issue
Block a user