Skip to content

Commit e8eb463

Browse files
hyperpolymathclaude
andcommitted
feat: add extern type / extern fn parsing and codegen (closes #42)
extern was not a recognised keyword, causing a parse error at character 1 of any file containing extern declarations. Adds: - Token.EXTERN in lib/token.ml - ("extern", EXTERN) in the lexer keyword table (lib/lexer.ml) - TopExternType { et_name } and TopExternFn { ef_name; ef_params; ef_ret_ty } variants in the AST (lib/ast.ml) - extern_type_decl and extern_fn_decl parser rules in lib/parser.mly; both added to the top_level production - gen_decl cases in lib/codegen.ml: TopExternType is a no-op (opaque type for the type-checker); TopExternFn adds a Wasm import with module "env" and registers the name in func_indices so call sites resolve correctly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 04501be commit e8eb463

5 files changed

Lines changed: 46 additions & 1 deletion

File tree

lib/ast.ml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,14 @@ type top_level =
367367
tc_ty : type_expr;
368368
tc_value : expr;
369369
}
370+
| TopExternType of {
371+
et_name : ident;
372+
}
373+
| TopExternFn of {
374+
ef_name : ident;
375+
ef_params : param list;
376+
ef_ret_ty : type_expr option;
377+
}
370378
[@@deriving show, eq]
371379

372380
(** Complete program *)

lib/codegen.ml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,6 +1871,28 @@ let gen_decl (ctx : context) (decl : top_level) : context result =
18711871
(* These declarations don't generate code *)
18721872
Ok ctx
18731873

1874+
| TopExternType _ ->
1875+
(* Opaque host type — no code generated; type is available to the type-checker *)
1876+
Ok ctx
1877+
1878+
| TopExternFn ef ->
1879+
(* Add a WebAssembly import for the extern fn declaration.
1880+
Module name defaults to "env" (conventional host environment namespace). *)
1881+
let param_types = List.map (fun _ -> I32) ef.ef_params in
1882+
let result_types = match ef.ef_ret_ty with
1883+
| None -> []
1884+
| Some _ -> [I32]
1885+
in
1886+
let func_type = { ft_params = param_types; ft_results = result_types } in
1887+
let type_idx = List.length ctx.types in
1888+
let ctx_with_type = { ctx with types = ctx.types @ [func_type] } in
1889+
let func_idx = import_func_count ctx_with_type in
1890+
let import_entry = { i_module = "env"; i_name = ef.ef_name.name;
1891+
i_desc = ImportFunc type_idx } in
1892+
Ok { ctx_with_type with
1893+
imports = ctx_with_type.imports @ [import_entry];
1894+
func_indices = (ef.ef_name.name, func_idx) :: ctx_with_type.func_indices }
1895+
18741896
(** Generate WASM module from AffineScript program *)
18751897
let generate_module (prog : program) : wasm_module result =
18761898
let ctx = create_context () in

lib/lexer.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ let () =
4141
("use", USE);
4242
("pub", PUB);
4343
("as", AS);
44+
("extern", EXTERN);
4445
("unsafe", UNSAFE);
4546
("assume", ASSUME);
4647
("transmute", TRANSMUTE);

lib/parser.mly

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ let mk_ident name startpos endpos =
3939
%token FN LET CONST MUT OWN REF TYPE STRUCT ENUM TRAIT IMPL
4040
%token EFFECT HANDLE RESUME MATCH IF ELSE WHILE FOR
4141
%token RETURN BREAK CONTINUE IN WHERE TOTAL MODULE USE
42-
%token PUB AS UNSAFE ASSUME TRANSMUTE FORGET TRY CATCH FINALLY
42+
%token PUB AS EXTERN UNSAFE ASSUME TRANSMUTE FORGET TRY CATCH FINALLY
4343

4444
/* Built-in types */
4545
%token NAT INT_T BOOL FLOAT_T STRING_T CHAR_T TYPE_K ROW NEVER
@@ -121,12 +121,24 @@ top_level:
121121
| tr = trait_decl { TopTrait tr }
122122
| i = impl_block { TopImpl i }
123123
| c = const_decl { c }
124+
| e = extern_type_decl { e }
125+
| e = extern_fn_decl { e }
124126

125127
const_decl:
126128
| vis = visibility? CONST name = ident COLON ty = type_expr EQ value = expr SEMICOLON
127129
{ TopConst { tc_vis = Option.value vis ~default:Private;
128130
tc_name = name; tc_ty = ty; tc_value = value } }
129131

132+
extern_type_decl:
133+
| EXTERN TYPE name = upper_ident SEMICOLON
134+
{ TopExternType { et_name = name } }
135+
136+
extern_fn_decl:
137+
| EXTERN FN name = ident LPAREN params = separated_list(COMMA, param) RPAREN SEMICOLON
138+
{ TopExternFn { ef_name = name; ef_params = params; ef_ret_ty = None } }
139+
| EXTERN FN name = ident LPAREN params = separated_list(COMMA, param) RPAREN ARROW ret = type_expr SEMICOLON
140+
{ TopExternFn { ef_name = name; ef_params = params; ef_ret_ty = Some ret } }
141+
130142
/* ========== Functions ========== */
131143

132144
fn_decl:

lib/token.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type t =
4747
| USE
4848
| PUB
4949
| AS
50+
| EXTERN
5051
| UNSAFE
5152
| ASSUME
5253
| SELF_KW (** self receiver keyword *)
@@ -164,6 +165,7 @@ let to_string = function
164165
| USE -> "use"
165166
| PUB -> "pub"
166167
| AS -> "as"
168+
| EXTERN -> "extern"
167169
| UNSAFE -> "unsafe"
168170
| ASSUME -> "assume"
169171
| SELF_KW -> "self"

0 commit comments

Comments
 (0)