feat(lang): backtick raw-identifier escape + #import c foreign-name exemption [F0.6]

Reserved type-name spellings (s1, s2, u8, …) can now be used as value
identifiers two ways, resolving issue 0089:

1. Backtick raw identifier: a leading backtick (`s2) lexes to an
   .identifier token carrying a new Token.is_raw flag, with the backtick
   excluded from the text. A raw identifier is never type-classified — the
   parser skips Type.fromName for it — so it is always a value identifier.
   The flag threads to VarDecl.is_raw / Param.is_raw at binding sites, and
   the reserved-type-name check (UnknownTypeChecker) skips raw bindings.
   Because the token tag stays .identifier, the escape works in every
   position (local, global, param, field, fn name, struct member, later
   reference) with no per-site parser change.

2. #import c exemption: c_import.zig synthesizes foreign decls with
   Param.is_raw = true, so generated C param names that collide with
   reserved type names (s1, s2) import unedited.

A bare reserved-name binding in sx still errors (issue 0076 preserved):
the is_raw-gated skip only fires for backtick / foreign names, and a raw
binding's address-of / autoref lowering stays correct because every
occurrence is an .identifier, never a .type_expr.

Tests: examples/0151 (backtick, every position),
examples/1220 (foreign exemption, compiled+run), lexer unit tests.
1119 (bare-binding rejection) stays green. specs.md + readme.md updated.
This commit is contained in:
agra
2026-06-04 17:40:42 +03:00
parent 7911494809
commit 0dbdc530ba
19 changed files with 317 additions and 14 deletions

View File

@@ -13,6 +13,50 @@ Line comments start with `//` and extend to end of line.
- UPPER_SNAKE_CASE for constants: `SOME_INT`, `SOME_STR`
- PascalCase for types: `Foo`
#### Reserved type names
A spelling that names a builtin type — the arbitrary-width integers `s1`..`s64` /
`u1`..`u64`, plus `bool`, `string`, `void`, `f32`, `f64`, `usize`, `isize`, `Any`
is reserved. A bare value binding (`:=` / typed local / parameter name) spelled as
one of these is rejected: such a spelling parses as a *type*, not a value, so the
address-of / autoref paths would mis-lower it.
```sx
s2 := 2.5; // ERROR: 's2' is a reserved type name and cannot be used as an identifier
```
#### Backtick raw-identifier escape
A leading backtick makes the following identifier **raw**: its text excludes the
backtick and it is never type-classified, so a reserved-type-name spelling can be
used as an ordinary value identifier. The backtick is required at every occurrence
of that identifier (declaration and each reference); a *bare* `s2` is still the
signed-int type.
```sx
`s2 := 2.5; // OK — value identifier "s2", distinct from the s2 type
print("{}\n", `s2); // 2.5
```
The escape works in every identifier position — local, global, parameter, struct
field, function name, and a later reference:
```sx
`u8 := 100; // global
`s2 :: (`s1: s64) -> s64 { `s1 } // function name + parameter
P :: struct { `s2: f64; } // struct field
```
A backtick may also escape a keyword spelling (`` `for ``, `` `struct ``), yielding
an identifier with that text.
**`#import c` exemption.** Foreign declarations synthesized by an `#import c { … }`
block are treated as raw automatically: a generated C parameter or name that
collides with a reserved type name (e.g. `s1`, `s2`) imports unedited, with no
backticks and no reserved-name error. The exemption is scoped to the foreign decls —
it does not make a foreign `s2` usable as the sx `s2` type, nor relax the rule for
hand-written sx code.
### Literals
| Kind | Examples | Type |