docs: alias carry semantics + stdlib layout (step 4)
- specs §9: Namespace Alias Carry section (one level, own-wins, ambiguity, no chaining — 0114 noted for the still-ungated bare-call path), the three-tier import resolution (file dir -> cwd -> stdlib search path / SX_STDLIB_PATH), a Standard Library Layout section, real-layout examples replacing the stale modules/std/std.sx ones. - readme: carry-rule teaser with the std namespace-tail example (verified to compile and run as written). - CLAUDE.md: file-roles rows for std.sx/std//ffi//math//build.sx, tests/fixtures, and the PLAN-STDLIB tracker.
This commit is contained in:
@@ -572,8 +572,15 @@ Wiring a new bundling step:
|
|||||||
| `current/CHECKPOINT-LANG.md` | **Active** LANG progress tracker. Update after every step. |
|
| `current/CHECKPOINT-LANG.md` | **Active** LANG progress tracker. Update after every step. |
|
||||||
| `current/PLAN-ERR.md` | **Active** ERR implementation plan (`!` errors, `try` / `catch` / `or` / `onfail`, return traces). |
|
| `current/PLAN-ERR.md` | **Active** ERR implementation plan (`!` errors, `try` / `catch` / `or` / `onfail`, return traces). |
|
||||||
| `current/CHECKPOINT-ERR.md` | **Active** ERR progress tracker. Update after every step. |
|
| `current/CHECKPOINT-ERR.md` | **Active** ERR progress tracker. Update after every step. |
|
||||||
|
| `current/PLAN-STDLIB.md` | **Active** STDLIB restructure plan (alias carry rule + std/ffi/math layout). Progress tracked in its `## Status` section — no separate checkpoint file. |
|
||||||
| `implementation_plan.md` | Archive of completed work (closures, protocols, etc.). Do not pick up tasks from here. |
|
| `implementation_plan.md` | Archive of completed work (closures, protocols, etc.). Do not pick up tasks from here. |
|
||||||
| `readme.md` | User-facing language overview — **maintained**. Update it whenever a user-facing sx change lands (new/changed syntax, semantics, gating diagnostics, language behavior), per the docs-track-changes rule. |
|
| `readme.md` | User-facing language overview — **maintained**. Update it whenever a user-facing sx change lands (new/changed syntax, semantics, gating diagnostics, language behavior), per the docs-track-changes rule. |
|
||||||
| `CLAUDE.md` | This file. Session instructions. |
|
| `CLAUDE.md` | This file. Session instructions. |
|
||||||
|
| `library/modules/std.sx` | The prelude (print/format/List/string ops/Context/Allocator) + the namespace tail (`mem`/`xml`/`log` carried to flat importers). |
|
||||||
|
| `library/modules/std/` | Stdlib modules: mem (allocators), fs, process, socket, json, cli, hash, xml, log, trace, test. Import directly until they join the tail. |
|
||||||
|
| `library/modules/ffi/` | FFI bindings: objc, objc_block, sdl3, opengl, raylib, stb, stb_truetype, wasm. |
|
||||||
|
| `library/modules/math/` | scalar / vector2 / matrix44 — one spelling: `#import "modules/math"` (directory import). |
|
||||||
|
| `library/modules/build.sx` | `BuildOptions` compile-time build DSL. See "Bundling lives in sx" above. |
|
||||||
| `library/modules/platform/bundle.sx` | sx-side `.app` / `.apk` bundler. See "Bundling lives in sx" above. |
|
| `library/modules/platform/bundle.sx` | sx-side `.app` / `.apk` bundler. See "Bundling lives in sx" above. |
|
||||||
| `library/modules/std/fs.sx`, `library/modules/std/process.sx` | POSIX stdlib for the bundler + general consumer use. |
|
| `library/modules/std/fs.sx`, `library/modules/std/process.sx` | POSIX stdlib for the bundler + general consumer use. |
|
||||||
|
| `tests/fixtures/` | Test-only import fixtures (testpkg/, test_c.sx) — resolve CWD-relative from the repo root, not via the stdlib search path. |
|
||||||
|
|||||||
20
readme.md
20
readme.md
@@ -441,6 +441,26 @@ struct / pack fn / protocol body is instantiated in the module that defines it,
|
|||||||
e.g. `List(T).append`'s `alloc: Allocator` is visible there regardless of the call
|
e.g. `List(T).append`'s `alloc: Allocator` is visible there regardless of the call
|
||||||
site.)
|
site.)
|
||||||
|
|
||||||
|
**Namespace aliases carry one level.** A namespaced import is an ordinary
|
||||||
|
declaration, and flat-importing the module that declares it makes the alias
|
||||||
|
usable in the importer — there is no `pub` keyword. The stdlib prelude uses
|
||||||
|
exactly this: `#import "modules/std.sx"` gives every bare prelude name
|
||||||
|
(`print`, `List`, `Context`, …) plus the carried namespaces of std's tail:
|
||||||
|
|
||||||
|
```sx
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
main :: () {
|
||||||
|
gpa := mem.GPA.init(); // mem :: #import — carried from std.sx
|
||||||
|
log.warn("count = {}", 3);
|
||||||
|
s := xml.escape("<a & b>");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Carried aliases follow declaration rules: an own declaration shadows a carried
|
||||||
|
alias, two flat imports carrying the same alias make its use ambiguous, and
|
||||||
|
carry does not chain through a second flat hop.
|
||||||
|
|
||||||
### Implicit Context
|
### Implicit Context
|
||||||
|
|
||||||
Every program gets an implicit `context` with a default allocator:
|
Every program gets an implicit `context` with a default allocator:
|
||||||
|
|||||||
91
specs.md
91
specs.md
@@ -2479,7 +2479,7 @@ The `#import` directive brings declarations from another `.sx` file or directory
|
|||||||
|
|
||||||
**Flat import** — splices all declarations from the imported file into the current scope:
|
**Flat import** — splices all declarations from the imported file into the current scope:
|
||||||
```sx
|
```sx
|
||||||
#import "modules/std/math.sx";
|
#import "modules/std/fs.sx";
|
||||||
```
|
```
|
||||||
|
|
||||||
**Namespaced import** — wraps all declarations under a namespace name:
|
**Namespaced import** — wraps all declarations under a namespace name:
|
||||||
@@ -2502,10 +2502,58 @@ Namespaced declarations are accessed with dot notation:
|
|||||||
std.print("hello");
|
std.print("hello");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Namespace Alias Carry
|
||||||
|
|
||||||
|
A namespaced import is an ordinary declaration of the alias name. There is no
|
||||||
|
`pub` keyword: flat-importing a module carries the module's namespace aliases
|
||||||
|
**one level** — a file's aliases are usable by its DIRECT flat importers, with
|
||||||
|
declaration-like collision semantics.
|
||||||
|
|
||||||
|
```sx
|
||||||
|
// facade.sx
|
||||||
|
r :: #import "rich.sx"; // an ordinary declaration of `r`
|
||||||
|
|
||||||
|
// main.sx
|
||||||
|
#import "facade.sx"; // flat import carries facade's aliases
|
||||||
|
|
||||||
|
main :: () {
|
||||||
|
r.helper(); // plain fn through the carried alias
|
||||||
|
t := r.Thing.init(); // static method
|
||||||
|
x : r.Thing = t; // type annotation
|
||||||
|
n := r.LIMIT; // module const
|
||||||
|
c := r.Color.green; // enum variant
|
||||||
|
b := r.Box(s64).{ item = 3 }; // generic struct head
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Every qualified shape resolves through a carried alias exactly as through a
|
||||||
|
directly-declared one: function calls, `alias.Type.method()`, type
|
||||||
|
annotations, enum variants, module constants, and generic struct heads.
|
||||||
|
|
||||||
|
Collision rules mirror ordinary declarations:
|
||||||
|
|
||||||
|
- **Own wins** — a file's own declaration of a name (including its own
|
||||||
|
`ns :: #import`) shadows any same-named alias carried from a flat import.
|
||||||
|
- **Ambiguity** — two direct flat imports each carrying a distinct alias of
|
||||||
|
the same name make a bare use of that alias an error; declare the alias
|
||||||
|
locally to disambiguate.
|
||||||
|
- **One level only** — carry does not chain: a flat import of a flat import
|
||||||
|
does not surface the inner file's aliases. (The bare `alias.fn()` call path
|
||||||
|
does not yet enforce this gate — issue 0114 tracks the tightening.)
|
||||||
|
|
||||||
|
`#import c { ... }` aliases (`tc :: #import c { ... }`) carry the same way.
|
||||||
|
|
||||||
### Import Resolution
|
### Import Resolution
|
||||||
|
|
||||||
- Imports are resolved after parsing and before code generation.
|
- Imports are resolved after parsing and before code generation.
|
||||||
- Paths are first resolved relative to the directory of the file containing the `#import`. If not found, they fall back to the working directory (cwd). This allows modules in subdirectories to import shared modules using the same paths as the root file.
|
- Paths are resolved in three tiers, first hit wins:
|
||||||
|
1. relative to the directory of the file containing the `#import`;
|
||||||
|
2. relative to the working directory (cwd);
|
||||||
|
3. relative to each stdlib search path — the `library/` directory discovered
|
||||||
|
from the compiler binary's location (dev: `zig-out/bin/sx` →
|
||||||
|
`<repo>/library`; install: `<prefix>/library`), overridable with the
|
||||||
|
`SX_STDLIB_PATH` environment variable. This is how
|
||||||
|
`#import "modules/std.sx"` resolves from any project.
|
||||||
- If the path resolves to a file, it is imported directly. If it resolves to a directory, all `.sx` files in that directory are aggregated.
|
- If the path resolves to a file, it is imported directly. If it resolves to a directory, all `.sx` files in that directory are aggregated.
|
||||||
- Nested imports are supported (imported files may themselves contain `#import`).
|
- Nested imports are supported (imported files may themselves contain `#import`).
|
||||||
- Circular imports are detected and silently skipped (each file is imported at most once).
|
- Circular imports are detected and silently skipped (each file is imported at most once).
|
||||||
@@ -2529,22 +2577,45 @@ Functions within a namespaced import can call each other without the namespace p
|
|||||||
### Example
|
### Example
|
||||||
|
|
||||||
```sx
|
```sx
|
||||||
// modules/std/math.sx
|
// modules/std/json.sx
|
||||||
mul :: (base: $T, exp: T) -> T { base * exp; }
|
parse :: (text: string) -> ?JsonValue { ... }
|
||||||
|
|
||||||
// modules/std/std.sx
|
|
||||||
out :: (str: string) -> void #builtin;
|
|
||||||
|
|
||||||
// main.sx
|
// main.sx
|
||||||
std :: #import "modules/std.sx";
|
std :: #import "modules/std.sx";
|
||||||
#import "modules/std/math.sx";
|
#import "modules/std/json.sx";
|
||||||
|
|
||||||
main :: () -> s32 {
|
main :: () -> s32 {
|
||||||
std.out("hello there");
|
std.print("hello there\n");
|
||||||
mul(5, 2);
|
v := parse("{}");
|
||||||
|
0
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Standard Library Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
modules/std.sx the prelude — print/format, string ops (concat, substr,
|
||||||
|
path_join, ...), List(T), Context + push, the Allocator
|
||||||
|
protocol; plus the namespace tail:
|
||||||
|
mem / xml / log :: #import "modules/std/<m>.sx"
|
||||||
|
modules/std/ mem.sx (CAllocator, GPA, Arena, TrackingAllocator),
|
||||||
|
fs.sx, process.sx, socket.sx, json.sx, cli.sx, hash.sx,
|
||||||
|
xml.sx, log.sx, trace.sx, test.sx
|
||||||
|
modules/ffi/ objc.sx, objc_block.sx, sdl3.sx, opengl.sx, raylib.sx,
|
||||||
|
stb.sx, stb_truetype.sx, wasm.sx
|
||||||
|
modules/math/ scalar.sx, vector2.sx, matrix44.sx — import the
|
||||||
|
directory: #import "modules/math"
|
||||||
|
modules/build.sx BuildOptions — compile-time build configuration (§10.5)
|
||||||
|
modules/platform/ bundle.sx, uikit.sx, android.sx, sdl3.sx, ... —
|
||||||
|
windowing/bundling backends
|
||||||
|
modules/gpu/, modules/ui/ GPU protocol + retained UI toolkit
|
||||||
|
```
|
||||||
|
|
||||||
|
`#import "modules/std.sx"` gives every prelude name bare, plus `mem.GPA`,
|
||||||
|
`xml.escape`, `log.warn`, ... through the carried namespace tail (see
|
||||||
|
Namespace Alias Carry). The remaining `std/` modules are imported directly
|
||||||
|
(`#import "modules/std/json.sx"`) until they join the tail.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 10. CLI & Cross-Compilation
|
## 10. CLI & Cross-Compilation
|
||||||
|
|||||||
Reference in New Issue
Block a user