Skip to content

Commit d7c9e62

Browse files
hyperpolymathclaude
andcommitted
docs: restore extern coverage after rebase onto main (#90 merged)
PR #90 landed `extern fn` / `extern type` parsing and codegen on main while this branch was in review. Restore the SPEC.md coverage to match what actually shipped: - 1.2: re-add `extern` to the keyword list. - 2.1: re-add `extern_fn_decl` and `extern_type_decl` to `top_level`. Note the parser uses two separate productions that both feed back into `TopFn` / `TopType` AST variants (with `FnExtern` / `TyExtern` as the body kind); the spec describes the surface grammar, not the AST shape. - 2.10: re-introduce, but with the actual parsed shape (the productions accept `type_params`, the fn form accepts `effects`, and both accept optional `visibility`) rather than the simplified form from the original PR. Clarify the runtime contract: extern fn lowers to a Wasm import, extern type generates no artifact. - 8.1 / 8.2: split the `TopFn` population case into the `FnExtern` / non-`FnExtern` variants so the description matches the guard pattern in lib/codegen.ml. Hard-coded `"env"` Wasm module name is called out explicitly. Also update the lib/codegen.ml doc-comment on `func_indices` to mention both `TopFn` paths (defined and extern) and clarify that insertion order is source order. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b29a1dd commit d7c9e62

2 files changed

Lines changed: 61 additions & 8 deletions

File tree

docs/specs/SPEC.md

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ row_var = ".." lower_ident
2525
### 1.2 Keywords
2626

2727
```
28-
fn let const mut own ref type struct enum trait impl effect handle
28+
fn let const extern mut own ref type struct enum trait impl effect handle
2929
resume handler match if else while for return break continue in
3030
true false where total module use pub as unsafe assume transmute
3131
forget Nat Int Bool Float String Type Row
@@ -68,7 +68,7 @@ Special: \ (row restriction)
6868
```ebnf
6969
program = [module_decl] {import_decl} {top_level}
7070
top_level = fn_decl | type_decl | trait_decl | impl_block | effect_decl
71-
| const_decl
71+
| const_decl | extern_fn_decl | extern_type_decl
7272
```
7373

7474
### 2.2 Type Declarations
@@ -211,6 +211,28 @@ environment so that later top-level declarations may refer to either kind of
211211
binding by name. See §8 (*Codegen Module Environment*) for the encoding and
212212
the current single-pass population order.
213213

214+
### 2.10 Extern Declarations
215+
216+
```ebnf
217+
extern_fn_decl = [visibility] "extern" "fn" LOWER_IDENT
218+
[type_params] "(" [param_list] ")"
219+
["->" type_expr] ["/" effects] ";"
220+
extern_type_decl = [visibility] "extern" "type" UPPER_IDENT
221+
[type_params] ";"
222+
```
223+
224+
`extern fn` declares a function whose implementation is supplied by the host
225+
environment at link time. The linear-memory WebAssembly backend lowers each
226+
`extern fn` to an `(import "env" "<name>" (func …))` entry; the import slot
227+
is registered in the codegen name environment so call sites resolve through
228+
`call k` exactly as for locally-defined functions (see §8).
229+
230+
`extern type` declares an opaque, host-provided type. It carries no runtime
231+
representation and generates no Wasm artifact; the typechecker treats the
232+
name as a nominal opaque type whose internal structure is unknown.
233+
234+
Both forms are terminated by `;` and carry no body.
235+
214236
## 3. Type System
215237

216238
### 3.1 Judgement Forms
@@ -569,11 +591,18 @@ single integer per name keeps the lookup uniform across both kinds of binding.
569591

570592
**Population.** Top-level declarations are visited in source order by
571593
`gen_decl`, which is folded over `prog.prog_decls` from `generate_module`.
572-
The two relevant cases are:
573-
574-
- `TopFn fd` — registers `(fd.fd_name.name, func_idx)` in `func_indices`
575-
*before* generating the function body, so the body may recursively refer
576-
to its own name.
594+
The relevant cases are:
595+
596+
- `TopFn fd` with `fd.fd_body <> FnExtern` — picks the next Wasm function
597+
index (`import_func_count ctx + List.length ctx.funcs`), registers
598+
`(fd.fd_name.name, func_idx)` in `func_indices` *before* generating the
599+
body so the body may recursively refer to its own name, then appends the
600+
emitted function to `ctx.funcs`.
601+
- `TopFn fd` with `fd.fd_body = FnExtern` — emits a Wasm import (module
602+
`"env"`, name `fd.fd_name.name`) and registers
603+
`(fd.fd_name.name, import_func_idx)` in `func_indices`, where
604+
`import_func_idx` is the number of imports before adding this one. No
605+
function body is generated. See §8.2.
577606
- `TopConst tc` — generates the global initializer, appends the global to
578607
`ctx.globals`, then registers `(tc.tc_name.name, -(global_idx + 1))` in
579608
`func_indices`.
@@ -590,6 +619,27 @@ declaration's body — is tracked as a known gap in issue #73. The encoding
590619
documented in this section is the data layout the fix relies on; the
591620
call-site decode path will land alongside that fix.
592621

622+
### 8.2 Extern Bindings
623+
624+
An `extern fn name(…) -> Ret;` declaration produces a `TopFn` with
625+
`fd_body = FnExtern`. Codegen lowers it to a Wasm import:
626+
627+
```
628+
(import "env" "<name>" (func (param …) (result …)))
629+
```
630+
631+
The resulting import function index is positive (it counts among the
632+
combined "imports + defined functions" view used by every other call
633+
site), so the name is registered in `func_indices` with `k ≥ 0` and call
634+
sites resolve through `call k` indistinguishably from a locally-defined
635+
function. The Wasm module name is currently hard-coded to `"env"`,
636+
matching the convention adopted by the Node-CJS host shim.
637+
638+
An `extern type Name;` declaration produces a `TopType` with
639+
`td_body = TyExtern`. It generates no Wasm artifact — opaque types are
640+
purely a typechecker concern — and the codegen `TopType TyExtern` case
641+
returns the unchanged context.
642+
593643
## Appendix: Grammar Reference
594644

595645
See the full specification at `affinescript-spec.md` for:

lib/codegen.ml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@ type context = {
3131
func_indices : (string * int) list;
3232
(** Top-level name environment shared by functions and constants.
3333
- [k >= 0]: Wasm function index (imports + defined functions).
34+
Populated by both [TopFn] (defined function) and
35+
[TopFn _ with fd_body = FnExtern] (host-supplied import).
3436
- [k < 0]: Constant (global): actual global index is [-(k+1)].
35-
Both [TopFn] and [TopConst] insert into this table in declaration order. *)
37+
Populated by [TopConst].
38+
Entries are inserted in source declaration order by [gen_decl]. *)
3639
lambda_funcs : func list; (** lifted lambda functions *)
3740
next_lambda_id : int; (** next lambda function ID *)
3841
heap_ptr : int option; (** global index for heap pointer, if initialized *)

0 commit comments

Comments
 (0)