Skip to content

Commit da2164e

Browse files
mjambonclaude
andcommitted
Follow-up improvements from code review
- Merge import.ml into stdlib_extra.ml: import.ml now just re-exports stdlib_extra.ml via `include Stdlib_extra`, eliminating the duplicated utility code. - atd/imports.ml: use Hashtbl.Make with a PathTbl functor (keyed on string list) for the globals deduplication table, replacing the polymorphic Hashtbl. - atdts: dotted module paths in import declarations now map to path separators in the generated TypeScript import. For example, `import long.module.path` generates `./long/module/path` rather than just `./path`. - Add a gen-expect test for dotted module path imports in atdts. - doc/atd-language-reference.rst: add an experimental warning about dotted module paths, noting that their behavior may vary across backends and support may be removed in a future release. - doc/atdts.rst: update import path example to reflect the path-separator behavior for dotted module paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e784bbd commit da2164e

File tree

9 files changed

+454
-104
lines changed

9 files changed

+454
-104
lines changed

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
unreleased
22
----------
33

4+
* atdts: Dotted module paths in `import` declarations are now mapped to
5+
path-separator form in the generated TypeScript import path. For example,
6+
`import long.module.path` generates `import * as path from "./long/module/path"`
7+
rather than `"./path"`.
8+
49
* ATD: Import statements now support aliases and per-component language
510
annotations. The full syntax is:
611

atd/src/import.ml

Lines changed: 6 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,6 @@
1-
module List = struct
2-
include List
3-
4-
let rec filter_map f = function
5-
[] -> []
6-
| x :: l ->
7-
match f x with
8-
None -> filter_map f l
9-
| Some y -> y :: filter_map f l
10-
11-
let concat_map f l =
12-
List.map f l
13-
|> List.flatten
14-
15-
let map_first f = function
16-
| [] -> []
17-
| x :: l ->
18-
let y = f ~is_first:true x in
19-
y :: List.map (f ~is_first:false) l
20-
21-
let init n f = Array.to_list (Array.init n f)
22-
23-
let mapi l f =
24-
Array.of_list l
25-
|> Array.mapi f
26-
|> Array.to_list
27-
28-
let rec find_map f = function
29-
| [] -> None
30-
| x :: l ->
31-
match f x with
32-
None -> find_map f l
33-
| Some _ as y -> y
34-
35-
(* replace first occurrence, if any *)
36-
let rec assoc_update k v = function
37-
| (k', _) as x :: l ->
38-
if k = k' then
39-
(k, v) :: l
40-
else
41-
x :: assoc_update k v l
42-
| [] ->
43-
[]
44-
45-
let rec insert_sep t ~sep =
46-
match t with
47-
| []
48-
| [_] -> t
49-
| x :: xs -> x :: sep @ (insert_sep xs ~sep)
50-
51-
let split3 l =
52-
let (x, y, z) =
53-
List.fold_left (fun (xs, ys, zs) (x, y, z) ->
54-
(x::xs, y::ys, z::zs)
55-
) ([], [], []) l in
56-
(List.rev x, List.rev y, List.rev z)
57-
58-
let assoc_exn = assoc
59-
60-
let assoc key xs =
61-
match List.assoc key xs with
62-
| s -> Some s
63-
| exception Not_found -> None
64-
end
65-
66-
module Option = struct
67-
let map f = function
68-
| None -> None
69-
| Some s -> Some (f s)
70-
71-
let value_exn = function
72-
| None -> failwith "Option.value_exn"
73-
| Some s -> s
74-
75-
let value ~default = function
76-
| None -> default
77-
| Some s -> s
78-
79-
let is_some = function
80-
| None -> false
81-
| Some _ -> true
82-
83-
module O = struct
84-
let (>>=) x f =
85-
match x with
86-
| None -> None
87-
| Some x -> f x
88-
end
89-
end
90-
91-
let sprintf = Printf.sprintf
92-
let printf = Printf.printf
93-
let eprintf = Printf.eprintf
94-
let bprintf = Printf.bprintf
95-
let fprintf = Printf.fprintf
1+
(*
2+
This module re-exports the utilities from Stdlib_extra.
3+
It exists as a compatibility shim for external consumers of the Atd library
4+
who open Atd.Import (atdgen, atdcat, atds, etc.).
5+
*)
6+
include Stdlib_extra

atd/src/imports.ml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,16 @@ open Ast
88
(* Map local module name to import info. *)
99
type t = (string, import) Hashtbl.t
1010

11+
(* Use a type-safe hash table keyed on string list (module path). *)
12+
module PathTbl = Hashtbl.Make (struct
13+
type t = string list
14+
let equal = ( = )
15+
let hash = Hashtbl.hash
16+
end)
17+
1118
let load imports =
1219
(* keep track of full module names that were already loaded *)
13-
let globals = Hashtbl.create 100 in
20+
let globals : unit PathTbl.t = PathTbl.create 16 in
1421
(* our main table *)
1522
let locals = Hashtbl.create 100 in
1623
imports
@@ -23,12 +30,12 @@ let load imports =
2330
Consider using 'as' to give it a non-conflicting name.|}
2431
name
2532
)
26-
else if Hashtbl.mem globals x.path then
33+
else if PathTbl.mem globals x.path then
2734
error_at x.loc
2835
(sprintf "Module %s is loaded twice." (String.concat "." x.path))
2936
else (
3037
Hashtbl.add locals name x;
31-
Hashtbl.add globals x.path ()
38+
PathTbl.add globals x.path ()
3239
)
3340
);
3441
locals

atdts/src/lib/Codegen.ml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1233,8 +1233,10 @@ let reserve_good_type_names env (items: A.type_def list) =
12331233
items
12341234

12351235
let format_ts_import (x : A.import) =
1236+
(* Dotted module paths (e.g. long.module.path) are mapped to path separators
1237+
in the TypeScript import (e.g. "./long/module/path"). *)
12361238
let module_file =
1237-
"./" ^ String.lowercase_ascii (List.rev x.path |> List.hd)
1239+
"./" ^ String.concat "/" (List.map String.lowercase_ascii x.path)
12381240
in
12391241
sprintf "import * as %s from \"%s\"" (ts_name_of_import x) module_file
12401242

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import long.module.path
2+
3+
(* The local name of the imported module is "path" (the last component).
4+
In the TypeScript output, it is imported from "./long/module/path". *)
5+
type foo = {
6+
tag: path.tag;
7+
}

0 commit comments

Comments
 (0)