Skip to content

Commit 321f2d5

Browse files
committed
[spectec] Support record extension analogous to variant extension
1 parent 5aebe3f commit 321f2d5

File tree

16 files changed

+238
-83
lines changed

16 files changed

+238
-83
lines changed

spectec/doc/Language.md

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,22 +236,22 @@ not administrative ones.
236236
In addition to the basic grammar shown above,
237237
definitions of syntax variants can also be split into multiple _fragments_:
238238
```
239-
def ::=
240-
"syntax" varid subid* "=" deftyp-frag syntax fragment definition
239+
deftyp ::= ...
240+
deftyp-frag syntax fragment definition
241241
242242
deftyp-frag ::=
243243
"..."
244244
("..." "|")? casetyp*"|" ("|" "...")? variant fragment
245245
```
246246
A variant with dots "..." at the end can be extended further by later variant definitions of the same name.
247-
This definition must start with dots, accordingly.
247+
These definitions must start with dots, accordingly.
248248
A variant is completed by a fragment without trailing dots.
249249
Each fragment must be named uniquely by amending the type name with a (possibly empty) list of hierarchical sub-identifiers of the form `x/y`,
250250
which can be used to refer to a fragment from splices.
251251
Currently, variant types defined in fragments cannot have parameters.
252252

253253
**Example:**
254-
The instruction syntax above could be defined in two fragments:
254+
The instruction syntax above could be defined in multiple fragments:
255255
```
256256
syntax instr/stack = DROP | ...
257257
syntax instr/arith = ... | CONST numtyp const | ...
@@ -297,6 +297,32 @@ syntax person = {NAME text, AGE nat, ADDRESS text}
297297
SpecTec provides special syntax for accessing values of record type,
298298
in particular, dot notation for field access.
299299

300+
In addition to the basic grammar shown above,
301+
like [variant types](#variant-types),
302+
definitions of record syntax can also be split into multiple _fragments_:
303+
```
304+
deftyp-frag ::=
305+
"{" "..." "}"
306+
"{" ("..." ",")? fieldtyp*"," (...")? "}" record fragment
307+
```
308+
A record with dots "..." at the end can be extended further by later record definitions of the same name.
309+
These definitions must start with dots, accordingly.
310+
A record is completed by a fragment without trailing dots.
311+
Each fragment must be named uniquely by amending the type name with a (possibly empty) list of hierarchical sub-identifiers of the form `x/y`,
312+
which can be used to refer to a fragment from splices.
313+
Currently, record types defined in fragments cannot have parameters.
314+
315+
**Example:**
316+
The person record above could be defined in multiple fragments:
317+
```
318+
syntax person/name = { NAME text, ... }
319+
syntax person/age = { ..., AGE nat, ... }
320+
syntax person/address = { ..., ADDRESS text }
321+
```
322+
323+
Semantically, a record definition split into fragments is equivalent to a combined one.
324+
Splitting a definition only has organisational purpose.
325+
300326

301327
##### Premises
302328

0 Bytes
Binary file not shown.

spectec/src/backend-latex/render.ml

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,9 +288,15 @@ let env_typdef env tid t : typ list option =
288288
| VarT (id, _) ->
289289
map_append tid.it (Map.find id.it !(env.atoms)) env.atoms;
290290
Some [t]
291-
| StrT tfs ->
291+
| StrT (dots1, ts, tfs, _) ->
292292
iter_nl_list (env_typfield env tid) tfs;
293-
Some []
293+
iter_nl_list (fun t ->
294+
match t.it with
295+
| VarT (id, _) ->
296+
map_append tid.it (Map.find id.it !(env.atoms)) env.atoms
297+
| _ -> ()
298+
) ts;
299+
if dots1 = Dots && ts = [] then None else Some (filter_nl ts)
294300
| CaseT (dots1, ts, tcs, _) ->
295301
iter_nl_list (env_typcase env tid) tcs;
296302
iter_nl_list (fun t ->
@@ -1168,11 +1174,21 @@ and render_nottyp env t : table =
11681174
(string_of_region t.at) (El.Print.string_of_typ t);
11691175
*)
11701176
match t.it with
1171-
| StrT tfs ->
1177+
| StrT (dots1, ts, tfs, _dots2) ->
1178+
let render env = function
1179+
| `Dots -> render_dots Dots
1180+
| `Typ t -> render_nottyp env t
1181+
| `TypField tf -> render_typfield env tf
1182+
in
11721183
[Row [Col (
11731184
"\\{ " ^
11741185
render_table env "@{}" ["l"; "l"] 0 0
1175-
(concat_table "" (render_nl_list env (`H, ", ") render_typfield tfs) [Row [Col " \\}"]])
1186+
(concat_table "" (render_nl_list env (`H, ", ") render (
1187+
(match dots1 with Dots -> [Elem `Dots] | NoDots -> []) @
1188+
map_nl_list (fun t -> `Typ t) ts @
1189+
map_nl_list (fun tf -> `TypField tf) tfs @
1190+
[] (* (match dots2 with Dots -> [Elem `Dots] | NoDots -> []) *)
1191+
)) [Row [Col " \\}"]])
11761192
)]]
11771193
| CaseT (dots1, ts, tcs, _dots2) ->
11781194
let render env = function

