open Pcaml let id_prefix = "__fr_" type dependent = Behavior | Event let dependents = Stack.create () let cur_context () = try Stack.top dependents with Stack.Empty -> failwith "[@expr] or [%expr] occurs outside of [let @] or [lift]" let with_new_context f = Stack.push (ref []) dependents; try let r = f () in ignore (Stack.pop dependents); r with e -> ignore (Stack.pop dependents); raise e (* Create a unique ID for the expression in the current context and add it to the list of dependents. If the expression was already encountered, return its previously assigned ID. *) let gen_id dtype context expr = let loc = MLast.loc_of_expr expr in let reloc = expr_reloc (fun _ -> loc) Lexing.dummy_pos in let reloc_expr = reloc expr in try fst (List.find (fun (id, (dtype', expr')) -> dtype = dtype' && reloc_expr = reloc expr') !context) with Not_found -> (* should this use rename_id? *) let id = id_prefix ^ string_of_int (List.length !context) in context := (id, (dtype, expr)) :: !context; id (* Add an expression to the list of dependents and return an expression referencing its current value. *) let gen_dependent _loc dtype context expr = try <:expr< $lid:gen_id dtype context expr$ >> with e -> Stdpp.raise_with_loc _loc e let extract_dependents dtype dependents = List.rev (List.fold_left (fun l (id, (dtype', expr)) -> if dtype' = dtype then (id, expr) :: l else l) [] dependents) (* Create a signal expression from an expression and a dependency map. *) let gen_behavior _loc (expr, dependents) = let merge_dependents a b = match a, b with | (Behavior, a), (Behavior, b) -> Behavior, <:expr< Fr.zip_b $a$ $b$ >> | (Event, a), (Event, b) -> Event, <:expr< Fr.zip_e $a$ $b$ >> | (Behavior, a), (Event, b) -> Event, <:expr< let __fr_b = $b$ in Fr.zip_e (Fr.snapshot_b $a$ __fr_b) __fr_b >> | (Event, a), (Behavior, b) -> Event, <:expr< let __fr_a = $a$ in Fr.zip_e __fr_a (Fr.snapshot_b $b$ __fr_a) >> in match dependents with | [] -> <:expr< Fr.lift0 $expr$ >> | hd :: tl -> let bindings = List.fold_left (fun p (id, d) -> <:patt< ($p$, $lid:id$) >>) <:patt< $lid:fst hd$ >> tl and signal = List.fold_left (fun s (id, d) -> merge_dependents s d) (snd hd) tl in match signal with | Event, event -> <:expr< Fr.map_e (fun $bindings$ -> $expr$) $event$ >> | Behavior, behavior -> <:expr< Fr.lift1 (fun $bindings$ -> $expr$) $behavior$ >> (* Rule to parse an expr in a new dependency context. Returns a tuple of the expression and its dependents' expr -> ID map. *) let dependent_expr = Grammar.Entry.of_parser gram "dependent_expr" (fun s -> with_new_context (fun () -> let e = Grammar.Entry.parse_token expr s in e, !(cur_context ()))) let simple_dependent_expr = let simple_expr = Grammar.Entry.create gram "simple_expr" in EXTEND GLOBAL: simple_expr; simple_expr: [[ e = expr LEVEL "." -> e ]]; END; Grammar.Entry.of_parser gram "simple_dependent_expr" (fun s -> with_new_context (fun () -> let e = Grammar.Entry.parse_token simple_expr s in e, !(cur_context ()))) EXTEND GLOBAL: expr let_binding; expr: LEVEL "expr1" [ [ "on"; event = expr LEVEL "simple"; args = LIST0 patt LEVEL "simple"; "->"; body = expr LEVEL "top" -> let f = List.fold_right (fun arg body -> <:expr< fun $arg$ -> $body$ >>) args body in <:expr< Fr.map_e $f$ $event$ >> ] ]; expr: LEVEL "apply" [ [ "lift"; de = simple_dependent_expr -> gen_behavior _loc de ] ]; let_binding: [ [ "@"; p = patt LEVEL "simple"; e = dependent_fun_binding -> p, e ] ]; dependent_fun_binding: [ LEFTA [ "="; de = dependent_expr -> gen_behavior _loc de ] | [ p = patt LEVEL "simple"; e = dependent_fun_binding -> <:expr< fun $p$ -> $e$ >> ] ]; expr: LEVEL "simple" [ [ "@"; e = expr LEVEL "simple" -> try gen_dependent _loc Behavior (cur_context ()) e with exn -> Stdpp.raise_with_loc _loc exn ] | [ "%"; e = expr LEVEL "simple" -> try gen_dependent _loc Event (cur_context ()) e with exn -> Stdpp.raise_with_loc _loc exn ] ]; END;;