Skip to content

Commit 065163d

Browse files
authored
Support for OCaml 5.3 effect syntax (#2562)
1 parent 2f840d4 commit 065163d

26 files changed

+372
-11
lines changed

CHANGES.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ profile. This started with version 0.26.0.
1414
This might change the formatting of some functions due to the formatting code
1515
being completely rewritten.
1616

17-
- Support OCaml 5.3 syntax (#2609, #2610, #2611, #2622, #2623, @Julow)
18-
This adds support for short functor type arguments syntax and utf8
17+
- Support OCaml 5.3 syntax (#2609, #2610, #2611, #2622, #2623, #2562, @Julow, @Zeta611)
18+
This adds support for effect patterns, short functor type arguments and utf8
1919
identifiers.
2020
To format code using the new `effect` syntax, add this option to your
2121
`.ocamlformat`:

lib/Ast.ml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,7 @@ end = struct
12511251
|Ppat_open (_, p1)
12521252
|Ppat_variant (_, Some p1) ->
12531253
assert (p1 == pat)
1254+
| Ppat_effect (p1, p2) -> assert (p1 == pat || p2 == pat)
12541255
| Ppat_extension (_, ext) -> assert (check_extensions ext)
12551256
| Ppat_any | Ppat_constant _
12561257
|Ppat_construct (_, None)
@@ -1944,8 +1945,9 @@ end = struct
19441945
, Ppat_tuple _ )
19451946
|( ( Pat
19461947
{ ppat_desc=
1947-
( Ppat_construct _ | Ppat_exception _ | Ppat_or _
1948-
| Ppat_lazy _ | Ppat_tuple _ | Ppat_variant _ | Ppat_list _ )
1948+
( Ppat_construct _ | Ppat_exception _ | Ppat_effect _
1949+
| Ppat_or _ | Ppat_lazy _ | Ppat_tuple _ | Ppat_variant _
1950+
| Ppat_list _ )
19491951
; _ }
19501952
| Exp {pexp_desc= Pexp_function (_, _, Pfunction_body _); _} )
19511953
, Ppat_alias _ )
@@ -1955,25 +1957,27 @@ end = struct
19551957
| Ppat_or _ ) )
19561958
|( Pat
19571959
{ ppat_desc=
1958-
( Ppat_construct _ | Ppat_exception _ | Ppat_tuple _
1959-
| Ppat_variant _ | Ppat_list _ )
1960+
( Ppat_construct _ | Ppat_exception _ | Ppat_effect _
1961+
| Ppat_tuple _ | Ppat_variant _ | Ppat_list _ )
19601962
; _ }
19611963
, Ppat_or _ )
19621964
|Pat {ppat_desc= Ppat_lazy _; _}, Ppat_tuple _
19631965
|Pat {ppat_desc= Ppat_tuple _; _}, Ppat_tuple _
19641966
|Pat _, Ppat_lazy _
19651967
|Pat _, Ppat_exception _
1968+
|Pat _, Ppat_effect _
19661969
|Cl {pcl_desc= Pcl_fun _; _}, Ppat_variant (_, Some _)
19671970
|Cl {pcl_desc= Pcl_fun _; _}, Ppat_tuple _
19681971
|Cl {pcl_desc= Pcl_fun _; _}, Ppat_construct _
19691972
|Cl {pcl_desc= Pcl_fun _; _}, Ppat_alias _
19701973
|Cl {pcl_desc= Pcl_fun _; _}, Ppat_lazy _
1971-
|(Exp {pexp_desc= Pexp_letop _; _} | Bo _), Ppat_exception _ ->
1974+
|( (Exp {pexp_desc= Pexp_letop _; _} | Bo _)
1975+
, (Ppat_exception _ | Ppat_effect _) ) ->
19721976
true
19731977
| (Str _ | Exp _ | Lb _), Ppat_lazy _ -> true
19741978
| ( (Fpe _ | Fpc _)
19751979
, ( Ppat_tuple _ | Ppat_construct _ | Ppat_alias _ | Ppat_variant _
1976-
| Ppat_lazy _ | Ppat_exception _ | Ppat_or _ ) )
1980+
| Ppat_lazy _ | Ppat_exception _ | Ppat_effect _ | Ppat_or _ ) )
19771981
|( Pat {ppat_desc= Ppat_construct _ | Ppat_variant _; _}
19781982
, (Ppat_construct (_, Some _) | Ppat_cons _ | Ppat_variant (_, Some _))
19791983
) ->