spectec/src/backend-prose/prose_util.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ let env_hintdef ?(partial = false) hd =
8484
let env_typ id t =
8585
let open El.Ast in
8686
match t.it with
87-
| El.Ast.StrT l ->
87+
| El.Ast.StrT (_, _, l, _) ->
8888
List.iter (function
8989
| Nl -> ()
9090
| Elem (atom, _, hints) ->

spectec/src/el/ast.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ and typ' =
4646
| TupT of typ list (* `(` list2(typ, `,`) `)` *)
4747
| IterT of typ * iter (* typ iter *)
4848
(* The forms below are only allowed in type definitions *)
49-
| StrT of typfield nl_list (* `{` list(typfield,`,`') `}` *)
49+
| StrT of dots * typ nl_list * typfield nl_list * dots (* `{` list(`...`|typ|typfield, `,`) `}` *)
5050
| CaseT of dots * typ nl_list * typcase nl_list * dots (* `|` list(`...`|typ|typcase, `|`) *)
5151
| ConT of typcon (* typ prem* *)
5252
| RangeT of typenum nl_list (* exp `|` `...` `|` exp *)

spectec/src/el/convert.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ let rec typ_of_exp e =
7171
| ParenE e1 -> ParenT (typ_of_exp e1)
7272
| TupE es -> TupT (List.map typ_of_exp es)
7373
| IterE (e1, iter) -> IterT (typ_of_exp e1, iter)
74-
| StrE efs -> StrT (map_nl_list typfield_of_expfield efs)
74+
| StrE efs -> StrT (NoDots, [], map_nl_list typfield_of_expfield efs, NoDots)
7575
| AtomE atom -> AtomT atom
7676
| SeqE es -> SeqT (List.map typ_of_exp es)
7777
| InfixE (e1, atom, e2) -> InfixT (typ_of_exp e1, atom, typ_of_exp e2)
@@ -90,12 +90,12 @@ let rec exp_of_typ t =
9090
| ParenT t1 -> ParenE (exp_of_typ t1)
9191
| TupT ts -> TupE (List.map exp_of_typ ts)
9292
| IterT (t1, iter) -> IterE (exp_of_typ t1, iter)
93-
| StrT tfs -> StrE (map_nl_list expfield_of_typfield tfs)
93+
| StrT (NoDots, [], tfs, NoDots) -> StrE (map_nl_list expfield_of_typfield tfs)
9494
| AtomT atom -> AtomE atom
9595
| SeqT ts -> SeqE (List.map exp_of_typ ts)
9696
| InfixT (t1, atom, t2) -> InfixE (exp_of_typ t1, atom, exp_of_typ t2)
9797
| BrackT (l, t1, r) -> BrackE (l, exp_of_typ t1, r)
98-
| CaseT _ | ConT _ | RangeT _ -> error t.at "malformed expression"
98+
| StrT _ | CaseT _ | ConT _ | RangeT _ -> error t.at "malformed expression"
9999
) $ t.at
100100

101101
and expfield_of_typfield (atom, (t, _prems), _) =

spectec/src/el/eq.ml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ and eq_typ t1 t2 =
4747
| TupT ts1, TupT ts2 -> eq_list eq_typ ts1 ts2
4848
| IterT (t11, iter1), IterT (t21, iter2) ->
4949
eq_typ t11 t21 && eq_iter iter1 iter2
50-
| StrT tfs1, StrT tfs2 -> eq_nl_list eq_typfield tfs1 tfs2
50+
| StrT (dots11, ts1, tfs1, dots12), StrT (dots21, ts2, tfs2, dots22) ->
51+
dots11 = dots21 && eq_nl_list eq_typ ts1 ts2 &&
52+
eq_nl_list eq_typfield tfs1 tfs2 && dots12 = dots22
5153
| CaseT (dots11, ts1, tcs1, dots12), CaseT (dots21, ts2, tcs2, dots22) ->
5254
dots11 = dots21 && eq_nl_list eq_typ ts1 ts2 &&
5355
eq_nl_list eq_typcase tcs1 tcs2 && dots12 = dots22

spectec/src/el/free.ml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,9 @@ and free_typ t =
102102
| ParenT t1 -> free_typ t1
103103
| TupT ts -> free_list free_typ ts
104104
| IterT (t1, iter) -> free_typ t1 + free_iter iter
105-
| StrT tfs ->
106-
free_nl_list (fun tf -> free_typfield tf - det_typfield tf) tfs
105+
| StrT (_, ts, tfs, _) ->
106+
free_nl_list free_typ ts +
107+
free_nl_list (fun tf -> free_typfield tf - det_typfield tf) tfs
107108
| CaseT (_, ts, tcs, _) ->
108109
free_nl_list free_typ ts +
109110
free_nl_list (fun tc -> free_typcase tc - det_typcase tc) tcs

spectec/src/el/iter.ml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ and typ t =
100100
| ParenT t1 -> typ t1
101101
| TupT ts -> list typ ts
102102
| IterT (t1, it) -> typ t1; iter it
103-
| StrT tfs -> nl_list typfield tfs
103+
| StrT (dots1, ts, tfs, dots2) ->
104+
dots dots1; nl_list typ ts; nl_list typfield tfs; dots dots2
104105
| CaseT (dots1, ts, tcs, dots2) ->
105106
dots dots1; nl_list typ ts; nl_list typcase tcs; dots dots2
106107
| ConT tc -> typcon tc
@@ -263,7 +264,7 @@ and clone_typ t =
263264
| SeqT ts -> SeqT (List.map clone_typ ts)
264265
| InfixT (t1, atom, t2) -> InfixT (clone_typ t1, clone_atom atom, clone_typ t2)
265266
| BrackT (atom1, t1, atom2) -> BrackT (clone_atom atom1, clone_typ t1, clone_atom atom2)
266-
| StrT tfs -> StrT (Convert.map_nl_list clone_typfield tfs)
267+
| StrT (dots1, ts, tfs, dots2) -> StrT (dots1, Convert.map_nl_list clone_typ ts, Convert.map_nl_list clone_typfield tfs, dots2)
267268
| CaseT (dots1, ts, tcs, dots2) -> CaseT (dots1, Convert.map_nl_list clone_typ ts, Convert.map_nl_list clone_typcase tcs, dots2)
268269
| ConT tc -> ConT (clone_typcon tc)
269270
| RangeT tes -> RangeT (Convert.map_nl_list clone_typenum tes)

spectec/src/el/print.ml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,20 @@ and string_of_typ ?(short=false) t =
7878
| ParenT t -> "(" ^ string_of_typ t ^ ")"
7979
| TupT ts -> "(" ^ string_of_typs ", " ts ^ ")"
8080
| IterT (t1, iter) -> string_of_typ t1 ^ string_of_iter iter
81-
| StrT tfs when short && List.length tfs > 3 ->
82-
"{" ^ concat ", " (map_filter_nl_list (string_of_typfield ~short) (Lib.List.take 3 tfs)) ^ ", ..}"
83-
| StrT tfs ->
84-
"{" ^ concat ", " (map_filter_nl_list (string_of_typfield ~short) tfs) ^ "}"
81+
| StrT (dots1, ts, tfs, dots2) when short && List.length tfs > 3 ->
82+
"{" ^ concat ", " (
83+
strings_of_dots dots1 @
84+
map_filter_nl_list string_of_typ ts @
85+
map_filter_nl_list (string_of_typfield ~short) (Lib.List.take 3 tfs) @
86+
".." :: strings_of_dots dots2
87+
) ^ "}"
88+
| StrT (dots1, ts, tfs, dots2) ->
89+
"{" ^ concat ", " (
90+
strings_of_dots dots1 @
91+
map_filter_nl_list string_of_typ ts @
92+
map_filter_nl_list (string_of_typfield ~short) tfs @
93+
strings_of_dots dots2
94+
) ^ "}"
8595
| CaseT (dots1, ts, tcs, dots2) when short && List.length tcs > 3 ->
8696
"| " ^ concat " | " (
8797
strings_of_dots dots1 @

0 commit comments

Comments
 (0)