Skip to content

Commit 05588d8

Browse files
mjambonclaude
andauthored
Replace 'import' with 'from ... import type1, type2, ...' syntax (#457)
* Replace 'import' syntax with 'from ... import type1, type2, ...' The old 'import module' statement is replaced by explicit per-type imports: from module.path [<annots>] [as alias] import type1 [<annots>], type2, ... Key changes: - New AST node 'imported_type' carries name, arity params, and per-type annot - 'import.alias' is now 'string option' (no annotation on alias) - Two-phase validation: Imports.load checks structure; check_type_refs validates all qualified type refs (a.b) against the explicit import list and checks arity - New 'Imported_type' annotation position (node_kind) - atdgen: fatal error if any import statements are present, pointing to atdml - atdpy: auto-escape Python keywords in module alias names (e.g. class -> class_) - All backends updated; all test ATD files converted to new syntax Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Update documentation and changelog for 'from ... import' syntax - CHANGES.md: replace the old import entry with the new from...import entry - atd-language-reference.rst: update keywords list, grammar, and the Import declarations section to describe the new syntax - atdml-reference.rst: rewrite Import declarations section - atdts.rst: rewrite Import declarations section - atdpy.rst: rewrite Import annotations section including auto-escaping of Python keywords in alias names Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 4d2be5d commit 05588d8

33 files changed

+448
-250
lines changed

CHANGES.md

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,34 @@ unreleased
2828
`import long.module.path` generates `import * as path from "./long/module/path"`
2929
rather than `"./path"`.
3030

31-
* ATD: Import statements now support aliases and per-component language
32-
annotations. The full syntax is:
33-
34-
import module.path <path-annotations> as alias <alias-annotations>
35-
36-
The `path-annotations` control language-specific details for the module path
37-
(e.g. `<python name="def_">` to rename a module that collides with a Python
38-
keyword). The `alias-annotations` control the local name used in the
39-
generated code (e.g. `<ocaml name="Definitions">`). If no alias is given,
40-
the local name defaults to the last component of the dotted path. Example:
41-
42-
(* Import 'def' module as 'def_' in Python, aliased locally as 'class_' *)
43-
import def <python name="def_"> as class <python name="class_">
44-
45-
(* Import 'long.module.path', aliased as 'ext' in all languages *)
46-
import long.module.path as ext
47-
48-
Supported language-specific name annotations: `<python name="...">` for
49-
atdpy, `<ts name="...">` for atdts, `<ocaml name="...">` for atdml.
31+
* ATD: New `from ... import` syntax replaces the old `import` statement.
32+
Types to be used must be listed explicitly; the full syntax is:
33+
34+
from module.path <annots> [as alias] import type1 <annots>, type2, ...
35+
36+
Key properties:
37+
- Types must be listed explicitly (e.g. `from mylib import date, status`).
38+
- Parameterized types declare their arity: `from mylib import 'a list_ne`.
39+
- The arity of each imported type is enforced at all use-sites within the
40+
importing file.
41+
- Aliases are plain names without annotations: `from foo as f import t`.
42+
- Language-specific name annotations go on the module path:
43+
`<python name="...">` for atdpy, `<ts name="...">` for atdts,
44+
`<ocaml name="...">` for atdml.
45+
- atdpy: alias names that are Python keywords are automatically suffixed
46+
with `_` (e.g. alias `class``class_` in generated code).
47+
- atdgen does not support import statements; use atdml instead.
48+
49+
Examples:
50+
51+
(* Import specific types from a module *)
52+
from mylib.common import date, status
53+
54+
(* Import with alias *)
55+
from long.module.path as ext import tag
56+
57+
(* Override OCaml module name *)
58+
from mylib.common <ocaml name="Mylib_common"> import date
5059

5160
* atdml: New tool. Generates a single self-contained OCaml module (`.ml` +
5261
`.mli`) from a single `.atd` file, with JSON support via `Yojson.Safe.t`.

atd/src/annot.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ let create_id =
151151
type node_kind =
152152
| Module_head
153153
| Import
154+
| Imported_type
154155
| Type_def
155156
| Type_expr
156157
| Variant
@@ -170,6 +171,7 @@ let validate_section sec root =
170171
(* split fields by location where they may occur *)
171172
let in_module_head = ref [] in
172173
let in_import = ref [] in
174+
let in_imported_type = ref [] in
173175
let in_type_def = ref [] in
174176
let in_type_expr = ref [] in
175177
let in_variant = ref [] in
@@ -181,6 +183,7 @@ let validate_section sec root =
181183
match kind with
182184
| Module_head -> in_module_head
183185
| Import -> in_import
186+
| Imported_type -> in_imported_type
184187
| Type_def -> in_type_def
185188
| Type_expr -> in_type_expr
186189
| Variant -> in_variant
@@ -207,6 +210,7 @@ let validate_section sec root =
207210
Ast.fold_annot
208211
~module_:(check in_module_head)
209212
~import:(check in_import)
213+
~imported_type:(check in_imported_type)
210214
~type_def:(check in_type_def)
211215
~type_expr:(check in_type_expr)
212216
~variant:(check in_variant)

atd/src/annot.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ val create_id : unit -> string
167167
type node_kind =
168168
| Module_head
169169
| Import
170+
| Imported_type
170171
| Type_def
171172
| Type_expr
172173
| Variant

atd/src/ast.ml

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,16 @@ and type_def = {
3434
and import = {
3535
loc: loc;
3636
path: string list;
37-
annot: annot;
38-
alias: (string * annot) option;
39-
name: string;
37+
annot: annot; (* module-level annotation, e.g. <ocaml name="Format_"> *)
38+
alias: string option; (* optional "as name"; no annotation on the alias *)
39+
name: string; (* local reference name: alias if given, else last path component *)
40+
types: imported_type list; (* explicitly imported types *)
41+
}
42+
43+
and imported_type = {
44+
it_params: string list; (* [] for non-parametric; ["a"] for 'a t; ["a";"b"] for ('a,'b) t *)
45+
it_name: string;
46+
it_annot: annot; (* per-type annotation, e.g. <ocaml name="if_2"> *)
4047
}
4148

4249
and type_param = string list
@@ -80,6 +87,7 @@ and field =
8087
type any =
8188
| Module of module_
8289
| Import of import
90+
| Imported_type of imported_type
8391
| Type_def of type_def
8492
| Type_expr of type_expr
8593
| Variant of variant
@@ -128,7 +136,9 @@ and amap_cell f (loc, x, a) =
128136
(loc, amap_type_expr f x, f a)
129137

130138
let amap_import f (x : import) =
131-
{ x with annot = f x.annot }
139+
{ x with
140+
annot = f x.annot;
141+
types = List.map (fun it -> { it with it_annot = f it.it_annot }) x.types }
132142

133143
let amap_type_def f (x : type_def) : type_def =
134144
{ x with value = amap_type_expr f x.value }
@@ -204,6 +214,7 @@ let map_annot f = function
204214
type visitor_hooks = {
205215
module_: (module_ -> unit) -> module_ -> unit;
206216
import: (import -> unit) -> import -> unit;
217+
imported_type: (imported_type -> unit) -> imported_type -> unit;
207218
type_def: (type_def -> unit) -> type_def -> unit;
208219
type_expr: (type_expr -> unit) -> type_expr -> unit;
209220
variant: (variant -> unit) -> variant -> unit;
@@ -251,8 +262,13 @@ and visit_cell hooks x =
251262
let cont (loc, x, a) = visit_type_expr hooks x in
252263
hooks.cell cont x
253264

265+
let visit_imported_type hooks x =
266+
hooks.imported_type (fun _ -> ()) x
267+
254268
let visit_import hooks x =
255-
hooks.import (fun _ -> ()) x
269+
hooks.import (fun x ->
270+
List.iter (visit_imported_type hooks) x.types
271+
) x
256272

257273
let visit_type_def hooks (x : type_def) =
258274
let cont x = visit_type_expr hooks x.value in
@@ -268,6 +284,7 @@ let visit_module hooks (x : module_) =
268284
let visit
269285
?(module_ = fun cont x -> cont x)
270286
?(import = fun cont x -> cont x)
287+
?(imported_type = fun cont x -> cont x)
271288
?(type_def = fun cont x -> cont x)
272289
?(type_expr = fun cont x -> cont x)
273290
?(variant = fun cont x -> cont x)
@@ -277,6 +294,7 @@ let visit
277294
let hooks : visitor_hooks = {
278295
module_;
279296
import;
297+
imported_type;
280298
type_def;
281299
type_expr;
282300
variant;
@@ -287,6 +305,7 @@ let visit
287305
match any with
288306
| Module x -> visit_module hooks x
289307
| Import x -> visit_import hooks x
308+
| Imported_type x -> visit_imported_type hooks x
290309
| Type_def x -> visit_type_def hooks x
291310
| Type_expr x -> visit_type_expr hooks x
292311
| Variant x -> visit_variant hooks x
@@ -298,6 +317,7 @@ let visit
298317
let fold_annot
299318
?module_
300319
?import
320+
?imported_type
301321
?type_def
302322
?type_expr
303323
?variant
@@ -318,6 +338,7 @@ let fold_annot
318338
~module_:(fold module_
319339
(fun { module_head = (_, an); _ } -> an))
320340
~import:(fold import (fun (x : import) -> x.annot))
341+
~imported_type:(fold imported_type (fun (x : imported_type) -> x.it_annot))
321342
~type_def:(fold type_def (fun (x : type_def) -> x.annot))
322343
~type_expr:(fold type_expr annot_of_type_expr)
323344
~variant:(fold variant annot_of_variant)
@@ -521,17 +542,18 @@ let rec shallow_unwrap = function
521542
let remove_wrap_constructs (m : module_) : module_ =
522543
Map.module_ { type_expr = shallow_unwrap } m
523544
524-
let create_import ~loc ~path ~annot ?alias () : import =
545+
let create_import ~loc ~path ~annot ?alias ~types () : import =
525546
let name =
526547
match List.rev path, alias with
527548
| [], _ -> invalid_arg "Ast.create_import: empty path"
528549
| name :: _, None -> name
529-
| _ :: _, Some (override, _an) -> override
550+
| _ :: _, Some override -> override
530551
in
531552
{
532553
loc;
533554
path;
555+
annot;
534556
alias;
535557
name;
536-
annot;
558+
types;
537559
}

atd/src/ast.mli

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,33 +38,46 @@ and module_head = loc * annot
3838
(** The head of an ATD file is just a list of annotations. *)
3939

4040
(** Require the existence of another ATD module.
41-
The concrete syntax is [import external_name as local_name].
42-
The annotations specify language-specific options such as where to
43-
find the implementation or alternate names e.g.
44-
[import def <python name="def_"> as defs <ocaml name="Definitions">]
41+
The concrete syntax is [from module.path [as alias] import type1, type2, ...].
4542
*)
46-
and import = private {
43+
and import = {
4744
loc: loc;
4845

4946
path: string list;
5047
(** The full name of the ATD module.
5148
Unless an alias is specified, the local name of the module is
52-
the last component e.g. [import foo.bar] imports module [foo.bar]
49+
the last component e.g. [from foo.bar import t] imports module [foo.bar]
5350
which is known locally as just [bar]. *)
5451

5552
annot: annot;
5653

57-
alias: (string * annot) option;
54+
alias: string option;
5855
(** The local name used to identify the imported ATD module, overriding
5956
the default name.
6057
Typically, this is an abbreviation as in
61-
[import bubble_gum_factory_api as bg].
58+
[from bubble_gum_factory_api as bg import ...].
6259
*)
6360

6461
name: string;
6562
(** The local name of the module. It's the value of [alias] if there is
6663
one, otherwise it's the last component of [path].
6764
It's a single path component, i.e. it doesn't contain periods. *)
65+
66+
types: imported_type list;
67+
(** The list of types explicitly imported from the module. *)
68+
}
69+
70+
and imported_type = {
71+
it_params: string list;
72+
(** Type parameter names, e.g. ["a"] for ['a t] or ["a";"b"] for [('a,'b) t].
73+
Empty for non-parametric types. Used only to enforce arity checking
74+
within the current ATD file. *)
75+
76+
it_name: string;
77+
(** The name of the type in the external module. *)
78+
79+
it_annot: annot;
80+
(** Per-type annotation, e.g. [<ocaml name="if_2">]. *)
6881
}
6982

7083
(** A type definition. *)
@@ -197,6 +210,7 @@ and field =
197210
type any =
198211
| Module of module_
199212
| Import of import
213+
| Imported_type of imported_type
200214
| Type_def of type_def
201215
| Type_expr of type_expr
202216
| Variant of variant
@@ -212,7 +226,8 @@ val create_import :
212226
loc:loc ->
213227
path:string list ->
214228
annot:annot ->
215-
?alias:(string * annot) ->
229+
?alias:string ->
230+
types:imported_type list ->
216231
unit -> import
217232

218233
val loc_of_type_expr : type_expr -> loc
@@ -263,6 +278,7 @@ val map_all_annot : (annot -> annot) -> module_ -> module_
263278
val visit :
264279
?module_: ((module_ -> unit) -> module_ -> unit) ->
265280
?import: ((import -> unit) -> import -> unit) ->
281+
?imported_type: ((imported_type -> unit) -> imported_type -> unit) ->
266282
?type_def: ((type_def -> unit) -> type_def -> unit) ->
267283
?type_expr: ((type_expr -> unit) -> type_expr -> unit) ->
268284
?variant: ((variant -> unit) -> variant -> unit) ->
@@ -297,6 +313,7 @@ v}
297313
val fold_annot :
298314
?module_: (module_ -> annot -> 'a -> 'a) ->
299315
?import: (import -> annot -> 'a -> 'a) ->
316+
?imported_type: (imported_type -> annot -> 'a -> 'a) ->
300317
?type_def: (type_def -> annot -> 'a -> 'a) ->
301318
?type_expr: (type_expr -> annot -> 'a -> 'a) ->
302319
?variant: (variant -> annot -> 'a -> 'a) ->
@@ -320,7 +337,7 @@ val extract_type_names :
320337
type_expr -> type_name list
321338
(**
322339
Extract all the type names occurring in a type expression
323-
under [`Name], without duplicates.
340+
under [Name], without duplicates.
324341
@param ignorable specifies a list of type names to exclude from the result
325342
*)
326343

0 commit comments

Comments
 (0)