lib/Fmt_ast.ml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1322,10 +1322,21 @@ and fmt_pattern ?ext c ?pro ?parens ?(box = false)
13221322
$ fmt_extension_suffix c ext
13231323
$ space_break
13241324
$ fmt_pattern c (sub_pat ~ctx pat) ) )
1325+
| Ppat_effect (pat1, pat2) ->
1326+
cbox 2
1327+
(Params.parens_if parens c.conf
1328+
( str "effect"
1329+
$ fmt_extension_suffix c ext
1330+
$ space_break
1331+
$ fmt_pattern c (sub_pat ~ctx pat1)
1332+
$ str ", "
1333+
$ fmt_pattern c (sub_pat ~ctx pat2) ) )
13251334
| Ppat_extension
13261335
( ext
13271336
, PPat
1328-
( ( { ppat_desc= Ppat_lazy _ | Ppat_unpack _ | Ppat_exception _
1337+
( ( { ppat_desc=
1338+
( Ppat_lazy _ | Ppat_unpack _ | Ppat_exception _
1339+
| Ppat_effect _ )
13291340
; ppat_loc
13301341
; ppat_attributes= []
13311342
; _ } as pat )

test/passing/gen/dune.inc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,6 +1433,21 @@
14331433
(alias runtest)
14341434
(action (diff docstrings_toplevel_directives.mlt.err docstrings_toplevel_directives.mlt.stderr)))
14351435

1436+
(rule
1437+
(deps .ocamlformat dune-project)
1438+
(action
1439+
(with-stdout-to effects.ml.stdout
1440+
(with-stderr-to effects.ml.stderr
1441+
(run %{bin:ocamlformat} --name effects.ml --margin-check --ocaml-version=5.3 %{dep:../tests/effects.ml})))))
1442+
1443+
(rule
1444+
(alias runtest)
1445+
(action (diff effects.ml.ref effects.ml.stdout)))
1446+
1447+
(rule
1448+
(alias runtest)
1449+
(action (diff effects.ml.err effects.ml.stderr)))
1450+
14361451
(rule
14371452
(deps .ocamlformat dune-project)
14381453
(action
@@ -3727,6 +3742,21 @@
37273742
(alias runtest)
37283743
(action (diff pre42_syntax.ml.err pre42_syntax.ml.stderr)))
37293744

3745+
(rule
3746+
(deps .ocamlformat dune-project)
3747+
(action
3748+
(with-stdout-to pre53_syntax.ml.stdout
3749+
(with-stderr-to pre53_syntax.ml.stderr
3750+
(run %{bin:ocamlformat} --name pre53_syntax.ml --margin-check --ocaml-version=5.2 %{dep:../tests/pre53_syntax.ml})))))
3751+
3752+
(rule
3753+
(alias runtest)
3754+
(action (diff pre53_syntax.ml.ref pre53_syntax.ml.stdout)))
3755+
3756+
(rule
3757+
(alias runtest)
3758+
(action (diff pre53_syntax.ml.err pre53_syntax.ml.stderr)))
3759+
37303760
(rule
37313761
(deps .ocamlformat dune-project)
37323762
(action
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
let step (f : unit -> 'a) () : 'a status =
2+
match f () with
3+
| v -> Complete v
4+
| effect Xchg msg, cont -> Suspended { msg; cont }
5+
6+
(* A concurrent round-robin scheduler *)
7+
let run (main : unit -> unit) : unit =
8+
let exchanger : (int * (int, unit) continuation) option ref =
9+
ref None (* waiting exchanger *)
10+
in
11+
let run_q = Queue.create () in
12+
(* scheduler queue *)
13+
let enqueue k v =
14+
let task () = continue k v in
15+
Queue.push task run_q
16+
in
17+
let dequeue () =
18+
if Queue.is_empty run_q then () (* done *)
19+
else
20+
let task = Queue.pop run_q in
21+
task ()
22+
in
23+
let rec spawn (f : unit -> unit) : unit =
24+
match f () with
25+
| () -> dequeue ()
26+
| exception e ->
27+
print_endline (Printexc.to_string e);
28+
dequeue ()
29+
| effect Yield, k ->
30+
enqueue k ();
31+
dequeue ()
32+
| effect Fork f, k ->
33+
enqueue k ();
34+
spawn f
35+
| effect Xchg n, k -> (
36+
match !exchanger with
37+
| Some (n', k') ->
38+
exchanger := None;
39+
enqueue k' n;
40+
continue k n'
41+
| None ->
42+
exchanger := Some (n, k);
43+
dequeue ())
44+
in
45+
spawn main
46+
47+
let invert (type a) ~(iter : (a -> unit) -> unit) : a Seq.t =
48+
let module M = struct
49+
type _ Effect.t += Yield : a -> unit t
50+
end in
51+
let yield v = perform (M.Yield v) in
52+
fun () ->
53+
match iter yield with
54+
| () -> Seq.Nil
55+
| effect M.Yield v, k -> Seq.Cons (v, continue k)
56+
57+
type _ Effect.t += E : int t | F : string t
58+
59+
let foo () = perform F
60+
let bar () = try foo () with effect E, k -> failwith "impossible"
61+
let baz () = try bar () with effect F, k -> continue k "Hello, world!";;
62+
63+
try perform (Xchg 0) with effect Xchg n, k -> continue k 21 + continue k 21
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(** The [effect] keyword was added in OCaml 5.3. *)
2+
3+
type effect = effect
4+
5+
let effect effect : effect = effect
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
let step (f : unit -> 'a) () : 'a status =
2+
match f () with
3+
| v -> Complete v
4+
| effect Xchg msg, cont -> Suspended { msg; cont }
5+
;;
6+
7+
(* A concurrent round-robin scheduler *)
8+
let run (main : unit -> unit) : unit =
9+
let exchanger : (int * (int, unit) continuation) option ref =
10+
ref None (* waiting exchanger *)
11+
in
12+
let run_q = Queue.create () in
13+
(* scheduler queue *)
14+
let enqueue k v =
15+
let task () = continue k v in
16+
Queue.push task run_q
17+
in
18+
let dequeue () =
19+
if Queue.is_empty run_q
20+
then () (* done *)
21+
else (
22+
let task = Queue.pop run_q in
23+
task ())
24+
in
25+
let rec spawn (f : unit -> unit) : unit =
26+
match f () with
27+
| () -> dequeue ()
28+
| exception e ->
29+
print_endline (Printexc.to_string e);
30+
dequeue ()
31+
| effect Yield, k ->
32+
enqueue k ();
33+
dequeue ()
34+
| effect Fork f, k ->
35+
enqueue k ();
36+
spawn f
37+
| effect Xchg n, k ->
38+
(match !exchanger with
39+
| Some (n', k') ->
40+
exchanger := None;
41+
enqueue k' n;
42+
continue k n'
43+
| None ->
44+
exchanger := Some (n, k);
45+
dequeue ())
46+
in
47+
spawn main
48+
;;
49+
50+
let invert (type a) ~(iter : (a -> unit) -> unit) : a Seq.t =
51+
let module M = struct
52+
type _ Effect.t += Yield : a -> unit t
53+
end
54+
in
55+
let yield v = perform (M.Yield v) in
56+
fun () ->
57+
match iter yield with
58+
| () -> Seq.Nil
59+
| effect M.Yield v, k -> Seq.Cons (v, continue k)
60+
;;
61+
62+
type _ Effect.t += E : int t | F : string t
63+
64+
let foo () = perform F
65+
66+
let bar () =
67+
try foo () with
68+
| effect E, k -> failwith "impossible"
69+
;;
70+
71+
let baz () =
72+
try bar () with
73+
| effect F, k -> continue k "Hello, world!"
74+
;;
75+
76+
try perform (Xchg 0) with
77+
| effect Xchg n, k -> continue k 21 + continue k 21
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(** The [effect] keyword was added in OCaml 5.3. *)
2+
3+
type effect = effect
4+
5+
let effect effect : effect = effect
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
let step (f : unit -> 'a) () : 'a status =
2+
match f () with
3+
| v ->
4+
Complete v
5+
| effect Xchg msg, cont ->
6+
Suspended {msg; cont}
7+
8+
(* A concurrent round-robin scheduler *)
9+
let run (main : unit -> unit) : unit =
10+
let exchanger : (int * (int, unit) continuation) option ref =
11+
ref None (* waiting exchanger *)
12+
in
13+
let run_q = Queue.create () in
14+
(* scheduler queue *)
15+
let enqueue k v =
16+
let task () = continue k v in
17+
Queue.push task run_q
18+
in
19+
let dequeue () =
20+
if Queue.is_empty run_q then () (* done *)
21+
else
22+
let task = Queue.pop run_q in
23+
task ()
24+
in
25+
let rec spawn (f : unit -> unit) : unit =
26+
match f () with
27+
| () ->
28+
dequeue ()
29+
| exception e ->
30+
print_endline (Printexc.to_string e) ;
31+
dequeue ()
32+
| effect Yield, k ->
33+
enqueue k () ; dequeue ()
34+
| effect Fork f, k ->
35+
enqueue k () ; spawn f
36+
| effect Xchg n, k -> (
37+
match !exchanger with
38+
| Some (n', k') ->
39+
exchanger := None ;
40+
enqueue k' n ;
41+
continue k n'
42+
| None ->
43+
exchanger := Some (n, k) ;
44+
dequeue () )
45+
in
46+
spawn main
47+
48+
let invert (type a) ~(iter : (a -> unit) -> unit) : a Seq.t =
49+
let module M = struct
50+
type _ Effect.t += Yield : a -> unit t
51+
end in
52+
let yield v = perform (M.Yield v) in
53+
fun () ->
54+
match iter yield with
55+
| () ->
56+
Seq.Nil
57+
| effect M.Yield v, k ->
58+
Seq.Cons (v, continue k)
59+
60+
type _ Effect.t += E : int t | F : string t
61+
62+
let foo () = perform F
63+
64+
let bar () = try foo () with effect E, k -> failwith "impossible"
65+
66+
let baz () = try bar () with effect F, k -> continue k "Hello, world!" ;;
67+
68+
try perform (Xchg 0) with effect Xchg n, k -> continue k 21 + continue k 21
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(** The [effect] keyword was added in OCaml 5.3. *)
2+
3+
type effect = effect
4+
5+
let effect effect : effect = effect

0 commit comments

Comments
 (0)