Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal 869: pattern-matching syntax #908

Merged
merged 53 commits into from
Sep 4, 2020
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
bc50823
Using buffer in SQL query building
Emanon42 May 27, 2020
93f50ed
unique bufferize query building
Emanon42 Jun 3, 2020
ebd092a
fix sprintf and intermediate string building
Emanon42 Jun 3, 2020
a7adb2c
better higher-order buf_mapstrcat
Emanon42 Jun 5, 2020
c5e90d2
add wrappers
Emanon42 Jun 8, 2020
665b9ee
fix wrong side-effect execute order
Emanon42 Jun 8, 2020
3e6a25f
sweep the code
Emanon42 Jun 8, 2020
89bcdae
trim the trailing whitespace
Emanon42 Jun 8, 2020
6a6aa27
Fix #154 by raising runtime error
Emanon42 Jun 19, 2020
01a3b4f
move to Format module
Emanon42 Jul 2, 2020
709d7a6
improve fprintf structure
Emanon42 Jul 3, 2020
9fcaf34
use asprintf in tables
Emanon42 Jul 3, 2020
749d22e
merge upstream
Emanon42 Jul 3, 2020
6ddfbe8
fix quote
Emanon42 Jul 3, 2020
c5619c4
resolve conflict in webif
Emanon42 Jul 3, 2020
80fc6b1
remove old bufconcat
Emanon42 Jul 3, 2020
e86649e
trim space
Emanon42 Jul 3, 2020
f4e1019
many fine-grained fix
Emanon42 Jul 5, 2020
3594cd1
many fine-grained fix
Emanon42 Jul 5, 2020
5eb6b12
improve by james advice
Emanon42 Jul 7, 2020
f9780db
sweep the code
Emanon42 Jul 7, 2020
4e7881e
add delete where unit test
Emanon42 Jul 7, 2020
719ac5f
add pattern matching sugared node
Emanon42 Jul 14, 2020
47cdf67
add some traversal
Emanon42 Jul 20, 2020
cca27e8
fix type
Emanon42 Jul 24, 2020
90fd8f5
add sugar to parser
Emanon42 Jul 25, 2020
be36d66
fix BRACE
Emanon42 Jul 25, 2020
65839f7
fix pattern
Emanon42 Jul 25, 2020
7318fae
add transformation to normalfunlit
Emanon42 Aug 3, 2020
388fdf1
add non-exhaustive case and test
Emanon42 Aug 4, 2020
2071b57
merge upstream
Emanon42 Aug 4, 2020
ff47876
trim space
Emanon42 Aug 4, 2020
3043dd1
trim space
Emanon42 Aug 4, 2020
d1fe1ba
resolve Simon's review
Emanon42 Aug 4, 2020
6703c89
trim space
Emanon42 Aug 4, 2020
e802471
fix transformsugar
Emanon42 Aug 4, 2020
49f0122
fix test cases
Emanon42 Aug 4, 2020
e866a9f
change keyword to switch and remove redundant productions
Emanon42 Aug 13, 2020
7e12319
fix parser
Emanon42 Aug 13, 2020
b9ba88d
add flag and fix tests
Emanon42 Aug 24, 2020
2b6cc31
Merge remote-tracking branch 'upstream/master' into proposal-869
Emanon42 Aug 24, 2020
a2dbf5b
fix test
Emanon42 Aug 24, 2020
6b678ea
resolve daniel's review
Emanon42 Aug 25, 2020
f56256c
rename and fix nullary guard
Emanon42 Aug 26, 2020
6310d72
change switch function arg structure
Emanon42 Aug 26, 2020
873eebd
renaming pass and error
Emanon42 Aug 27, 2020
685d429
move currying guard to desugar pass
Emanon42 Aug 28, 2020
75895f9
make get_normal_funlit utility func
Emanon42 Aug 29, 2020
cc1d2e3
resolve simon's second review
Emanon42 Aug 31, 2020
85c68c3
fix last get_normal_fnlit
Emanon42 Aug 31, 2020
7f161bd
fix phrasenode desugaring
Emanon42 Sep 2, 2020
d2ff01f
space nitpick
Emanon42 Sep 2, 2020
e816b0f
fix error msg
Emanon42 Sep 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 11 additions & 14 deletions core/desugarFuns.ml
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,14 @@ let unwrap_def (bndr, linearity, (tyvars, lam), location) =
let ft = Binder.to_type bndr in
let rt = TypeUtils.return_type ft in
let lam =
let rec make_lam t : funlit -> funlit =
function
| ([_ps], _body) as lam -> lam
| (ps::pss, body) ->
let g = gensym ~prefix:"_fun_" () in
let rt = TypeUtils.return_type t in
([ps], block
([fun_binding' ~linearity ~location (binder ~ty:t g)
(make_lam rt (pss, body))],
freeze_var g))
| _, _ -> assert false
let rec make_lam t funlit =
match funlit with
| NormalFunlit ([ps], body) -> NormalFunlit ([ps], body)
| NormalFunlit (ps::pss, body) ->
let g = gensym ~prefix:"_fun_" () in
let rt = TypeUtils.return_type t in
NormalFunlit ([ps], block ([fun_binding' ~linearity ~location (binder ~ty:t g) (make_lam rt (NormalFunlit (pss, body)))], freeze_var g))
| _ -> assert false
in make_lam rt lam
in (binder ~ty:ft f, linearity, (tyvars, lam), location)

Expand Down Expand Up @@ -141,7 +138,7 @@ object (o : 'self_type)
let tyvars = List.map SugarQuantifier.mk_resolved [ab; rhob; effb] in
let e : phrasenode =
block_node
([fun_binding' ~tyvars:tyvars (binder ~ty:ft f) (pss, body)],
([fun_binding' ~tyvars:tyvars (binder ~ty:ft f) (NormalFunlit (pss, body))],
freeze_var f)
in (o, e, ft)
| e -> super#phrasenode e
Expand Down Expand Up @@ -186,13 +183,13 @@ object
| e -> super#phrasenode e

method! bindingnode = function
| Fun { fun_definition = (_, ([_], _)); _ } as b -> super#bindingnode b
| Fun { fun_definition = (_, (NormalFunlit ([_], _))); _ } as b -> super#bindingnode b
| Fun _ -> {< has_no_funs = false >}
| Funs defs as b ->
if
List.exists
(function
| {WithPos.node={ rec_definition = (_, ([_], _)); _ }; _ } -> false
| {WithPos.node={ rec_definition = (_, (NormalFunlit ([_], _))); _ }; _ } -> false
| _ -> true) defs
then
{< has_no_funs = false >}
Expand Down
7 changes: 6 additions & 1 deletion core/desugarInners.ml
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,12 @@ object (o : 'self_type)
let o = o#with_visiting (StringSet.add (Binder.to_name rec_binder) visiting_funs) in
let (o, tyvars) = o#quantifiers tyvars in
let (o, inner) = o#datatype inner in
let inner_effects = TransformSugar.fun_effects inner (fst lam) in
let lam_in =
Emanon42 marked this conversation as resolved.
Show resolved Hide resolved
match lam with
| NormalFunlit x -> x
| _ -> assert false
in
let inner_effects = TransformSugar.fun_effects inner (fst lam_in) in
let (o, lam, _) = o#funlit inner_effects lam in
let o = o#restore_quantifiers outer_tyvars in
let o = o#with_visiting visiting_funs in
Expand Down
94 changes: 94 additions & 0 deletions core/desugarMatching.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
open Sugartypes
Emanon42 marked this conversation as resolved.
Show resolved Hide resolved
open Utility
open SourceCode

(* This module desugars pattern-matching functions
Emanon42 marked this conversation as resolved.
Show resolved Hide resolved

This transformation convert function like that:

fun foo(a1, ..., an) switch {
case (p1_1, ..., p1_n) -> b_1
...
case (pm_1, pm_n) -> b_m
}

to function with switch body like that:

fun foo(a1 as x1, ..., an as xn) {
switch ((x1, ..., xn)) {
case (p1_1, ..., p1_n) -> b_1
...
case (pm_1, ..., pm_n) -> b_m
case (_, ..., _) -> error("non-exhaustive")
}

The last non-exhaustive case with wild card pattern is always attached to the end of switch body.

*)

let with_pos = SourceCode.WithPos.make

let pattern_matching_sugar =
Settings.(
flag "pattern_matching_sugar"
|> synopsis
"Toggles whether to enable the switch pattern matching syntax sugar"
|> convert parse_bool
|> sync)

let pattern_matching_sugar_guard pos =
let pattern_matching_sugar_disabled pos =
Errors.disabled_extension ~pos ~setting:("pattern_matching_sugar", true) "Pattern Matching Sugar"
in
if not (Settings.get pattern_matching_sugar)
then raise (pattern_matching_sugar_disabled pos)

let nullary_guard pss pos =
let nullary_error pos =
Errors.desugaring_error ~pos:pos ~stage:Errors.DesugarMatching ~message:"Can't match over nullary function"
in
match pss with
| [[]] -> raise (nullary_error pos)
| _ -> ()
Emanon42 marked this conversation as resolved.
Show resolved Hide resolved

let desugar_matching =
object ((self : 'self_type))
inherit SugarTraversals.map as super
method! binding = fun b ->
let pos = WithPos.pos b in
match WithPos.node b with
| Fun ({ fun_definition = (tvs, SwitchFunlit (patterns, cases)); _ } as fn) ->
pattern_matching_sugar_guard pos;
nullary_guard patterns pos;
(* bind the arguments with unique var name *)
let name_list = List.map (fun pats -> List.map (fun pat -> (pat, Utility.gensym())) pats) patterns in
let switch_tuple = List.map (fun (_, name) -> with_pos (Var name)) (List.flatten name_list) in
(* assemble exhaustive handler *)
let exhaustive_patterns = with_pos (Pattern.Any) in
let exhaustive_position = Format.sprintf "non-exhaustive pattern matching at %s" (SourceCode.Position.show pos) in
let exhaustive_case = FnAppl (with_pos (Var "error"), [with_pos (Constant (CommonTypes.Constant.String exhaustive_position))]) in
let normal_args =
List.map (fun pats -> List.map (fun (pat, name) ->
with_pos (Pattern.As (with_pos (Binder.make ~name ()), pat)))
pats) name_list in
let cases = cases@[(exhaustive_patterns, with_pos exhaustive_case)] in
let switch_body = Switch (with_pos (TupleLit switch_tuple), cases, None) in
let normal_fnlit = NormalFunlit (normal_args, with_pos switch_body) in
let normal_fnlit = self#funlit normal_fnlit in
let node = Fun { fun_binder = fn.fun_binder;
fun_linearity = fn.fun_linearity;
fun_definition = (tvs, normal_fnlit);
fun_location = fn.fun_location;
fun_signature = fn.fun_signature;
fun_unsafe_signature = fn.fun_unsafe_signature;
fun_frozen = fn.fun_frozen;
} in
WithPos.make ~pos node
| _ -> super#binding b
end

module Untyped
= Transform.Untyped.Make.Transformer(struct
let name = "desugar_match_functions"
let obj = desugar_matching
end)
3 changes: 3 additions & 0 deletions core/desugarMatching.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
val desugar_matching : SugarTraversals.map

include Transform.Untyped.S
23 changes: 13 additions & 10 deletions core/desugarModules.ml
Original file line number Diff line number Diff line change
Expand Up @@ -307,16 +307,19 @@ and desugar ?(toplevel=false) (renamer' : Epithet.t) (scope' : Scope.t) =
raise (Errors.module_error ~pos (Printf.sprintf "Unbound module %s" (Scope.Resolve.best_guess path)))

method! funlit : funlit -> funlit
= fun (paramss, body) ->
let visitor = self#clone in
let paramss' =
List.map
(fun params ->
List.map (fun param -> visitor#pattern param) params)
paramss
in
let body' = visitor#phrase body in
(paramss', body')
= fun f ->
match f with
| NormalFunlit (paramss, body) ->
let visitor = self#clone in
let paramss' =
List.map
(fun params ->
List.map (fun param -> visitor#pattern param) params)
paramss
in
let body' = visitor#phrase body in
NormalFunlit (paramss', body')
| _ -> assert false

method cases : (Pattern.with_pos * phrase) list -> (Pattern.with_pos * phrase) list
= fun cases ->
Expand Down
2 changes: 2 additions & 0 deletions core/errors.ml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type sugar_error_stage =
| CheckXML
| DesugarInners
| DesugarModules
| DesugarMatching

let string_of_stage = function
| DesugarFormlets -> "compiling formlets"
Expand All @@ -26,6 +27,7 @@ let string_of_stage = function
| CheckXML -> "checking XML"
| DesugarInners -> "desugaring inner types"
| DesugarModules -> "desugaring modules"
| DesugarMatching -> "desugaring matching"

exception RuntimeError of string
exception UndefinedVariable of string
Expand Down
1 change: 1 addition & 0 deletions core/errors.mli
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type sugar_error_stage =
| CheckXML
| DesugarInners
| DesugarModules
| DesugarMatching


exception RuntimeError of string
Expand Down
1 change: 1 addition & 0 deletions core/frontend.ml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ module Untyped = struct
let transformers : transformer array
= [| (module ResolvePositions)
; (module CheckXmlQuasiquotes)
; (module DesugarMatching)
Emanon42 marked this conversation as resolved.
Show resolved Hide resolved
; (module DesugarModules)
; (module Shunting)
; (module Collect_FFI_Files)
Expand Down
5 changes: 3 additions & 2 deletions core/lens_sugar_conv.ml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ let is_static _typ p =
if body contains any external references. If it does, then it is dynamic,
otherwise it is static. *)
match WithPos.node p with
| S.FunLit (_, _, ([ [ var ] ], body), _) -> (
| S.FunLit (_, _, Sugartypes.NormalFunlit ([ [ var ] ], body), _) -> (
let var = WithPos.node var in
match var with
| S.Pattern.Variable x -> no_ext_deps x body
Expand Down Expand Up @@ -105,13 +105,14 @@ let rec lens_sugar_phrase_of_body v p =

let lens_sugar_phrase_of_sugar p =
( match WithPos.node p with
| S.FunLit (_, _, ([ [ var ] ], body), _) -> (
| S.FunLit (_, _, Sugartypes.NormalFunlit ([ [ var ] ], body), _) -> (
let var = WithPos.node var in
match var with
| S.Pattern.Variable x ->
lens_sugar_phrase_of_body (S.Binder.to_name x) body
| _ ->
Format.asprintf "Unsupported binder: %a" S.pp_phrase p
|> Error.internal_error_res )
| S.FunLit (_, _, Sugartypes.SwitchFunlit (_, _), _) -> assert false
| _ -> lens_sugar_phrase_of_body "" p )
|> Result.ok_exn
11 changes: 11 additions & 0 deletions core/parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,8 @@ fun_declarations:
fun_declaration:
| tlfunbinding { fun_binding ~ppos:$loc($1) None $1 }
| signatures tlfunbinding { fun_binding ~ppos:$loc($2) (fst $1) ~unsafe_sig:(snd $1) $2 }
| switch_tlfunbinding { switch_fun_binding ~ppos:$loc($1) None $1 }
| signatures switch_tlfunbinding { switch_fun_binding ~ppos:$loc($2) (fst $1) ~unsafe_sig:(snd $1) $2 }

Emanon42 marked this conversation as resolved.
Show resolved Hide resolved
linearity:
| FUN { dl_unl }
Expand All @@ -443,6 +445,12 @@ tlfunbinding:
| OP OPERATOR pattern perhaps_location block { ((dl_unl, false), $2, [[$3]], $4, $5) }
| OP pattern OPERATOR perhaps_location block { ((dl_unl, false), $3, [[$2]], $4, $5) }

switch_tlfunbinding:
| fun_kind VARIABLE arg_lists perhaps_location switch_body { ($1, $2, $3, $4, $5) }

switch_body:
Emanon42 marked this conversation as resolved.
Show resolved Hide resolved
| SWITCH LBRACE case+ RBRACE { $3 }

tlvarbinding:
| VAR VARIABLE perhaps_location EQ exp { (PatName $2, $5, $3) }

Expand Down Expand Up @@ -547,6 +555,7 @@ primary_expression:
| LBRACKET exp DOTDOT exp RBRACKET { with_pos $loc (RangeLit($2, $4)) }
| xml { $1 }
| linearity arg_lists block { fun_lit ~ppos:$loc $1 $2 $3 }
| linearity arg_lists switch_body { switch_fun_lit ~ppos:$loc $1 $2 $3 }
| LEFTTRIANGLE cp_expression RIGHTTRIANGLE { with_pos $loc (CP $2) }
| DOLLAR primary_expression { with_pos $loc (Generalise $2) }

Expand Down Expand Up @@ -829,6 +838,8 @@ binding:
| exp SEMICOLON { with_pos $loc (Exp $1) }
| signatures fun_kind VARIABLE arg_lists block { fun_binding ~ppos:$loc (fst $1) ~unsafe_sig:(snd $1) ($2, $3, $4, loc_unknown, $5) }
| fun_kind VARIABLE arg_lists block { fun_binding ~ppos:$loc None ($1, $2, $3, loc_unknown, $4) }
| signatures fun_kind VARIABLE arg_lists switch_body { switch_fun_binding ~ppos:$loc (fst $1) ~unsafe_sig:(snd $1) ($2, $3, $4, loc_unknown, $5) }
| fun_kind VARIABLE arg_lists switch_body { switch_fun_binding ~ppos:$loc None ($1, $2, $3, loc_unknown, $4) }
| typedecl SEMICOLON | links_module
| links_open SEMICOLON { $1 }

Expand Down
36 changes: 24 additions & 12 deletions core/renamer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,19 @@ let renamer qs_from qs_to =
function_definition -> 'self * function_definition
= fun { fun_binder
; fun_linearity
; fun_definition = (tyvars, (pats, body))
; fun_definition = (tyvars, f)
; fun_location
; fun_signature
; fun_frozen
; fun_unsafe_signature } ->
let o, (pats', tyvars', typ', _, signature', body') =
o#handle_function pats tyvars (Binder.to_type fun_binder) None
fun_signature body in
match f with
| NormalFunlit (pats, body) -> o#handle_function pats tyvars (Binder.to_type fun_binder) None fun_signature body
| _ -> assert false in
let function_definition' =
{ fun_binder = Binder.set_type fun_binder typ'
; fun_linearity
; fun_definition = (tyvars', (pats', body'))
; fun_definition = (tyvars', NormalFunlit (pats', body'))
; fun_location
; fun_signature = signature'
; fun_frozen
Expand All @@ -158,18 +159,19 @@ let renamer qs_from qs_to =
recursive_functionnode -> 'self * recursive_functionnode
= fun { rec_binder
; rec_linearity
; rec_definition = ((tyvars, ty), (pats, body))
; rec_definition = ((tyvars, ty), f)
; rec_location
; rec_signature
; rec_unsafe_signature
; rec_frozen } ->
let o, (pats', tyvars', typ', ty', signature', body') =
o#handle_function pats tyvars (Binder.to_type rec_binder) ty
rec_signature body in
match f with
| NormalFunlit (pats, body) -> o#handle_function pats tyvars (Binder.to_type rec_binder) ty rec_signature body
| _ -> assert false in
let recursive_definition' =
{ rec_binder = Binder.set_type rec_binder typ'
; rec_linearity
; rec_definition = ((tyvars', ty'), (pats', body'))
; rec_definition = ((tyvars', ty'), NormalFunlit (pats', body'))
; rec_location
; rec_signature = signature'
; rec_unsafe_signature
Expand Down Expand Up @@ -213,11 +215,16 @@ let renamer qs_from qs_to =
let rename_function_definition : function_definition -> function_definition =
fun { fun_binder
; fun_linearity
; fun_definition = (tyvars_from, (pats, body))
; fun_definition = (tyvars_from, f)
; fun_location
; fun_signature
; fun_frozen
; fun_unsafe_signature } ->
let (pats, body) =
match f with
| NormalFunlit (ps, bd) -> (ps, bd)
| _ -> assert false
in
let qs_from = List.map SugarQuantifier.get_resolved_exn tyvars_from in
let qs_to, _ = Instantiate.build_fresh_quantifiers qs_from in
let tyvars_to = List.map SugarQuantifier.mk_resolved qs_to in
Expand All @@ -228,7 +235,7 @@ let rename_function_definition : function_definition -> function_definition =
let _, signature' = o#option (fun o -> o#datatype') fun_signature in
{ fun_binder = Binder.set_type fun_binder typ'
; fun_linearity
; fun_definition = (tyvars_to, (pats', body'))
; fun_definition = (tyvars_to, NormalFunlit (pats', body'))
; fun_location
; fun_signature = signature'
; fun_frozen
Expand All @@ -239,11 +246,16 @@ let rename_recursive_functionnode :
recursive_functionnode -> recursive_functionnode =
fun { rec_binder
; rec_linearity
; rec_definition = ((tyvars_from, ty), (pats, body))
; rec_definition = ((tyvars_from, ty), f)
; rec_location
; rec_signature
; rec_frozen
; rec_unsafe_signature } ->
let (pats, body) =
match f with
| NormalFunlit (ps, bd) -> (ps, bd)
| _ -> assert false
in
let qs_from = List.map SugarQuantifier.get_resolved_exn tyvars_from in
let qs_to, _ = Instantiate.build_fresh_quantifiers qs_from in
let tyvars_to = List.map SugarQuantifier.mk_resolved qs_to in
Expand All @@ -255,7 +267,7 @@ let rename_recursive_functionnode :
let _, signature' = o#option (fun o -> o#datatype') rec_signature in
{ rec_binder = Binder.set_type rec_binder typ'
; rec_linearity
; rec_definition = ((tyvars_to, ty'), (pats', body'))
; rec_definition = ((tyvars_to, ty'), NormalFunlit (pats', body'))
; rec_location
; rec_signature = signature'
; rec_frozen
Expand Down
Loading