diff --git a/docs/stepper-and-filter.md b/docs/stepper-and-filter.md index d2057fcfda..6c8e19a7ea 100644 --- a/docs/stepper-and-filter.md +++ b/docs/stepper-and-filter.md @@ -109,7 +109,7 @@ program, issue command for change the evaluation mode (big-step or small-step, lazy or eager), and a narrower filter has a higher priority. For the matching part, I choose the Hazel language it's self as the -pattern language. `UPat` and `DHPat` won't work since they only matches +pattern language. `Pat` and `DHPat` won't work since they only matches against *values*, indicating I have to extend them somehow so that they can match against *expressions*. The empty hole is take as the match all filter, i.e. `*` in many other matching languages. It will diff --git a/dune-project b/dune-project index 4dd3442bc4..96cc1c1de3 100644 --- a/dune-project +++ b/dune-project @@ -1,4 +1,5 @@ (lang dune 3.16) + (using menhir 2.0) (name hazel) @@ -9,6 +10,7 @@ (github hazelgrove/hazel)) (authors "Hazel Development Team") + (maintainers "Hazel Development Team") (license MIT) @@ -16,8 +18,9 @@ (package (name hazel) (allow_empty) - (synopsis "Hazel, a live functional programming environment with typed holes") -; (description "A longer description") + (synopsis + "Hazel, a live functional programming environment with typed holes") + ; (description "A longer description") ; (tags ; (topics "to describe" your project)) (depends @@ -26,20 +29,31 @@ (menhir (>= 2.0)) yojson - (reason (>= 3.12.0)) + (reason + (>= 3.12.0)) ppx_yojson_conv_lib ppx_yojson_conv incr_dom bisect_ppx - (omd (>= 2.0.0~alpha4)) + (omd + (>= 2.0.0~alpha4)) ezjs_idb bonsai ppx_deriving ptmap - (uuidm (= 0.9.8)) ; 0.9.9 has breaking deprecated changes + (uuidm + (= 0.9.8)) ; 0.9.9 has breaking deprecated changes unionFind ocamlformat - (junit_alcotest :with-test) - ocaml-lsp-server)) ; After upgrading to opam 2.2 use with-dev https://opam.ocaml.org/blog/opam-2-2-0/ + (junit_alcotest + (and + (>= 2.1.0) + :with-test)) + ocaml-lsp-server + qcheck + qcheck-alcotest + ppx_deriving_qcheck)) + +; After upgrading to opam 2.2 use with-dev https://opam.ocaml.org/blog/opam-2-2-0/ ; See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html diff --git a/hazel.opam b/hazel.opam index 09ee887ab3..00159279fc 100644 --- a/hazel.opam +++ b/hazel.opam @@ -14,6 +14,7 @@ depends: [ "reason" {>= "3.12.0"} "ppx_yojson_conv_lib" "ppx_yojson_conv" + "incr_dom" "bisect_ppx" "omd" {>= "2.0.0~alpha4"} "ezjs_idb" @@ -23,8 +24,11 @@ depends: [ "uuidm" {= "0.9.8"} "unionFind" "ocamlformat" - "junit_alcotest" {with-test} + "junit_alcotest" {>= "2.1.0" & with-test} "ocaml-lsp-server" + "qcheck" + "qcheck-alcotest" + "ppx_deriving_qcheck" "odoc" {with-doc} ] build: [ diff --git a/hazel.opam.locked b/hazel.opam.locked index 856b83ad33..dd4d38bbb8 100644 --- a/hazel.opam.locked +++ b/hazel.opam.locked @@ -10,7 +10,7 @@ homepage: "https://github.com/hazelgrove/hazel" bug-reports: "https://github.com/hazelgrove/hazel/issues" depends: [ "abstract_algebra" {= "v0.16.0"} - "alcotest" {= "1.8.0" & with-test} + "alcotest" {= "1.8.0"} "angstrom" {= "0.16.1"} "astring" {= "0.8.5"} "async" {= "v0.16.0"} @@ -38,8 +38,8 @@ depends: [ "bignum" {= "v0.16.0"} "bigstringaf" {= "0.10.0"} "bin_prot" {= "v0.16.0"} - "bonsai" {= "v0.16.0"} "bisect_ppx" {= "2.8.3"} + "bonsai" {= "v0.16.0"} "camlp-streams" {= "5.0.1"} "chrome-trace" {= "3.16.0"} "cmdliner" {= "1.3.0"} @@ -102,8 +102,8 @@ depends: [ "jsonm" {= "1.0.2"} "jsonrpc" {= "1.19.0"} "jst-config" {= "v0.16.0"} - "junit" {= "2.0.2" & with-test} - "junit_alcotest" {= "2.0.2" & with-test} + "junit" {= "2.1.0" & with-test} + "junit_alcotest" {= "2.1.0" & with-test} "lambdasoup" {= "1.1.1"} "logs" {= "0.7.0"} "lsp" {= "1.19.0"} @@ -142,6 +142,7 @@ depends: [ "omd" {= "2.0.0~alpha4"} "ordering" {= "3.16.0"} "ordinal_abbreviation" {= "v0.16.0"} + "ounit2" {= "2.2.7"} "parsexp" {= "v0.16.0"} "patdiff" {= "v0.16.1"} "patience_diff" {= "v0.16.0"} @@ -157,6 +158,7 @@ depends: [ "ppx_custom_printf" {= "v0.16.0"} "ppx_derivers" {= "1.2.1"} "ppx_deriving" {= "6.0.2"} + "ppx_deriving_qcheck" {= "0.5"} "ppx_disable_unused_warnings" {= "v0.16.0"} "ppx_enumerate" {= "v0.16.0"} "ppx_expect" {= "v0.16.0"} @@ -193,6 +195,10 @@ depends: [ "protocol_version_header" {= "v0.16.0"} "ptime" {= "1.2.0" & with-test} "ptmap" {= "2.0.5"} + "qcheck" {= "0.23"} + "qcheck-alcotest" {= "0.23"} + "qcheck-core" {= "0.23"} + "qcheck-ounit" {= "0.23"} "re" {= "1.12.0"} "reason" {= "3.12.0"} "record_builder" {= "v0.16.0"} diff --git a/licenses/Icons.md b/licenses/Icons.md new file mode 100644 index 0000000000..0ec567f4eb --- /dev/null +++ b/licenses/Icons.md @@ -0,0 +1 @@ +The icons used in Hazel, including the Hazel icon, are from The Noun Project (https://thenounproject.com/) under the Icon Pro license (https://thenounproject.com/legal/terms-of-use/#icon-licenses). diff --git a/src/haz3lcore/dynamics/Builtins.re b/src/haz3lcore/dynamics/Builtins.re index 2f722caa85..ffbaf691a1 100644 --- a/src/haz3lcore/dynamics/Builtins.re +++ b/src/haz3lcore/dynamics/Builtins.re @@ -1,3 +1,4 @@ +open Util; open DHExp; /* @@ -12,13 +13,13 @@ open DHExp; [@deriving (show({with_path: false}), sexp)] type builtin = | Const(Typ.t, DHExp.t) - | Fn(Typ.t, Typ.t, DHExp.t => DHExp.t); + | Fn(Typ.t, Typ.t, DHExp.t => option(DHExp.t)); [@deriving (show({with_path: false}), sexp)] type t = VarMap.t_(builtin); [@deriving (show({with_path: false}), sexp)] -type forms = VarMap.t_(DHExp.t => DHExp.t); +type forms = VarMap.t_(DHExp.t => option(DHExp.t)); type result = Result.t(DHExp.t, EvaluatorError.t); @@ -29,7 +30,7 @@ let fn = name: Var.t, t1: Typ.term, t2: Typ.term, - impl: DHExp.t => DHExp.t, + impl: DHExp.t => option(DHExp.t), // None if indet builtins: t, ) : t => @@ -51,8 +52,8 @@ module Pervasives = { let unary = (f: DHExp.t => result, d: DHExp.t) => { switch (f(d)) { - | Ok(r') => r' - | Error(e) => EvaluatorError.Exception(e) |> raise + | Ok(r') => Some(r') + | Error(_) => None }; }; @@ -60,8 +61,8 @@ module Pervasives = { switch (term_of(d)) { | Tuple([d1, d2]) => switch (f(d1, d2)) { - | Ok(r) => r - | Error(e) => EvaluatorError.Exception(e) |> raise + | Ok(r) => Some(r) + | Error(_) => None } | _ => raise(EvaluatorError.Exception(InvalidBoxedTuple(d))) }; @@ -71,8 +72,8 @@ module Pervasives = { switch (term_of(d)) { | Tuple([d1, d2, d3]) => switch (f(d1, d2, d3)) { - | Ok(r) => r - | Error(e) => EvaluatorError.Exception(e) |> raise + | Ok(r) => Some(r) + | Error(_) => None } | _ => raise(EvaluatorError.Exception(InvalidBoxedTuple(d))) }; @@ -308,6 +309,8 @@ module Pervasives = { }; open Impls; + + // Update src/haz3lmenhir/Lexer.mll when any new builtin is added let builtins = VarMap.empty |> const("infinity", Float, infinity) diff --git a/src/haz3lcore/dynamics/DHPat.re b/src/haz3lcore/dynamics/DHPat.re index 95fd561b4c..eeb64a200c 100644 --- a/src/haz3lcore/dynamics/DHPat.re +++ b/src/haz3lcore/dynamics/DHPat.re @@ -3,32 +3,3 @@ include Pat; /* A Dynamic Pattern (DHPat) is a pattern that is part of an expression that has been type-checked. Hence why these functions take both a pattern, dp, and an info map, m, with type information. */ - -/** - * Whether dp contains the variable x outside of a hole. - */ -let rec binds_var = (m: Statics.Map.t, x: Var.t, dp: t): bool => - switch (Statics.get_pat_error_at(m, rep_id(dp))) { - | Some(_) => false - | None => - switch (dp |> term_of) { - | EmptyHole - | MultiHole(_) - | Wild - | Invalid(_) - | Int(_) - | Float(_) - | Bool(_) - | String(_) - | Constructor(_) => false - | Cast(y, _, _) - | Wrap(y, _) => binds_var(m, x, y) - | Var(y) => Var.eq(x, y) - | Tuple(dps) => dps |> List.exists(binds_var(m, x)) - | Cons(dp1, dp2) => binds_var(m, x, dp1) || binds_var(m, x, dp2) - | ListLit(d_list) => - let new_list = List.map(binds_var(m, x), d_list); - List.fold_left((||), false, new_list); - | Ap(_, _) => false - } - }; diff --git a/src/haz3lcore/dynamics/EvalCtx.re b/src/haz3lcore/dynamics/EvalCtx.re index da6eeba5eb..49a6428d36 100644 --- a/src/haz3lcore/dynamics/EvalCtx.re +++ b/src/haz3lcore/dynamics/EvalCtx.re @@ -33,12 +33,12 @@ type term = | Cast(t, Typ.t, Typ.t) | FailedCast(t, Typ.t, Typ.t) | DynamicErrorHole(t, InvalidOperationError.t) - | MatchScrut(t, list((UPat.t, DHExp.t))) + | MatchScrut(t, list((Pat.t, DHExp.t))) | MatchRule( DHExp.t, - UPat.t, + Pat.t, t, - (list((UPat.t, DHExp.t)), list((UPat.t, DHExp.t))), + (list((Pat.t, DHExp.t)), list((Pat.t, DHExp.t))), ) and t = | Mark diff --git a/src/haz3lcore/dynamics/Evaluator.re b/src/haz3lcore/dynamics/Evaluator.re index b91f76107b..7cc93d1b44 100644 --- a/src/haz3lcore/dynamics/Evaluator.re +++ b/src/haz3lcore/dynamics/Evaluator.re @@ -2,40 +2,60 @@ open Transition; open ProgramResult.Result; +// This module defines the stack machine for the evaluator. +module Trampoline = { + type t('a) = + | Bind(t('b), 'b => t('a)): t('a) + | Next(unit => t('a)): t('a) + | Done('a): t('a); + + type callstack('a, 'b) = + | Finished: callstack('a, 'a) + | Continue('a => t('b), callstack('b, 'c)): callstack('a, 'c); + + let rec run: type a b. (t(b), callstack(b, a)) => a = + (t: t(b), callstack: callstack(b, a)) => ( + switch (t) { + | Bind(t, f) => (run(t, Continue(f, callstack)): a) + | Next(f) => run(f(), callstack) + | Done(x) => + switch (callstack) { + | Finished => x + | Continue(f, callstack) => run(f(x), callstack) + } + }: a + ); + + let run = run(_, Finished); + + let return = x => Done(x); + + let bind = (t, f) => Bind(t, f); + + module Syntax = { + let (let.trampoline) = (x, f) => bind(x, f); + }; +}; + module EvaluatorEVMode: { type status = - | BoxedValue - | Indet + | Final | Uneval; include EV_MODE with - type state = ref(EvaluatorState.t) and type result = (status, DHExp.t); + type state = ref(EvaluatorState.t) and + type result = Trampoline.t((status, DHExp.t)); } = { + open Trampoline.Syntax; + type status = - | BoxedValue - | Indet + | Final | Uneval; - type result = (status, DHExp.t); - - type reqstate = - | BoxedReady - | IndetReady - | IndetBlocked; - - let (&&) = (x, y) => - switch (x, y) { - | (IndetBlocked, _) => IndetBlocked - | (_, IndetBlocked) => IndetBlocked - | (IndetReady, _) => IndetReady - | (_, IndetReady) => IndetReady - | (BoxedReady, BoxedReady) => BoxedReady - }; - - type requirement('a) = (reqstate, 'a); - - type requirements('a, 'b) = (reqstate, 'a, 'b); // cumulative state, cumulative arguments, cumulative 'undo' + type result = Trampoline.t((status, DHExp.t)); + type requirement('a) = Trampoline.t('a); + type requirements('a, 'b) = Trampoline.t(('a, 'b)); type state = ref(EvaluatorState.t); let update_test = (state, id, v) => @@ -44,87 +64,60 @@ module EvaluatorEVMode: { let update_probe = (state, id, v) => state := EvaluatorState.add_closure(state^, id, v); - let req_value = (f, _, x) => - switch (f(x)) { - | (BoxedValue, x) => (BoxedReady, x) - | (Indet, x) => (IndetBlocked, x) - | (Uneval, _) => failwith("Unexpected Uneval") - }; - - let rec req_all_value = (f, i) => - fun - | [] => (BoxedReady, []) - | [x, ...xs] => { - let (r1, x') = req_value(f, x => x, x); - let (r2, xs') = req_all_value(f, i, xs); - (r1 && r2, [x', ...xs']); - }; - - let req_final = (f, _, x) => - switch (f(x)) { - | (BoxedValue, x) => (BoxedReady, x) - | (Indet, x) => (IndetReady, x) - | (Uneval, _) => failwith("Unexpected Uneval") - }; - - let rec req_all_final = (f, i) => - fun - | [] => (BoxedReady, []) - | [x, ...xs] => { - let (r1, x') = req_final(f, x => x, x); - let (r2, xs') = req_all_final(f, i, xs); - (r1 && r2, [x', ...xs']); - }; - - let req_final_or_value = (f, _, x) => - switch (f(x)) { - | (BoxedValue, x) => (BoxedReady, (x, true)) - | (Indet, x) => (IndetReady, (x, false)) - | (Uneval, _) => failwith("Unexpected Uneval") + let req_final = (f, _, x) => { + let.trampoline x' = Next(() => f(x)); + Trampoline.return(x' |> snd); + }; + let rec req_all_final = (f, i, xs) => + switch (xs) { + | [] => Trampoline.return([]) + | [x, ...xs] => + let.trampoline x' = req_final(f, x => x, x); + let.trampoline xs' = req_all_final(f, i, xs); + Trampoline.return([x', ...xs']); }; - let otherwise = (_, c) => (BoxedReady, (), c); - - let (and.) = ((r1, x1, c1), (r2, x2)) => (r1 && r2, (x1, x2), c1(x2)); - - let (let.) = ((r, x, c), s) => - switch (r, s(x)) { - | (BoxedReady, Step({expr, state_update, is_value: true, _})) => - state_update(); - (BoxedValue, expr); - | (IndetReady, Step({expr, state_update, is_value: true, _})) => + let otherwise = (_, c) => Trampoline.return(((), c)); + let (and.) = (t1, t2) => { + let.trampoline (x1, c1) = t1; + let.trampoline x2 = t2; + Trampoline.return(((x1, x2), c1(x2))); + }; + let (let.) = (t1, s) => { + let.trampoline (x, c) = t1; + switch (s(x)) { + | Step({expr, state_update, is_value: true, _}) => state_update(); - (Indet, expr); - | (BoxedReady, Step({expr, state_update, is_value: false, _})) - | (IndetReady, Step({expr, state_update, is_value: false, _})) => + Trampoline.return((Final, expr)); + | Step({expr, state_update, is_value: false, _}) => state_update(); - (Uneval, expr); - | (BoxedReady, Constructor) => (BoxedValue, c) - | (IndetReady, Constructor) => (Indet, c) - | (IndetBlocked, _) => (Indet, c) - | (_, Value) => (BoxedValue, c) - | (_, Indet) => (Indet, c) + Trampoline.return((Uneval, expr)); + | Constructor + | Value + | Indet => Trampoline.return((Final, c)) }; + }; }; + module Eval = Transition(EvaluatorEVMode); let rec evaluate = (state, env, d) => { - let u = Eval.transition(evaluate, state, env, d); + open Trampoline.Syntax; + let.trampoline u = Eval.transition(evaluate, state, env, d); switch (u) { - | (BoxedValue, x) => (BoxedValue, x) - | (Indet, x) => (Indet, x) - | (Uneval, x) => evaluate(state, env, x) + | (Final, x) => (EvaluatorEVMode.Final, x) |> Trampoline.return + | (Uneval, x) => Trampoline.Next(() => evaluate(state, env, x)) }; }; -let evaluate' = (env, {d, _}: Elaborator.Elaboration.t) => { +let evaluate' = (env, d: DHExp.t) => { let state = ref(EvaluatorState.init); let env = ClosureEnvironment.of_environment(env); let result = evaluate(state, env, d); + let result = Trampoline.run(result); let result = switch (result) { - | (BoxedValue, x) => BoxedValue(x |> DHExp.repair_ids) - | (Indet, x) => Indet(x |> DHExp.repair_ids) + | (Final, x) => BoxedValue(x |> DHExp.repair_ids) | (Uneval, x) => Indet(x |> DHExp.repair_ids) }; (state^, result); @@ -134,9 +127,9 @@ let evaluate = (~settings: CoreSettings.t, ~env=Builtins.env_init, elab: DHExp.t) : ProgramResult.t(ProgramResult.inner) => switch () { - | _ when !settings.dynamics => Off({d: elab}) + | _ when !settings.dynamics => Off(elab) | _ => - switch (evaluate'(env, {d: elab})) { + switch (evaluate'(env, elab)) { | exception (EvaluatorError.Exception(reason)) => print_endline("EvaluatorError:" ++ EvaluatorError.show(reason)); ResultFail(EvaulatorError(reason)); diff --git a/src/haz3lcore/dynamics/EvaluatorStep.re b/src/haz3lcore/dynamics/EvaluatorStep.re index eeb8eab7ec..e01e791f02 100644 --- a/src/haz3lcore/dynamics/EvaluatorStep.re +++ b/src/haz3lcore/dynamics/EvaluatorStep.re @@ -240,17 +240,6 @@ module Decompose = { type requirements('a, 'b) = ('b, Result.t, ClosureEnvironment.t, 'a); type result = Result.t; - let req_value = (cont, wr, d) => { - switch (cont(d)) { - | Result.Indet => (Result.Indet, d) - | Result.BoxedValue => (Result.BoxedValue, d) - | Result.Step(objs) => ( - Result.Step(List.map(EvalObj.wrap(wr), objs)), - d, - ) - }; - }; - let (&&): (Result.t, Result.t) => Result.t = (u, v) => switch (u, v) { @@ -263,18 +252,6 @@ module Decompose = { | (BoxedValue, BoxedValue) => BoxedValue }; - let rec req_all_value' = (cont, wr, ds') => - fun - | [] => (Result.BoxedValue, []) - | [d, ...ds] => { - let (r1, v) = req_value(cont, wr(_, (ds', ds)), d); - let (r2, vs) = req_all_value'(cont, wr, [d, ...ds'], ds); - (r1 && r2, [v, ...vs]); - }; - let req_all_value = (cont, wr, ds) => { - req_all_value'(cont, wr, [], ds); - }; - let req_final = (cont, wr, d) => { ( switch (cont(d)) { @@ -287,17 +264,6 @@ module Decompose = { ); }; - let req_final_or_value = (cont, wr, d) => { - switch (cont(d)) { - | Result.Indet => (Result.BoxedValue, (d, false)) - | Result.BoxedValue => (Result.BoxedValue, (d, true)) - | Result.Step(objs) => ( - Result.Step(List.map(EvalObj.wrap(wr), objs)), - (d, false), - ) - }; - }; - let rec req_all_final' = (cont, wr, ds') => fun | [] => (Result.BoxedValue, []) @@ -359,13 +325,9 @@ module TakeStep = { type result = option(DHExp.t); // Assume that everything is either value or final as required. - let req_value = (_, _, d) => d; - let req_all_value = (_, _, ds) => ds; let req_final = (_, _, d) => d; let req_all_final = (_, _, ds) => ds; - let req_final_or_value = (_, _, d) => (d, true); - let (let.) = (rq: requirements('a, DHExp.t), rl: 'a => rule) => switch (rl(rq)) { | Step({expr, state_update, _}) => diff --git a/src/haz3lcore/dynamics/PatternMatch.re b/src/haz3lcore/dynamics/PatternMatch.re index 481d93abd2..a01a0ace20 100644 --- a/src/haz3lcore/dynamics/PatternMatch.re +++ b/src/haz3lcore/dynamics/PatternMatch.re @@ -49,6 +49,7 @@ let rec matches = (dp: Pat.t, d: DHExp.t): match_result => | String(s) => let* s' = Unboxing.unbox(String, d); s == s' ? Matches(Environment.empty) : DoesNotMatch; + | Cast({term: ListLit([] as xs), _}, _, _) // Shortcut for empty list pattern match perf | ListLit(xs) => let* s' = Unboxing.unbox(List, d); if (List.length(xs) == List.length(s')) { diff --git a/src/haz3lcore/dynamics/Probe.re b/src/haz3lcore/dynamics/Probe.re index 5530c941da..3fb72610a1 100644 --- a/src/haz3lcore/dynamics/Probe.re +++ b/src/haz3lcore/dynamics/Probe.re @@ -23,7 +23,7 @@ type tag = [@deriving (show({with_path: false}), sexp, yojson)] type frame = { ap_id: Id.t, /* Syntax ID of the ap */ - env_id: Id.t /* ID of ClosureEnv created by ap */ + env_id: Id.t /* ID of env in which ap was applied */ }; /* List of applications prior to some evaluation */ diff --git a/src/haz3lcore/dynamics/Substitution.re b/src/haz3lcore/dynamics/Substitution.re index f8ac65f4bd..e38efa4a7e 100644 --- a/src/haz3lcore/dynamics/Substitution.re +++ b/src/haz3lcore/dynamics/Substitution.re @@ -1,3 +1,32 @@ +/** + * Whether dp contains the variable x outside of a hole. + */ +let rec binds_var = (m: Statics.Map.t, x: Var.t, dp: DHPat.t): bool => + switch (Statics.get_pat_error_at(m, Pat.rep_id(dp))) { + | Some(_) => false + | None => + switch (dp |> Pat.term_of) { + | EmptyHole + | MultiHole(_) + | Wild + | Invalid(_) + | Int(_) + | Float(_) + | Bool(_) + | String(_) + | Constructor(_) => false + | Cast(y, _, _) + | Wrap(y, _) => binds_var(m, x, y) + | Var(y) => Var.eq(x, y) + | Tuple(dps) => dps |> List.exists(binds_var(m, x)) + | Cons(dp1, dp2) => binds_var(m, x, dp1) || binds_var(m, x, dp2) + | ListLit(d_list) => + let new_list = List.map(binds_var(m, x), d_list); + List.fold_left((||), false, new_list); + | Ap(_, _) => false + } + }; + /* closed substitution [d1/x]d2 */ let rec subst_var = (m, d1: DHExp.t, x: Var.t, d2: DHExp.t): DHExp.t => { let (term, rewrap) = DHExp.unwrap(d2); @@ -21,7 +50,7 @@ let rec subst_var = (m, d1: DHExp.t, x: Var.t, d2: DHExp.t): DHExp.t => { | Let(dp, d3, d4) => let d3 = subst_var(m, d1, x, d3); let d4 = - if (DHPat.binds_var(m, x, dp)) { + if (binds_var(m, x, dp)) { d4; } else { subst_var(m, d1, x, d4); @@ -30,7 +59,7 @@ let rec subst_var = (m, d1: DHExp.t, x: Var.t, d2: DHExp.t): DHExp.t => { | FixF(y, d3, env) => let env' = Option.map(subst_var_env(m, d1, x), env); let d3 = - if (DHPat.binds_var(m, x, y)) { + if (binds_var(m, x, y)) { d3; } else { subst_var(m, d1, x, d3); @@ -40,7 +69,7 @@ let rec subst_var = (m, d1: DHExp.t, x: Var.t, d2: DHExp.t): DHExp.t => { /* Function closure shouldn't appear during substitution (which only is called from elaboration currently) */ let env' = Option.map(subst_var_env(m, d1, x), env); - if (DHPat.binds_var(m, x, dp)) { + if (binds_var(m, x, dp)) { Fun(dp, d3, env', s) |> rewrap; } else { let d3 = subst_var(m, d1, x, d3); @@ -87,7 +116,7 @@ let rec subst_var = (m, d1: DHExp.t, x: Var.t, d2: DHExp.t): DHExp.t => { let rules = List.map( ((p, v)) => - if (DHPat.binds_var(m, x, p)) { + if (binds_var(m, x, p)) { (p, v); } else { (p, subst_var(m, d1, x, v)); diff --git a/src/haz3lcore/dynamics/Transition.re b/src/haz3lcore/dynamics/Transition.re index 10a1fdefa5..d9e2339f7e 100644 --- a/src/haz3lcore/dynamics/Transition.re +++ b/src/haz3lcore/dynamics/Transition.re @@ -18,11 +18,10 @@ open PatternMatch; to wrap the expression back up if the step couldn't be evaluated. This is followed by a series of `and. d1' = req_final(req(state, env), , )` - which indicate that in order to evaluate the step, must be final. (req_value - is also available if it needs to be a value). Note that if successful, d1' will - be the fully-evaluated version of d1. The sub-expressions are all enumerated by - the field, so i=0 indicates that it is the first sub-expression, i=1 the - second etc. + which indicate that in order to evaluate the step, must be final. Note that + if successful, d1' will be the fully-evaluated version of d1. The sub-expressions + are all enumerated by the field, so i=0 indicates that it is the first + sub-expression, i=1 the second etc. Finally, we have the Step construct that defines the actual step. Note "Step"s should be used if and only if they change the expression. If they do not change @@ -100,16 +99,6 @@ module type EV_MODE = { type requirement('a); type requirements('a, 'b); - let req_value: - (DHExp.t => result, EvalCtx.t => EvalCtx.t, DHExp.t) => - requirement(DHExp.t); - let req_all_value: - ( - DHExp.t => result, - (EvalCtx.t, (list(DHExp.t), list(DHExp.t))) => EvalCtx.t, - list(DHExp.t) - ) => - requirement(list(DHExp.t)); let req_final: (DHExp.t => result, EvalCtx.t => EvalCtx.t, DHExp.t) => requirement(DHExp.t); @@ -120,9 +109,6 @@ module type EV_MODE = { list(DHExp.t) ) => requirement(list(DHExp.t)); - let req_final_or_value: - (DHExp.t => result, EvalCtx.t => EvalCtx.t, DHExp.t) => - requirement((DHExp.t, bool)); let (let.): (requirements('a, DHExp.t), 'a => rule) => result; let (and.): @@ -175,12 +161,18 @@ module Transition = (EV: EV_MODE) => { let. _ = otherwise(env, Var(x) |> rewrap); switch (ClosureEnvironment.lookup(env, x)) { | Some(d) => + let is_value = + switch (d |> Exp.term_of) { + | FixF(_, _, _) => false // fixpoints aren't final + | Let(_, _, _) => false // could be mutually-recursive fixpoint + | _ => true // all other closure entries should be final + }; Step({ expr: d |> fast_copy(Id.mk()), state_update, kind: VarLookup, - is_value: false, - }) + is_value, + }); | None => Indet }; | Seq(d1, d2) => @@ -264,18 +256,13 @@ module Transition = (EV: EV_MODE) => { }); } | Test(d'') => - let. _ = otherwise(env, ((d, _)) => Test(d) |> rewrap) - and. (d', is_value) = - req_final_or_value(req(state, env), d => Test(d) |> wrap_ctx, d''); + let. _ = otherwise(env, d => Test(d) |> rewrap) + and. d' = req_final(req(state, env), d => Test(d) |> wrap_ctx, d''); let result: TestStatus.t = - if (is_value) { - switch (Unboxing.unbox(Bool, d')) { - | DoesNotMatch - | IndetMatch => Indet - | Matches(b) => b ? Pass : Fail - }; - } else { - Indet; + switch (Unboxing.unbox(Bool, d')) { + | DoesNotMatch + | IndetMatch => Indet + | Matches(b) => b ? Pass : Fail }; Step({ expr: Tuple([]) |> fresh, @@ -287,8 +274,9 @@ module Transition = (EV: EV_MODE) => { | TypAp(d, tau) => let. _ = otherwise(env, d => TypAp(d, tau) |> rewrap) and. d' = - req_value(req(state, env), d => TypAp(d, tau) |> wrap_ctx, d); - switch (DHExp.term_of(d')) { + req_final(req(state, env), d => TypAp(d, tau) |> wrap_ctx, d); + let-unbox typfun = (TypFun, d'); + switch (typfun) { | TypFun(utpat, tfbody, name) => /* Rule ITTLam */ Step({ @@ -305,11 +293,7 @@ module Transition = (EV: EV_MODE) => { kind: TypFunAp, is_value: false, }) - | Cast( - d'', - {term: Forall(tp1, _), _} as t1, - {term: Forall(tp2, _), _} as t2, - ) => + | TFunCast(d'', tp1, t1, tp2, t2) => /* Rule ITTApCast */ Step({ expr: @@ -323,7 +307,6 @@ module Transition = (EV: EV_MODE) => { kind: CastTypAp, is_value: false, }) - | _ => raise(EvaluatorError.Exception(InvalidBoxedTypFun(d'))) }; | DeferredAp(d1, ds) => let. _ = otherwise(env, (d1, ds) => DeferredAp(d1, ds) |> rewrap) @@ -341,20 +324,16 @@ module Transition = (EV: EV_MODE) => { ); Value; | Ap(dir, d1, d2) => - let. _ = otherwise(env, (d1, (d2, _)) => Ap(dir, d1, d2) |> rewrap) + let. _ = otherwise(env, (d1, d2) => Ap(dir, d1, d2) |> rewrap) and. d1' = - req_value(req(state, env), d1 => Ap1(dir, d1, d2) |> wrap_ctx, d1) - and. (d2', d2_is_value) = - req_final_or_value( - req(state, env), - d2 => Ap2(dir, d1, d2) |> wrap_ctx, - d2, - ); - switch (DHExp.term_of(d1')) { + req_final(req(state, env), d1 => Ap1(dir, d1, d2) |> wrap_ctx, d1) + and. d2' = + req_final(req(state, env), d2 => Ap2(dir, d1, d2) |> wrap_ctx, d2); + let-unbox unboxed_fun = (Fun, d1'); + switch (unboxed_fun) { | Constructor(_) => Constructor - | Fun(dp, d3, Some(env'), _) => + | FunEnv(dp, d3, env') => let matches = matches(dp, d2'); - switch (matches.matches) { | IndetMatch | DoesNotMatch => Indet @@ -373,11 +352,7 @@ module Transition = (EV: EV_MODE) => { is_value: false, }); }; - | Cast( - d3', - {term: Arrow(ty1, ty2), _}, - {term: Arrow(ty1', ty2'), _}, - ) => + | FunCast(d3', ty1, ty2, ty1', ty2') => Step({ expr: Cast( @@ -391,28 +366,21 @@ module Transition = (EV: EV_MODE) => { is_value: false, }) | BuiltinFun(ident) => - if (d2_is_value) { - Step({ - expr: { - let builtin = - VarMap.lookup(Builtins.forms_init, ident) - |> OptUtil.get(() => { - /* This exception should never be raised because there is - no way for the user to create a BuiltinFun. They are all - inserted into the context before evaluation. */ - raise( - EvaluatorError.Exception(InvalidBuiltin(ident)), - ) - }); - builtin(d2'); - }, - state_update, - kind: BuiltinAp(ident), - is_value: false // Not necessarily a value because of InvalidOperations - }); - } else { - Indet; - } + let builtin = + VarMap.lookup(Builtins.forms_init, ident) + |> OptUtil.get(() => { + /* This exception should never be raised because there is + no way for the user to create a BuiltinFun. They are all + inserted into the context before evaluation. */ + raise( + EvaluatorError.Exception(InvalidBuiltin(ident)), + ) + }); + switch (builtin(d2')) { + | Some(expr) => + Step({expr, state_update, kind: BuiltinAp(ident), is_value: false}) + | None => Indet + }; | DeferredAp(d3, d4s) => let n_args = List.length( @@ -450,22 +418,6 @@ module Transition = (EV: EV_MODE) => { kind: DeferredAp, is_value: false, }); - | Cast(_) - | FailedCast(_) => Indet - | FixF(_) => - print_endline(Exp.show(d1)); - print_endline(Exp.show(d1')); - print_endline("FIXF"); - failwith("FixF in Ap"); - | _ => - Step({ - expr: { - raise(EvaluatorError.Exception(InvalidBoxedFun(d1'))); - }, - state_update, - kind: InvalidStep, - is_value: true, - }) }; | Deferral(_) => let. _ = otherwise(env, d); @@ -481,7 +433,7 @@ module Transition = (EV: EV_MODE) => { | If(c, d1, d2) => let. _ = otherwise(env, c => If(c, d1, d2) |> rewrap) and. c' = - req_value(req(state, env), c => If1(c, d1, d2) |> wrap_ctx, c); + req_final(req(state, env), c => If1(c, d1, d2) |> wrap_ctx, c); let-unbox b = (Bool, c'); Step({ expr: { @@ -498,7 +450,7 @@ module Transition = (EV: EV_MODE) => { | UnOp(Int(Minus), d1) => let. _ = otherwise(env, d1 => UnOp(Int(Minus), d1) |> rewrap) and. d1' = - req_value( + req_final( req(state, env), c => UnOp(Int(Minus), c) |> wrap_ctx, d1, @@ -513,7 +465,7 @@ module Transition = (EV: EV_MODE) => { | UnOp(Bool(Not), d1) => let. _ = otherwise(env, d1 => UnOp(Bool(Not), d1) |> rewrap) and. d1' = - req_value( + req_final( req(state, env), c => UnOp(Bool(Not), c) |> wrap_ctx, d1, @@ -528,7 +480,7 @@ module Transition = (EV: EV_MODE) => { | BinOp(Bool(And), d1, d2) => let. _ = otherwise(env, d1 => BinOp(Bool(And), d1, d2) |> rewrap) and. d1' = - req_value( + req_final( req(state, env), d1 => BinOp1(Bool(And), d1, d2) |> wrap_ctx, d1, @@ -543,7 +495,7 @@ module Transition = (EV: EV_MODE) => { | BinOp(Bool(Or), d1, d2) => let. _ = otherwise(env, d1 => BinOp(Bool(Or), d1, d2) |> rewrap) and. d1' = - req_value( + req_final( req(state, env), d1 => BinOp1(Bool(Or), d1, d2) |> wrap_ctx, d1, @@ -558,13 +510,13 @@ module Transition = (EV: EV_MODE) => { | BinOp(Int(op), d1, d2) => let. _ = otherwise(env, (d1, d2) => BinOp(Int(op), d1, d2) |> rewrap) and. d1' = - req_value( + req_final( req(state, env), d1 => BinOp1(Int(op), d1, d2) |> wrap_ctx, d1, ) and. d2' = - req_value( + req_final( req(state, env), d2 => BinOp2(Int(op), d1, d2) |> wrap_ctx, d2, @@ -608,13 +560,13 @@ module Transition = (EV: EV_MODE) => { let. _ = otherwise(env, (d1, d2) => BinOp(Float(op), d1, d2) |> rewrap) and. d1' = - req_value( + req_final( req(state, env), d1 => BinOp1(Float(op), d1, d2) |> wrap_ctx, d1, ) and. d2' = - req_value( + req_final( req(state, env), d2 => BinOp2(Float(op), d1, d2) |> wrap_ctx, d2, @@ -647,13 +599,13 @@ module Transition = (EV: EV_MODE) => { let. _ = otherwise(env, (d1, d2) => BinOp(String(op), d1, d2) |> rewrap) and. d1' = - req_value( + req_final( req(state, env), d1 => BinOp1(String(op), d1, d2) |> wrap_ctx, d1, ) and. d2' = - req_value( + req_final( req(state, env), d2 => BinOp2(String(op), d1, d2) |> wrap_ctx, d2, @@ -684,7 +636,7 @@ module Transition = (EV: EV_MODE) => { and. d1' = req_final(req(state, env), d1 => Cons1(d1, d2) |> wrap_ctx, d1) and. d2' = - req_value(req(state, env), d2 => Cons2(d1, d2) |> wrap_ctx, d2); + req_final(req(state, env), d2 => Cons2(d1, d2) |> wrap_ctx, d2); let-unbox ds = (List, d2'); Step({ expr: ListLit([d1', ...ds]) |> fresh, @@ -695,13 +647,13 @@ module Transition = (EV: EV_MODE) => { | ListConcat(d1, d2) => let. _ = otherwise(env, (d1, d2) => ListConcat(d1, d2) |> rewrap) and. d1' = - req_value( + req_final( req(state, env), d1 => ListConcat1(d1, d2) |> wrap_ctx, d1, ) and. d2' = - req_value( + req_final( req(state, env), d2 => ListConcat2(d1, d2) |> wrap_ctx, d2, @@ -795,9 +747,9 @@ module Transition = (EV: EV_MODE) => { | Wrap(d'', Probe(pr)) => /* When evaluated, a probe adds a dynamics info entry * reflecting the evaluation of the contained expression */ - let. _ = otherwise(env, ((d, _)) => Wrap(d, Probe(pr)) |> rewrap) - and. (d', _) = - req_final_or_value( + let. _ = otherwise(env, d => Wrap(d, Probe(pr)) |> rewrap) + and. d' = + req_final( req(state, env), d => Wrap(d, Probe(pr)) |> wrap_ctx, d'', diff --git a/src/haz3lcore/dynamics/Unboxing.re b/src/haz3lcore/dynamics/Unboxing.re index d1b9669898..d018133dc5 100644 --- a/src/haz3lcore/dynamics/Unboxing.re +++ b/src/haz3lcore/dynamics/Unboxing.re @@ -15,6 +15,17 @@ open Util; the inner lists may still have casts around them after unboxing. */ +type unboxed_tfun = + | TypFun(TPat.t, Exp.t, option(string)) + | TFunCast(DHExp.t, TPat.t, Typ.t, TPat.t, Typ.t); + +type unboxed_fun = + | Constructor(string) + | FunEnv(Pat.t, Exp.t, ClosureEnvironment.t) + | FunCast(DHExp.t, Typ.t, Typ.t, Typ.t, Typ.t) + | BuiltinFun(string) + | DeferredAp(DHExp.t, list(DHExp.t)); + type unbox_request('a) = | Int: unbox_request(int) | Float: unbox_request(float) @@ -24,7 +35,9 @@ type unbox_request('a) = | List: unbox_request(list(DHExp.t)) | Cons: unbox_request((DHExp.t, DHExp.t)) | SumNoArg(string): unbox_request(unit) - | SumWithArg(string): unbox_request(DHExp.t); + | SumWithArg(string): unbox_request(DHExp.t) + | TypFun: unbox_request(unboxed_tfun) + | Fun: unbox_request(unboxed_fun); type unboxed('a) = | DoesNotMatch @@ -135,6 +148,35 @@ let rec unbox: type a. (unbox_request(a), DHExp.t) => unboxed(a) = }; // There should be some sort of failure here when the cast doesn't go through. + /* Function-like things can look like the following when values */ + | (Fun, Constructor(name, _)) => Matches(Constructor(name)) // Perhaps we should check if the constructor actually is a function? + | (Fun, Fun(dp, d3, Some(env'), _)) => Matches(FunEnv(dp, d3, env')) + | ( + Fun, + Cast( + d3', + {term: Arrow(ty1, ty2), _}, + {term: Arrow(ty1', ty2'), _}, + ), + ) => + Matches(FunCast(d3', ty1, ty2, ty1', ty2')) + | (Fun, BuiltinFun(name)) => Matches(BuiltinFun(name)) + | (Fun, DeferredAp(d1, ds)) => Matches(DeferredAp(d1, ds)) + + /* TypFun-like things can look like the following when values */ + | (TypFun, TypFun(utpat, tfbody, name)) => + Matches(TypFun(utpat, tfbody, name)) + // Note: We might be able to handle this cast like other casts + | ( + TypFun, + Cast( + d'', + {term: Forall(tp1, _), _} as t1, + {term: Forall(tp2, _), _} as t2, + ), + ) => + Matches(TFunCast(d'', tp1, t1, tp2, t2)) + /* Any cast from unknown is indet */ | (_, Cast(_, {term: Unknown(_), _}, _)) => IndetMatch @@ -169,6 +211,8 @@ let rec unbox: type a. (unbox_request(a), DHExp.t) => unboxed(a) = | SumNoArg(_) | SumWithArg(_) => raise(EvaluatorError.Exception(InvalidBoxedSumConstructor(expr))) + | Fun => raise(EvaluatorError.Exception(InvalidBoxedFun(expr))) + | TypFun => raise(EvaluatorError.Exception(InvalidBoxedTypFun(expr))) } /* Forms that are not yet or will never be a value */ diff --git a/src/haz3lcore/dynamics/ValueChecker.re b/src/haz3lcore/dynamics/ValueChecker.re index c5fa1d4693..490061855c 100644 --- a/src/haz3lcore/dynamics/ValueChecker.re +++ b/src/haz3lcore/dynamics/ValueChecker.re @@ -11,66 +11,35 @@ module ValueCheckerEVMode: { type state = unit; type result = t; - type requirement('a) = ('a, (result, bool)); - type requirements('a, 'b) = ('a, (result, bool)); + type requirement('a) = ('a, result); + type requirements('a, 'b) = ('a, result); - let combine = ((r1, b1), (r2, b2)) => ( + let combine = (r1, r2) => switch (r1, r2) { | (Expr, _) => Expr | (_, Expr) => Expr | (Indet, _) => Indet | (_, Indet) => Indet | (Value, Value) => Value - }, - b1 && b2, - ); + }; - let req_value = (vc, _, d) => ( - d, - switch (vc(d)) { - | Value => (Value, true) - | Indet => (Indet, false) - | Expr => (Expr, false) - }, - ); - let req_all_value = (vc, _, ds) => - List.fold_right( - ((v1, r1), (v2, r2)) => ([v1, ...v2], combine(r1, r2)), - List.map(req_value(vc, x => x), ds), - ([], (Value, true)), - ); - let req_final = (vc, _, d) => ( - d, - switch (vc(d)) { - | Value => (Value, true) - | Indet => (Indet, true) - | Expr => (Expr, false) - }, - ); + let req_final = (vc, _, d) => (d, vc(d)); let req_all_final = (vc, _, ds) => List.fold_right( ((v1, r1), (v2, r2)) => ([v1, ...v2], combine(r1, r2)), List.map(req_final(vc, x => x), ds), - ([], (Value, true)), + ([], Value), ); - let req_final_or_value = (vc, _, d) => - switch (vc(d)) { - | Value => ((d, true), (Value, true)) - | Indet => ((d, false), (Value, true)) - | Expr => ((d, false), (Value, false)) - }; - - let otherwise = (_, _) => ((), (Value, true)); + let otherwise = (_, _) => ((), Value); - let (let.) = ((v, (r, b)), rule) => - switch (b, r, rule(v)) { - | (_, _, Constructor) => r - | (_, Expr, Indet) => Expr - | (_, _, Indet) => Indet - | (_, _, Value) => Value - | (true, _, Step(_)) => Expr - | (false, _, Step(_)) => r + let (let.) = ((v, r), rule) => + switch (r, rule(v)) { + | (_, Constructor) => r + | (Expr, Indet) => Expr + | (_, Indet) => Indet + | (_, Value) => Value + | (_, Step(_)) => Expr }; let (and.) = ((v1, r1), (v2, r2)) => { diff --git a/src/haz3lcore/lang/Form.re b/src/haz3lcore/lang/Form.re index 5ab6368cc8..512962e778 100644 --- a/src/haz3lcore/lang/Form.re +++ b/src/haz3lcore/lang/Form.re @@ -256,7 +256,7 @@ let forms: list((string, t)) = [ ("divide", mk_infix("/", Exp, P.mult)), ("equals", mk_infix("==", Exp, P.eqs)), ("string_equals", mk_infix("$==", Exp, P.eqs)), - ("string_concat", mk_infix("++", Exp, P.plus)), + ("string_concat", mk_infix("++", Exp, P.concat)), ("lt", mk_infix("<", Exp, P.eqs)), ("gt", mk_infix(">", Exp, P.eqs)), ("not_equals", mk_infix("!=", Exp, P.eqs)), @@ -275,7 +275,7 @@ let forms: list((string, t)) = [ ("logical_and", mk_infix("&&", Exp, P.and_)), ("logical_or_legacy", mk_infix("\\/", Exp, P.or_)), ("logical_or", mk_infix("||", Exp, P.or_)), - ("list_concat", mk_infix("@", Exp, P.plus)), + ("list_concat", mk_infix("@", Exp, P.concat)), ("cons_exp", mk_infix("::", Exp, P.cons)), ("cons_pat", mk_infix("::", Pat, P.cons)), ("typeann", mk(ss, [":"], mk_bin'(P.cast, Pat, Pat, [], Typ))), diff --git a/src/haz3lcore/lang/Precedence.re b/src/haz3lcore/lang/Precedence.re index 3a5b54ec5b..1c76d241fa 100644 --- a/src/haz3lcore/lang/Precedence.re +++ b/src/haz3lcore/lang/Precedence.re @@ -70,10 +70,10 @@ let concat = 29 |> right_associative; let eqs = 30 |> left_associative; // _____ == x // _____ && true -let and_ = 31; +let and_ = 31 |> right_associative; // true && _____ // _____ || false -let or_ = 32; +let or_ = 32 |> right_associative; // false || _____ let if_ = 34; let fun_ = 35; diff --git a/src/haz3lcore/lang/term/IdTagged.re b/src/haz3lcore/lang/term/IdTagged.re index 200be8a4ab..1bd91daf80 100644 --- a/src/haz3lcore/lang/term/IdTagged.re +++ b/src/haz3lcore/lang/term/IdTagged.re @@ -5,7 +5,7 @@ type t('a) = { [@show.opaque] ids: list(Id.t), [@show.opaque] - /* UExp invariant: copied should always be false, and the id should be unique + /* Exp invariant: copied should always be false, and the id should be unique DHExp invariant: if copied is true, then this term and its children may not have unique ids. The flag is used to avoid deep-copying expressions during evaluation, while keeping track of where we will need to replace the ids diff --git a/src/haz3lcore/pretty/Abbreviate.re b/src/haz3lcore/pretty/Abbreviate.re index 0cc242db76..fffb9acb19 100644 --- a/src/haz3lcore/pretty/Abbreviate.re +++ b/src/haz3lcore/pretty/Abbreviate.re @@ -164,10 +164,11 @@ let rec abbreviate_exp = (exp: Exp.t): Exp.t => { let abbreviate_exp = (~available as a=12, exp: Exp.t): (Exp.t, bool) => { available := a; - available^ <= 1 + let exp = abbreviate_exp(exp); + let length_exp = a - available^; + a < 0 || a <= 1 && length_exp > 1 ? (ellipses_term(), false) : { - let exp = abbreviate_exp(exp); (exp, available^ < 0); }; }; diff --git a/src/haz3lcore/pretty/ExpToSegment.re b/src/haz3lcore/pretty/ExpToSegment.re index f245e20caa..0e6fb76c8b 100644 --- a/src/haz3lcore/pretty/ExpToSegment.re +++ b/src/haz3lcore/pretty/ExpToSegment.re @@ -9,6 +9,7 @@ module Settings = { fold_fn_bodies: bool, hide_fixpoints: bool, fold_cast_types: bool, + show_filters: bool, }; let of_core = (~inline, settings: CoreSettings.t) => { @@ -17,6 +18,7 @@ module Settings = { fold_fn_bodies: !settings.evaluation.show_fn_bodies, hide_fixpoints: !settings.evaluation.show_fixpoints, fold_cast_types: !settings.evaluation.show_casts, + show_filters: false, }; }; @@ -151,7 +153,13 @@ let rec exp_to_pretty = (~settings: Settings.t, exp: Exp.t): pretty => { switch (exp |> Exp.term_of) { // Assume these have been removed by the parenthesizer | DynamicErrorHole(_) - | Filter(_) => failwith("printing these not implemented yet") + | Filter(Residue(_), _) => failwith("printing these not implemented yet") + | Filter(Filter({pat, act}), e) => + let label = FilterAction.string_of_t(act); + let id = exp |> Exp.rep_id; + let* p = go(pat); + let+ e = go(e); + [mk_form("filter_" ++ label, id, [p])] @ e; // Forms which should be removed by substitute_closures | Closure(_) => failwith("closure not removed before printing") // Other cases @@ -164,7 +172,7 @@ let rec exp_to_pretty = (~settings: Settings.t, exp: Exp.t): pretty => { | Int(n) => text_to_pretty(exp |> Exp.rep_id, Sort.Exp, Int.to_string(n)) // TODO: do floats print right? | Float(f) => - text_to_pretty(exp |> Exp.rep_id, Sort.Exp, Float.to_string(f)) + text_to_pretty(exp |> Exp.rep_id, Sort.Exp, Printf.sprintf("%f", f)) | String(s) => text_to_pretty(exp |> Exp.rep_id, Sort.Exp, "\"" ++ s ++ "\"") // TODO: Make sure types are correct @@ -176,7 +184,7 @@ let rec exp_to_pretty = (~settings: Settings.t, exp: Exp.t): pretty => { // @ [mk_form("typeasc", id, [])] // @ (t |> fold_if(settings.fold_cast_types)); | ListLit([]) => text_to_pretty(exp |> Exp.rep_id, Sort.Exp, "[]") - | Deferral(_) => text_to_pretty(exp |> Exp.rep_id, Sort.Exp, "deferral") + | Deferral(_) => text_to_pretty(exp |> Exp.rep_id, Sort.Exp, "_") | ListLit([x, ...xs]) => // TODO: Add optional newlines let* x = go(x) @@ -444,11 +452,12 @@ let rec exp_to_pretty = (~settings: Settings.t, exp: Exp.t): pretty => { @ ( List.map2( (id, (p, e)) => - settings.inline - ? [] - : [Secondary(Secondary.mk_newline(Id.mk()))] - @ [mk_form("rule", id, [p])] - @ (e |> fold_if(settings.fold_case_clauses)), + ( + settings.inline + ? [] : [Secondary(Secondary.mk_newline(Id.mk()))] + ) + @ [mk_form("rule", id, [p])] + @ (e |> fold_if(settings.fold_case_clauses)), ids, rs, ) @@ -474,7 +483,7 @@ and pat_to_pretty = (~settings: Settings.t, pat: Pat.t): pretty => { | Var(v) => text_to_pretty(pat |> Pat.rep_id, Sort.Pat, v) | Int(n) => text_to_pretty(pat |> Pat.rep_id, Sort.Pat, Int.to_string(n)) | Float(f) => - text_to_pretty(pat |> Pat.rep_id, Sort.Pat, Float.to_string(f)) + text_to_pretty(pat |> Pat.rep_id, Sort.Pat, Printf.sprintf("%f", f)) | Bool(b) => text_to_pretty(pat |> Pat.rep_id, Sort.Pat, Bool.to_string(b)) | String(s) => text_to_pretty(pat |> Pat.rep_id, Sort.Pat, "\"" ++ s ++ "\"") @@ -542,11 +551,28 @@ and typ_to_pretty = (~settings: Settings.t, typ: Typ.t): pretty => { let go = typ_to_pretty(~settings: Settings.t); let go_constructor: ConstructorMap.variant(Typ.t) => pretty = fun - | Variant(c, ids, None) => text_to_pretty(List.hd(ids), Sort.Typ, c) + | Variant(c, ids, None) => { + text_to_pretty( + Option.value(~default=Id.invalid, ListUtil.hd_opt(ids)), + Sort.Typ, + c, + ); + } | Variant(c, ids, Some(x)) => { let+ constructor = - text_to_pretty(List.hd(List.tl(ids)), Sort.Typ, c); - constructor @ [mk_form("ap_typ", List.hd(ids), [go(x)])]; + text_to_pretty( + Option.value(~default=Id.invalid, ListUtil.nth_opt(1, ids)), + Sort.Typ, + c, + ); + constructor + @ [ + mk_form( + "ap_typ", + Option.value(~default=Id.invalid, ListUtil.hd_opt(ids)), + [go(x)], + ), + ]; } | BadEntry(x) => go(x); switch (typ |> Typ.term_of) { @@ -794,7 +820,10 @@ let paren_typ_assoc_at = external_precedence_typ(typ) > internal_precedence ? Typ.fresh(Wrap(typ)) : typ; -let rec parenthesize = (exp: Exp.t): Exp.t => { +let rec parenthesize = (~show_filters: bool, exp: Exp.t): Exp.t => { + let parenthesize = parenthesize(~show_filters); + let parenthesize_pat = parenthesize_pat(~show_filters); + let parenthesize_typ = parenthesize_typ(~show_filters); let (term, rewrap) = Exp.unwrap(exp); switch (term) { // Indivisible forms dont' change @@ -813,9 +842,11 @@ let rec parenthesize = (exp: Exp.t): Exp.t => { // Forms that currently need to stripped before outputting | Closure(_, x) | DynamicErrorHole(x, _) - | Tuple([x]) - | Filter(_, x) => x |> parenthesize - + | Tuple([x]) => parenthesize(x) + | Filter(Filter({pat, act}), x) => + Filter(Filter({pat: parenthesize(pat), act}), parenthesize(x)) + |> rewrap + | Filter(Residue(_), x) => x |> parenthesize // Other forms | Constructor(c, t) => Constructor(c, paren_typ_at(Precedence.cast, t)) |> rewrap @@ -857,7 +888,7 @@ let rec parenthesize = (exp: Exp.t): Exp.t => { | TyAlias(tp, t, e) => TyAlias( tp, - t, // TODO: Types + parenthesize_typ(t) |> paren_typ_at(Precedence.min), parenthesize(e) |> paren_assoc_at(Precedence.let_), ) |> rewrap @@ -943,10 +974,22 @@ let rec parenthesize = (exp: Exp.t): Exp.t => { | UnOp(Int(Minus), e) => UnOp(Int(Minus), parenthesize(e) |> paren_at(Precedence.neg)) |> rewrap | BinOp(op, e1, e2) => - BinOp( - op, - parenthesize(e1) |> paren_assoc_at(Precedence.of_bin_op(op)), - parenthesize(e2) |> paren_at(Precedence.of_bin_op(op)), + ( + switch (Precedence.of_bin_op(op) |> Precedence.associativity) { + | Some(Left) + | None => + BinOp( + op, + parenthesize(e1) |> paren_assoc_at(Precedence.of_bin_op(op)), + parenthesize(e2) |> paren_at(Precedence.of_bin_op(op)), + ) + | Some(Right) => + BinOp( + op, + parenthesize(e1) |> paren_at(Precedence.of_bin_op(op)), + parenthesize(e2) |> paren_assoc_at(Precedence.of_bin_op(op)), + ) + } ) |> rewrap | Match(e, rs) => @@ -961,10 +1004,13 @@ let rec parenthesize = (exp: Exp.t): Exp.t => { ), ) |> rewrap - | MultiHole(xs) => MultiHole(List.map(parenthesize_any, xs)) |> rewrap + | MultiHole(xs) => + MultiHole(List.map(parenthesize_any(~show_filters), xs)) |> rewrap }; } -and parenthesize_pat = (pat: Pat.t): Pat.t => { +and parenthesize_pat = (~show_filters: bool, pat: Pat.t): Pat.t => { + let parenthesize_pat = parenthesize_pat(~show_filters); + let parenthesize_typ = parenthesize_typ(~show_filters); let (term, rewrap) = Pat.unwrap(pat); switch (term) { // Indivisible forms dont' change @@ -1007,7 +1053,8 @@ and parenthesize_pat = (pat: Pat.t): Pat.t => { parenthesize_pat(p2) |> paren_pat_at(Precedence.min), ) |> rewrap - | MultiHole(xs) => MultiHole(List.map(parenthesize_any, xs)) |> rewrap + | MultiHole(xs) => + MultiHole(List.map(parenthesize_any(~show_filters), xs)) |> rewrap | Cast(p, t1, t2) => Cast( parenthesize_pat(p) |> paren_pat_assoc_at(Precedence.cast), @@ -1018,7 +1065,8 @@ and parenthesize_pat = (pat: Pat.t): Pat.t => { }; } -and parenthesize_typ = (typ: Typ.t): Typ.t => { +and parenthesize_typ = (~show_filters: bool, typ: Typ.t): Typ.t => { + let parenthesize_typ = parenthesize_typ(~show_filters); let (term, rewrap) = Typ.unwrap(typ); switch (term) { // Indivisible forms dont' change @@ -1080,11 +1128,14 @@ and parenthesize_typ = (typ: Typ.t): Typ.t => { ) |> rewrap | Unknown(Hole(MultiHole(xs))) => - Unknown(Hole(MultiHole(List.map(parenthesize_any, xs)))) |> rewrap + Unknown( + Hole(MultiHole(List.map(parenthesize_any(~show_filters), xs))), + ) + |> rewrap }; } -and parenthesize_tpat = (tpat: TPat.t): TPat.t => { +and parenthesize_tpat = (~show_filters: bool, tpat: TPat.t): TPat.t => { let (term, rewrap: TPat.term => TPat.t) = IdTagged.unwrap(tpat); switch (term) { // Indivisible forms dont' change @@ -1093,11 +1144,12 @@ and parenthesize_tpat = (tpat: TPat.t): TPat.t => { | EmptyHole => tpat // Other forms - | MultiHole(xs) => MultiHole(List.map(parenthesize_any, xs)) |> rewrap + | MultiHole(xs) => + MultiHole(List.map(parenthesize_any(~show_filters), xs)) |> rewrap }; } -and parenthesize_rul = (rul: Rul.t): Rul.t => { +and parenthesize_rul = (~show_filters: bool, rul: Rul.t): Rul.t => { let (term, rewrap: Rul.term => Rul.t) = IdTagged.unwrap(rul); switch (term) { // Indivisible forms dont' change @@ -1106,33 +1158,44 @@ and parenthesize_rul = (rul: Rul.t): Rul.t => { // Other forms | Rules(e, ps) => Rules( - parenthesize(e), - List.map(((p, e)) => (parenthesize_pat(p), parenthesize(e)), ps), + parenthesize(~show_filters, e), + List.map( + ((p, e)) => + ( + parenthesize_pat(~show_filters, p), + parenthesize(~show_filters, e), + ), + ps, + ), ) |> rewrap - | Hole(xs) => Hole(List.map(parenthesize_any, xs)) |> rewrap + | Hole(xs) => + Hole(List.map(parenthesize_any(~show_filters), xs)) |> rewrap }; } -and parenthesize_any = (any: Any.t): Any.t => +and parenthesize_any = (~show_filters: bool, any: Any.t): Any.t => switch (any) { - | Exp(e) => Exp(parenthesize(e)) - | Pat(p) => Pat(parenthesize_pat(p)) - | Typ(t) => Typ(parenthesize_typ(t)) - | TPat(tp) => TPat(parenthesize_tpat(tp)) - | Rul(r) => Rul(parenthesize_rul(r)) + | Exp(e) => Exp(parenthesize(~show_filters, e)) + | Pat(p) => Pat(parenthesize_pat(~show_filters, p)) + | Typ(t) => Typ(parenthesize_typ(~show_filters, t)) + | TPat(tp) => TPat(parenthesize_tpat(~show_filters, tp)) + | Rul(r) => Rul(parenthesize_rul(~show_filters, r)) | Any(_) => any | Nul(_) => any }; -let exp_to_segment = (~settings, exp: Exp.t): Segment.t => { - let exp = exp |> Exp.substitute_closures(Builtins.env_init) |> parenthesize; +let exp_to_segment = (~settings: Settings.t, exp: Exp.t): Segment.t => { + let exp = + exp + |> Exp.substitute_closures(Builtins.env_init) + |> parenthesize(~show_filters=settings.show_filters); let p = exp_to_pretty(~settings, exp); p |> PrettySegment.select; }; let typ_to_segment = (~settings, typ: Typ.t): Segment.t => { let typ = parenthesize_typ(typ); - let p = typ_to_pretty(~settings, typ); + let p = typ_to_pretty(~settings, typ(~show_filters=settings.show_filters)); p |> PrettySegment.select; }; diff --git a/src/haz3lcore/prog/CachedStatics.re b/src/haz3lcore/prog/CachedStatics.re index 106ce24ab0..75d3b81f30 100644 --- a/src/haz3lcore/prog/CachedStatics.re +++ b/src/haz3lcore/prog/CachedStatics.re @@ -2,8 +2,8 @@ open Util; [@deriving (show({with_path: false}), sexp, yojson)] type t = { - term: UExp.t, - elaborated: UExp.t, + term: Exp.t, + elaborated: Exp.t, info_map: Statics.Map.t, error_ids: list(Id.t), }; diff --git a/src/haz3lcore/prog/ProgramResult.re b/src/haz3lcore/prog/ProgramResult.re index fbbb313a08..a45c3a1130 100644 --- a/src/haz3lcore/prog/ProgramResult.re +++ b/src/haz3lcore/prog/ProgramResult.re @@ -40,7 +40,7 @@ type error = [@deriving (show({with_path: false}), sexp, yojson)] type t('a) = - | Off(Elaborator.Elaboration.t) + | Off(DHExp.t) // Elaboration | ResultOk('a) | ResultFail(error) | ResultPending; diff --git a/src/haz3lcore/statics/Elaborator.re b/src/haz3lcore/statics/Elaborator.re index d078a0d280..b2b5d00a27 100644 --- a/src/haz3lcore/statics/Elaborator.re +++ b/src/haz3lcore/statics/Elaborator.re @@ -6,11 +6,6 @@ open Util; exception MissingTypeInfo; -module Elaboration = { - [@deriving (show({with_path: false}), sexp, yojson)] - type t = {d: DHExp.t}; -}; - module ElaborationResult = { [@deriving sexp] type t = @@ -47,7 +42,7 @@ let fresh_pat_cast = (p: DHPat.t, t1: Typ.t, t2: Typ.t): DHPat.t => { }; }; -let elaborated_type = (m: Statics.Map.t, uexp: UExp.t): (Typ.t, Ctx.t, 'a) => { +let elaborated_type = (m: Statics.Map.t, uexp: Exp.t): (Typ.t, Ctx.t, 'a) => { let (mode, self_ty, ctx, co_ctx) = switch (Id.Map.find_opt(Exp.rep_id(uexp), m)) { | Some(Info.InfoExp({mode, ty, ctx, co_ctx, _})) => ( @@ -74,9 +69,9 @@ let elaborated_type = (m: Statics.Map.t, uexp: UExp.t): (Typ.t, Ctx.t, 'a) => { (elab_ty |> Typ.normalize(ctx) |> Typ.all_ids_temp, ctx, co_ctx); }; -let elaborated_pat_type = (m: Statics.Map.t, upat: UPat.t): (Typ.t, Ctx.t) => { +let elaborated_pat_type = (m: Statics.Map.t, upat: Pat.t): (Typ.t, Ctx.t) => { let (mode, self_ty, ctx, prev_synswitch) = - switch (Id.Map.find_opt(UPat.rep_id(upat), m)) { + switch (Id.Map.find_opt(Pat.rep_id(upat), m)) { | Some(Info.InfoPat({mode, ty, ctx, prev_synswitch, _})) => ( mode, ty, @@ -104,11 +99,10 @@ let elaborated_pat_type = (m: Statics.Map.t, upat: UPat.t): (Typ.t, Ctx.t) => { (elab_ty |> Typ.normalize(ctx) |> Typ.all_ids_temp, ctx); }; -let rec elaborate_pattern = - (m: Statics.Map.t, upat: UPat.t): (DHPat.t, Typ.t) => { +let rec elaborate_pattern = (m: Statics.Map.t, upat: Pat.t): (DHPat.t, Typ.t) => { let (elaborated_type, ctx) = elaborated_pat_type(m, upat); let cast_from = (ty, exp) => fresh_pat_cast(exp, ty, elaborated_type); - let (term, rewrap) = UPat.unwrap(upat); + let (term, rewrap) = Pat.unwrap(upat); let dpat = switch (term) { | Int(_) => upat |> cast_from(Int |> Typ.temp) @@ -207,10 +201,10 @@ let rec elaborate_pattern = [Matt] A lot of these fresh_cast calls are redundant, however if you want to remove one, I'd ask you instead comment it out and leave a comment explaining why it's redundant. */ -let rec elaborate = (m: Statics.Map.t, uexp: UExp.t): (DHExp.t, Typ.t) => { +let rec elaborate = (m: Statics.Map.t, uexp: Exp.t): (DHExp.t, Typ.t) => { let (elaborated_type, ctx, co_ctx) = elaborated_type(m, uexp); let cast_from = (ty, exp) => fresh_cast(exp, ty, elaborated_type); - let (term, rewrap) = UExp.unwrap(uexp); + let (term, rewrap) = Exp.unwrap(uexp); let dhexp = switch (term) { | Invalid(_) @@ -575,7 +569,7 @@ let rec elaborate = (m: Statics.Map.t, uexp: UExp.t): (DHExp.t, Typ.t) => { let fix_typ_ids = Exp.map_term(~f_typ=(cont, e) => e |> IdTagged.new_ids |> cont); -let uexp_elab = (m: Statics.Map.t, uexp: UExp.t): ElaborationResult.t => +let uexp_elab = (m: Statics.Map.t, uexp: Exp.t): ElaborationResult.t => switch (elaborate(m, uexp)) { | exception MissingTypeInfo => DoesNotElaborate | (d, ty) => Elaborates(d |> fix_typ_ids, ty, Delta.empty) diff --git a/src/haz3lcore/statics/Info.re b/src/haz3lcore/statics/Info.re index 8aaaeaae3d..e8769ac892 100644 --- a/src/haz3lcore/statics/Info.re +++ b/src/haz3lcore/statics/Info.re @@ -192,7 +192,7 @@ type status_tpat = [@deriving (show({with_path: false}), sexp, yojson)] type exp = { - term: UExp.t, /* The term under consideration */ + term: Exp.t, /* The term under consideration */ ancestors, /* Ascending list of containing term ids */ ctx: Ctx.t, /* Typing context for the term */ mode: Mode.t, /* Parental type expectations */ @@ -205,7 +205,7 @@ type exp = { [@deriving (show({with_path: false}), sexp, yojson)] type pat = { - term: UPat.t, + term: Pat.t, ancestors, ctx: Ctx.t, co_ctx: CoCtx.t, @@ -585,7 +585,7 @@ let fixed_typ_pat = (ctx, mode: Mode.t, self: Self.pat): Typ.t => { let fixed_constraint_pat = ( - upat: UPat.t, + upat: Pat.t, ctx, mode: Mode.t, self: Self.pat, @@ -609,9 +609,8 @@ let fixed_typ_exp = (ctx, mode: Mode.t, self: Self.exp): Typ.t => }; /* Add derivable attributes for expression terms */ -let derived_exp = - (~uexp: UExp.t, ~ctx, ~mode, ~ancestors, ~self, ~co_ctx): exp => { - let cls = Cls.Exp(UExp.cls_of_term(uexp.term)); +let derived_exp = (~uexp: Exp.t, ~ctx, ~mode, ~ancestors, ~self, ~co_ctx): exp => { + let cls = Cls.Exp(Exp.cls_of_term(uexp.term)); let status = status_exp(ctx, mode, self); let ty = fixed_typ_exp(ctx, mode, self); {cls, self, ty, mode, status, ctx, co_ctx, ancestors, term: uexp}; @@ -620,7 +619,7 @@ let derived_exp = /* Add derivable attributes for pattern terms */ let derived_pat = ( - ~upat: UPat.t, + ~upat: Pat.t, ~ctx, ~co_ctx, ~prev_synswitch, @@ -630,7 +629,7 @@ let derived_pat = ~constraint_, ) : pat => { - let cls = Cls.Pat(UPat.cls_of_term(upat.term)); + let cls = Cls.Pat(Pat.cls_of_term(upat.term)); let status = status_pat(ctx, mode, self); let ty = fixed_typ_pat(ctx, mode, self); let constraint_ = fixed_constraint_pat(upat, ctx, mode, self, constraint_); @@ -650,10 +649,10 @@ let derived_pat = }; /* Add derivable attributes for types */ -let derived_typ = (~utyp: UTyp.t, ~ctx, ~ancestors, ~expects): typ => { +let derived_typ = (~utyp: Typ.t, ~ctx, ~ancestors, ~expects): typ => { let cls: Cls.t = /* Hack to improve CI display */ - switch (expects, UTyp.cls_of_term(utyp.term)) { + switch (expects, Typ.cls_of_term(utyp.term)) { | (VariantExpected(_) | ConstructorExpected(_), Var) => Cls.Typ(Constructor) | (_, cls) => Cls.Typ(cls) diff --git a/src/haz3lcore/statics/MakeTerm.re b/src/haz3lcore/statics/MakeTerm.re index 4d4b05eb5a..53056817e2 100644 --- a/src/haz3lcore/statics/MakeTerm.re +++ b/src/haz3lcore/statics/MakeTerm.re @@ -46,7 +46,7 @@ type unsorted = | Bin(t, tiles, t); type t = { - term: UExp.t, + term: Exp.t, terms: TermMap.t, projectors: Id.Map.t(Piece.projector), }; @@ -68,7 +68,7 @@ let is_typ_bsum = is_nary(Any.is_typ, "+"); let is_grout = tiles => Aba.get_as(tiles) |> List.map(snd) |> List.for_all((==)(([" "], []))); -let is_rules = ((ts, kids): tiles): option(Aba.t(UPat.t, UExp.t)) => { +let is_rules = ((ts, kids): tiles): option(Aba.t(Pat.t, Exp.t)) => { open OptUtil.Syntax; let+ ps = (ts: list(tile)) @@ -138,7 +138,7 @@ let should_instrument = (id: Id.t): bool => | None => failwith("MakeTerm.exp: projector not found") }; -let parse_sum_term: UTyp.t => ConstructorMap.variant(UTyp.t) = +let parse_sum_term: Typ.t => ConstructorMap.variant(Typ.t) = fun | {term: Var(ctr), ids, _} => Variant(ctr, ids, None) | {term: Ap({term: Var(ctr), ids: ids_ctr, _}, u), ids: ids_ap, _} => @@ -184,9 +184,9 @@ and exp = unsorted => { let ids = ids(unsorted) @ inner_ids; return(e => Exp(e), ids, {ids, copied: false, term}); } -and exp_term: unsorted => (UExp.term, list(Id.t)) = { - let ret = (tm: UExp.term) => (tm, []); - let hole = unsorted => UExp.hole(kids_of_unsorted(unsorted)); +and exp_term: unsorted => (Exp.term, list(Id.t)) = { + let ret = (tm: Exp.term) => (tm, []); + let hole = unsorted => Exp.hole(kids_of_unsorted(unsorted)); fun | Op(tiles) as tm => switch (tiles) { @@ -270,19 +270,19 @@ and exp_term: unsorted => (UExp.term, list(Id.t)) = { ), ) | (["(", ")"], [Exp(arg)]) => - let use_deferral = (arg: UExp.t): UExp.t => { + let use_deferral = (arg: Exp.t): Exp.t => { ids: arg.ids, copied: false, term: Deferral(InAp), }; switch (arg.term) { - | _ when UExp.is_deferral(arg) => + | _ when Exp.is_deferral(arg) => ret(DeferredAp(l, [use_deferral(arg)])) - | Tuple(es) when List.exists(UExp.is_deferral, es) => ( + | Tuple(es) when List.exists(Exp.is_deferral, es) => ( DeferredAp( l, List.map( - arg => UExp.is_deferral(arg) ? use_deferral(arg) : arg, + arg => Exp.is_deferral(arg) ? use_deferral(arg) : arg, es, ), ), @@ -352,9 +352,9 @@ and pat = unsorted => { let ids = ids(unsorted) @ inner_ids; return(p => Pat(p), ids, {ids, term, copied: false}); } -and pat_term: unsorted => (UPat.term, list(Id.t)) = { - let ret = (term: UPat.term) => (term, []); - let hole = unsorted => UPat.hole(kids_of_unsorted(unsorted)); +and pat_term: unsorted => (Pat.term, list(Id.t)) = { + let ret = (term: Pat.term) => (term, []); + let hole = unsorted => Pat.hole(kids_of_unsorted(unsorted)); fun | Op(tiles) as tm => switch (tiles) { @@ -421,9 +421,9 @@ and typ = unsorted => { let ids = ids(unsorted) @ inner_ids; return(ty => Typ(ty), ids, {ids, term, copied: false}); } -and typ_term: unsorted => (UTyp.term, list(Id.t)) = { - let ret = (term: UTyp.term) => (term, []); - let hole = unsorted => UTyp.hole(kids_of_unsorted(unsorted)); +and typ_term: unsorted => (Typ.term, list(Id.t)) = { + let ret = (term: Typ.term) => (term, []); + let hole = unsorted => Typ.hole(kids_of_unsorted(unsorted)); fun | Op(tiles) as tm => switch (tiles) { diff --git a/src/haz3lcore/statics/Statics.re b/src/haz3lcore/statics/Statics.re index c6f39cef1d..84fc30b6ce 100644 --- a/src/haz3lcore/statics/Statics.re +++ b/src/haz3lcore/statics/Statics.re @@ -214,7 +214,7 @@ and uexp_to_info_map = ~mode=Mode.Syn, ~is_in_filter=false, ~ancestors, - {ids, copied: _, term} as uexp: UExp.t, + {ids, copied: _, term} as uexp: Exp.t, m: Map.t, ) : (Info.exp, Map.t) => { @@ -230,14 +230,14 @@ and uexp_to_info_map = (info, add_info(ids, InfoExp(info), m)); }; let add = (~self, ~co_ctx, m) => add'(~self=Common(self), ~co_ctx, m); - let ancestors = [UExp.rep_id(uexp)] @ ancestors; + let ancestors = [Exp.rep_id(uexp)] @ ancestors; let uexp_to_info_map = ( ~ctx, ~mode=Mode.Syn, ~is_in_filter=is_in_filter, ~ancestors=ancestors, - uexp: UExp.t, + uexp: Exp.t, m: Map.t, ) => { uexp_to_info_map(~ctx, ~mode, ~is_in_filter, ~ancestors, uexp, m); @@ -276,7 +276,7 @@ and uexp_to_info_map = | Float(_) => atomic(Just(Float |> Typ.temp)) | String(_) => atomic(Just(String |> Typ.temp)) | ListLit(es) => - let ids = List.map(UExp.rep_id, es); + let ids = List.map(Exp.rep_id, es); let modes = Mode.of_list_lit(ctx, List.length(es), mode); let (es, m) = map_m_go(m, modes, es); let tys = List.map(Info.exp_ty, es); @@ -296,7 +296,7 @@ and uexp_to_info_map = ); | ListConcat(e1, e2) => let mode = Mode.of_list_concat(ctx, mode); - let ids = List.map(UExp.rep_id, [e1, e2]); + let ids = List.map(Exp.rep_id, [e1, e2]); let (e1, m) = go(~mode, e1, m); let (e2, m) = go(~mode, e2, m); add( @@ -307,7 +307,7 @@ and uexp_to_info_map = | Var(name) => add'( ~self=Self.of_exp_var(ctx, name), - ~co_ctx=CoCtx.singleton(name, UExp.rep_id(uexp), Mode.ty_of(mode)), + ~co_ctx=CoCtx.singleton(name, Exp.rep_id(uexp), Mode.ty_of(mode)), m, ) | DynamicErrorHole(e, _) @@ -320,7 +320,7 @@ and uexp_to_info_map = * in order to associate it through dynamics */ go(~mode, e, m) | UnOp(Meta(Unquote), e) when is_in_filter => - let e: UExp.t = { + let e: Exp.t = { ids: e.ids, copied: false, term: @@ -377,7 +377,7 @@ and uexp_to_info_map = add(~self=Just(e2.ty), ~co_ctx=CoCtx.union([e1.co_ctx, e2.co_ctx]), m); | Constructor(ctr, _) => atomic(Self.of_ctr(ctx, ctr)) | Ap(_, fn, arg) => - let fn_mode = Mode.of_ap(ctx, mode, UExp.ctr_name(fn)); + let fn_mode = Mode.of_ap(ctx, mode, Exp.ctr_name(fn)); let (fn, m) = go(~mode=fn_mode, fn, m); let (ty_in, ty_out) = Typ.matched_arrow(ctx, fn.ty); let (arg, m) = go(~mode=Ana(ty_in), arg, m); @@ -397,7 +397,7 @@ and uexp_to_info_map = | None => add(~self=Just(ty_body), ~co_ctx=fn.co_ctx, m) /* invalid name matches with no free type variables. */ }; | DeferredAp(fn, args) => - let fn_mode = Mode.of_ap(ctx, mode, UExp.ctr_name(fn)); + let fn_mode = Mode.of_ap(ctx, mode, Exp.ctr_name(fn)); let (fn, m) = go(~mode=fn_mode, fn, m); let (ty_in, ty_out) = Typ.matched_arrow(ctx, fn.ty); let num_args = List.length(args); @@ -525,7 +525,7 @@ and uexp_to_info_map = m, ); | If(e0, e1, e2) => - let branch_ids = List.map(UExp.rep_id, [e1, e2]); + let branch_ids = List.map(Exp.rep_id, [e1, e2]); let (cond, m) = go(~mode=Ana(Bool |> Typ.temp), e0, m); let (cons, m) = go(~mode, e1, m); let (alt, m) = go(~mode, e2, m); @@ -537,7 +537,7 @@ and uexp_to_info_map = | Match(scrut, rules) => let (scrut, m) = go(~mode=Syn, scrut, m); let (ps, es) = List.split(rules); - let branch_ids = List.map(UExp.rep_id, es); + let branch_ids = List.map(Exp.rep_id, es); let (ps', _) = map_m( go_pat( @@ -574,7 +574,7 @@ and uexp_to_info_map = let (self, m) = switch (constraint_ty) { | Some(constraint_ty) => - let pats_to_info_map = (ps: list(UPat.t), m) => { + let pats_to_info_map = (ps: list(Pat.t), m) => { /* Add co-ctxs to patterns */ List.fold_left( ((m, acc_constraint), (p, co_ctx)) => { @@ -651,7 +651,7 @@ and uexp_to_info_map = switch (typat.term) { | Var(name) when !Ctx.shadows_typ(ctx, name) => /* Currently we disallow all type shadowing */ - /* NOTE(andrew): Currently, UTyp.to_typ returns Unknown(TypeHole) + /* NOTE(andrew): Currently, Typ.to_typ returns Unknown(TypeHole) for any type variable reference not in its ctx. So any free variables in the definition would be obliterated. But we need to check for free variables to decide whether to make a recursive type or not. So we @@ -683,14 +683,14 @@ and uexp_to_info_map = // (ty_rec, ctx_def, ctx_def); // } // : { - // let ty = Term.UTyp.to_typ(ctx, utyp); + // let ty = Term.Typ.to_typ(ctx, utyp); // (ty, ctx, Ctx.add_alias(ctx, name, utpat_id(typat), ty)); // }; }; }; let ctx_body = switch (Typ.get_sum_constructors(ctx, ty_def)) { - | Some(sm) => Ctx.add_ctrs(ctx_body, name, UTyp.rep_id(utyp), sm) + | Some(sm) => Ctx.add_ctrs(ctx_body, name, Typ.rep_id(utyp), sm) | None => ctx_body }; let ({co_ctx, ty: ty_body, _}: Info.exp, m) = @@ -717,7 +717,7 @@ and upat_to_info_map = ~co_ctx, ~ancestors: Info.ancestors, ~mode: Mode.t=Mode.Syn, - {ids, term, _} as upat: UPat.t, + {ids, term, _} as upat: Pat.t, m: Map.t, ) : (Info.pat, Map.t) => { @@ -743,7 +743,7 @@ and upat_to_info_map = (info, add_info(ids, InfoPat(info), m)); }; let atomic = (self, constraint_) => add(~self, ~ctx, ~constraint_, m); - let ancestors = [UPat.rep_id(upat)] @ ancestors; + let ancestors = [Pat.rep_id(upat)] @ ancestors; let go = upat_to_info_map(~is_synswitch, ~ancestors, ~co_ctx); let unknown = Unknown(is_synswitch ? SynSwitch : Internal) |> Typ.temp; let ctx_fold = (ctx: Ctx.t, m) => @@ -781,7 +781,7 @@ and upat_to_info_map = | String(string) => atomic(Just(String |> Typ.temp), Constraint.String(string)) | ListLit(ps) => - let ids = List.map(UPat.rep_id, ps); + let ids = List.map(Pat.rep_id, ps); let modes = Mode.of_list_lit(ctx, List.length(ps), mode); let (ctx, tys, cons, m) = ctx_fold(ctx, m, ps, modes); let rec cons_fold_list = cs => @@ -818,7 +818,7 @@ and upat_to_info_map = mode, Common(Just(Unknown(Internal) |> Typ.temp)), ); - let entry = Ctx.VarEntry({name, id: UPat.rep_id(upat), typ: ctx_typ}); + let entry = Ctx.VarEntry({name, id: Pat.rep_id(upat), typ: ctx_typ}); add( ~self=Just(unknown), ~ctx=Ctx.extend(ctx, entry), @@ -852,7 +852,7 @@ and upat_to_info_map = let self = Self.of_ctr(ctx, ctr); atomic(self, Constraint.of_ctr(ctx, mode, ctr, self)); | Ap(fn, arg) => - let ctr = UPat.ctr_name(fn); + let ctr = Pat.ctr_name(fn); let fn_mode = Mode.of_ap(ctx, mode, ctr); let (fn, m) = go(~ctx, ~mode=fn_mode, fn, m); let (ty_in, ty_out) = Typ.matched_arrow(ctx, fn.ty); @@ -875,7 +875,7 @@ and utyp_to_info_map = ~ctx, ~expects=Info.TypeExpected, ~ancestors, - {ids, term, _} as utyp: UTyp.t, + {ids, term, _} as utyp: Typ.t, m: Map.t, ) : (Info.typ, Map.t) => { @@ -883,7 +883,7 @@ and utyp_to_info_map = let info = Info.derived_typ(~utyp, ~ctx, ~ancestors, ~expects); (info, add_info(ids, InfoTyp(info), m)); }; - let ancestors = [UTyp.rep_id(utyp)] @ ancestors; + let ancestors = [Typ.rep_id(utyp)] @ ancestors; let go' = utyp_to_info_map(~ctx, ~ancestors); let go = go'(~expects=TypeExpected); switch (term) { @@ -994,7 +994,7 @@ and variant_to_info_map = ~ancestors, ~ty_sum, (m, ctrs), - uty: ConstructorMap.variant(UTyp.t), + uty: ConstructorMap.variant(Typ.t), ) => { let go = expects => utyp_to_info_map(~ctx, ~ancestors, ~expects); switch (uty) { diff --git a/src/haz3lcore/statics/Term.re b/src/haz3lcore/statics/Term.re index 3a7a76e26f..7c4513b99c 100644 --- a/src/haz3lcore/statics/Term.re +++ b/src/haz3lcore/statics/Term.re @@ -100,7 +100,7 @@ module Pat = { switch (pat.term) { | Wrap(pat, _) => is_fun_var(pat) | Cast(pat, typ, _) => - is_var(pat) && (UTyp.is_arrow(typ) || Typ.is_forall(typ)) + is_var(pat) && (Typ.is_arrow(typ) || Typ.is_forall(typ)) | Invalid(_) | EmptyHole | MultiHole(_) @@ -189,7 +189,7 @@ module Pat = { switch (pat.term) { | Wrap(pat, _) => get_fun_var(pat) | Cast(pat, t1, _) => - if (Typ.is_arrow(t1) || UTyp.is_forall(t1)) { + if (Typ.is_arrow(t1) || Typ.is_forall(t1)) { get_var(pat) |> Option.map(var => var); } else { None; @@ -803,7 +803,7 @@ module Rul = { let rep_id = (~any_ids, tm) => switch (ids(~any_ids, tm)) { - | [] => raise(Invalid_argument("UExp.rep_id")) + | [] => raise(Invalid_argument("Exp.rep_id")) | [id, ..._] => id }; }; diff --git a/src/haz3lcore/statics/TermBase.re b/src/haz3lcore/statics/TermBase.re index 9f4195520b..8754b7e888 100644 --- a/src/haz3lcore/statics/TermBase.re +++ b/src/haz3lcore/statics/TermBase.re @@ -985,13 +985,14 @@ and ClosureEnvironment: { let without_keys = keys => update_env(Environment.without_keys(keys)); - let update_stack = (ap_id: option(Id.t), env) => { + let update_stack = (dyn_env, ap_id: option(Id.t), env) => { let (stack, dyn_stack) = switch (ap_id) { | None => (stack_of(env), dyn_stack_of(env)) | Some(ap_id) => let frame = Probe.mk_frame(~env_id=id_of(env), ~ap_id); - ([frame, ...stack_of(env)], [frame, ...dyn_stack_of(env)]); + let dyn_frame = Probe.mk_frame(~env_id=id_of(dyn_env), ~ap_id); + ([frame, ...stack_of(env)], [dyn_frame, ...dyn_stack_of(env)]); }; {...env, stack, dyn_stack}; }; @@ -1016,7 +1017,7 @@ and ClosureEnvironment: { dyn_stack: dyn_stack_of(dyn_env.env == Environment.empty ? to_extend : dyn_env) //TODO(andrew): cleanup } - |> update_stack(frame); + |> update_stack(dyn_env, frame); } and StepperFilterKind: { [@deriving (show({with_path: false}), sexp, yojson)] diff --git a/src/haz3lcore/statics/uterm/UExp.re b/src/haz3lcore/statics/uterm/UExp.re deleted file mode 100644 index 16d6db0412..0000000000 --- a/src/haz3lcore/statics/uterm/UExp.re +++ /dev/null @@ -1 +0,0 @@ -include Exp; diff --git a/src/haz3lcore/statics/uterm/UPat.re b/src/haz3lcore/statics/uterm/UPat.re deleted file mode 100644 index 9bd15c6ba8..0000000000 --- a/src/haz3lcore/statics/uterm/UPat.re +++ /dev/null @@ -1 +0,0 @@ -include Pat; diff --git a/src/haz3lcore/statics/uterm/UTyp.re b/src/haz3lcore/statics/uterm/UTyp.re deleted file mode 100644 index 7dcfba5350..0000000000 --- a/src/haz3lcore/statics/uterm/UTyp.re +++ /dev/null @@ -1 +0,0 @@ -include Typ; diff --git a/src/haz3lcore/zipper/Editor.re b/src/haz3lcore/zipper/Editor.re index 13bd37f838..906c27c796 100644 --- a/src/haz3lcore/zipper/Editor.re +++ b/src/haz3lcore/zipper/Editor.re @@ -8,7 +8,7 @@ module CachedSyntax = { tiles: TileMap.t, holes: list(Grout.t), selection_ids: list(Id.t), - term: UExp.t, + term: Exp.t, /* This term, and the term-derived data structured below, may differ * from the term used for semantics. These terms are identical when * the backpack is empty. If the backpack is non-empty, then when we diff --git a/src/haz3lcore/zipper/EditorUtil.re b/src/haz3lcore/zipper/EditorUtil.re index f5318016d2..ea9b5a549b 100644 --- a/src/haz3lcore/zipper/EditorUtil.re +++ b/src/haz3lcore/zipper/EditorUtil.re @@ -47,7 +47,7 @@ let rec append_exp = (e1: Exp.t, e2: Exp.t): Exp.t => { }; }; -let wrap_filter = (act: FilterAction.action, term: UExp.t): UExp.t => { +let wrap_filter = (act: FilterAction.action, term: Exp.t): Exp.t => { term: Filter( Filter({ diff --git a/src/haz3lcore/zipper/Projector.re b/src/haz3lcore/zipper/Projector.re index d6e96bd772..c8bf26dc57 100644 --- a/src/haz3lcore/zipper/Projector.re +++ b/src/haz3lcore/zipper/Projector.re @@ -20,7 +20,7 @@ let to_module = (kind: Base.kind): (module Cooked) => let minimum_projection_condition = (syntax: syntax): bool => Piece.is_convex(syntax); -let init = (kind: t, syntax: syntax): syntax => { +let init = (kind: Base.kind, syntax: syntax): syntax => { /* We set the projector id equal to the Piece id for convienence * including cursor-info association. We maintain this invariant * when we update a projector's contained syntax */ @@ -31,7 +31,8 @@ let init = (kind: t, syntax: syntax): syntax => { }; }; -let init_from_str = (kind: t, syntax: syntax, model_str: string): syntax => { +let init_from_str = + (kind: Base.kind, syntax: syntax, model_str: string): syntax => { let (module P) = to_module(kind); switch (P.can_project(syntax) && minimum_projection_condition(syntax)) { | false => syntax diff --git a/src/haz3lcore/zipper/ProjectorBase.re b/src/haz3lcore/zipper/ProjectorBase.re index a6619b613b..d15dbb40d6 100644 --- a/src/haz3lcore/zipper/ProjectorBase.re +++ b/src/haz3lcore/zipper/ProjectorBase.re @@ -1,8 +1,14 @@ open Util; open Virtual_dom.Vdom; -[@deriving (show({with_path: false}), sexp, yojson)] -type t = Base.kind; +/* This determines the API for projectors, GUIs which + * can replace part of the program syntax, and perform + * actions which changes that underlying syntax, as well + * as mainting their own custom state. The comments below + * detail the procedure of defining a new projector. + * + * See the zipper/projectors/ folder for the implementations + * of currently available projectors */ /* The type of syntax which a projector can replace. * Right now projectors can replace a single piece */ @@ -15,37 +21,46 @@ type external_action = | Escape(Util.Direction.t) /* Pass focus to parent editor */ | SetSyntax(syntax); /* Set underlying syntax */ -/* External info fed to all projectors. Eventually - * dynamic information will be added here. Projector - * position and dimensions in base editor could be - * added here if needed */ +/* External info proivded to all projectors */ [@deriving (show({with_path: false}), sexp, yojson)] type info = { + /* The id of the projector, equal to the id of the root + * term of the syntax, provided directly here for convenience. + * This is mostly intended to be used as a persistent unique + * identifier to allow individual projectors to distiguish + * their DOM nodes. */ id: Id.t, + /* The syntax underlying the projector. Currently this + * is a single piece representing a complete term, but + * this may be relaxed in the future. */ syntax, + /* Static information about the syntax including type + * information. Statics may be disabled by the user; + * this case (None) must be handled by projector authors */ statics: option(Statics.Info.t), + /* Dynamic information about the syntax including + * live values of the syntax. Dynamics may be + * disabled by the user; this case (None) must be + * handled by projector authors */ dynamics: option(Dynamics.Info.t), }; /* Utility functions/values for to projector views. - * These should be considered unstable/experimental - * features which have yet to be integrated into the - * projector API in a disciplined way */ + * These should be considered unstable/experimental */ [@deriving (show({with_path: false}), sexp, yojson)] type utility = { /* The current font metrics for the editor, usable * to coordinate with the parent coordinate grid */ font_metrics: FontMetrics.t, - /* X position in pixels of the end of the row where - * where the projector starts; usable to position part - * of the projector UI at the end of the row */ - offside_offset: float, /* Non-interactive view for segments, included here * because of cyclic dependency issues*/ - view: (Sort.t, Base.segment) => Node.t, + view_seg: (Sort.t, Base.segment) => Node.t, /* Convert an expression to a segment, included here * because of cyclic dependency issues*/ exp_to_seg: Exp.t => Base.segment, + /* Convert a segment to an expression, included here + * because of cyclic dependency issues*/ + seg_to_exp: Base.segment => Exp.t, }; /* To add a new projector: @@ -86,24 +101,59 @@ module type Projector = { * is pressed when the caret is to the immediate * right/left of the projector */ let can_focus: bool; + /* If dynamics is true, this projector will be + * instrumented with a probe to collect dynamic + * information during evaluation */ + let dynamics: bool; /* Renders a DOM view for the projector, given the * model, an info packet (see info type for details), * and has two callbacks: ~parent for parent editor * actions(see external_action type above), and ~local * for this projector's local update function. */ - let dynamics: bool; - /* If dynamics is true, this projector will be - * instrumented with a probe to collect dynamic - * information during evaluation */ let view: ( model, - ~info: info, + info, ~local: action => Ui_effect.t(unit), ~parent: external_action => Ui_effect.t(unit), ~utility: utility ) => Node.t; + /* An optional additional view to be rendered at the + * end of the row which includes the projector */ + let offside_view: + option( + ( + model, + info, + ~local: action => Ui_effect.t(unit), + ~parent: external_action => Ui_effect.t(unit), + ~utility: utility + ) => + Node.t, + ); + /* An optional view to be rendered above + * the code / regular projector layer */ + let overlay_view: + option( + ( + model, + info, + ~local: action => Ui_effect.t(unit), + ~parent: external_action => Ui_effect.t(unit), + ~utility: utility + ) => + Node.t, + ); + /* An optional view to be rendered below the code and + * regular projector layer. If this is provided, + * regular underlays like indication and selection + * decorations will not be drawn; projector clients + * should use the classes placed on the wrapping + * element to trigger their own custom indication and + * selection decorations. Pointer handlers should not + * be placed on this layer. */ + let underlay_view: option((model, info, ~utility: utility) => Node.t); /* How much space should be left in the code view for * this projector? This determines how the base code * view is laid out, including how movement around the @@ -113,7 +163,7 @@ module type Projector = { * in sync with each other. */ let placeholder: (model, info) => ProjectorShape.t; /* Update the local projector model given an action */ - let update: (model, action) => model; + let update: (model, info, action) => model; /* Does whatever needs to be done to give a projector * keyboard focus. Right now this is only for side * effects but could be extended in the future to @@ -142,25 +192,54 @@ module Cook = (C: Projector) : Cooked => { let can_project = C.can_project; let can_focus = C.can_focus; let dynamics = C.dynamics; - let view = (m, ~info, ~local, ~parent, ~utility) => + let view = (m, info, ~local, ~parent, ~utility) => C.view( deserialize_m(m), - ~info, + info, ~local=a => local(serialize_a(a)), ~parent, ~utility, ); + let offside_view = + Option.map( + (f, m, info, ~local, ~parent, ~utility) => + f( + deserialize_m(m), + info, + ~local=a => local(serialize_a(a)), + ~parent, + ~utility, + ), + C.offside_view, + ); + let overlay_view = + Option.map( + (f, m, info, ~local, ~parent, ~utility) => + f( + deserialize_m(m), + info, + ~local=a => local(serialize_a(a)), + ~parent, + ~utility, + ), + C.overlay_view, + ); + let underlay_view = + Option.map( + (f, m, info, ~utility) => f(deserialize_m(m), info, ~utility), + C.underlay_view, + ); let placeholder = m => m |> Sexplib.Sexp.of_string |> C.model_of_sexp |> C.placeholder; - let update = (m, a) => - C.update(m |> deserialize_m, a |> deserialize_a) |> serialize_m; + let update = (m, i, a) => + C.update(m |> deserialize_m, i, a |> deserialize_a) |> serialize_m; let focus = C.focus; }; /* Projectors currently are all convex */ let shapes = (_: Base.projector) => Nib.Shape.(Convex, Convex); -/* Projectors currently have a unique molding */ +/* Projectors currently have a fixed molding */ let mold_of = (p, sort: Sort.t): Mold.t => { let (l, r) = shapes(p); { diff --git a/src/haz3lcore/zipper/projectors/CheckboxProj.re b/src/haz3lcore/zipper/projectors/CheckboxProj.re index 3a0f39ab03..68745febbd 100644 --- a/src/haz3lcore/zipper/projectors/CheckboxProj.re +++ b/src/haz3lcore/zipper/projectors/CheckboxProj.re @@ -27,7 +27,7 @@ let toggle = (piece: Piece.t) => put(!get(piece)); let view = ( _, - ~info, + info, ~local as _, ~parent: external_action => Ui_effect.t(unit), ~utility as _, @@ -54,7 +54,10 @@ module M: Projector = { let can_focus = false; let dynamics = false; let placeholder = (_, _) => ProjectorShape.inline(2); - let update = (model, _) => model; + let update = (model, _, _) => model; let view = view; + let offside_view = Option.None; + let overlay_view = Option.None; + let underlay_view = Option.None; let focus = _ => (); }; diff --git a/src/haz3lcore/zipper/projectors/FoldProj.re b/src/haz3lcore/zipper/projectors/FoldProj.re index 5942d6653c..9593719707 100644 --- a/src/haz3lcore/zipper/projectors/FoldProj.re +++ b/src/haz3lcore/zipper/projectors/FoldProj.re @@ -20,11 +20,14 @@ module M: Projector = { let dynamics = false; let placeholder = (m, _) => ProjectorShape.inline(m.text == "⋱" ? 2 : m.text |> String.length); - let update = (m, _) => m; - let view = (m: model, ~info as _, ~local as _, ~parent, ~utility as _) => + let update = (m, _, _) => m; + let view = (m: model, _, ~local as _, ~parent, ~utility as _) => div( ~attrs=[Attr.on_double_click(_ => parent(Remove))], [text(m.text)], ); + let offside_view = Option.None; + let overlay_view = Option.None; + let underlay_view = Option.None; let focus = _ => (); }; diff --git a/src/haz3lcore/zipper/projectors/InfoProj.re b/src/haz3lcore/zipper/projectors/InfoProj.re index 9d65060355..de69e7c5bd 100644 --- a/src/haz3lcore/zipper/projectors/InfoProj.re +++ b/src/haz3lcore/zipper/projectors/InfoProj.re @@ -73,13 +73,13 @@ module M: Projector = { (display(model, info.statics) |> String.length) + 5, ); - let update = (model, a: action) => + let update = (model, _, a: action) => switch (a, model) { | (ToggleDisplay, Expected) => Self | (ToggleDisplay, Self) => Expected }; - let view = (model, ~info, ~local, ~parent as _, ~utility as _) => + let view = (model, info, ~local, ~parent as _, ~utility as _) => div( ~attrs=[ Attr.classes(["info", "code"]), @@ -93,5 +93,8 @@ module M: Projector = { ), ], ); + let offside_view = Option.None; + let overlay_view = Option.None; + let underlay_view = Option.None; let focus = _ => (); }; diff --git a/src/haz3lcore/zipper/projectors/ProbeProj.re b/src/haz3lcore/zipper/projectors/ProbeProj.re index c12ddf8acd..3cd060da15 100644 --- a/src/haz3lcore/zipper/projectors/ProbeProj.re +++ b/src/haz3lcore/zipper/projectors/ProbeProj.re @@ -4,6 +4,9 @@ open Virtual_dom.Vdom; open Node; open Js_of_ocaml; +[@deriving (show({with_path: false}), sexp, yojson)] +type closure = Dynamics.Probe.Closure.t; + [@deriving (show({with_path: false}), sexp, yojson)] type model = { /* Max col length for value display, indexed by closure id */ @@ -16,9 +19,10 @@ type model = { [@deriving (show({with_path: false}), sexp, yojson)] type action = + | PinAp(Id.t) | ChangeLength(Id.t, int) | Offset(int) - | ToggleShowAllVals; + | ToggleShowAllVals(int); let init = {display_lengths: Id.Map.empty, max_closures: 30, index_offset: 0}; @@ -28,50 +32,204 @@ let model_of_sexp = (sexp): model => | x => x }; -let display_length = (model: model, id: Id.t): int => - Id.Map.find_opt(id, model.display_lengths) |> Option.value(~default=12); +/* Remove opaque values like function literals */ +let rm_opaques: + list(Dynamics.Probe.Env.entry) => list(Dynamics.Probe.Env.entry) = + List.filter_map((en: Dynamics.Probe.Env.entry) => + switch (en.value) { + | Opaque => None + | Val(_) => Some(en) + } + ); -let stack = stack => - stack - |> List.rev - |> List.map(({env_id, ap_id}: Probe.frame) => - "" - ++ String.sub(Id.to_string(env_id), 0, 2) - ++ " : " - ++ String.sub(Id.to_string(ap_id), 0, 2) - ) - |> String.concat("\n"); - -let env_cursor: ref(list(Id.t)) = ref([]); -let last_target: ref(list('a)) = ref([]); -let cur_ap: ref(option(Id.t)) = ref(Option.None); -let cur_ap_depth: ref(option(int)) = ref(Option.None); -let mousedown: ref(option(Js.t(Dom_html.element))) = ref(Option.None); +/* Is the underlying syntax a variable reference? */ +let is_var_ref = (info: info): bool => + switch (info.statics) { + | Some(InfoExp({term: {term: Var(_), _}, _})) + | Some(InfoPat({term: {term: Var(_), _}, _})) => true + | _ => false + }; -let comparor = (a: Dynamics.Probe.Closure.t, b: Dynamics.Probe.Closure.t) => { - compare( - ListUtil.common_suffix_length(env_cursor^, Probe.env_stack(b.stack)), - ListUtil.common_suffix_length(env_cursor^, Probe.env_stack(a.stack)), - ); +let cur_ap_id = (info: info): option(Id.t) => + switch (info.statics) { + | Some(InfoExp({term: {term: Ap(_), _} as ap, _})) => + Some(Term.Exp.rep_id(ap)) + | Some(InfoExp({term: {term: Wrap({term: Ap(_), _} as ap, _), _}, _})) => + Some(Term.Exp.rep_id(ap)) + | _ => None + }; + +let cur_outer_ap_id = (_info: info, dyn_stack: Probe.stack): option(Id.t) => + switch (dyn_stack) { + | [frame, ..._] => Some(frame.ap_id) + | _ => None + }; + +module State = { + /* Manages shared state between probes */ + + type t = { + mutable pinned_ap: option(Id.t), + mutable env_cursor: list(Id.t), + mutable dyn_env_cursor: list(Probe.frame), + mutable cur_ap: option(Id.t), + mutable outer_ap_id: option(Id.t), + }; + + let s: t = { + pinned_ap: None, + env_cursor: [], + dyn_env_cursor: [], + cur_ap: None, + outer_ap_id: None, + }; + + let reset = () => { + s.pinned_ap = None; + s.env_cursor = []; + s.dyn_env_cursor = []; + s.cur_ap = None; + s.outer_ap_id = None; + }; + + let capture = (info: info, closure: closure) => { + s.env_cursor = Probe.env_stack(closure.stack); + s.dyn_env_cursor = closure.dyn_stack; + s.cur_ap = cur_ap_id(info); + s.outer_ap_id = cur_outer_ap_id(info, closure.dyn_stack); + }; }; -let show_indicator = stack => { - let local = Probe.env_stack(stack); - env_cursor^ == [] - && local == [] - || env_cursor^ != [] - && ListUtil.one_is_suffix_of_other(env_cursor^, local); +module Closures = { + let num = (info: info): int => + switch (info.dynamics) { + | Some(di) => List.length(di) + | None => 0 + }; + + let filter_frames_by_pin = + (info: info, frames: list(closure)): list(closure) => + //TODO(andrew): make this logic work more generally... + switch (State.s.pinned_ap) { + | Some(pinned_ap) => + frames + |> List.filter((closure: closure) => + switch (closure.dyn_stack |> List.rev) { + | _ when Some(pinned_ap) == cur_ap_id(info) => true + | [frame, ..._] => frame.ap_id == pinned_ap + | [] => false + } + ) + | None => frames + }; + + let comparor = (a: closure, b: closure): int => { + compare( + ListUtil.common_suffix_length( + State.s.env_cursor, + Probe.env_stack(b.stack), + ), + ListUtil.common_suffix_length( + State.s.env_cursor, + Probe.env_stack(a.stack), + ), + ); + }; + + let select_frames = + (info: info, model: model, closures: list(closure)): list(closure) => { + switch (List.sort(comparor, closures)) { + | [] => [] + | _ => + closures + |> filter_frames_by_pin(info) + |> ListUtil.slice(model.index_offset, model.max_closures) + }; + }; + + let group_by_predicate = + /* Precondition: Items to be grouped are contigious in list */ + (should_group: ('a, 'a) => bool, xs: list('a)): list(list('a)) => { + List.fold_left( + (acc: list(list('a)), item: 'a) => { + switch (acc) { + | [] => [[item]] + | [[rep, ..._] as first, ...init] when should_group(rep, item) => [ + first @ [item], + ...init, + ] + | _ => [[item]] @ acc + } + }, + [], + xs, + ); + }; + + let is_same_call = ((_, c1: closure), (_, c2: closure)): bool => { + switch (List.rev(c2.dyn_stack), List.rev(c1.dyn_stack)) { + | ([], _) + | (_, []) => false + | ([f1, ..._], [f2, ..._]) => f1 == f2 + }; + }; + + let group = + (closures: list((int, closure))): list(list((int, closure))) => { + let grouped = + closures |> group_by_predicate(is_same_call) |> List.map(List.rev); + /* Flatten if all groups are singletons */ + List.for_all(group => List.length(group) == 1, grouped) + ? [List.concat(grouped)] : grouped; + }; + + let collate = + (info: info, model: model, di: list(closure)) + : (int, list(list((int, closure)))) => { + let closures = select_frames(info, model, di); + let numbered_closures = + List.mapi((i, c) => (List.length(closures) - i - 1, c), closures); + (List.length(closures), group(numbered_closures)); + }; +}; + +module Debug = { + let of_id = (id: Id.t): string => String.sub(Id.to_string(id), 0, 3); + + let stack = (stack: Probe.stack): string => + stack + |> List.rev + |> List.map(({env_id, ap_id}: Probe.frame) => + "" ++ of_id(env_id) ++ " : " ++ of_id(ap_id) + ) + |> String.concat("\n"); + + let str = (closure: closure): string => + "closure_id: " + ++ of_id(closure.closure_id) + ++ "\nenv_id: " + ++ of_id(closure.env_id) + ++ "\ndyn_stack:\n" + ++ stack(closure.dyn_stack) + ++ "\nstack:\n" + ++ stack(closure.stack); }; -let seg_view = (utility, available, seg) => +let depth_in_cur_ap_stack = (dyn_stack: list(Probe.frame)): option(int) => + List.find_index( + ({ap_id, _}: Probe.frame) => Some(ap_id) == State.s.cur_ap, + dyn_stack, + ); + +let seg_view = (utility: utility, available: int, seg: Exp.t): Node.t => seg |> DHExp.strip_casts |> Abbreviate.abbreviate_exp(~available) |> fst |> utility.exp_to_seg - |> utility.view(Exp); + |> utility.view_seg(Exp); -let get_goal = (utility: utility, e: Js.t(Dom_html.mouseEvent)) => +let get_goal = (utility: utility, e: Js.t(Dom_html.mouseEvent)): Point.t => FontMetrics.get_goal( ~font_metrics=utility.font_metrics, e##.currentTarget @@ -81,31 +239,60 @@ let get_goal = (utility: utility, e: Js.t(Dom_html.mouseEvent)) => e |> Js.Unsafe.coerce, ); -let cur_ap_id = (info: info) => - switch (info.statics) { - | Some(InfoExp({term: {term: Ap(_), _} as ap, _})) => - Some(Term.Exp.rep_id(ap)) - | Some(InfoExp({term: {term: Wrap({term: Ap(_), _} as ap, _), _}, _})) => - Some(Term.Exp.rep_id(ap)) - | _ => None +let on_outer_ap = (info: info, closure: closure): bool => + switch (cur_ap_id(info), State.s.outer_ap_id) { + | (Some(ap_id), Some(outer_ap_id)) => + ap_id == outer_ap_id + && closure.env_id + == Option.value( + ~default={ap_id: Id.invalid, env_id: Id.invalid}, + ListUtil.hd_opt(State.s.dyn_env_cursor), + ). + env_id + | _ => false }; -let depth_in_stack = (dyn_stack): option(int) => - List.find_index( - ({ap_id, _}: Probe.frame) => Some(ap_id) == cur_ap^, - dyn_stack, +let show_indicator = (stack: Probe.stack): bool => { + let local = Probe.env_stack(stack); + State.s.env_cursor == [] + && local == [] + || State.s.env_cursor != [] + && ( + ListUtil.is_suffix_of(local, State.s.env_cursor) + || ListUtil.is_suffix_of(State.s.env_cursor, local) ); +}; + +let dynamic_cursor_cls = (info: info, closure: closure): list(string) => + switch (depth_in_cur_ap_stack(closure.dyn_stack)) { + | _ when on_outer_ap(info, closure) => ["cursor-outer-ap"] + | Some(depth) + when ListUtil.is_suffix_of(State.s.dyn_env_cursor, closure.dyn_stack) => + ["cursor-ap-lex"] @ (depth == 0 ? [] : ["light"]) + | Some(depth) => ["cursor-ap"] @ (depth == 0 ? [] : ["light"]) + | _ when show_indicator(closure.stack) => ["cursor-lex"] + | None => ["cursor-none"] + }; + +let display_length = (model: model, id: Id.t): int => + Id.Map.find_opt(id, model.display_lengths) |> Option.value(~default=12); + +let mousedown: ref(option(Js.t(Dom_html.element))) = ref(Option.None); let value_view = - (info: info, model, utility, local, closure: Dynamics.Probe.Closure.t) => { + ( + info: info, + model: model, + utility: utility, + local, + closure: closure, + index: int, + ) => { let val_pointerdown = (e: Js.t(Dom_html.pointerEvent)) => { let target = e##.target |> Js.Opt.get(_, _ => failwith("no target")); JsUtil.setPointerCapture(target, e##.pointerId) |> ignore; mousedown := Some(target); - env_cursor := Probe.env_stack(closure.stack); - last_target := [target]; - cur_ap := cur_ap_id(info); - cur_ap_depth := Some(List.length(closure.dyn_stack)); + State.capture(info, closure); Effect.Ignore; }; @@ -131,31 +318,13 @@ let value_view = div( ~attrs=[ - Attr.title( - "dyn_stack:\n" - ++ stack(closure.dyn_stack) - ++ "\nstack:\n" - ++ stack(closure.stack), - ), + Attr.title(Debug.str(closure)), Attr.classes( ["val-resize"] - @ ( - switch (depth_in_stack(closure.dyn_stack)) { - | Some(0) => ["dyn-cursor"] - | Some(_) => ["dyn-cursor", "light"] - | None => [] - } - ) - @ (Option.is_some(cur_ap_id(info)) ? ["ap"] : []) - @ ( - switch (cur_ap_depth^) { - | Some(0) => ["top-ap"] - | _ => [] - } - ) - @ (show_indicator(closure.stack) ? ["cursor"] : []), + @ dynamic_cursor_cls(info, closure) + @ (Option.is_some(cur_ap_id(info)) ? ["ap"] : []), ), - Attr.on_double_click(_ => local(ToggleShowAllVals)), + Attr.on_double_click(_ => local(ToggleShowAllVals(index))), Attr.on_pointerdown(val_pointerdown), Attr.on_pointerup(val_pointerup), Attr.on_mousemove(val_mousemove), @@ -170,7 +339,7 @@ let value_view = ); }; -let env_val = (utility: utility, en: Dynamics.Probe.Env.entry) => { +let env_val = (utility: utility, en: Dynamics.Probe.Env.entry): Node.t => { Node.div( ~attrs=[Attr.classes(["live-env-entry"])], [ @@ -183,25 +352,7 @@ let env_val = (utility: utility, en: Dynamics.Probe.Env.entry) => { ); }; -/* Remove opaque values like function literals */ -let rm_opaques: - list(Dynamics.Probe.Env.entry) => list(Dynamics.Probe.Env.entry) = - List.filter_map((en: Dynamics.Probe.Env.entry) => - switch (en.value) { - | Opaque => None - | Val(_) => Some(en) - } - ); - -/* Is the underlying syntax a variable reference? */ -let is_var_ref = (info: info): bool => - switch (info.statics) { - | Some(InfoExp({term: {term: Var(_), _}, _})) - | Some(InfoPat({term: {term: Var(_), _}, _})) => true - | _ => false - }; - -let env_view = (closure: Dynamics.Probe.Closure.t, utility: utility): Node.t => +let env_view = (closure: closure, utility: utility): Node.t => Node.div( ~attrs=[Attr.classes(["live-env"])], closure.env |> ListUtil.dedup |> rm_opaques |> List.map(env_val(utility)), @@ -209,11 +360,11 @@ let env_view = (closure: Dynamics.Probe.Closure.t, utility: utility): Node.t => let closure_view = ( - info, + info: info, utility: utility, model: model, local, - closure: Dynamics.Probe.Closure.t, + (index: int, closure: closure), ) => div( ~attrs=[ @@ -221,81 +372,71 @@ let closure_view = ["closure"] @ (show_indicator(closure.stack) ? ["cursor"] : []), ), ], - [value_view(info, model, utility, local, closure)] + [value_view(info, model, utility, local, closure, index)] @ (is_var_ref(info) ? [] : [env_view(closure, utility)]), ); -let select_vals = (model: model, vals) => { - switch (List.sort(comparor, vals)) { - | [] => [] - | _ => - vals - |> ListUtil.remove_first_n(model.index_offset) - |> ListUtil.truncate(model.max_closures) - }; +let closure_group_view = + (info, utility, model, local, groups: list(list((int, closure)))) => { + let group_views = + List.map( + closures => + Node.div( + ~attrs=[Attr.classes(["closure-group"])], + List.map(closure_view(info, utility, model, local), closures), + ), + groups, + ); + group_views == [] + ? [] : [div(~attrs=[Attr.classes(["closure-groups"])], group_views)]; }; -let offside_pos = utility => - Attr.create( - "style", - Printf.sprintf("position: absolute; left: %fpx;", utility.offside_offset), +let ellipsis_view = (local): Node.t => + div( + ~attrs=[ + Attr.classes(["ellipsis"]), + Attr.on_double_click(_ => {local(ToggleShowAllVals(0))}), + ], + [text("⋯")], ); -let nav_back = (di, model, local, left_cond) => - List.length(di) > model.max_closures - ? [ - Node.div( - ~attrs=[ - Attr.classes( - ["closures-header"] @ (left_cond ? ["disabled"] : []), - ), - Attr.on_click(_ => left_cond ? Effect.Ignore : local(Offset(1))), - ], - [Node.text("<")], - ), - ] - : []; - -let nav_forward = (di, model, local, right_cond) => - List.length(di) > model.max_closures - ? [ - Node.div( - ~attrs=[ - Attr.classes( - ["closures-tail"] @ (right_cond ? ["disabled"] : []), - ), - Attr.on_click(_ => right_cond ? Effect.Ignore : local(Offset(-1))), - ], - [Node.text(">")], - ), - ] - : []; +let nav_bar_view = (model: model, di: list(closure), local) => { + let nav_arrow = (cond: bool, offset: int): Node.t => + Node.div( + ~attrs=[ + Attr.classes(["nav-arrow"] @ (cond ? ["disabled"] : [])), + Attr.on_click(_ => cond ? Effect.Ignore : local(Offset(offset))), + ], + [], + ); + let show_left = model.index_offset >= List.length(di) - model.max_closures; + let show_right = model.index_offset <= 0; + div( + ~attrs=[Attr.classes(["nav-bar"])], + [nav_arrow(show_left, 1), nav_arrow(show_right, -1)], + ); +}; + +let equals_view = + div(~attrs=[Attr.classes(["live-equals"])], [text("=")]); -let offside_view = (info, ~model: model, ~local, ~utility: utility) => { +let offside_view = (model: model, info: info, local, utility: utility) => Node.div( - ~attrs=[Attr.classes(["live-offside"]), offside_pos(utility)], + ~attrs=[Attr.classes(["live-offside"])], switch (info.dynamics) { | Some(di) => - let vals = select_vals(model, di); - let left_cond = - model.index_offset >= List.length(di) - model.max_closures; - let right_cond = model.index_offset <= 0; - nav_back(di, model, local, left_cond) - @ nav_forward(di, model, local, right_cond) - @ List.map(closure_view(info, utility, model, local), vals); + let (num_shown, groups) = Closures.collate(info, model, di); + let is_cut_off = num_shown != Closures.num(info) && num_shown > 0; + let extras = [nav_bar_view(model, di, local), ellipsis_view(local)]; + (num_shown > 0 ? [equals_view] : []) + @ closure_group_view(info, utility, model, local, groups) + @ (is_cut_off ? extras : []); | _ => [] }, ); -}; - -let num_closures = (info: info) => - switch (info.dynamics) { - | Some(di) => List.length(di) - | _ => 0 - }; let num_closures_view = (info: info) => { - let num_closures = num_closures(info); + let num_closures = Closures.num(info); let description = num_closures < 1000 ? string_of_int(num_closures) : "1k+"; div( ~attrs=[ @@ -306,6 +447,10 @@ let num_closures_view = (info: info) => { ); }; +let pin_view = (info: info) => + State.s.pinned_ap != None && State.s.pinned_ap == cur_ap_id(info) + ? [div(~attrs=[Attr.classes(["pin"])], [])] : []; + let syntax_str = (info: info) => { let max_len = 30; let str = Printer.of_segment(~holes=None, [info.syntax]); @@ -318,30 +463,61 @@ let syntax_view = (info: info) => info |> syntax_str |> text; let placeholder = (_m, info) => ProjectorShape.inline(3 + String.length(syntax_str(info))); -// let icon = div(~attrs=[Attr.classes(["icon"])], [text("🔍")]); let icon = div(~attrs=[Attr.classes(["icon"])], []); -let view = (model: model, ~info, ~local, ~parent as _, ~utility: utility) => { - div([ - offside_view(info, ~model, ~local, ~utility), - div( - ~attrs=[ - Attr.classes( - ["main"] @ (Option.is_some(cur_ap_id(info)) ? ["ap"] : []), - ), - Attr.on_click(_ => { - env_cursor := []; - cur_ap := None; - Effect.Ignore; - }), - ], - [syntax_view(info), icon, num_closures_view(info)], - ), - ]); +let view = (info: info): Node.t => { + let on_double_click = _ => { + //State.reset(); + switch (State.s.pinned_ap) { + | Some(pinned_ap) when Some(pinned_ap) == cur_ap_id(info) => + State.s.pinned_ap = None + | Some(_) + | None => State.s.pinned_ap = cur_ap_id(info) + }; + Effect.Ignore; + }; + + let on_pointerdown = _ => { + switch (info.dynamics) { + | Some(di) => + switch (di) { + | [first_closure, ..._] => State.capture(info, first_closure) + | [] => () + } + | None => () + }; + Effect.Ignore; + }; + + div( + ~attrs=[ + Attr.classes( + ["main"] + @ (Option.is_some(cur_ap_id(info)) ? ["ap"] : []) + @ (State.s.pinned_ap == cur_ap_id(info) ? ["pinned"] : []), + ), + Attr.on_double_click(on_double_click), + Attr.on_pointerdown(on_pointerdown), + ], + [syntax_view(info), icon], + ); }; -let update = (m: model, a: action) => { - print_endline("update: action:" ++ show_action(a)); +let overlay_view = (info: info): Node.t => { + div( + ~attrs=[ + Attr.classes( + ["overlay"] + @ (Option.is_some(cur_ap_id(info)) ? ["ap"] : []) + @ (State.s.pinned_ap == cur_ap_id(info) ? ["pinned"] : []), + ), + ], + [num_closures_view(info)] @ pin_view(info), + ); +}; + +let update = (m: model, _info: info, a: action) => { + //print_endline("update: action:" ++ show_action(a)); switch (a) { | ChangeLength(id, len) => if (len > (-1)) { @@ -349,14 +525,21 @@ let update = (m: model, a: action) => { } else { m; } - | ToggleShowAllVals => { + | ToggleShowAllVals(offset) => { ...m, + index_offset: offset, max_closures: m.max_closures == 1 ? init.max_closures : 1, } | Offset(offset) => let index_offset = m.index_offset + offset; let index_offset = index_offset < 0 ? 0 : index_offset; {...m, index_offset}; + | PinAp(id) => + switch (State.s.pinned_ap) { + | Some(_) => State.s.pinned_ap = None + | None => State.s.pinned_ap = Some(id) + }; + m; }; }; @@ -376,6 +559,18 @@ module M: Projector = { let dynamics = true; let placeholder = placeholder; let update = update; - let view = view; + let view = (_model, info, ~local as _, ~parent as _, ~utility as _) => + view(info); + let offside_view = + Some( + (model, info, ~local, ~parent as _, ~utility) => + offside_view(model, info, local, utility), + ); + let overlay_view = + Some( + (_model, info, ~local as _, ~parent as _, ~utility as _) => + overlay_view(info), + ); + let underlay_view = Option.None; //TODO let focus = _ => (); }; diff --git a/src/haz3lcore/zipper/projectors/SliderFProj.re b/src/haz3lcore/zipper/projectors/SliderFProj.re index 04a4ee3690..af58373a3a 100644 --- a/src/haz3lcore/zipper/projectors/SliderFProj.re +++ b/src/haz3lcore/zipper/projectors/SliderFProj.re @@ -26,11 +26,11 @@ module M: Projector = { let can_focus = false; let dynamics = false; let placeholder = (_, _) => ProjectorShape.inline(10); - let update = (model, _) => model; + let update = (model, _, _) => model; let view = ( _, - ~info, + info, ~local as _, ~parent: external_action => Ui_effect.t(unit), ~utility as _, @@ -39,5 +39,8 @@ module M: Projector = { ~attrs=[Attr.on_input((_, v) => parent(SetSyntax(put(v))))], get(info.syntax) |> Printf.sprintf("%.2f"), ); + let offside_view = Option.None; + let overlay_view = Option.None; + let underlay_view = Option.None; let focus = _ => (); }; diff --git a/src/haz3lcore/zipper/projectors/SliderProj.re b/src/haz3lcore/zipper/projectors/SliderProj.re index f3681fd40f..bfe0cb5df4 100644 --- a/src/haz3lcore/zipper/projectors/SliderProj.re +++ b/src/haz3lcore/zipper/projectors/SliderProj.re @@ -23,11 +23,11 @@ module M: Projector = { let can_focus = false; let dynamics = false; let placeholder = (_, _) => ProjectorShape.inline(10); - let update = (model, _) => model; + let update = (model, _, _) => model; let view = ( _, - ~info, + info, ~local as _, ~parent: external_action => Ui_effect.t(unit), ~utility as _, @@ -36,5 +36,8 @@ module M: Projector = { ~attrs=[Attr.on_input((_, v) => parent(SetSyntax(put(v))))], get(info.syntax), ); + let offside_view = Option.None; + let overlay_view = Option.None; + let underlay_view = Option.None; let focus = _ => (); }; diff --git a/src/haz3lcore/zipper/projectors/TextAreaProj.re b/src/haz3lcore/zipper/projectors/TextAreaProj.re index 55bb0caec1..cf519edb20 100644 --- a/src/haz3lcore/zipper/projectors/TextAreaProj.re +++ b/src/haz3lcore/zipper/projectors/TextAreaProj.re @@ -77,7 +77,7 @@ let textarea = [], ); -let view = (_, ~info, ~local as _, ~parent, ~utility as _) => { +let view = (_, info, ~local as _, ~parent, ~utility as _) => { let text = info.syntax |> get |> Form.strip_quotes; Node.div( ~attrs=[Attr.classes(["wrapper"])], @@ -107,8 +107,11 @@ module M: Projector = { horizontal: 2 + StringUtil.max_line_width(str), }; }; - let update = (model, _) => model; + let update = (model, _, _) => model; let view = view; + let offside_view = Option.None; + let overlay_view = Option.None; + let underlay_view = Option.None; //TODO let focus = ((id: Id.t, d: option(Direction.t))) => { JsUtil.get_elem_by_id(of_id(id))##focus; switch (d) { diff --git a/src/haz3lmenhir/AST.re b/src/haz3lmenhir/AST.re new file mode 100644 index 0000000000..4a1ab7c69a --- /dev/null +++ b/src/haz3lmenhir/AST.re @@ -0,0 +1,588 @@ +open Sexplib.Std; + +[@deriving (show({with_path: false}), sexp, qcheck, eq)] +type filter_action = + | Pause + | Debug + | Hide + | Eval; + +[@deriving (show({with_path: false}), sexp, qcheck, eq)] +type op_bin_float = + | Plus + | Minus + | Times + | Power + | Divide + | LessThan + | LessThanOrEqual + | GreaterThan + | GreaterThanOrEqual + | Equals + | NotEquals; + +[@deriving (show({with_path: false}), sexp, qcheck, eq)] +type op_bin_bool = + | And + | Or; + +[@deriving (show({with_path: false}), sexp, qcheck, eq)] +type op_bin_int = + | Plus + | Minus + | Times + | Power + | Divide + | LessThan + | LessThanOrEqual + | GreaterThan + | GreaterThanOrEqual + | Equals + | NotEquals; + +[@deriving (show({with_path: false}), sexp, qcheck, eq)] +type op_bin_string = + | Concat + | Equals; + +[@deriving (show({with_path: false}), sexp, qcheck, eq)] +type bin_op = + | IntOp(op_bin_int) + | FloatOp(op_bin_float) + | StringOp(op_bin_string) + | BoolOp(op_bin_bool); + +[@deriving (show({with_path: false}), sexp, qcheck, eq)] +type op_un_meta = + | Unquote; + +[@deriving (show({with_path: false}), sexp, qcheck, eq)] +type op_un_int = + | Minus; + +[@deriving (show({with_path: false}), sexp, qcheck, eq)] +type op_un_bool = + | Not; + +[@deriving (show({with_path: false}), sexp, qcheck, eq)] +type op_un = + | Meta(op_un_meta) + | Int(op_un_int) + | Bool(op_un_bool); + +[@deriving (show({with_path: false}), sexp, qcheck, eq)] +type typ_provenance = + | Internal + | EmptyHole; + +[@deriving (show({with_path: false}), sexp, eq)] +type tpat = + | InvalidTPat(string) + | EmptyHoleTPat + | VarTPat(string); + +[@deriving (show({with_path: false}), sexp, eq)] +type typ = + | IntType + | StringType + | FloatType + | BoolType + | SumTyp(sumtype) + | UnknownType(typ_provenance) + | TupleType(list(typ)) + | ArrayType(typ) + | ArrowType(typ, typ) + | TypVar(string) + | InvalidTyp(string) + | ForallType(tpat, typ) + | RecType(tpat, typ) +and sumterm = + | Variant(string, option(typ)) + | BadEntry(typ) +and sumtype = list(sumterm); + +[@deriving (show({with_path: false}), sexp, eq)] +type pat = + | CastPat(pat, typ, typ) + | EmptyHolePat + | WildPat + | IntPat(int) + | FloatPat( + [@equal (a, b) => Printf.(sprintf("%f", a) == sprintf("%f", b))] float, + ) + | VarPat(string) + | ConstructorPat(string, typ) + | StringPat(string) + | TuplePat(list(pat)) + | BoolPat(bool) + | ConsPat(pat, pat) + | ListPat(list(pat)) + | ApPat(pat, pat) + | InvalidPat(string); // Menhir parser doesn't actually support invalid pats + +[@deriving (show({with_path: false}), sexp, qcheck, eq)] +type if_consistency = + | Consistent + | Inconsistent; + +[@deriving (show({with_path: false}), sexp, qcheck, eq)] +type deferral_pos = + | InAp + | OutsideAp; + +[@deriving (show({with_path: false}), sexp, eq)] +type exp = + | Int(int) + | Float + // This equality condition is used to say that two floats are equal if they are equal in the ExpToSegment serialization + ( + [@equal (a, b) => Printf.(sprintf("%f", a) == sprintf("%f", b))] float, + ) + | Var(string) + | Constructor(string, typ) + | String(string) + | ListExp(list(exp)) + | TupleExp(list(exp)) + | BinExp(exp, bin_op, exp) + | UnOp(op_un, exp) + | Let(pat, exp, exp) + | Fun(pat, exp, option(string)) + | CaseExp(exp, list((pat, exp))) + | ApExp(exp, exp) + | FixF(pat, exp) + | Bool(bool) + | Cast(exp, typ, typ) + | FailedCast(exp, typ, typ) + | EmptyHole + | Filter(filter_action, exp, exp) + | BuiltinFun(string) + | Undefined + | Seq(exp, exp) + | Test(exp) + | Deferral + | TypFun(tpat, exp) + | Cons(exp, exp) + | ListConcat(exp, exp) + | If(exp, exp, exp) + | InvalidExp(string) + | TypAp(exp, typ) + | DynamicErrorHole(exp, string) + | TyAlias(tpat, typ, exp); + +/** + * Generates a random CONSTRUCTOR_IDENT string. Used for CONSTRUCTOR_IDENT in the lexer. + * + * @return A QCheck generator for Constructor Identifier. + * + * ['A'-'Z'] ['a'-'z' 'A'-'Z' '0'-'9' '_']* + */ +let gen_constructor_ident: QCheck.Gen.t(string) = + // TODO handle full constructor ident including nums + QCheck.Gen.( + let* leading = char_range('A', 'Z'); + let+ tail = string_size(~gen=char_range('a', 'z'), int_range(1, 4)); + let ident = String.make(1, leading) ++ tail; + if (List.exists(a => a == ident, Haz3lcore.Form.base_typs)) { + "Keyword"; + } else { + ident; + } + ); + +/** + * Generates a random IDENT string. Used for IDENT in the lexer. + * + * @return A QCheck generator for Identifier. + * + * ['a'-'z' '_'] ['a'-'z' 'A'-'Z' '0'-'9' '_']* + */ +let gen_ident: QCheck.Gen.t(string) = + // Currently there is an issue if the keyword is a prefix of another word. + // `let ? = ina in ?` + // Temporarily doing single char identifiers as a fix + QCheck.Gen.(string_size(~gen=char_range('a', 'z'), int_range(1, 1))); + +/** + * Generates an array of natural numbers of a given size. + * Useful for generating recursive structures with arrays/lists. + * + * @param size - The size of the array, which also represents the number of elements in the array. + * @return A QCheck generator that produces arrays of integers that have a size (sum of elements + num of elements) of n + + * This function is useful for size tracking purposes. + */ +let gen_sized_array = (n: int): QCheck.Gen.t(array(int)) => + QCheck.Gen.( + let* list_size = n <= 1 ? pure(0) : int_range(2, n); + switch (list_size) { + | 0 => pure([||]) + | _ => nat_split(~size=list_size, n - list_size) + } + ); + +/** + * Generates an array of natural numbers that is either empty or has a length of at least 2. + * Useful for generating recursive structures with arrays/lists. + * + * @param n The size parameter used for generating the array. + * @return A QCheck generator that produces arrays of integers that have a size (sum of elements + num of elements) of n + * + * This function is useful for size tracking purposes, similar to `gen_sized_array`. + */ +let gen_non_singleton_array = (n: int): QCheck.Gen.t(array(int)) => + QCheck.Gen.( + let* list_size = + frequency([(1, pure(0)), (n, n <= 1 ? pure(0) : int_range(2, n))]); + + switch (list_size) { + | 0 => pure([||]) + | _ => nat_split(~size=list_size, n - list_size) + } + ); + +/** + * Generates an array of natural numbers has a length of at least 1. + * Useful for generating recursive structures with arrays/lists. + * + * @param n The size parameter used for generating the array. + * @return A QCheck generator that produces arrays of integers that have a size (sum of elements + num of elements) of n + * + * This function is useful for size tracking purposes, similar to `gen_sized_array`. + */ +let gen_non_empty_array = (n: int): QCheck.Gen.t(array(int)) => + QCheck.Gen.( + let* list_size = n <= 1 ? pure(0) : int_range(1, n); + + switch (list_size) { + | 0 => pure([|0|]) // I'm a bit concerned about this not tracking size. But it seems to work in practice. + | _ => nat_split(~size=list_size, n - list_size) + } + ); + +/** + * Generates a random `tpat` value using QCheck. + * + * @return A generator for `tpat` values. + */ +let gen_tpat: QCheck.Gen.t(tpat) = + QCheck.Gen.( + let gen_var = map(x => VarTPat(x), gen_ident); + let gen_empty = pure(EmptyHoleTPat); + // let gen_invalid = map(x => InvalidTPat(x), gen_ident); // Menhir parser doesn't actually support invalid tpat + oneof([gen_var, gen_empty]) + ); + +/** + * Generates a string literal for use in the program. + * This generator produces strings that match the `string` pattern in the lexer. + */ +let gen_string_literal: QCheck.Gen.t(string) = + // TODO This should be anything printable other than `"` + QCheck.Gen.(string_small_of(char_range('a', 'z'))); + +/** + * Generates an expression of a given size. + * + * @param n The size of the expression to generate. + * @return A generator for expressions of the specified size. + * + * This function is currently used for property tests between MakeTerm and the Menhir parser, + * so it's not currently set up to generate every possible expression. + */ +let rec gen_exp_sized = (n: int): QCheck.Gen.t(exp) => + QCheck.Gen.( + let leaf = + oneof([ + map(x => Int(x), small_int), + map(x => String(x), gen_string_literal), + map(x => Float(x), QCheck.pos_float.gen), // Floats are positive because we use UnOp minus + map(x => Var(x), gen_ident), + map(x => Bool(x), bool), + pure(EmptyHole), + pure(TupleExp([])), + pure(ListExp([])), + ]); + fix( + (self: int => t(exp), n) => { + switch (n) { + | 0 + | 1 => leaf + | _ => + oneof([ + leaf, + { + let* sizes = gen_sized_array(n); + let+ exps = + flatten_a(Array.map((size: int) => self(size), sizes)); + ListExp(Array.to_list(exps)); + }, + { + let* sizes = gen_non_singleton_array(n); + let+ exps = + flatten_a(Array.map((size: int) => self(size), sizes)); + TupleExp(Array.to_list(exps)); + }, + { + let+ inner = self(n - 1); + Test(inner); + }, + { + let+ name = gen_constructor_ident; + Constructor(name, UnknownType(Internal)); + }, + { + let* op = gen_bin_op; + let* e1 = self((n - 1) / 2); + let+ e2 = self((n - 1) / 2); + BinExp(e1, op, e2); + }, + { + let* op = gen_op_un; + let+ e = self((n - 1) / 2); + UnOp(op, e); + }, + { + let* e1 = self((n - 1) / 3); + let* e2 = self((n - 1) / 3); + let+ e3 = self((n - 1) / 3); + If(e1, e2, e3); + }, + { + let* p = gen_pat_sized((n - 1) / 3); + let* e1 = self((n - 1) / 3); + let+ e2 = self((n - 1) / 3); + Let(p, e1, e2); + }, + { + let* p = gen_pat_sized((n - 1) / 2); + let+ e = self((n - 1) / 2); + Fun(p, e, None); + }, + { + let case = n => { + let p = gen_pat_sized(n / 2); + let e = self(n / 2); + tup2(p, e); + }; + let* e = self(n - 1 / 2); + let* sizes = gen_sized_array(n - 1 / 2); + let+ cases = flatten_a(Array.map(case, sizes)); + CaseExp(e, Array.to_list(cases)); + }, + { + let* e1 = self((n - 1) / 2); + let+ e2 = + frequency([ + (5, self((n - 1) / 2)), + (1, return(Deferral)), + ]); + ApExp(e1, e2); + }, + { + let* p = gen_pat_sized((n - 1) / 2); + let+ e = self((n - 1) / 2); + FixF(p, e); + }, + { + let* fa = gen_filter_action; + let* e1 = self(n - 1); + let+ e2 = self(n - 1); + Filter(fa, e1, e2); + }, + { + let* e1 = self((n - 1) / 2); + let+ e2 = self((n - 1) / 2); + Seq(e1, e2); + }, + { + let* e1 = self((n - 1) / 2); + let+ e2 = self((n - 1) / 2); + Cons(e1, e2); + }, + { + let* e1 = self((n - 1) / 2); + let+ e2 = self((n - 1) / 2); + ListConcat(e1, e2); + }, + { + let* tp = gen_tpat; + let+ e = self(n - 1); + TypFun(tp, e); + }, + { + let* t = gen_typ_sized((n - 1) / 2); + let+ e = self((n - 1) / 2); + TypAp(e, t); + }, + { + let* tp = gen_tpat; + let* t = gen_typ_sized((n - 1) / 2); + let+ e = self((n - 1) / 2); + TyAlias(tp, t, e); + }, + ]) + } + }, + n, + ) + ) +/** + * Generates a type of a given size. + * + * @param n The size of the type to generate. + * @return A generator for types of the specified size. + * + * This function is currently used for property tests between MakeTerm and the Menhir parser, + * so it's not currently set up to generate every possible type. + */ +and gen_typ_sized: int => QCheck.Gen.t(typ) = + n => + QCheck.Gen.( + let leaf_nodes = + oneof([ + return(StringType), + return(FloatType), + return(BoolType), + return(TupleType([])), + return(UnknownType(EmptyHole)), // Only doing emptyhole because internal doesn't have a distinct representation in ExpToSegment + map(x => SumTyp([Variant(x, None)]), gen_constructor_ident), + ]); + fix( + (self, n) => + switch (n) { + | 0 => leaf_nodes + | _ => + oneof([ + leaf_nodes, + { + let* sizes = gen_non_singleton_array(n); + let+ typs = + flatten_a(Array.map((size: int) => self(size), sizes)); + TupleType(Array.to_list(typs)); + }, + { + let+ t = self(n - 1); + ArrayType(t); + }, + { + let* t1 = self((n - 1) / 2); + let+ t2 = self((n - 1) / 2); + ArrowType(t1, t2); + }, + { + let+ ident = gen_ident; + TypVar(ident); + }, + { + let* gen_tpat = gen_tpat; + let+ t = self(n - 1); + ForallType(gen_tpat, t); + }, + { + let* gen_tpat = gen_tpat; + let+ t = self(n - 1); + RecType(gen_tpat, t); + }, + { + let* sizes = gen_non_empty_array(n - 1); + let+ sumterms = + flatten_a( + Array.map( + (size: int) => { + frequency([ + (1, return(BadEntry(UnknownType(EmptyHole)))), + ( + 5, + { + let* optional_typ = option(self(size)); + let+ constructor = gen_constructor_ident; + Variant(constructor, optional_typ); + }, + ), + ]) + }, + sizes, + ), + ); + + SumTyp(Array.to_list(sumterms)); + }, + ]) + }, + n, + ) + ) + +/** + * Generates an pattern of a given size. + * + * @param n The size of the pattern to generate. + * @return A generator for expressions of the specified size. + * + * This function is currently used for property tests between MakeTerm and the Menhir parser, + * so it's not currently set up to generate every possible pattern. + */ +and gen_pat_sized: int => QCheck.Gen.t(pat) = + n => + QCheck.Gen.( + fix( + (self, n) => { + let leaf_nodes = + oneof([ + return(WildPat), + return(EmptyHolePat), + map(x => IntPat(x), small_int), + map(x => FloatPat(x), QCheck.pos_float.gen), + map(x => VarPat(x), gen_ident), + map(x => StringPat(x), gen_string_literal), + map(x => BoolPat(x), bool), + map( + x => ConstructorPat(x, UnknownType(Internal)), + gen_constructor_ident, + ), + return(TuplePat([])), + return(ListPat([])), + ]); + + switch (n) { + | 0 => leaf_nodes + | _ => + oneof([ + leaf_nodes, + { + let* p1 = self((n - 1) / 2); + let+ p2 = self((n - 1) / 2); + ConsPat(p1, p2); + }, + { + let* sizes = gen_non_singleton_array(n - 1); + let+ pats = + flatten_a(Array.map((size: int) => self(size), sizes)); + TuplePat(Array.to_list(pats)); + }, + { + let* sizes = gen_sized_array(n - 1); + let+ pats = + flatten_a(Array.map((size: int) => self(size), sizes)); + ListPat(Array.to_list(pats)); + }, + { + let* constructor = gen_constructor_ident; + let+ p = self(n - 1); + ApPat( + ConstructorPat(constructor, UnknownType(Internal)), + p, + ); + }, // The parser only handles ApPat with a constructor + { + let* p = self((n - 1) / 2); + let+ t1 = gen_typ_sized((n - 1) / 2); + CastPat(p, t1, UnknownType(Internal)); + } // The second cast pat isn't present in syntax + ]) + }; + }, + n, + ) + ); +// TODO Printers, shrinkers stuff diff --git a/src/haz3lmenhir/Conversion.re b/src/haz3lmenhir/Conversion.re new file mode 100644 index 0000000000..871276f127 --- /dev/null +++ b/src/haz3lmenhir/Conversion.re @@ -0,0 +1,539 @@ +include Sexplib.Std; +module FilterAction = { + open Haz3lcore.FilterAction; + let of_menhir_ast = (a: AST.filter_action): t => { + switch (a) { + | Eval => (Eval, All) + | Pause => (Step, One) + | Debug => (Step, All) + | Hide => (Eval, One) + }; + }; + + let of_core = (a: t): AST.filter_action => { + switch (a) { + | (Eval, All) => Eval + | (Step, One) => Pause + | (Step, All) => Debug + | (Eval, One) => Hide + }; + }; +}; + +module Operators = { + open Haz3lcore.Operators; + + let op_un_meta_of_menhir_ast = (op: AST.op_un_meta) => { + switch (op) { + | Unquote => Unquote + }; + }; + + let op_un_int_of_menhir_ast = (op: AST.op_un_int): op_un_int => { + switch (op) { + | Minus => Minus + }; + }; + + let op_un_bool_of_menhir_ast = (op: AST.op_un_bool): op_un_bool => { + switch (op) { + | Not => Not + }; + }; + + let op_un_of_menhir_ast = (op: AST.op_un): op_un => { + switch (op) { + | Meta(meta) => Meta(op_un_meta_of_menhir_ast(meta)) + | Int(i) => Int(op_un_int_of_menhir_ast(i)) + | Bool(b) => Bool(op_un_bool_of_menhir_ast(b)) + }; + }; + + let of_core_op_bin = (op: op_bin): AST.bin_op => { + switch (op) { + | Int(op_int) => + IntOp( + switch (op_int) { + | Plus => Plus + | Minus => Minus + | Times => Times + | Power => Power + | Divide => Divide + | LessThan => LessThan + | LessThanOrEqual => LessThanOrEqual + | GreaterThan => GreaterThan + | GreaterThanOrEqual => GreaterThanOrEqual + | Equals => Equals + | NotEquals => NotEquals + }, + ) + | Float(op_float) => + FloatOp( + switch (op_float) { + | Plus => Plus + | Minus => Minus + | Times => Times + | Power => Power + | Divide => Divide + | LessThan => LessThan + | LessThanOrEqual => LessThanOrEqual + | GreaterThan => GreaterThan + | GreaterThanOrEqual => GreaterThanOrEqual + | Equals => Equals + | NotEquals => NotEquals + }, + ) + | Bool(op_bool) => + BoolOp( + switch (op_bool) { + | And => And + | Or => Or + }, + ) + | String(op_string) => + StringOp( + switch (op_string) { + | Concat => Concat + | Equals => Equals + }, + ) + }; + }; + + let of_core_op_un = (op: op_un): AST.op_un => { + switch (op) { + | Meta(meta) => + Meta( + switch (meta) { + | Unquote => Unquote + }, + ) + | Int(i) => + Int( + switch (i) { + | Minus => Minus + }, + ) + | Bool(b) => + Bool( + switch (b) { + | Not => Not + }, + ) + }; + }; + + [@deriving (show({with_path: false}), sexp, yojson)] + let float_op_of_menhir_ast = (op: AST.op_bin_float): op_bin_float => { + switch (op) { + | Plus => Plus + | Minus => Minus + | Times => Times + | Power => Power + | Divide => Divide + | LessThan => LessThan + | LessThanOrEqual => LessThanOrEqual + | GreaterThan => GreaterThan + | GreaterThanOrEqual => GreaterThanOrEqual + | Equals => Equals + | NotEquals => NotEquals + }; + }; + + [@deriving (show({with_path: false}), sexp, yojson)] + let string_op_of_menhir_ast = (op: AST.op_bin_string): op_bin_string => { + switch (op) { + | Concat => Concat + | Equals => Equals + }; + }; + + [@deriving (show({with_path: false}), sexp, yojson)] + let bool_op_of_menhir_ast = (op: AST.op_bin_bool): op_bin_bool => { + switch (op) { + | And => And + | Or => Or + }; + }; + + [@deriving (show({with_path: false}), sexp, yojson)] + let int_op_of_menhir_ast = (op: AST.op_bin_int): op_bin_int => { + switch (op) { + | Plus => Plus + | Minus => Minus + | Times => Times + | Power => Power + | Divide => Divide + | LessThan => LessThan + | LessThanOrEqual => LessThanOrEqual + | GreaterThan => GreaterThan + | GreaterThanOrEqual => GreaterThanOrEqual + | Equals => Equals + | NotEquals => NotEquals + }; + }; + + [@deriving (show({with_path: false}), sexp, yojson)] + let of_menhir_ast = (op: AST.bin_op): op_bin => { + switch (op) { + | IntOp(op_int) => Int(int_op_of_menhir_ast(op_int)) + | FloatOp(op_float) => Float(float_op_of_menhir_ast(op_float)) + | BoolOp(op_bool) => Bool(bool_op_of_menhir_ast(op_bool)) + | StringOp(op_string) => String(string_op_of_menhir_ast(op_string)) + }; + }; +}; + +module rec Exp: { + let term_of_menhir_ast: AST.exp => Haz3lcore.Exp.term; + let of_menhir_ast: AST.exp => Haz3lcore.Exp.t; + let of_core: Haz3lcore.Exp.t => AST.exp; +} = { + let rec term_of_menhir_ast = (exp: AST.exp): Haz3lcore.Exp.term => { + switch (exp) { + | InvalidExp(s) => Invalid(s) + | Int(i) => Int(i) + | Float(f) => Float(f) + | String(s) => String(s) + | Bool(b) => Bool(b) + | Var(x) => Var(x) + | Constructor(x, ty) => Constructor(x, Typ.of_menhir_ast(ty)) + | Deferral => Deferral(InAp) + // switch (pos) { + // | InAp => Deferral(InAp) + // | OutsideAp => Deferral(OutsideAp) + // } + | ListExp(l) => ListLit(List.map(of_menhir_ast, l)) + | TupleExp(t) => + if (List.length(t) == 1) { + Wrap(of_menhir_ast(List.hd(t)), Paren); + } else { + Wrap( + Tuple(List.map(of_menhir_ast, t)) |> Haz3lcore.Exp.fresh, + Paren, + ); + } + | Let(p, e1, e2) => + Let(Pat.of_menhir_ast(p), of_menhir_ast(e1), of_menhir_ast(e2)) + | FixF(p, e) => FixF(Pat.of_menhir_ast(p), of_menhir_ast(e), None) + | TypFun(t, e) => TypFun(TPat.of_menhir_ast(t), of_menhir_ast(e), None) + | Undefined => Undefined + | TyAlias(tp, ty, e) => + TyAlias( + TPat.of_menhir_ast(tp), + Typ.of_menhir_ast(ty), + of_menhir_ast(e), + ) + | BuiltinFun(s) => BuiltinFun(s) + | Fun(p, e, name_opt) => + switch (name_opt) { + | Some(name_str) => + Fun(Pat.of_menhir_ast(p), of_menhir_ast(e), None, Some(name_str)) + | None => Fun(Pat.of_menhir_ast(p), of_menhir_ast(e), None, None) + } + | ApExp(e1, args) => + switch (args) { + | TupleExp(l) => + List.mem(AST.Deferral, l) + ? DeferredAp(of_menhir_ast(e1), List.map(of_menhir_ast, l)) + : Ap( + Haz3lcore.Operators.Forward, + of_menhir_ast(e1), + of_menhir_ast(args), + ) + | Deferral => DeferredAp(of_menhir_ast(e1), [args |> of_menhir_ast]) + + | _ => + Ap( + Haz3lcore.Operators.Forward, + of_menhir_ast(e1), + of_menhir_ast(args), + ) + } + | BinExp(e1, op, e2) => + BinOp( + Operators.of_menhir_ast(op), + of_menhir_ast(e1), + of_menhir_ast(e2), + ) + + | If(e1, e2, e3) => + If(of_menhir_ast(e1), of_menhir_ast(e2), of_menhir_ast(e3)) + | CaseExp(e, l) => + let d_scrut = of_menhir_ast(e); + let d_rules = + List.map( + ((pat, exp)) => (Pat.of_menhir_ast(pat), of_menhir_ast(exp)), + l, + ); + Match(d_scrut, d_rules); + | Cast(e, t1, t2) => + Cast(of_menhir_ast(e), Typ.of_menhir_ast(t1), Typ.of_menhir_ast(t2)) + | FailedCast(e, t1, t2) => + FailedCast( + of_menhir_ast(e), + Typ.of_menhir_ast(t1), + Typ.of_menhir_ast(t2), + ) + | EmptyHole => EmptyHole + | Seq(e1, e2) => Seq(of_menhir_ast(e1), of_menhir_ast(e2)) + | Test(e) => Test(of_menhir_ast(e)) + | Cons(e1, e2) => Cons(of_menhir_ast(e1), of_menhir_ast(e2)) + | ListConcat(e1, e2) => + ListConcat(of_menhir_ast(e1), of_menhir_ast(e2)) + | Filter(a, cond, body) => + let dcond = of_menhir_ast(cond); + let dbody = of_menhir_ast(body); + let act = FilterAction.of_menhir_ast(a); + Filter(Filter({pat: dcond, act}), dbody); + | TypAp(e, ty) => TypAp(of_menhir_ast(e), Typ.of_menhir_ast(ty)) + | UnOp(op, e) => + UnOp(Operators.op_un_of_menhir_ast(op), of_menhir_ast(e)) + | DynamicErrorHole(e, s) => + DynamicErrorHole( + of_menhir_ast(e), + Haz3lcore.InvalidOperationError.t_of_sexp(sexp_of_string(s)), + ) + }; + } + and of_menhir_ast = (exp: AST.exp): Haz3lcore.Exp.t => { + Haz3lcore.IdTagged.fresh(term_of_menhir_ast(exp)); + }; + + let rec of_core = (exp: Haz3lcore.Exp.t): AST.exp => { + switch (exp.term) { + | Invalid(_) => InvalidExp("Invalid") + | Int(i) => Int(i) + | Float(f) => Float(f) + | String(s) => String(s) + | Bool(b) => Bool(b) + | Var(x) => Var(x) + | Deferral(InAp) => Deferral + | ListLit(l) => ListExp(List.map(of_core, l)) + | Tuple(l) => TupleExp(List.map(of_core, l)) + | Let(p, e1, e2) => Let(Pat.of_core(p), of_core(e1), of_core(e2)) + | FixF(p, e, _) => FixF(Pat.of_core(p), of_core(e)) + | TypFun(tp, e, _) => TypFun(TPat.of_core(tp), of_core(e)) + | Undefined => Undefined + | TyAlias(tp, ty, e) => + TyAlias(TPat.of_core(tp), Typ.of_core(ty), of_core(e)) + | BuiltinFun(s) => BuiltinFun(s) + | Ap(Forward, e1, e2) => ApExp(of_core(e1), TupleExp([of_core(e2)])) + | BinOp(op, e1, e2) => + BinExp(of_core(e1), Operators.of_core_op_bin(op), of_core(e2)) + | If(e1, e2, e3) => If(of_core(e1), of_core(e2), of_core(e3)) + | Match(e, l) => + CaseExp( + of_core(e), + List.map(((p, e)) => (Pat.of_core(p), of_core(e)), l), + ) + | Cast(e, t1, t2) => + Cast(of_core(e), Typ.of_core(t1), Typ.of_core(t2)) + | FailedCast(e, t1, t2) => + FailedCast(of_core(e), Typ.of_core(t1), Typ.of_core(t2)) + | EmptyHole => EmptyHole + | Seq(e1, e2) => Seq(of_core(e1), of_core(e2)) + | Test(e) => Test(of_core(e)) + | Cons(e1, e2) => Cons(of_core(e1), of_core(e2)) + | ListConcat(e1, e2) => ListConcat(of_core(e1), of_core(e2)) + | Filter(Filter({pat, act}), body) => + Filter(FilterAction.of_core(act), of_core(pat), of_core(body)) + | TypAp(e, ty) => TypAp(of_core(e), Typ.of_core(ty)) + | UnOp(op, e) => UnOp(Operators.of_core_op_un(op), of_core(e)) + | DynamicErrorHole(e, s) => + DynamicErrorHole( + of_core(e), + Sexplib.Sexp.to_string(Haz3lcore.InvalidOperationError.sexp_of_t(s)), + ) + | Deferral(_) => Deferral + | Filter(Residue(_), _) => raise(Failure("Residue not supported")) + | MultiHole(_) => raise(Failure("MultiHole not supported")) + | Closure(_) => raise(Failure("Closure not supported")) + | Wrap(e, _) => of_core(e) + | Constructor(s, typ) => Constructor(s, Typ.of_core(typ)) + | DeferredAp(e, es) => + ApExp(of_core(e), TupleExp(List.map(of_core, es))) + | Fun(p, e, _, name_opt) => Fun(Pat.of_core(p), of_core(e), name_opt) + | Ap(Reverse, _, _) => raise(Failure("Reverse not supported")) + }; + }; +} +and Typ: { + let of_menhir_ast: AST.typ => Haz3lcore.Typ.t; + + let of_core: Haz3lcore.Typ.t => AST.typ; +} = { + let rec of_menhir_ast = (typ: AST.typ): Haz3lcore.Typ.t => { + Haz3lcore.IdTagged.fresh(term_of_menhir_ast(typ)); + } + and term_of_menhir_ast = (typ: AST.typ): Haz3lcore.Typ.term => { + switch (typ) { + | InvalidTyp(s) => Unknown(Hole(Invalid(s))) + | IntType => Int + | FloatType => Float + | BoolType => Bool + | StringType => String + | UnknownType(p) => + switch (p) { + | Internal => Unknown(Internal) + | EmptyHole => Unknown(Hole(EmptyHole)) + } + | TypVar(s) => Var(s) + | TupleType([t]) => Wrap(of_menhir_ast(t)) + | TupleType(ts) => + Wrap(Prod(List.map(of_menhir_ast, ts)) |> Haz3lcore.Typ.fresh) + | ArrayType(t) => List(of_menhir_ast(t)) + | ArrowType(t1, t2) => Arrow(of_menhir_ast(t1), of_menhir_ast(t2)) + | SumTyp(sumterms) => + open Haz3lcore; + let converted_terms: list(ConstructorMap.variant(TermBase.typ_t)) = + List.map( + (sumterm: AST.sumterm): ConstructorMap.variant(TermBase.typ_t) => + switch (sumterm) { + | Variant(name, typ) => + Variant(name, [], Option.map(of_menhir_ast, typ)) + | BadEntry(typ) => BadEntry(of_menhir_ast(typ)) + }, + sumterms, + ); + Wrap(Sum(converted_terms) |> Typ.fresh); // Adds Wrap due to MakeTerm + | ForallType(tp, t) => + Wrap( + Forall(TPat.of_menhir_ast(tp), of_menhir_ast(t)) + |> Haz3lcore.Typ.fresh, + ) + | RecType(tp, t) => + Wrap( + Rec(TPat.of_menhir_ast(tp), of_menhir_ast(t)) |> Haz3lcore.Typ.fresh, + ) // Wrap because of (rec ? -> Bool, ()) + }; + }; + let of_core_type_provenance = + (p: Haz3lcore.TermBase.type_provenance): AST.typ_provenance => { + switch (p) { + | Internal => Internal + | Hole(EmptyHole) => EmptyHole + | _ => raise(Failure("Unknown type_provenance")) + }; + }; + let rec of_core = (typ: Haz3lcore.Typ.t): AST.typ => { + switch (typ.term) { + | Int => IntType + | Float => FloatType + | String => StringType + | Bool => BoolType + | Var(x) => TypVar(x) + | Prod(ts) => TupleType(List.map(of_core, ts)) + | List(t) => ArrayType(of_core(t)) + | Arrow(t1, t2) => ArrowType(of_core(t1), of_core(t2)) + | Unknown(p) => UnknownType(of_core_type_provenance(p)) + | Forall(tp, t) => ForallType(TPat.of_core(tp), of_core(t)) + | Rec(tp, t) => RecType(TPat.of_core(tp), of_core(t)) + | Wrap(t) => of_core(t) + | Ap(_) => raise(Failure("Ap not supported")) + | Sum(constructors) => + let sumterms = + List.map( + (variant: Haz3lcore.ConstructorMap.variant(Haz3lcore.Typ.t)): AST.sumterm => { + switch (variant) { + | Variant(name, _, None) => Variant(name, None) + | Variant(name, _, Some(t)) => Variant(name, Some(of_core(t))) + | BadEntry(t) => BadEntry(of_core(t)) + } + }, + constructors, + ); + SumTyp(sumterms); + }; + }; +} +and TPat: { + let of_menhir_ast: AST.tpat => Haz3lcore.TPat.t; + let term_of_menhir_ast: AST.tpat => Haz3lcore.TPat.term; + let of_core: Haz3lcore.TPat.t => AST.tpat; +} = { + let rec term_of_menhir_ast = (tpat: AST.tpat): Haz3lcore.TPat.term => { + switch (tpat) { + | InvalidTPat(s) => Invalid(s) + | EmptyHoleTPat => EmptyHole + | VarTPat(s) => Var(s) + }; + } + and of_menhir_ast = (tpat: AST.tpat) => { + Haz3lcore.IdTagged.fresh(term_of_menhir_ast(tpat)); + }; + + let of_core = (tpat: Haz3lcore.TPat.t): AST.tpat => { + switch (tpat.term) { + | EmptyHole => EmptyHoleTPat + | Var(x) => VarTPat(x) + | Invalid(i) => InvalidTPat(i) + | MultiHole(_) => raise(Failure("MultiHole not supported")) + }; + }; +} +and Pat: { + let term_of_menhir_ast: AST.pat => Haz3lcore.Pat.term; + let of_menhir_ast: AST.pat => Haz3lcore.Pat.t; + let of_core: Haz3lcore.Pat.t => AST.pat; +} = { + let rec term_of_menhir_ast = (pat: AST.pat): Haz3lcore.Pat.term => { + switch (pat) { + | InvalidPat(s) => Invalid(s) + | IntPat(i) => Int(i) + | FloatPat(f) => Float(f) + | CastPat(p, t1, t2) => + Wrap( + Cast( + of_menhir_ast(p), + Typ.of_menhir_ast(t1), + Typ.of_menhir_ast(t2), + ) + |> Haz3lcore.Pat.fresh, + Paren, + ) + | VarPat(x) => Var(x) + | ConstructorPat(x, ty) => Constructor(x, Typ.of_menhir_ast(ty)) + | StringPat(s) => String(s) + | TuplePat(pats) => + Wrap( + Tuple(List.map(of_menhir_ast, pats)) |> Haz3lcore.Pat.fresh, + Paren, + ) + | ApPat(pat1, pat2) => Ap(of_menhir_ast(pat1), of_menhir_ast(pat2)) + | ConsPat(p1, p2) => + Wrap( + Cons(of_menhir_ast(p1), of_menhir_ast(p2)) |> Haz3lcore.Pat.fresh, + Paren, + ) + | BoolPat(b) => Bool(b) + | EmptyHolePat => EmptyHole + | WildPat => Wild + | ListPat(l) => ListLit(List.map(of_menhir_ast, l)) + }; + } + and of_menhir_ast = (pat: AST.pat): Haz3lcore.Pat.t => { + Haz3lcore.IdTagged.fresh(term_of_menhir_ast(pat)); + }; + let rec of_core = (pat: Haz3lcore.Pat.t): AST.pat => { + switch (pat.term) { + | Invalid(_) => InvalidPat("Invalid") + | Int(i) => IntPat(i) + | Float(f) => FloatPat(f) + | Var(x) => VarPat(x) + | Constructor(x, ty) => ConstructorPat(x, Typ.of_core(ty)) + | String(s) => StringPat(s) + | Tuple(l) => TuplePat(List.map(of_core, l)) + | Bool(b) => BoolPat(b) + | Cons(p1, p2) => ConsPat(of_core(p1), of_core(p2)) + | ListLit(l) => ListPat(List.map(of_core, l)) + | Ap(p1, p2) => ApPat(of_core(p1), of_core(p2)) + | EmptyHole => EmptyHolePat + | Wild => WildPat + | MultiHole(_) => raise(Failure("MultiHole not supported")) + | Cast(p, t1, t2) => + CastPat(of_core(p), Typ.of_core(t1), Typ.of_core(t2)) + | Wrap(p, _) => of_core(p) + }; + }; +}; diff --git a/src/haz3lmenhir/Interface.re b/src/haz3lmenhir/Interface.re new file mode 100644 index 0000000000..dfc8cd5f97 --- /dev/null +++ b/src/haz3lmenhir/Interface.re @@ -0,0 +1,22 @@ +open Lexing; +let column_num = (pos: position) => { + pos.pos_cnum - pos.pos_bol - 1; +}; + +let string_of_pos = (pos: position) => { + let l = string_of_int(pos.pos_lnum); + let c = string_of_int(column_num(pos) + 1); + "line " ++ l ++ ", column " ++ c; +}; + +let parse = (f, s) => { + let lexbuf = Lexing.from_string(s); + let result = + try(f(Lexer.token, lexbuf)) { + | Parser.Error => + raise(Failure("Parse error at: " ++ string_of_pos(lexbuf.lex_curr_p))) + }; + result; +}; + +let parse_program = s => parse(Parser.program, s); diff --git a/src/haz3lmenhir/Lexer.mll b/src/haz3lmenhir/Lexer.mll new file mode 100644 index 0000000000..6b87f803e9 --- /dev/null +++ b/src/haz3lmenhir/Lexer.mll @@ -0,0 +1,137 @@ +{ +open Lexing +open Parser + +let advance_line lexbuf = + let pos = lexbuf.lex_curr_p in + let pos' = { pos with + pos_bol = lexbuf.lex_curr_pos; + pos_lnum = pos.pos_lnum + 1 + } in + lexbuf.lex_curr_p <- pos' + +let parse_float_string s = + try + let f = float_of_string s in + f + with + | Failure _ -> print_endline ("Parse Float String Lexing Error On: " ^ s); 0.0 + +} +(* TODO We don't yet support negative floats in MakeTerm *) +let float = ['0'-'9']* '.' ['0'-'9']* +(* negative ints are done through unop *) +let int = ['0'-'9'] ['0'-'9']* + +let string = '"' ([^ '"' '\\'] | '\\' ['"' '\\'])* '"' + +let newline = '\r' | '\n' | "\r\n" + +let whitespace = [' ' '\t']+ + +let identifier = ['a'-'z' '_'] ['a'-'z' 'A'-'Z' '0'-'9' '_']* +let constructor_ident = ['A'-'Z'] ['a'-'z' 'A'-'Z' '0'-'9' '_']* +let sexp_string = '`' [^'`']* '`' +let ints = ['0'-'9']+ + +rule token = + parse + | "undef" { UNDEF} + | "infinity" | "neg_infinity" | "nan" | "epsilon_float" | "pi" | "max_int" | "min_int" | "is_finite" | "is_infinite" | "int_of_float" | "float_of_int" | "string_of_int" | "string_of_float" | "string_of_bool" | "int_of_string" | "float_of_string" | "bool_of_string" | "abs" | "abs_float" | "ceil" | "floor" | "exp" | "log" | "log10" | "sqrt" | "sin" | "cos" | "tan" | "asin" | "acos" | "atan" | "mod" | "string_length" | "string_compare" | "string_trim" | "string_concat" | "string_sub" | "string_split" { BUILTIN(Lexing.lexeme lexbuf)} + | whitespace {token lexbuf } + | newline { advance_line lexbuf; token lexbuf} + | ints as i { INT (int_of_string i) } + | float as f { FLOAT (parse_float_string f )} + | string as s { STRING (String.sub s 1 (String.length s - 2)) } + | sexp_string as s { SEXP_STRING (String.sub s 1 (String.length s - 2)) } + | "true" { TRUE } + | "false" { FALSE } + | "let" { LET } + | "in" { IN } + | "end" { END } + | "fun" { FUN } + | "case" { CASE } + | "if" { IF } + | "then" { THEN } + | "else" { ELSE } + | "[" { OPEN_SQUARE_BRACKET } + | "]" { CLOSE_SQUARE_BRACKET } + | "{" { OPEN_CURLY } + | "}" { CLOSE_CURLY } + | "(" { OPEN_PAREN } + | ")" { CLOSE_PAREN } + | "->" { DASH_ARROW } + | "=>" { EQUAL_ARROW } + | "=" { SINGLE_EQUAL } + (* Int ops*) + | "+" { PLUS } + | "-" { MINUS } + | "*" { TIMES } + | "/" { DIVIDE } + | "**" {POWER} + | "==" { DOUBLE_EQUAL } + | "!=" { NOT_EQUAL } + | "<" { LESS_THAN} + | "<=" { LESS_THAN_EQUAL } + | ">" { GREATER_THAN } + | ">=" { GREATER_THAN_EQUAL } + (* Float ops *) + | "+." { PLUS_FLOAT } + | "-." { MINUS_FLOAT } + | "*." { TIMES_FLOAT } + | "/." { DIVIDE_FLOAT } + | "**." {POWER_FLOAT} + | "==." { DOUBLE_EQUAL_FLOAT } + | "!=." { NOT_EQUAL_FLOAT } + | "<." { LESS_THAN_FLOAT} + | "<=." { LESS_THAN_EQUAL_FLOAT } + | ">." { GREATER_THAN_FLOAT } + | ">=." { GREATER_THAN_EQUAL_FLOAT } + (* String Ops *) + | "++" { STRING_CONCAT } + | "$==" { STRING_EQUAL } + (* Bool ops *) + | "&&" { L_AND } + | "||" { L_OR } + | "!" { L_NOT } + | "|" { TURNSTILE } + | "," { COMMA } + | ":" { COLON } + (* Types *) + | "Int" { INT_TYPE } + | "Float" { FLOAT_TYPE } + | "Bool" { BOOL_TYPE } + | "String" { STRING_TYPE } + | "Unknown" { UNKNOWN } + | "Internal" { INTERNAL } + (* DHExp Annotations *) + | "()" { UNIT } + (* Filters *) + | "pause" {PAUSE} + | "debug" {DEBUG} + | "hide" {HIDE} + | "eval" {EVAL} + (* Other *) + | ";" {SEMI_COLON} + | "test" {TEST} + | "::" { CONS } + | "@<" {TYP_AP_SYMBOL} + | "@" {AT_SYMBOL} + | "?" {QUESTION} + | "_" {WILD} + | "fix" {FIX} + | "typfun" {TYP_FUN} + | "type" {TYP} + | "$" {DOLLAR_SIGN} + | "~" {TILDE} + | "?t" {T_TYP} + | "?p" {P_PAT} + | "?tp" {TP_TPAT} + | "?e" {E_EXP} + | "named_fun" {NAMED_FUN} + | "forall" {FORALL} + | "rec" {REC} + | identifier as i { IDENT(i) } + | constructor_ident as i { CONSTRUCTOR_IDENT(i)} + | eof { EOF } + | _ { raise (Failure ("Lex error: unknown char: '" ^ Lexing.lexeme lexbuf ^ "'")) } diff --git a/src/haz3lmenhir/Parser.mly b/src/haz3lmenhir/Parser.mly new file mode 100644 index 0000000000..109cd2af0c --- /dev/null +++ b/src/haz3lmenhir/Parser.mly @@ -0,0 +1,339 @@ +%{ +open AST +%} + + + +%token OPEN_CURLY +%token CLOSE_CURLY +%token T_TYP +%token P_PAT +%token TP_TPAT +%token E_EXP +%token TILDE +%token NAMED_FUN +%token FORALL +%token REC +%token UNDEF +%token SEXP_STRING +%token DOLLAR_SIGN +%token TYP +%token TYP_FUN +%token FIX +%token WILD +%token QUESTION +%token AT_SYMBOL +%token TYP_AP_SYMBOL +%token CONS +%token TEST +%token PAUSE +%token DEBUG +%token HIDE +%token EVAL +%token IDENT +%token CONSTRUCTOR_IDENT +%token STRING +%token BUILTIN +%token TRUE +%token FALSE +%token INT +%token FLOAT +%token LET +%token FUN +%token CASE +%token OPEN_SQUARE_BRACKET +%token CLOSE_SQUARE_BRACKET +%token OPEN_PAREN +%token CLOSE_PAREN +%token DASH_ARROW +%token EQUAL_ARROW +%token SINGLE_EQUAL +%token TURNSTILE + +(* String ops *) +%token STRING_CONCAT +%token STRING_EQUAL + +(* Int ops *) +%token DOUBLE_EQUAL +%token NOT_EQUAL +%token PLUS +%token MINUS +%token POWER +%token TIMES +%token DIVIDE +%token LESS_THAN +%token LESS_THAN_EQUAL +%token GREATER_THAN +%token GREATER_THAN_EQUAL +(* Float ops *) +%token DOUBLE_EQUAL_FLOAT +%token NOT_EQUAL_FLOAT +%token PLUS_FLOAT +%token MINUS_FLOAT +%token DIVIDE_FLOAT +%token POWER_FLOAT +%token TIMES_FLOAT +%token LESS_THAN_FLOAT +%token LESS_THAN_EQUAL_FLOAT +%token GREATER_THAN_FLOAT +%token GREATER_THAN_EQUAL_FLOAT +(*logical ops*) +%token L_AND +%token L_OR +%token L_NOT +(*bitwise ops*) +%token COMMA +%token COLON +%token EOF +%token IN +%token UNIT +%token END + +(* type tokens *) +%token INT_TYPE +%token FLOAT_TYPE +%token BOOL_TYPE +%token STRING_TYPE +%token UNKNOWN +%token INTERNAL + +%token IF +%token THEN +%token ELSE + +%token SEMI_COLON + + + +(* Precedences *) + + + +%nonassoc LET_EXP +%right SEMI_COLON + +%right SUM_TYP + + +%right DASH_ARROW +%nonassoc IF_EXP + +%right L_OR +%right L_AND + + +%left GREATER_THAN LESS_THAN DOUBLE_EQUAL NOT_EQUAL LESS_THAN_EQUAL GREATER_THAN_EQUAL NOT_EQUAL_FLOAT LESS_THAN_FLOAT LESS_THAN_EQUAL_FLOAT GREATER_THAN_FLOAT GREATER_THAN_EQUAL_FLOAT DOUBLE_EQUAL_FLOAT STRING_EQUAL +%right STRING_CONCAT AT_SYMBOL +%right CONS + +%left PLUS MINUS PLUS_FLOAT MINUS_FLOAT +%left DIVIDE TIMES TIMES_FLOAT DIVIDE_FLOAT L_NOT + +%right POWER POWER_FLOAT +%nonassoc UMINUS /* Unary minus (prefix) */ +%left COLON + +%left OPEN_CURLY + + +%nonassoc TYP_AP_SYMBOL + +%left OPEN_PAREN CLOSE_PAREN +%left DOLLAR_SIGN + +%left QUESTION +%left TILDE + + + + +%type exp +%type sumTyp + +%start program + +%% + +program: + | e = exp; EOF {e} + +%inline intOp: + | MINUS { IntOp(Minus) } + | PLUS { IntOp(Plus) } + | TIMES { IntOp(Times) } + | POWER { IntOp(Power) } + | DIVIDE { IntOp(Divide) } + | DOUBLE_EQUAL { IntOp(Equals) } + | NOT_EQUAL { IntOp(NotEquals) } + | LESS_THAN { IntOp(LessThan) } + | LESS_THAN_EQUAL { IntOp(LessThanOrEqual) } + | GREATER_THAN { IntOp(GreaterThan) } + | GREATER_THAN_EQUAL { IntOp(GreaterThanOrEqual) } + + +%inline floatOp: + | PLUS_FLOAT { FloatOp(Plus) } + | MINUS_FLOAT { FloatOp(Minus) } + | TIMES_FLOAT { FloatOp(Times) } + | POWER_FLOAT { FloatOp(Power) } + | DIVIDE_FLOAT { FloatOp(Divide) } + | DOUBLE_EQUAL_FLOAT { FloatOp(Equals) } + | NOT_EQUAL_FLOAT { FloatOp(NotEquals) } + | LESS_THAN_FLOAT { FloatOp(LessThan) } + | LESS_THAN_EQUAL_FLOAT { FloatOp(LessThanOrEqual) } + | GREATER_THAN_FLOAT { FloatOp(GreaterThan) } + | GREATER_THAN_EQUAL_FLOAT { FloatOp(GreaterThanOrEqual) } + +%inline boolOp: + | L_AND { BoolOp(And) } + | L_OR { BoolOp(Or) } + +%inline stringOp: + | STRING_CONCAT { StringOp(Concat) } + | STRING_EQUAL { StringOp(Equals) } + +%inline binOp: + | i = intOp { i } + | f = floatOp { f } + | b = boolOp { b } + | s = stringOp { s } + +binExp: + | e1 = exp; b = binOp; e2 = exp { BinExp (e1, b, e2) } + +%inline tupleType: + | OPEN_PAREN; hd = typ; COMMA; types = separated_list(COMMA, typ); CLOSE_PAREN { TupleType(hd :: types) } + + +%inline sumTerm: + | i = CONSTRUCTOR_IDENT; OPEN_PAREN; hd = typ; COMMA; types = separated_list(COMMA, typ); CLOSE_PAREN { Variant(i, Some(TupleType(hd :: types))) } + | i = CONSTRUCTOR_IDENT; OPEN_PAREN; t = typ; CLOSE_PAREN; { Variant(i, Some(t)) } + | i = CONSTRUCTOR_IDENT { Variant(i, None) } + | QUESTION { BadEntry(UnknownType(EmptyHole)) } + + +// We don't support sum types without the leading plus in the parser syntax +sumTyp: + | PLUS; s = sumTerm; { [s] } %prec SUM_TYP + | PLUS; s = sumTerm; t = sumTyp { [s] @ t } + +typ: + | c = CONSTRUCTOR_IDENT { TypVar(c) } + | c = IDENT { TypVar(c) } + | T_TYP; s = STRING { InvalidTyp(s) } + | INT_TYPE { IntType } + | FLOAT_TYPE { FloatType } + | BOOL_TYPE { BoolType } + | STRING_TYPE { StringType } + | UNKNOWN; INTERNAL { UnknownType(Internal) } + | QUESTION { UnknownType(EmptyHole) } + | UNIT { TupleType([]) } + | FORALL; a = tpat; DASH_ARROW; t = typ { ForallType(a, t) } + | t = tupleType { t } + | OPEN_SQUARE_BRACKET; t = typ; CLOSE_SQUARE_BRACKET { ArrayType(t) } + | t1 = typ; DASH_ARROW; t2 = typ { ArrowType(t1, t2) } + | s = sumTyp; { SumTyp(s) } + | REC; c=tpat; DASH_ARROW; t = typ { RecType(c, t) } + | OPEN_PAREN; t = typ; CLOSE_PAREN { t } + +nonAscriptingPat: + | OPEN_PAREN; p = pat; CLOSE_PAREN { p } + | OPEN_PAREN; p = pat; COMMA; pats = separated_list(COMMA, pat); CLOSE_PAREN { TuplePat(p :: pats) } + | P_PAT; s = STRING { InvalidPat(s) } + | WILD { WildPat } + | QUESTION { EmptyHolePat } + | OPEN_SQUARE_BRACKET; l = separated_list(COMMA, pat); CLOSE_SQUARE_BRACKET; { ListPat(l) } + | c = CONSTRUCTOR_IDENT { ConstructorPat(c, UnknownType(Internal))} + | c = CONSTRUCTOR_IDENT; TILDE; t = typ; { CastPat(ConstructorPat(c, UnknownType(Internal)), UnknownType(Internal), t) } + | p = IDENT { VarPat(p) } + | i = INT { IntPat i } + | f = FLOAT { FloatPat f } + | s = STRING { StringPat s} + | TRUE { BoolPat true} + | FALSE {BoolPat false} + | f = pat; OPEN_PAREN; a = pat; CLOSE_PAREN { ApPat(f, a) } + +funPat: + | OPEN_PAREN; p1 = pat; COLON; t1 = typ; CLOSE_PAREN; { CastPat(p1, t1, UnknownType(Internal)) } + | p = nonAscriptingPat; { p } + +pat: + | p1 = pat; COLON; t1 = typ; { CastPat(p1, t1, UnknownType(Internal)) } + (* | p1 = pat; AS; p2 = pat; { AsPat(p1, p2) } *) + | p1 = pat; CONS; p2 = pat { ConsPat(p1, p2) } + | UNIT { TuplePat([]) } + | p = nonAscriptingPat; { p } + + +rul: + | TURNSTILE; p = pat; EQUAL_ARROW; e = exp; { (p, e) } + +case: + | CASE; e = exp; l = list(rul); END; { CaseExp(e, l) } + +funExp: + | FUN; p = funPat; DASH_ARROW; e1 = exp; { Fun (p, e1, None) } + | NAMED_FUN; name = IDENT; p = funPat; DASH_ARROW; e1 = exp { Fun (p, e1, Some(name)) } + + +%inline ifExp: + | IF; e1 = exp; THEN; e2 = exp; ELSE; e3 = exp { If (e1, e2, e3) } %prec IF_EXP + +filterAction: + | PAUSE { Pause } + | DEBUG { Debug } + | HIDE { Hide } + | EVAL { Eval } + +tpat: + | TP_TPAT; s = STRING {InvalidTPat(s)} + | QUESTION {EmptyHoleTPat} + | v = IDENT {VarTPat v} + | v = CONSTRUCTOR_IDENT {VarTPat v} + +unExp: + | DOLLAR_SIGN; e = exp {UnOp(Meta(Unquote), e)} + | MINUS; e = exp {UnOp(Int(Minus), e)} %prec UMINUS + | L_NOT; e = exp {UnOp(Bool(Not), e)} + + +exp: + | b = binExp { b } + | i = INT { Int i } + | f = FLOAT { Float f } + | v = IDENT { Var v } + | c = CONSTRUCTOR_IDENT { Constructor(c, UnknownType(Internal))} + | c = CONSTRUCTOR_IDENT; TILDE; t = typ; { Constructor(c, t) } + | c = CONSTRUCTOR_IDENT; COLON; t = typ; { Cast(Constructor(c, UnknownType(Internal)), UnknownType(Internal), t) } + | s = STRING { String s} + | OPEN_PAREN; e = exp; CLOSE_PAREN { e } + | OPEN_PAREN; e = exp; COMMA; l = separated_list(COMMA, exp); CLOSE_PAREN { TupleExp(e :: l) } + | UNIT { TupleExp([]) } + | c = case { c } + | OPEN_SQUARE_BRACKET; e = separated_list(COMMA, exp); CLOSE_SQUARE_BRACKET { ListExp(e) } + | f = exp; OPEN_PAREN; a = exp; CLOSE_PAREN { ApExp(f, a) } + | f = exp; OPEN_PAREN; a = exp; COMMA; tl = separated_nonempty_list(COMMA, exp); CLOSE_PAREN { ApExp(f, TupleExp(a :: tl)) } + | LET; i = pat; SINGLE_EQUAL; e1 = exp; IN; e2 = exp { Let (i, e1, e2) } %prec LET_EXP + | i = ifExp { i } + | e1 = exp; QUESTION; OPEN_CURLY; t1 = typ; EQUAL_ARROW; t2 = typ; CLOSE_CURLY {FailedCast(e1, t1, t2)} + | e1 = exp; OPEN_CURLY; t1 = typ; EQUAL_ARROW; t2 = typ; CLOSE_CURLY { Cast(e1, t1, t2) } + | TRUE { Bool true } + | f = funExp {f} + | FALSE { Bool false } + | FIX; p = funPat; DASH_ARROW; e = exp { FixF(p, e) } + | TYP_FUN; t = tpat; DASH_ARROW; e = exp {TypFun(t, e)} + | QUESTION { EmptyHole } + | a = filterAction; cond = exp; IN; body = exp { Filter(a, cond, body)} %prec LET_EXP + | TEST; e = exp; END { Test(e) } + | e1 = exp; AT_SYMBOL; e2 = exp { ListConcat(e1, e2) } + | e1 = exp; CONS; e2 = exp { Cons(e1, e2) } + | e1 = exp; SEMI_COLON; e2 = exp { Seq(e1, e2) } + | E_EXP; s = STRING; { InvalidExp(s) } + | WILD {Deferral} + | e = exp; TYP_AP_SYMBOL; ty = typ; GREATER_THAN; {TypAp(e, ty)} + | TYP; tp = tpat; SINGLE_EQUAL; ty = typ; IN; e = exp {TyAlias(tp, ty, e)} %prec LET_EXP + | LESS_THAN; LESS_THAN; e = exp; QUESTION; s = SEXP_STRING; GREATER_THAN; GREATER_THAN {DynamicErrorHole(e, s)} + | b = BUILTIN; {BuiltinFun(b)} + | UNDEF; {Undefined} + | u = unExp { u } diff --git a/src/haz3lmenhir/README.md b/src/haz3lmenhir/README.md new file mode 100644 index 0000000000..059d9ec8f0 --- /dev/null +++ b/src/haz3lmenhir/README.md @@ -0,0 +1,8 @@ +# Hazel Menhir Parser +> This directory contains a Menhir parser for textual Hazel `Exp` syntax and a utility to convert the Menhir AST to post-elaboration `Exp`. This is used for Hazel elaborator unit tests +* The Menhir parser is located in the `Parser.mly` file +* The ocamllex lexer can be found in `Lexer.mll` +* `Interface.re` contains functions to interface with the parser and parse text into an `AST` structure +* The parser's AST is defined in `AST.re` +* `Conversion.re` contains utility functions to convert the `AST` structure into the Hazel post-elaboration `Exp` structure +* Examples of the textual syntax can be found in the `Exp` module of the `TermBase.re` file diff --git a/src/haz3lmenhir/dune b/src/haz3lmenhir/dune new file mode 100644 index 0000000000..d86a64d07e --- /dev/null +++ b/src/haz3lmenhir/dune @@ -0,0 +1,20 @@ +(library + (name haz3lmenhir) + (libraries util re sexplib unionFind haz3lcore qcheck-alcotest) + (modules AST Conversion Interface Lexer Parser) + (instrumentation + (backend bisect_ppx)) + (preprocess + (pps + ppx_let + ppx_sexp_conv + ppx_deriving.show + ppx_deriving.eq + ppx_yojson_conv + ppx_deriving_qcheck))) + +(ocamllex Lexer) + +(menhir + (modules Parser) + (flags --explain --dump)) diff --git a/src/haz3lweb/Init.ml b/src/haz3lweb/Init.ml index e8ee282e9d..48d248a09b 100644 --- a/src/haz3lweb/Init.ml +++ b/src/haz3lweb/Init.ml @@ -73,6 +73,2200 @@ let startup : PersistentData.t = documentation = ( 2, [ + ( "Probes", + { + zipper = + "((selection((focus Left)(content())(mode \ + Normal)))(backpack())(relatives((siblings(((Secondary((id \ + 65949c4d-4dc0-4213-9d7b-1e6f9681d5e1)(content(Whitespace\" \ + \")))))((Projector((id \ + c746675f-f2c5-4506-a061-1e6ca61bdf32)(kind \ + Probe)(syntax(Tile((id \ + c746675f-f2c5-4506-a061-1e6ca61bdf32)(label(z))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 688b7961-0d66-4bb9-89d3-2d8dbdba05a4)(content(Whitespace\" \ + \")))))))(ancestors((((id \ + 42645903-c2bc-4138-9fec-d33cd389926c)(label(fun \ + ->))(mold((out Exp)(in_(Pat))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 35))(sort \ + Exp))))))(shards((0)(1)))(children(()())))(((Secondary((id \ + 107f760c-f964-431e-bd87-4b3dfcf31c87)(content(Whitespace\" \ + \")))))((Secondary((id \ + ac00f982-59ad-4d96-b2f7-132ea096ffcc)(content(Whitespace\"\\n\"))))(Projector((id \ + eb7847c4-281c-45b3-98eb-9d980453d225)(kind \ + Probe)(syntax(Tile((id \ + eb7847c4-281c-45b3-98eb-9d980453d225)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + b0d72243-ed26-4c70-b612-dddb92d99315)(label(a))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 12e1fbc2-5cdc-4b4c-bf95-2ead8a40c581)(content(Whitespace\" \ + \"))))(Tile((id \ + 7d982400-433e-46df-9b6d-9903a078b46b)(label(+))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 27))(sort \ + Exp))((shape(Concave 27))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + df35a074-94c6-4d91-99c4-e6abe6d3cb02)(content(Whitespace\" \ + \"))))(Tile((id \ + bb62dca6-680d-4369-b185-1f67c8e95313)(label(2))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 901b33b8-2d77-4b42-85c6-f96f7c514c58)(label(*))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 26))(sort \ + Exp))((shape(Concave 26))(sort \ + Exp))))))(shards(0))(children())))(Tile((id \ + 10bc86f5-9a3e-44f6-af1e-0cc3f4c34f3e)(label(z))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children())))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 09dc6a52-e3ae-40de-9e0e-b9522f4c7ea9)(content(Whitespace\" \ + \"))))(Secondary((id \ + 48554c58-500a-4c9b-929e-60fbd039100e)(content(Whitespace\" \ + \"))))(Secondary((id \ + d551eb83-6401-48e9-b315-cbac4d682338)(content(Whitespace\" \ + \")))))))(((id \ + dcd3480d-b336-43ca-a16a-d48f58648016)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards((0 \ + 1)(2)))(children((((Secondary((id \ + b3799582-de99-4061-b417-cf6d0896d33f)(content(Whitespace\" \ + \"))))(Tile((id \ + 16a9176a-35f0-4bc0-8fbe-00dc383830e2)(label(inner))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + b293c23d-c771-4551-bd8c-334c7b5dbac5)(content(Whitespace\" \ + \"))))))())))(((Secondary((id \ + 5e1f2383-2e52-4fa1-994e-cfbe666a4dd5)(content(Whitespace\" \ + \"))))(Tile((id \ + c5dfe928-5571-47e5-81d3-f33d1b76f7bb)(label(fun \ + ->))(mold((out Exp)(in_(Pat))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 35))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 844a4e06-48a0-4d06-98f0-643f53638da7)(content(Whitespace\" \ + \"))))(Projector((id \ + 65038c37-8676-4e5f-8503-ada7771f4bd1)(kind \ + Probe)(syntax(Tile((id \ + 65038c37-8676-4e5f-8503-ada7771f4bd1)(label(y))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + f9b9146e-9253-4cdd-8610-14c83013ac34)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 2ca1cc0b-a5f3-4d12-a288-d98f28f37215)(content(Whitespace\"\\n\"))))(Secondary((id \ + a7eb8274-d1c9-4902-b0b6-598bfadd11bb)(content(Comment\"# \ + Select `40` then `6` then '4' above. #\"))))(Secondary((id \ + bc350b60-c66e-483c-9d84-c668d3d2a98c)(content(Whitespace\"\\n\"))))(Tile((id \ + c033dba0-3c72-468c-a80e-12e1e2fc8010)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + b1103b7f-de2b-4784-9cfb-5500b2d49513)(content(Whitespace\" \ + \"))))(Projector((id \ + 6aa88bd5-aaa4-4887-85c1-fc95509f3a29)(kind \ + Probe)(syntax(Tile((id \ + 6aa88bd5-aaa4-4887-85c1-fc95509f3a29)(label(a))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 7f430bc0-901d-4d23-affc-377c7957c067)(content(Whitespace\" \ + \")))))((Secondary((id \ + 2c67587b-246f-4a7b-ae96-f68f6c2dc94b)(content(Whitespace\"\\n\"))))(Projector((id \ + 2eb8dc02-6f03-42ed-a12e-724f741ddc32)(kind \ + Probe)(syntax(Tile((id \ + 2eb8dc02-6f03-42ed-a12e-724f741ddc32)(label(10))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + f6641228-8da6-4a3f-8388-b81811b0dee8)(content(Whitespace\"\\n\"))))(Tile((id \ + 00309920-7129-49eb-91c1-aa779761937c)(label(*))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 26))(sort \ + Exp))((shape(Concave 26))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 3e9c74fd-bd87-47d7-9cdb-e84ec281a8d2)(content(Whitespace\" \ + \"))))(Projector((id \ + 8416d273-f869-4b2d-8a92-d282559d0f31)(kind \ + Probe)(syntax(Tile((id \ + 8416d273-f869-4b2d-8a92-d282559d0f31)(label(y))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 59e9ecfd-8e8a-4b4d-a558-4585ba186b06)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + c459abe0-6c07-4b31-b3bc-571b7c52bf9a)(content(Whitespace\" \ + \"))))(Secondary((id \ + da68ac94-ca68-43b1-a9dd-2b3a9bdd4b54)(content(Whitespace\"\\n\"))))(Secondary((id \ + b61769e0-d6b7-463e-b9b0-c53f29885a80)(content(Comment\"# Note \ + how inside the `inner` function below #\"))))(Secondary((id \ + b9ccf427-f26b-41f2-82d6-84956cc49faf)(content(Whitespace\"\\n\"))))(Secondary((id \ + 40a2bbda-b3f5-48d1-89a5-c1074701cf66)(content(Comment\"# two \ + cells for each probe gets highlighted. #\"))))(Secondary((id \ + d0b3e592-ef8e-4f87-b930-37b761483036)(content(Whitespace\"\\n\")))))((Secondary((id \ + 0add31a6-9abc-463b-adfd-45c770508e85)(content(Whitespace\"\\n\"))))(Secondary((id \ + 0494dd90-9ee7-4648-85b2-dc4004cbc4c5)(content(Comment\"# This \ + is because there are two calls to `inner` \ + #\"))))(Secondary((id \ + 39c2b0c1-8429-493f-a1ae-5a99f0ec76fb)(content(Whitespace\"\\n\"))))(Secondary((id \ + 7cda68e9-d858-426d-965a-f1a08b6b0fae)(content(Comment\"# \ + below for each call to the containing `outer`. \ + #\"))))(Secondary((id \ + 5b730135-15e6-4322-8ed6-1a0b0ecb2cdd)(content(Whitespace\"\\n\"))))(Projector((id \ + a53be29e-095d-4d84-8994-82fba0135aa2)(kind \ + Probe)(syntax(Tile((id \ + a53be29e-095d-4d84-8994-82fba0135aa2)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 3c631b4c-254e-46c1-bb94-969299aca825)(label(inner))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 8b213872-054b-4b2e-b3c0-f65f22593ada)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + fe5a4c79-f803-429c-a63f-80e6020e9122)(label(3))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 9036ef14-9e80-4636-94b4-375dbaf30248)(label(*))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 26))(sort \ + Exp))((shape(Concave 26))(sort \ + Exp))))))(shards(0))(children())))(Tile((id \ + 7a85c3e6-698a-43cc-8271-90ff1f94b970)(label(a))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 28bf3ad4-9820-446c-a349-797406667036)(content(Whitespace\"\\n\"))))(Tile((id \ + 46e0b99b-1cc4-42ca-92cf-7c7524b1197d)(label(+))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 27))(sort \ + Exp))((shape(Concave 27))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 58457b4d-93fd-4a3c-bbb3-057100a0288e)(content(Whitespace\" \ + \"))))(Projector((id \ + bb7e8c82-548c-4929-968a-c36bf50e2112)(kind \ + Probe)(syntax(Tile((id \ + bb7e8c82-548c-4929-968a-c36bf50e2112)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + e6552a3e-0c4f-45c9-b755-cde3bc74a979)(label(inner))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 14ede4c3-6041-4a91-97e2-02ab0df83dfa)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + c56f4006-72d3-4ce4-acac-c2c8be6cf374)(label(5))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + b0c9ae3a-6c2b-46bc-8539-1f2b02d0cc08)(label(*))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 26))(sort \ + Exp))((shape(Concave 26))(sort \ + Exp))))))(shards(0))(children())))(Tile((id \ + a349aba2-c83d-4787-915f-76713a370dbb)(label(y))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 3d699957-92da-4786-8d5b-564dcaf29a62)(content(Whitespace\" \ + \"))))(Secondary((id \ + fb590cb3-72e1-4d3c-8b9d-f496cda760b8)(content(Whitespace\" \ + \"))))(Secondary((id \ + b67f09f9-cc16-4290-9855-6c87f3586d13)(content(Whitespace\"\\n\")))))))(((id \ + 5c8317fe-71eb-4888-921f-282519872b93)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards((0 \ + 1)(2)))(children((((Secondary((id \ + 9c6914fd-ee0f-4dc7-8547-c6ea8e0c94fd)(content(Whitespace\" \ + \"))))(Tile((id \ + 15450cd3-306b-46a0-8575-1a682d4ed4ba)(label(outer))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + 1e8f222d-5f65-4827-b8f4-b96314a20ba7)(content(Whitespace\" \ + \"))))))())))(((Secondary((id \ + 5b287b8d-cee1-4a37-be6d-22512f83a127)(content(Comment\"# \ + _____ _ #\"))))(Secondary((id \ + 67bc4c20-c62a-4db7-af01-908216b0d940)(content(Whitespace\"\\n\"))))(Secondary((id \ + 208c6dd4-02d1-431d-b694-e4487e1a0e95)(content(Comment\"# | \ + __ \\\\ | | #\"))))(Secondary((id \ + d2944d23-8ca9-4970-a985-40ab2b4a3af6)(content(Whitespace\"\\n\"))))(Secondary((id \ + 2d1074c0-2026-43fb-8398-cc639139d863)(content(Comment\"# | \ + |__) | __ ___ | |__ ___ ___ #\"))))(Secondary((id \ + 0ef82daa-6f9d-4ab5-8d54-f7d664752484)(content(Whitespace\"\\n\"))))(Secondary((id \ + 6caa8137-8b42-4169-934d-55d736d46de4)(content(Comment\"# | \ + ___/ '__/ _ \\\\| '_ \\\\ / _ \\\\/ __| #\"))))(Secondary((id \ + ee5947db-81ec-4ffb-b124-827e4dbc67a1)(content(Whitespace\"\\n\"))))(Secondary((id \ + 0cd2ec95-039a-4ca1-bddd-14a859fd7cdb)(content(Comment\"# | \ + | | | | (_) | |_) | __/\\\\__ \\\\ #\"))))(Secondary((id \ + 7ece1d07-c40a-44d4-bb4f-14419a6e16c8)(content(Whitespace\"\\n\"))))(Secondary((id \ + dfe91edb-50c5-40d0-aecc-e122780c0a63)(content(Comment\"# \ + |_| |_| \\\\___/|_.__/ \\\\___||___/ #\"))))(Secondary((id \ + 2fc4d2b0-e375-4812-b252-48f0892762d1)(content(Whitespace\"\\n\"))))(Secondary((id \ + 6b44f0c9-c83a-4cd8-a951-c34047838729)(content(Comment\"# \ + INLINE EVAL WITH PROBE PROJECTORS #\"))))(Secondary((id \ + cf887ddd-6849-4677-81c3-5bfe475fb383)(content(Whitespace\"\\n\"))))(Secondary((id \ + da7e9cb5-374c-4c7c-b8a5-9d59dea54161)(content(Whitespace\"\\n\"))))(Secondary((id \ + 3b244ca5-7533-4047-978c-be08d8f22508)(content(Comment\"# \ + INTRODUCTION #\"))))(Secondary((id \ + 8210bd57-34f5-4158-ab9b-10fabe1df80f)(content(Whitespace\"\\n\"))))(Secondary((id \ + 2f48cdfc-e0e5-45ed-a455-34d63b4b1399)(content(Whitespace\"\\n\"))))(Secondary((id \ + 65248bf0-b96b-4e77-a1cb-ed9bf22848fc)(content(Comment\"# \ + Probe projectors are a kind of inline evaluation, \ + #\"))))(Secondary((id \ + a25a5f30-6ab3-4a2a-839b-9f37efc20e7e)(content(Whitespace\"\\n\"))))(Secondary((id \ + ab6bef21-8e63-4167-aeab-5e055374279d)(content(Comment\"# \ + similar to value hints in Emacs or IntelliJ. \ + #\"))))(Secondary((id \ + f804062f-ceec-446b-beec-472f2f273213)(content(Whitespace\"\\n\"))))(Secondary((id \ + e7e85b2a-a8cc-4980-b759-c645577c9ec5)(content(Whitespace\"\\n\"))))(Secondary((id \ + ae0cbe88-9816-4caf-b4d0-e231c5cca2de)(content(Comment\"# You \ + can put these on any expression or variable binding to \ + #\"))))(Secondary((id \ + 33a8036d-74f2-4a34-9c0a-b8736aef5c01)(content(Whitespace\"\\n\"))))(Secondary((id \ + 0ca100bf-048d-4907-a81d-70b71cde7ba8)(content(Comment\"# see \ + a list of all values taken on by that expression/pattern. \ + #\"))))(Secondary((id \ + 33af093f-99de-41c6-9b7e-c6de31d474a7)(content(Whitespace\"\\n\"))))(Secondary((id \ + cafc993b-67f5-4c12-bf7a-97a479dc5fc8)(content(Comment\"# By \ + default values are sorted by left-to-right by most-recent. \ + #\"))))(Secondary((id \ + ed8a120e-3a98-4e92-a3e2-f5111facd461)(content(Whitespace\"\\n\"))))(Secondary((id \ + 176d3c71-e66d-4dd8-9bca-6c6cf34bab56)(content(Whitespace\"\\n\"))))(Secondary((id \ + 657b42ce-5031-4164-adfe-b21d8f65c518)(content(Comment\"# More \ + generally, each cell represents a stack state, \ + #\"))))(Secondary((id \ + 3c91844a-c185-4c0d-aa09-61e7721e31f0)(content(Whitespace\"\\n\"))))(Secondary((id \ + c8281cbd-49ba-4c0f-856e-0ba07786528e)(content(Comment\"# \ + including the top stack frame / closure and hence the \ + #\"))))(Secondary((id \ + 910d4431-8fd5-4460-919f-48373f98b3ef)(content(Whitespace\"\\n\"))))(Secondary((id \ + bf08867d-99b2-4e0a-a5bb-e6a842a00965)(content(Comment\"# \ + expression's value, the values of environment variables, \ + #\"))))(Secondary((id \ + 0bd2f997-3ce5-4fa3-ad04-cbd7539ae614)(content(Whitespace\"\\n\"))))(Secondary((id \ + 6595a069-f601-42a4-aa86-a6d221193d7d)(content(Comment\"# as \ + well as the surrounding call stack context. \ + #\"))))(Secondary((id \ + fe09e076-6943-4980-bcb6-51783cb66928)(content(Whitespace\"\\n\"))))(Secondary((id \ + 6882dbea-dcef-478b-8103-90720fac77fc)(content(Whitespace\"\\n\"))))(Secondary((id \ + 195a40ec-d2b7-4356-933a-2c88cc6f3f9e)(content(Comment\"# When \ + a cell is selected, you can hover over it to see \ + #\"))))(Secondary((id \ + 61b2d5b1-c3fb-4625-b1f9-ac5c0a51a193)(content(Whitespace\"\\n\"))))(Secondary((id \ + acd47988-36ff-496a-b41f-27b842766165)(content(Comment\"# \ + relevant environment variables, and all /other/ cells \ + #\"))))(Secondary((id \ + 2911123d-3700-4cd3-8985-ca6bb5467c35)(content(Whitespace\"\\n\"))))(Secondary((id \ + d2fb50cb-1567-41d1-a42a-d3d787a07e12)(content(Comment\"# are \ + decorated according to their relative position in \ + #\"))))(Secondary((id \ + 21198096-1ba1-469f-a669-85ff3b253173)(content(Whitespace\"\\n\"))))(Secondary((id \ + a64163a4-22fa-435c-8e94-28a3a7808b73)(content(Comment\"# to \ + the selected cell. in the context #\"))))(Secondary((id \ + f6f614fc-2bc6-44e9-a772-df4349410ad4)(content(Whitespace\"\\n\"))))(Secondary((id \ + 3f732f1e-718d-4eee-b0f4-bd139ba927c7)(content(Whitespace\"\\n\"))))(Secondary((id \ + 06843876-589e-44ca-aacb-9ebd7377f788)(content(Comment\"# \ + Probe are intended mostly as a println replacement \ + #\"))))(Secondary((id \ + f5162412-6db0-49b2-94b1-3d1974967ad0)(content(Whitespace\"\\n\"))))(Secondary((id \ + a0d4fd1d-bb6e-49f4-b590-d1ee57d12991)(content(Comment\"# for \ + exposing intermediate values, with the above decorations \ + #\"))))(Secondary((id \ + a59859da-5b5f-4c0f-9926-21bd62fbf4d2)(content(Whitespace\"\\n\"))))(Secondary((id \ + 654b4955-23bd-4864-99cc-be8741944aa3)(content(Comment\"# as a \ + supporting feature to help maintain context when \ + #\"))))(Secondary((id \ + 2983735d-9145-4f8a-9322-32b60bd14f3d)(content(Whitespace\"\\n\"))))(Secondary((id \ + a6c54746-4de4-4c92-b65d-a3ade86e060c)(content(Comment\"# \ + navigating between multiple probed expressions, which \ + #\"))))(Secondary((id \ + 8bf15a15-ea96-46c2-b315-2cc3f86f15fb)(content(Whitespace\"\\n\"))))(Secondary((id \ + a0f07411-7fe4-49dc-8088-ac0c3d7e296f)(content(Comment\"# may \ + take on many values across nested or recursive functions. \ + #\"))))(Secondary((id \ + 7c147ac0-b6ad-4279-aeaf-d36b5416f392)(content(Whitespace\"\\n\"))))(Secondary((id \ + bc968ebb-e86f-44a0-974f-d339055c1130)(content(Whitespace\"\\n\"))))(Secondary((id \ + ac49c56a-e570-41ad-b1e4-9a3833db1853)(content(Whitespace\"\\n\"))))(Secondary((id \ + bbdb30b4-5827-4173-a8e7-d789dca85f95)(content(Comment\"# \ + TUTORIAL #\"))))(Secondary((id \ + 58a09c39-f68c-4218-90ce-55e2680b08f2)(content(Whitespace\"\\n\"))))(Secondary((id \ + 4346c13e-3c97-47b8-895f-1847a9d118fd)(content(Whitespace\"\\n\"))))(Secondary((id \ + fa1f17d0-e7be-4578-8e81-32db9ecc9912)(content(Comment\"# The \ + expression (10 * 10) below has a probe. \ + #\"))))(Secondary((id \ + ecc94b58-cbb0-494d-936e-6c900d510bb6)(content(Whitespace\"\\n\"))))(Secondary((id \ + 6d365200-9122-4671-a1b2-8f6489691f84)(content(Comment\"# Its \ + value, 20, is shown in a cell to the right. \ + #\"))))(Secondary((id \ + 76885278-d622-4dcf-8e4c-79c44a75a0ef)(content(Whitespace\"\\n\"))))(Tile((id \ + 3000d752-d923-409e-8e67-8e010d9c9563)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 647dd5f0-1cc3-4462-967d-4548c7f2ea58)(content(Whitespace\" \ + \"))))(Tile((id \ + b2801029-9a24-4a2c-b399-6a94ea5793fb)(label(sum))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + f8e8d3af-8167-4f18-a675-1f1ce4f61477)(content(Whitespace\" \ + \")))))((Secondary((id \ + dcc1a4fb-847b-4a58-8c45-14ff9315089a)(content(Whitespace\" \ + \"))))(Projector((id \ + 80f5d605-8d87-4871-9e87-f7eeb16d54f3)(kind \ + Probe)(syntax(Tile((id \ + 80f5d605-8d87-4871-9e87-f7eeb16d54f3)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + a7fc353f-b559-4c06-acde-74a635fc1a2c)(label(10))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 623e5159-438d-4643-97bc-4ec7e3834c6c)(content(Whitespace\" \ + \"))))(Tile((id \ + 7d1772ba-c6ab-4985-bbf6-9e4ad50e056b)(label(+))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 27))(sort \ + Exp))((shape(Concave 27))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + fa60fa26-d258-4936-a8cf-27b492327806)(content(Whitespace\" \ + \"))))(Tile((id \ + 1ef47cd6-30e9-4f35-892a-d27fa4f03092)(label(10))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children())))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 8b1fc809-5b94-4340-b403-f642f7ee12c1)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 7c0da59a-85c9-41ec-b523-62d721849a48)(content(Whitespace\"\\n\"))))(Secondary((id \ + bb1b4aff-55bb-447c-9d16-ce5dc2617125)(content(Whitespace\"\\n\"))))(Secondary((id \ + ab255a91-92e1-42a9-9bae-54fac4df120f)(content(Comment\"# To \ + probe the below expression, put your caret to \ + #\"))))(Secondary((id \ + e845151d-6227-4353-af30-3e2bf74929d2)(content(Whitespace\"\\n\"))))(Secondary((id \ + 13446dd5-7c48-4205-968d-0a2c27660f32)(content(Comment\"# left \ + of the `(` and press option/alt-v (for value), \ + #\"))))(Secondary((id \ + cf530abf-7e7b-4255-8c62-e44c846fdd33)(content(Whitespace\"\\n\"))))(Secondary((id \ + 95bcffe5-c814-49cf-bd56-f82ab29bf1b2)(content(Comment\"# or \ + select `Probe` from the lower right corner menu: \ + #\"))))(Secondary((id \ + d4261756-48f5-48ef-a17d-de4435e3a330)(content(Whitespace\"\\n\"))))(Tile((id \ + 8b094a53-f8d0-44c8-a7a7-a22f29754490)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 9ebe9a8e-4c1e-4886-9974-9851ded32cfe)(content(Whitespace\" \ + \"))))(Tile((id \ + 34de50bd-fb16-411f-97e2-ae068e6d893b)(label(drink_me))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + ee1c01a4-bcbb-42b3-aa57-02a387f3c535)(content(Whitespace\" \ + \")))))((Secondary((id \ + 647d6520-b2aa-49eb-a0f6-284db9852848)(content(Whitespace\" \ + \"))))(Tile((id \ + 9b252383-4162-4def-a7ff-69489a182c12)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 8ed001eb-a891-432f-88c6-07d619237e34)(label(1))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 4d505eab-49b5-4cde-8aa3-06def5a3ed57)(content(Whitespace\" \ + \"))))(Tile((id \ + e3048ce1-b7ab-4492-a0ce-c1a260293bc1)(label(+))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 27))(sort \ + Exp))((shape(Concave 27))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 33053c39-e296-4080-bfcd-7a829a2c97be)(content(Whitespace\" \ + \"))))(Tile((id \ + 224bb15c-5a60-42d3-b8d1-04dbdccc88a0)(label(2))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 56501795-b884-4b3c-9126-3311b9f8f3d9)(content(Whitespace\" \ + \"))))(Tile((id \ + 3599233c-5d4f-4991-a2d3-e387726e1974)(label(*))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 26))(sort \ + Exp))((shape(Concave 26))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 22c6bfb1-04f8-4fee-b0fc-0d03a72e87aa)(content(Whitespace\" \ + \"))))(Tile((id \ + dba64d67-0efa-418f-a25b-f07fe8f54215)(label(3))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))(Secondary((id \ + 700a419e-6219-4b06-8eda-dd76ef32df96)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 47eb8a96-9cfc-4852-b7a1-e5eba861d75d)(content(Whitespace\"\\n\"))))(Secondary((id \ + 3725c352-6550-4086-99fb-2b79297052d3)(content(Comment\"# The \ + expression should be encased in a green block \ + #\"))))(Secondary((id \ + a8efa47c-f665-490c-b305-dc5fd620b1d7)(content(Whitespace\"\\n\"))))(Secondary((id \ + 1d976db7-fdf3-48e2-aa5f-e0b078a356de)(content(Comment\"# and \ + a cell reading `7` should appear to the right. \ + #\"))))(Secondary((id \ + 5c540111-c7d3-4e23-85d2-cf35e42d74b6)(content(Whitespace\"\\n\"))))(Secondary((id \ + 8cfbab20-e70b-41c4-9040-b23b7721c624)(content(Comment\"# The \ + same shortcut or menu toggle removes it. \ + #\"))))(Secondary((id \ + f422f7d0-6c78-4d2c-bff3-b2771a69d07b)(content(Whitespace\"\\n\"))))(Secondary((id \ + 8fea3b87-a12d-4c66-baba-312e33ec6138)(content(Whitespace\"\\n\"))))(Secondary((id \ + 9853138b-27f5-4ea2-8221-c4e0867d0ebc)(content(Comment\"# \ + Click the below cell (with value 60) to select it. \ + #\"))))(Secondary((id \ + 8f886a2e-412c-431d-9995-fdab6b02b6ca)(content(Whitespace\"\\n\"))))(Tile((id \ + f9853e31-5aa7-44dd-a82e-af03a8ee5b70)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + b9f34a21-57ea-4edd-9182-d65a1379602c)(content(Whitespace\" \ + \"))))(Tile((id \ + 196da6fc-fc11-4cd1-b9ff-8d800939635c)(label(thrice))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + 0e936f30-84e4-4d24-b1d8-95d5ff287339)(content(Whitespace\" \ + \")))))((Secondary((id \ + fa9336f6-5980-471f-9f58-38e0f9461563)(content(Whitespace\" \ + \"))))(Projector((id \ + 516a758a-28fe-4a23-a128-fb19a410e820)(kind \ + Probe)(syntax(Tile((id \ + 516a758a-28fe-4a23-a128-fb19a410e820)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + a5c46d4b-e294-40c7-97b5-234a97b1cefc)(label(3))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 0fe5e719-ddb8-4f56-8701-ad4c7575a29e)(content(Whitespace\" \ + \"))))(Tile((id \ + 50b01a1f-cd1b-4d2b-a27e-99fa45d87fe3)(label(*))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 26))(sort \ + Exp))((shape(Concave 26))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 57872b2c-1bf1-4a7f-babe-2e8788ea4efd)(content(Whitespace\" \ + \"))))(Tile((id \ + 4833e983-f57f-4556-884a-dad25d4f3d9a)(label(drink_me))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children())))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + a6eebd77-67a0-43d5-bd3b-49cfc50fbbe1)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 0ce9af82-59fc-49ed-86b9-f881a345913f)(content(Whitespace\"\\n\"))))(Secondary((id \ + b1772155-740a-49ef-a716-c02399cc1475)(content(Comment\"# \ + Notice when you hover over a selected cell, it \ + #\"))))(Secondary((id \ + 40025529-bba9-4742-9cb3-ab9813cd1e06)(content(Whitespace\"\\n\"))))(Secondary((id \ + efea37d3-bcfa-459e-9391-056fceba8584)(content(Comment\"# \ + shows the values of any contained variables. \ + #\"))))(Secondary((id \ + 84f13d8b-b3b3-49b6-a27f-e4ac5eba08ac)(content(Whitespace\"\\n\"))))(Secondary((id \ + 7ea42771-8218-481e-b490-714cb9c18f7a)(content(Whitespace\"\\n\"))))(Secondary((id \ + 41f6c6e5-c2ab-40ad-b4fa-53f034cbb5c5)(content(Comment\"# \ + Probes only have cells if the are evaluated. \ + #\"))))(Secondary((id \ + 40b30a8c-27a1-4a28-ae92-bbd6acbd47ca)(content(Whitespace\"\\n\"))))(Secondary((id \ + d0a3973a-f575-4a46-80d4-c18f4fe6f8de)(content(Comment\"# \ + Below, only the first case branch is evaluated. \ + #\"))))(Secondary((id \ + cf4c7749-cf95-465a-be97-2fc2cc652429)(content(Whitespace\"\\n\"))))(Tile((id \ + 66aae668-dbb8-4197-b0de-e7586e058ab2)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 816df199-6a01-4285-8d5c-306901774c37)(content(Whitespace\" \ + \"))))(Tile((id \ + 5f975019-a8f6-44b1-b5e5-bc49f793191e)(label(maybe))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + be4553fa-e053-4203-ba56-3256834ee4f8)(content(Whitespace\" \ + \")))))((Secondary((id \ + 66a3ee43-195f-4d63-b879-f08752a9733c)(content(Whitespace\" \ + \"))))(Tile((id \ + 075467c3-5185-4f42-9397-77eebcd666ba)(label(if then \ + else))(mold((out Exp)(in_(Exp Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 34))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 97112299-6dd3-44aa-b208-057637c540b2)(content(Whitespace\" \ + \"))))(Projector((id \ + 5e7b39cd-f3ac-42e4-9dcc-849236c0a59d)(kind \ + Checkbox)(syntax(Tile((id \ + 5e7b39cd-f3ac-42e4-9dcc-849236c0a59d)(label(true))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))(model\"()\")))(Secondary((id \ + 082ef8dc-afea-4e77-a812-594fd3141a87)(content(Whitespace\" \ + \")))))((Secondary((id \ + 5ec5e6bd-2231-4ef4-9b0a-df5bb4e9251a)(content(Whitespace\" \ + \"))))(Tile((id \ + 2b4e9ea3-0e74-4b17-bb68-a40a5cb502c6)(label(thrice))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 6fe14abb-3959-4260-8e94-7310d821a7a9)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 8d6aa0ef-6d6e-43eb-b4ce-a38d18a119e9)(content(Whitespace\" \ + \"))))(Tile((id \ + 1b47f773-650a-49bd-85cb-caa2a182fa26)(label(0))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 4a74b81f-443b-4c0e-98d2-f8927bef69ad)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 8fe10968-2399-49d8-89b9-aa636b3d729b)(content(Whitespace\"\\n\"))))(Tile((id \ + aaa68576-1ac7-421b-8c5a-2b77e1a8d1d1)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 9e7c06e7-99e4-4722-a568-89d4a1922cb5)(content(Whitespace\" \ + \"))))(Tile((id \ + 2bc4d667-ae8c-4f8e-9e24-7d9199cec322)(label(_))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + 41c413d8-eeee-4631-bf27-c966cd63facf)(content(Whitespace\" \ + \")))))((Secondary((id \ + 662d5a91-dfec-467f-8e8e-f6d45e51569a)(content(Whitespace\" \ + \"))))(Secondary((id \ + d59997e1-66bb-4fd0-b70b-11381ae85982)(content(Whitespace\" \ + \"))))(Tile((id \ + 7dbe8403-114f-4684-a156-da6588b7295c)(label(case \ + end))(mold((out Exp)(in_(Rul))(nibs(((shape Convex)(sort \ + Exp))((shape Convex)(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 80b1807b-461d-41b5-99cd-eec6bda0e92d)(content(Whitespace\" \ + \"))))(Projector((id \ + c2e07164-aabd-43d1-865c-34c8a9affff0)(kind \ + Probe)(syntax(Tile((id \ + c2e07164-aabd-43d1-865c-34c8a9affff0)(label(maybe))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 0d6706dd-66de-4d50-a437-bf33a4c3d40b)(content(Whitespace\"\\n\"))))(Tile((id \ + ce52bb5d-d9dd-47de-a97b-00dc28d387c9)(label(| =>))(mold((out \ + Rul)(in_(Pat))(nibs(((shape(Concave 41))(sort \ + Exp))((shape(Concave 41))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + c66c1715-d463-46ff-b542-91bc0c251b4c)(content(Whitespace\" \ + \"))))(Tile((id \ + bfb82f8b-5e99-4ca0-9e6c-050ec5d355d2)(label(21))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + be884491-0605-4868-8f40-96e8d76129e5)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 858d5896-0b1b-4ba3-8235-b281fa3eff7e)(content(Whitespace\" \ + \"))))(Projector((id \ + be6dc0e3-9415-495b-beb0-fe68776d87ac)(kind \ + Probe)(syntax(Tile((id \ + be6dc0e3-9415-495b-beb0-fe68776d87ac)(label(\"\\\"necessary\\\"\"))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + f851d05b-5187-467f-87b8-94a2708d4568)(content(Whitespace\"\\n\"))))(Tile((id \ + ef7b6bfd-dd58-4bb2-8d87-07c21364dc5f)(label(| =>))(mold((out \ + Rul)(in_(Pat))(nibs(((shape(Concave 41))(sort \ + Exp))((shape(Concave 41))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + bc56424a-f3b3-40f6-9ed2-0846a5849900)(content(Whitespace\" \ + \"))))(Tile((id \ + 2e1d3623-98c6-4d9c-9522-8aa57286a3c9)(label(_))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + 0cf69421-c132-4a6c-a1e4-513b7daf4686)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 2892df8d-c180-4229-8b2c-8eb21c5f984d)(content(Whitespace\" \ + \"))))(Projector((id \ + 6f59a915-1a20-4087-8438-505024ce8885)(kind \ + Probe)(syntax(Tile((id \ + 6f59a915-1a20-4087-8438-505024ce8885)(label(\"\\\"impossible\\\"\"))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 7a479696-fad2-4358-81ae-139eafdd97d5)(content(Whitespace\"\\n\")))))))))(Secondary((id \ + 45314477-f3e7-4121-ab8c-8f3c3498de67)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 0e6bf9c6-5ce8-44c8-94c3-78096f46b6ec)(content(Whitespace\"\\n\"))))(Secondary((id \ + efec0a05-7486-4240-b07f-6d94fa792fcf)(content(Comment\"# Note \ + the 2nd branch probe has a zero to the right. \ + #\"))))(Secondary((id \ + 6d7955af-41e4-4725-b7a0-39009a6fe44d)(content(Whitespace\"\\n\"))))(Secondary((id \ + a5a31326-522d-4c04-bd62-e656c3d3d69b)(content(Comment\"# This \ + is the cell's collected closure count, i.e. \ + #\"))))(Secondary((id \ + aa2c7356-aa30-48f9-b72a-d7584f09ce8e)(content(Whitespace\"\\n\"))))(Secondary((id \ + 0358cc96-f900-4486-9b3c-5e333ec50be7)(content(Comment\"# the \ + number of times the expression was evaluated \ + #\"))))(Secondary((id \ + 6039b3ef-8006-4d48-a892-d5e21f2e606b)(content(Whitespace\"\\n\"))))(Secondary((id \ + 754e1f3f-c839-4378-a213-64ca6795927a)(content(Whitespace\"\\n\"))))(Secondary((id \ + 2960bafc-8080-427b-9421-640fd96979b3)(content(Comment\"# \ + Probes can be placed on expressions: #\"))))(Secondary((id \ + 4708993e-454f-4d92-8c7e-1aab35d445ad)(content(Whitespace\"\\n\"))))(Tile((id \ + d73c2733-1a36-44d5-8bc1-7bf7d886e681)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + fa333a91-c114-463d-b656-f40f4b242470)(content(Whitespace\" \ + \"))))(Tile((id \ + aecac108-3ae8-4b8b-87d4-aa71b62add35)(label(story))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + c4b160c8-81a1-4071-a5df-5ab0270e3025)(content(Whitespace\" \ + \")))))((Secondary((id \ + c6a863ec-9669-4001-8ddd-e1cbbeffecc3)(content(Whitespace\" \ + \"))))(Projector((id \ + ab24c8a6-37c6-4044-aacc-a820db6304d8)(kind \ + Probe)(syntax(Tile((id \ + ab24c8a6-37c6-4044-aacc-a820db6304d8)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Projector((id \ + af2a1593-c5e3-42b5-ab50-e19f15b0e863)(kind \ + Slider)(syntax(Tile((id \ + af2a1593-c5e3-42b5-ab50-e19f15b0e863)(label(50))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))(model\"()\")))(Secondary((id \ + 872e00f6-d272-4c6b-a3d7-c0e53a8ad50a)(content(Whitespace\" \ + \"))))(Tile((id \ + 38ee9261-c31d-4c43-af84-78bdd13ba362)(label(**))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 25))(sort \ + Exp))((shape(Concave 25))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 4121ad36-3062-4100-976b-0652ab387011)(content(Whitespace\" \ + \"))))(Tile((id \ + 029f5e79-b90b-45cb-8589-1f74286a2272)(label(2))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children())))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 71768352-ef14-471a-b0dc-c33b32245f04)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + ff7c4dab-6c65-46fa-81fe-14540fa2c51c)(content(Whitespace\"\\n\"))))(Secondary((id \ + 9eebf709-32ca-465f-808d-d81975bfedfc)(content(Comment\"# And \ + also on patterns (e.g. variables), shown in blue: \ + #\"))))(Secondary((id \ + 08304d98-477a-4024-9896-e16b6922c1fc)(content(Whitespace\"\\n\"))))(Tile((id \ + 7c42b54b-2678-4702-b412-5427c5e9dc25)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + ce42e047-dda8-4e3a-92e3-28a7f0f42010)(content(Whitespace\" \ + \"))))(Projector((id \ + 846ed995-2357-4a56-9f44-00519baf0e1d)(kind \ + Probe)(syntax(Tile((id \ + 846ed995-2357-4a56-9f44-00519baf0e1d)(label(story))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths((017c60cf-3484-46f2-a06a-2b1514140f72 \ + 19)))(max_closures 30)(index_offset 0))\")))(Secondary((id \ + 8fb2b8f8-8f3d-48f4-a162-ca5d17735c0d)(content(Whitespace\" \ + \")))))((Secondary((id \ + ca238785-23de-4930-b270-2fd5cb4730a5)(content(Whitespace\" \ + \"))))(Projector((id \ + 6c9f6679-d04a-4b88-a8f5-d3c78d13f28a)(kind \ + Slider)(syntax(Tile((id \ + 6c9f6679-d04a-4b88-a8f5-d3c78d13f28a)(label(50))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))(model\"()\")))(Secondary((id \ + d6a7cd97-d986-4e6f-bf5a-0d4786df29fc)(content(Whitespace\" \ + \"))))(Tile((id \ + 32688aa2-5a9f-4c99-baee-30f5438b3106)(label(**))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 25))(sort \ + Exp))((shape(Concave 25))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 762e8c4c-469d-4959-bb9f-9512c02d7781)(content(Whitespace\" \ + \"))))(Tile((id \ + 90a1e850-bee7-4bc0-ad43-e2d7276986bd)(label(2))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + bcd11834-3c5d-40ab-9adf-1a7c3d434ef5)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + b35b8c4a-850a-468d-8695-d3b623147a35)(content(Whitespace\"\\n\"))))(Secondary((id \ + 7a979aec-b75b-41a0-85dd-068a454c5d85)(content(Comment\"# \ + Expressions currently CAN'T BE EDITED WHILE PROBED \ + #\"))))(Secondary((id \ + dcbe770e-e4d8-442a-bf4b-52bbf72047c3)(content(Whitespace\"\\n\"))))(Secondary((id \ + d77998e8-6313-4dfd-a5b3-7de7f1c62ac5)(content(Comment\"# So \ + probing a name instead makes iteration easier. \ + #\"))))(Secondary((id \ + 440973cc-3081-4e4d-8daa-357613d1e75b)(content(Whitespace\"\\n\"))))(Secondary((id \ + a820a36e-4f51-40a2-9ee0-d9c3c738ae98)(content(Whitespace\"\\n\"))))(Secondary((id \ + 84c8656b-4a11-4bdb-8050-b380883a7220)(content(Whitespace\"\\n\"))))(Secondary((id \ + e8e87b6b-7bb7-4bb0-8468-57912a7c410a)(content(Comment\"# \ + FUNCTIONS #\"))))(Secondary((id \ + 6838679b-5e10-44c9-ab60-9dd865e2a4a4)(content(Whitespace\"\\n\"))))(Tile((id \ + c644c9b0-d167-4874-9563-21f3f1595adf)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + c7837266-1cf5-4356-9f8b-c905dbd314af)(content(Whitespace\" \ + \"))))(Tile((id \ + 8445685c-ebe2-4502-990a-2ffc1c8f7f00)(label(_))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + f83b5579-4e89-4e61-b72b-c90247d0c919)(content(Whitespace\" \ + \")))))((Secondary((id \ + a7314c28-387e-46df-9622-dfa0cc2e5ed5)(content(Whitespace\"\\n\"))))(Secondary((id \ + 3ac998c3-4bc0-4288-83ba-507684856415)(content(Comment\"# \ + Because functions can run multiple times, they can \ + #\"))))(Secondary((id \ + 6f7e53a4-865c-424b-a85f-925018674a1d)(content(Whitespace\"\\n\"))))(Secondary((id \ + 4ba86ce5-1448-4492-9824-b9a6e65abbc5)(content(Comment\"# have \ + multiple cells. Note the closure counts below \ + #\"))))(Secondary((id \ + 5e3266ee-0048-477e-9458-a206cf8f798e)(content(Whitespace\"\\n\"))))(Secondary((id \ + 6d4c4c5c-c581-4eb2-a5ff-4a80d8d9fa61)(content(Comment\"# are \ + all 2, indicating each probe was evaluated twice. \ + #\"))))(Secondary((id \ + 0422c753-8ed4-4b41-98e7-4b3b7bfeb83f)(content(Whitespace\"\\n\"))))(Tile((id \ + 8a4f80e8-6138-4850-b9ae-2784df4fe0e1)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 0cf9522a-f0a4-4735-8708-b9c4f3fc2965)(content(Whitespace\" \ + \"))))(Tile((id \ + 7f231d91-d0c0-45ff-8555-99314a3fe5e9)(label(celcius))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + 4b12e5a8-60aa-4033-aac4-b1926ade5ba1)(content(Whitespace\" \ + \")))))((Secondary((id \ + f65fe921-ce9d-44e2-bdd9-61acc26eabdd)(content(Whitespace\" \ + \"))))(Tile((id \ + 007c1e57-3a4a-474a-b9c8-7df827ffe576)(label(fun \ + ->))(mold((out Exp)(in_(Pat))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 35))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 8d6e7f34-2f9b-472b-a202-e51b601471ab)(content(Whitespace\" \ + \"))))(Projector((id \ + 2a016e4b-98a0-445f-b208-15e00fc0d5ec)(kind \ + Probe)(syntax(Tile((id \ + 2a016e4b-98a0-445f-b208-15e00fc0d5ec)(label(farenheit))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 4c9c416d-bd83-46b3-b256-5e8b2d7605e9)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 1e26169d-dd12-4401-ae29-e2d1ab9bf030)(content(Whitespace\"\\n\"))))(Secondary((id \ + b5607be9-a027-4ca4-894a-adf34e2faf2c)(content(Comment\"# \ + Click to select the cell above reading 72.5 \ + #\"))))(Secondary((id \ + a22d958f-5a55-4873-94df-c97fa1db1873)(content(Whitespace\"\\n\"))))(Tile((id \ + 34affb5e-80bd-4b01-bcc6-9ec5349296e1)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 22ac8742-fd19-44af-838f-bd2a6e6422ac)(content(Whitespace\" \ + \"))))(Projector((id \ + fea0443b-485b-4680-a352-2096f979b0a3)(kind \ + Probe)(syntax(Tile((id \ + fea0443b-485b-4680-a352-2096f979b0a3)(label(diff))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 3b737ed5-6d09-4f11-8801-3d97849cd8c7)(content(Whitespace\" \ + \")))))((Secondary((id \ + aa100a1d-ba9e-4c29-9fab-3e6625307fb9)(content(Whitespace\" \ + \"))))(Tile((id \ + c808dc6e-b377-4384-b64c-987ec3ec0fed)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + f35d3359-2fe8-4b99-ad20-77f0db145062)(label(farenheit))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 1278a5d4-964c-49ad-95f5-bea6f577f321)(content(Whitespace\" \ + \"))))(Tile((id \ + 3981a456-f875-4b9b-8c2b-08f0c2e8612c)(label(-.))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 27))(sort \ + Exp))((shape(Concave 27))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 82c43d73-0b86-4169-a027-cead0a4d8c41)(content(Whitespace\" \ + \"))))(Tile((id \ + 882ba1dc-8006-48a0-b9db-45917e30a009)(label(32.))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))(Secondary((id \ + 79bbcca5-0718-4e57-a41b-258b07bb042c)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 4b623ff7-80a4-4726-9c25-5ceae0c7cf91)(content(Whitespace\"\\n\"))))(Secondary((id \ + 9c9befce-21bf-46c6-b609-973ef7954869)(content(Comment\"# This \ + highlights cells below corresponding to the same \ + #\"))))(Secondary((id \ + 5e3e3ee8-828b-4380-8c77-66ce9886e6ba)(content(Whitespace\"\\n\"))))(Secondary((id \ + e7632290-d8ac-46cb-90e3-07b8b1a74709)(content(Comment\"# \ + function call: the cells reading 40.5 and 22.5) \ + #\"))))(Secondary((id \ + d240c049-a28d-4712-936f-bc9a4243af2e)(content(Whitespace\"\\n\"))))(Projector((id \ + e748427c-ea98-481a-8e5a-5b1fbf332c91)(kind \ + Probe)(syntax(Tile((id \ + e748427c-ea98-481a-8e5a-5b1fbf332c91)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + b7e607fc-eea3-411a-afa1-0b10f79e685b)(label(5.))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 9b6f1478-2cc5-4c2b-9fad-05864c16a2ec)(label(/.))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 26))(sort \ + Exp))((shape(Concave 26))(sort \ + Exp))))))(shards(0))(children())))(Tile((id \ + 4b7be7d7-c7d0-43ff-91b7-516b3dadbb00)(label(9.))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 916af026-a1b2-4950-940b-c5327561d8f4)(content(Whitespace\" \ + \"))))(Tile((id \ + e5558659-6260-4431-bbca-5d882f8e298a)(label(*.))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 26))(sort \ + Exp))((shape(Concave 26))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + ce76a9b3-f576-4bcc-8ce2-1cba962ae76d)(content(Whitespace\" \ + \"))))(Tile((id \ + 24de60c3-7aec-42cc-bc89-bb069932e7dc)(label(diff))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children())))))))))(model\"((display_lengths((09ca2848-629b-4445-b671-ead905cb7731 \ + 6)(6f622f65-620e-402f-a5c2-432aab5c354b 7)))(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + a8173c79-0b37-4f23-8e70-21194638057e)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + d01c5efd-306e-4cfb-a965-ee78e2ae4f9b)(content(Whitespace\"\\n\"))))(Tile((id \ + e8feab30-b778-40a0-a22c-709c6b5385d8)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + fa1025f3-5c24-412b-825d-c98f01bed0fe)(content(Whitespace\" \ + \"))))(Tile((id \ + 9099e47d-6200-4421-bdf8-49ed12871ce5)(label(\"(\"\")\"))(mold((out \ + Pat)(in_(Pat))(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0 1))(children(((Tile((id \ + c3db17cc-fb7a-45a9-9a79-efe55ba03702)(label(t1))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Tile((id \ + 740871a0-f3e8-4592-92ea-ffeb16f0d8be)(label(,))(mold((out \ + Pat)(in_())(nibs(((shape(Concave 45))(sort \ + Pat))((shape(Concave 45))(sort \ + Pat))))))(shards(0))(children())))(Secondary((id \ + 1fddedcb-7ee6-4cce-851a-9f6f64c02028)(content(Whitespace\" \ + \"))))(Tile((id \ + 8d2a8ef4-78f8-4050-8dd1-e2b5dad870f6)(label(t2))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))))))(Secondary((id \ + bcb9efed-64e6-40fa-8331-e1641186c7cb)(content(Whitespace\" \ + \")))))((Secondary((id \ + 9e1beb73-b34b-457d-b0c9-5e4014f0d026)(content(Whitespace\" \ + \"))))(Tile((id \ + 0ab0a674-887f-4e0e-a6d6-095a799c5188)(label(72.5))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 92df64d7-4ee5-4b2d-8494-6b7297b1352b)(label(,))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 45))(sort \ + Exp))((shape(Concave 45))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 1d81e836-5052-43e3-b3df-e365f0d90c55)(content(Whitespace\" \ + \"))))(Tile((id \ + b3e242ef-7935-48f5-8466-1242c0a134d5)(label(103.1))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 9fedd895-554a-4d2a-a389-1f3e528a673b)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 3726f6c1-a848-4c3c-89c6-d6c0451076aa)(content(Whitespace\" \ + \"))))(Tile((id \ + 8e8b2ff0-8ec1-4cd9-bd64-2e2741b9b3af)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Secondary((id \ + a81d8964-7a0c-4936-9a98-388828530bd6)(content(Whitespace\"\\n\"))))(Secondary((id \ + 2a334b61-7c34-40a5-98a4-f9e8811f3134)(content(Comment\"# It \ + also highlights in purple the cell #\"))))(Secondary((id \ + cac19712-6bd8-46ca-b205-97a8955c2f76)(content(Whitespace\"\\n\"))))(Secondary((id \ + 484cce16-95ee-4b26-b3bc-a4067680a97d)(content(Comment\"# of \ + the function's call site#\"))))(Secondary((id \ + 54028b97-1919-405a-b932-b674dda7e596)(content(Whitespace\"\\n\"))))(Projector((id \ + d0a64e39-1183-4878-9b4d-ddeb25585a2f)(kind \ + Probe)(syntax(Tile((id \ + d0a64e39-1183-4878-9b4d-ddeb25585a2f)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + b280ccb9-0a8b-48a3-9741-ec8be29ce423)(label(celcius))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 87519f3e-32a5-4986-a518-b81d71374000)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 64fdfb6d-4349-4c40-a0d6-497bdbad2074)(label(t1))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Tile((id \ + e3552276-5dc1-4a26-9439-9c98a1440fb8)(label(,))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 45))(sort \ + Exp))((shape(Concave 45))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 7b5e5c50-067a-4e6a-b2e3-67ff157ee7d5)(content(Whitespace\"\\n\"))))(Secondary((id \ + c811b5f6-6202-47ee-8557-580653540b57)(content(Comment\"# Now \ + select the cell above reading 22.5 #\"))))(Secondary((id \ + cc332168-3240-4b03-b752-3dc3e66f7246)(content(Whitespace\"\\n\"))))(Projector((id \ + 694c2624-7283-493b-9104-37afcf3f576e)(kind \ + Probe)(syntax(Tile((id \ + 694c2624-7283-493b-9104-37afcf3f576e)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 7ed47029-3323-4f9e-8ef1-b4b8e02abf3f)(label(celcius))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 8f84a947-ec49-43f2-ac86-89fd1990ced4)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + eb2adc9d-b90d-4b81-8ed6-10d66f2b63be)(label(t2))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 873d7588-4fb9-489f-8be4-e299149e9d37)(content(Whitespace\"\\n\"))))(Secondary((id \ + 2d146356-7808-48da-a4c4-e681cbc30f15)(content(Comment\"# Note \ + the 103.1, 71.1, and 39.5 are no longer green-highlit \ + #\"))))(Secondary((id \ + 32bb59ab-bc35-4237-93c7-17ff41ec3e0e)(content(Whitespace\"\\n\"))))(Secondary((id \ + d2b1800a-ed37-4b4a-bee0-c6db2da7019b)(content(Comment\"# as \ + they are not part of the same call as /the expression/ \ + #\"))))(Secondary((id \ + d480e309-bd5d-4ee3-82b1-e9aedf5ce97b)(content(Whitespace\"\\n\"))))(Secondary((id \ + c96bfa9d-4ac4-4e9b-bb1a-2aaa3b1acb2a)(content(Comment\"# \ + `celcius(t1)`. However, they are now outlined in purple, as \ + #\"))))(Secondary((id \ + 12f76d6b-12e7-444d-9763-bbea991c9dab)(content(Whitespace\"\\n\"))))(Secondary((id \ + ecfbf453-7544-40d9-929c-545ab0334a82)(content(Comment\"# they \ + are contained in the callstack created by that call \ + #\"))))(Secondary((id \ + 7456b7e5-854f-4889-904c-f0c13c9eb040)(content(Whitespace\"\\n\")))))))))(Secondary((id \ + 979ab033-2251-4659-86aa-eaea979c9499)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + a080d60a-ccab-48c2-bdfd-22a4444a140f)(content(Whitespace\"\\n\"))))(Secondary((id \ + e662152b-c2a7-4c1b-9d0d-2a26a3cd27fe)(content(Whitespace\"\\n\"))))(Secondary((id \ + d046139b-ab4f-43e6-90d0-5bdfe3fc8091)(content(Whitespace\"\\n\"))))(Secondary((id \ + 1a6e74ac-ae49-4831-80b9-bc58e1c9785f)(content(Comment\"# \ + FUNCTIONS CALLING FUNCTIONS #\"))))(Secondary((id \ + 2c5deea0-6860-4ae6-8baa-62791e7a2e89)(content(Whitespace\"\\n\"))))(Tile((id \ + 3a7161f4-b180-46e6-afa1-b2b60ec23592)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 28dc3bdb-ec81-42ce-a47c-2ed8273964e3)(content(Whitespace\" \ + \"))))(Tile((id \ + e35ff13e-f5e0-4c06-8d44-b96090245b93)(label(_))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + c98ac483-d6bf-40fb-ad58-4927e83a325c)(content(Whitespace\" \ + \")))))((Secondary((id \ + f2f0c0de-b1ce-40ee-8ea4-f9a3d40b13c2)(content(Whitespace\"\\n\"))))(Tile((id \ + a137c9ef-2d61-4ee9-bb17-d9dda9b2a847)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 1acce041-c1d4-4f4b-b18f-62977e8355fb)(content(Whitespace\" \ + \"))))(Tile((id \ + 319eed34-9c43-4ada-84a2-07f132cf9050)(label(fourth))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + a9835df3-b589-47a8-8444-e59b3aa93f90)(content(Whitespace\" \ + \")))))((Secondary((id \ + ce3bf749-cf23-4fd7-ac92-0afac717100a)(content(Whitespace\" \ + \"))))(Tile((id \ + ef90a214-bb3f-46e7-ac4f-8189a8b0c912)(label(fun \ + ->))(mold((out Exp)(in_(Pat))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 35))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 4798d1b8-a19d-4827-aeb7-021c229b5692)(content(Whitespace\" \ + \"))))(Projector((id \ + e5ac31d7-515d-4f42-babd-c7e592b97b02)(kind \ + Probe)(syntax(Tile((id \ + e5ac31d7-515d-4f42-babd-c7e592b97b02)(label(f))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + ec63e8ad-0fdd-45bd-8e49-0005240ef297)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + e42f841a-9953-4a78-8433-754794af8049)(content(Whitespace\"\\n\"))))(Projector((id \ + cc0f99f4-f669-4e09-ab49-c8c61137ef8a)(kind \ + Probe)(syntax(Tile((id \ + cc0f99f4-f669-4e09-ab49-c8c61137ef8a)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + c76cde1c-39a3-478e-8c16-ca762e1c2af0)(label(4))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 7f72eec6-7abd-46f9-9ad2-171556923530)(content(Whitespace\" \ + \"))))(Tile((id \ + c31e4832-1dde-469e-9a0a-33f1006115e7)(label(*))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 26))(sort \ + Exp))((shape(Concave 26))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 97693e66-72d5-4215-abe6-d0b0df69672f)(content(Whitespace\" \ + \"))))(Tile((id \ + af87a272-b901-4642-ba00-c8eabf171fdd)(label(f))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + a5558001-51c6-4911-b41e-dfbc732632f6)(content(Whitespace\" \ + \"))))(Tile((id \ + 5b5f55e2-9620-4f24-90a8-303918341fd8)(label(-))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 27))(sort \ + Exp))((shape(Concave 27))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 881c5309-baa6-4c9e-ae9c-a6041e48b664)(content(Whitespace\" \ + \"))))(Tile((id \ + 24f814d3-5754-4133-8bfe-7800a782bd1e)(label(4))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children())))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 2070e7f5-1a9b-4ed3-8b42-a9e548ff82a0)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + f6c6558b-f412-48c9-8a51-a788b44e9ed7)(content(Whitespace\"\\n\"))))(Secondary((id \ + e2880dd3-3e9c-4fe2-b4c2-07c10167d636)(content(Comment\"# \ + Select `12` below, which reads the argument `t` of `third` \ + #\"))))(Secondary((id \ + 3442b621-9409-4458-b880-fd24ada82816)(content(Whitespace\"\\n\"))))(Tile((id \ + d35bfabb-d398-4b18-a150-f351d931159e)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 102fe5a8-c680-4a8d-8709-811d71142cff)(content(Whitespace\" \ + \"))))(Tile((id \ + f130e49d-aba3-43bd-8346-fec8c01c6fc5)(label(third))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + f35cba72-07c5-4bd9-afbb-f0d2b22a2e65)(content(Whitespace\" \ + \")))))((Secondary((id \ + fe151c0c-ce3d-4bc9-9227-97e51a0aa3ac)(content(Whitespace\" \ + \"))))(Tile((id \ + 301abc18-7d85-4c67-b19a-152cf8dcd25b)(label(fun \ + ->))(mold((out Exp)(in_(Pat))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 35))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + b88a1c5f-dcd1-4974-93c9-826b1fa88d7d)(content(Whitespace\" \ + \"))))(Projector((id \ + b415fa60-703e-4ed4-a10d-6f4559776823)(kind \ + Probe)(syntax(Tile((id \ + b415fa60-703e-4ed4-a10d-6f4559776823)(label(t))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 921b48b9-f216-448b-be7a-5bee37cc4af8)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 45d7b101-b573-4aa9-9e2d-808f46c1472e)(content(Whitespace\" \ + \"))))(Secondary((id \ + 8a64fe08-803e-45ec-baf7-d039afa40451)(content(Whitespace\"\\n\"))))(Secondary((id \ + d8ed669f-85ad-4f1f-a191-db1976736931)(content(Comment\"# Note \ + `32` below is highlit as it's from the same call, \ + #\"))))(Secondary((id \ + edfc04b0-eb7a-4666-ab37-3b6a37732781)(content(Whitespace\"\\n\"))))(Secondary((id \ + 74d2aa8e-7dba-4fac-b055-128baf850ef1)(content(Comment\"# and \ + the `10` below is purple because its the cell of \ + #\"))))(Secondary((id \ + 75c8cba9-318d-45dd-9623-0f240814c139)(content(Whitespace\"\\n\"))))(Secondary((id \ + b4c099c1-7d0b-4469-b852-1205f3b9dcf6)(content(Comment\"# the \ + relevant call to `third`. Now, select `32` below: \ + #\"))))(Secondary((id \ + 9c790c99-85f5-4161-a335-96a8cc16cd3a)(content(Whitespace\"\\n\"))))(Projector((id \ + efdbda6c-ad4a-45b0-9f7e-997608ce3f94)(kind \ + Probe)(syntax(Tile((id \ + efdbda6c-ad4a-45b0-9f7e-997608ce3f94)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + eb2ee1dc-1e4f-4613-92ff-7f3cd4a375dd)(label(fourth))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + fd9d5a3d-f0db-4d78-88da-c93cf444c040)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 4cbcb34c-934b-4931-83b5-98ee374a01de)(label(t))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + adfee94c-34b2-4252-80a9-1ccec6446ad6)(content(Whitespace\" \ + \"))))(Tile((id \ + df671424-204c-45f5-8bd7-0df3e29a8555)(label(-))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 27))(sort \ + Exp))((shape(Concave 27))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 9c4308fb-9fea-47f1-9a0c-da4b5de763b9)(content(Whitespace\" \ + \"))))(Tile((id \ + 52fbaa62-288b-425b-9ea4-32cfc11b502e)(label(3))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 175e08db-4d46-404b-82ee-ab6004481f6f)(content(Whitespace\" \ + \"))))(Tile((id \ + 6a0532d0-defa-41dd-9552-575ac03f5370)(label(/))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 26))(sort \ + Exp))((shape(Concave 26))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 69475242-7ac8-480b-b574-81086004e703)(content(Whitespace\" \ + \"))))(Tile((id \ + 40a9bed6-b527-464e-8f11-29b491c80f58)(label(3))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 8d87988d-bd62-4b44-b5d9-74e41f5ab98c)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 0d9dd41c-c722-4676-84e1-9b6aeb648fda)(content(Whitespace\"\\n\"))))(Secondary((id \ + e8b7e763-9d1b-45b7-9c1a-1b02c8ccae47)(content(Whitespace\" \ + \"))))(Secondary((id \ + 25fa63ea-e6d2-469e-af16-d767e3b1bb8d)(content(Whitespace\" \ + \"))))(Secondary((id \ + ea94e016-53c1-4490-89b9-2cebe784fcda)(content(Comment\"# Note \ + that `10` stays purple as we're still in the same call. \ + #\"))))(Secondary((id \ + 6129d855-247e-496c-b627-455bf239bd62)(content(Whitespace\"\\n\"))))(Secondary((id \ + c48c03ed-107a-49a2-9fd4-fcdd5a7556c6)(content(Whitespace\" \ + \"))))(Secondary((id \ + 1977526c-24ce-47d6-854c-4f4c2380ad7e)(content(Whitespace\" \ + \"))))(Secondary((id \ + d523176d-c7e4-4efa-8f64-c004c7d4dccb)(content(Comment\"# `9` \ + and `32` above get purple outlines as they come from the \ + #\"))))(Secondary((id \ + 15a70d33-ae1a-49e1-b0fd-b4ef81fa214a)(content(Whitespace\"\\n\"))))(Secondary((id \ + 1e0e4a43-fd61-4895-be13-227986820c1f)(content(Whitespace\" \ + \"))))(Secondary((id \ + 269aea03-dac8-4e78-98e7-37d355cb0893)(content(Whitespace\" \ + \"))))(Secondary((id \ + ea37e24e-d762-4f91-9027-28e82a99831d)(content(Comment\"# call \ + to `fourth` that we've now selected#\"))))(Secondary((id \ + ae8940e9-18ac-4881-8bcc-287818dbe598)(content(Whitespace\"\\n\"))))(Tile((id \ + d1ba74d0-8ed3-4a30-bacf-45bf2b2ef9d2)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + b7363479-109c-4cc8-b3be-81537f303114)(content(Whitespace\" \ + \"))))(Tile((id \ + c42b27f9-8833-4e4c-b0f0-266381abeeab)(label(second))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + 1ec1a828-bcf9-4534-8e30-4f127d2e4138)(content(Whitespace\" \ + \")))))((Secondary((id \ + e9031146-cf7f-4ddf-9a10-9c631c8f5621)(content(Whitespace\" \ + \"))))(Tile((id \ + c6685701-3932-4485-a9ee-23efed57c09b)(label(fun \ + ->))(mold((out Exp)(in_(Pat))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 35))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 300d8def-9265-44f8-a3da-d622a8238fba)(content(Whitespace\" \ + \"))))(Projector((id \ + ea6855d4-e2d9-49da-9959-1f2836ee5134)(kind \ + Probe)(syntax(Tile((id \ + ea6855d4-e2d9-49da-9959-1f2836ee5134)(label(s))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 2ee1be25-dfef-4425-a25a-562425ba13c8)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 9eca80af-103e-4c40-9d4d-cffc3fffd253)(content(Whitespace\"\\n\"))))(Secondary((id \ + 0370dbfc-6f85-4a1c-98b5-4f870c1ab4ad)(content(Comment\"# Now \ + select `10` below, which is a call to `third`: \ + #\"))))(Secondary((id \ + ff092233-29e9-4a75-864b-6b1e1373262c)(content(Whitespace\"\\n\"))))(Projector((id \ + d8b80f6d-ca66-4f59-924d-f21193b089fa)(kind \ + Probe)(syntax(Tile((id \ + d8b80f6d-ca66-4f59-924d-f21193b089fa)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 83ff51e6-1fd8-4572-bb31-cc75ddd19674)(label(third))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 6890df9e-1759-4b95-90b2-c4314ff0b6df)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + e2b985cd-60e7-43e4-958d-4718f04abbf5)(label(2))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 56670704-b1d7-49f1-b64a-ab605ca488f5)(content(Whitespace\" \ + \"))))(Tile((id \ + 47cbfa2a-dd14-426c-aaa5-8df036fef201)(label(*))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 26))(sort \ + Exp))((shape(Concave 26))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 0e9b96d2-a5d3-4b48-b607-191952c18f2b)(content(Whitespace\" \ + \"))))(Tile((id \ + a159dd1f-1a1a-4af2-a3c2-08e7e3184f25)(label(s))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 5837d481-a939-4ef0-97fb-3fcdc94bb684)(content(Whitespace\" \ + \"))))(Tile((id \ + a6360712-cd42-40d9-b77c-da44be856ef5)(label(+))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 27))(sort \ + Exp))((shape(Concave 27))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 1d1878cc-be40-4c1e-9093-9526004b14c2)(content(Whitespace\" \ + \"))))(Tile((id \ + 0a44b84e-9fa6-45e8-bd83-a11286a96b8b)(label(2))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + e2b7dbca-a7b3-4774-abf0-59295bf517b0)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 194bacce-ebc6-47bb-a81f-474d25e14f8c)(content(Whitespace\"\\n\"))))(Secondary((id \ + 514e8a18-f1c3-4040-ad1e-ce08d1866505)(content(Whitespace\" \ + \"))))(Secondary((id \ + 8bcc21ca-f2eb-47ed-ad75-7d8c9f476d90)(content(Whitespace\" \ + \"))))(Secondary((id \ + e432c424-8fd3-48b4-9c30-baa97f8b538b)(content(Comment\"# Note \ + that `32` and `9` stay outlined, but are now dimmed, \ + #\"))))(Secondary((id \ + a462fd1a-1ad4-4711-b4f2-6b84043b9ec3)(content(Whitespace\"\\n\"))))(Secondary((id \ + f0262381-3ea5-4a77-b9a5-56d9362d1eab)(content(Whitespace\" \ + \"))))(Secondary((id \ + a084251c-e653-481c-aa20-502a0313ee2d)(content(Whitespace\" \ + \"))))(Secondary((id \ + edf23633-1cab-47a0-86ca-81f96ad7a968)(content(Comment\"# \ + since they come from a call \\\\in a call\\\\ to `third`. \ + `12` #\"))))(Secondary((id \ + 31f57084-1f32-4c7a-9fff-d5b61a38967f)(content(Whitespace\"\\n\"))))(Secondary((id \ + 9478ae5e-9503-4e32-a9e6-77f075c9cb2c)(content(Whitespace\" \ + \"))))(Secondary((id \ + 9ac6ba9d-edec-46b8-a8e0-2cc53b018fe0)(content(Whitespace\" \ + \"))))(Secondary((id \ + d52592af-52eb-484c-9e7a-e6fed4cec0d5)(content(Comment\"# and \ + the other `32` are outline as they are directly from \ + #\"))))(Secondary((id \ + 36692b75-20e8-4b98-841c-e1350e050f84)(content(Whitespace\"\\n\"))))(Secondary((id \ + ea5c6715-ab9a-4f88-9da8-4ac31ec93209)(content(Whitespace\" \ + \"))))(Secondary((id \ + 07bb069a-2721-4d94-933b-f1e5d5238c6d)(content(Whitespace\" \ + \"))))(Secondary((id \ + b0614927-3a1a-48c0-96c5-e093509dcae3)(content(Comment\"# the \ + call to `third`. #\"))))(Secondary((id \ + 8c5f4629-286a-4837-836b-6a3eca6e848a)(content(Whitespace\"\\n\"))))(Tile((id \ + 5ff3f001-e57c-457b-a793-e1ac39f36b01)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + fd4070e2-cd56-4900-a487-32d201315e17)(content(Whitespace\" \ + \"))))(Tile((id \ + ec9e7c80-bc99-4071-a462-436549d0e797)(label(first))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + 8023e887-ef1e-46a1-be1b-f24ffb83c519)(content(Whitespace\" \ + \")))))((Secondary((id \ + e525abca-5049-45c8-aec4-4eee6e812953)(content(Whitespace\" \ + \"))))(Tile((id \ + 4add151a-e3b1-4e04-8dd7-b58328e4f040)(label(fun \ + ->))(mold((out Exp)(in_(Pat))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 35))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 04772292-fdc8-4b40-9a22-7138c6c9c065)(content(Whitespace\" \ + \"))))(Projector((id \ + 4dba3dc5-6e28-4de1-8427-1e7bb1b92455)(kind \ + Probe)(syntax(Tile((id \ + 4dba3dc5-6e28-4de1-8427-1e7bb1b92455)(label(f))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 30f629a3-ac23-4406-aada-20e87638273b)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 0f2deaeb-c710-4493-9e0e-b02f0933e5df)(content(Whitespace\" \ + \"))))(Secondary((id \ + 97b6f168-1755-4955-8ed9-67b282cb7967)(content(Whitespace\"\\n\"))))(Projector((id \ + 355ea9a0-0dcb-489f-8b65-a186ff68f6c8)(kind \ + Probe)(syntax(Tile((id \ + 355ea9a0-0dcb-489f-8b65-a186ff68f6c8)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 1b1adb02-c753-4c41-84f2-c0d7b1b7092e)(label(second))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 0a342842-94a0-48f8-b822-503599390f4a)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 3bbdf14b-c425-47a9-8564-b9b9bb4a7e47)(label(f))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + c0b40bd6-9bea-4bc8-a2aa-e581d586600d)(content(Whitespace\" \ + \"))))(Tile((id \ + 6afef7d4-2740-4462-a6bc-9861dcab73ff)(label(+))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 27))(sort \ + Exp))((shape(Concave 27))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + b007c652-05d4-4102-9319-da7f135fcea3)(content(Whitespace\" \ + \"))))(Tile((id \ + 933a09f6-b81e-48c4-b6a2-d38350faa49a)(label(1))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 98d68578-38c2-4791-a68f-14195d9e9ba9)(content(Whitespace\" \ + \"))))(Tile((id \ + 365455db-c825-4700-b86a-4aa0695cd7d3)(label(*))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 26))(sort \ + Exp))((shape(Concave 26))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 87263b4a-bba0-495a-9b1e-f066feddc0c4)(content(Whitespace\" \ + \"))))(Tile((id \ + d91c14a5-9ca9-4cf3-8285-6410365e0114)(label(2))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + ca96f4e7-fc99-4654-8cd6-52493c01ae4a)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + b4442ac4-cbb9-44f5-9911-7f77a9cdfd7d)(content(Whitespace\"\\n\"))))(Projector((id \ + cd624e97-f51d-4310-b622-6eae20023b8b)(kind \ + Probe)(syntax(Tile((id \ + cd624e97-f51d-4310-b622-6eae20023b8b)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + e98de5e1-368d-4c62-87e2-70dc67bb07f6)(label(first))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + cebdb748-72e4-4d22-b5b7-dfdf2115bd5d)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + fb1c8aaf-7386-470e-a9f0-1c11697d676e)(label(5))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 5a49e6ff-bb58-49bd-93c9-4d1347d2f44e)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 75e108ca-3987-4705-b0ff-fd98eac3832d)(content(Whitespace\"\\n\"))))(Secondary((id \ + 38ab14e8-b614-43e0-967f-ab8de0751a5a)(content(Whitespace\"\\n\"))))(Secondary((id \ + e39807a4-7e88-411b-88b4-d118659ef17d)(content(Comment\"# \ + BRANCHING IN FUNCTIONS #\"))))(Secondary((id \ + 16cfc601-f896-4f12-9cf3-d5290aed9eb1)(content(Whitespace\"\\n\"))))(Tile((id \ + 8df51538-1978-4be0-80a0-3ee27f1bff7a)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 419e221c-557b-43ca-8c12-3374cb06a06b)(content(Whitespace\" \ + \"))))(Tile((id \ + e96d2945-369d-43d3-b005-de58b46435d1)(label(cases))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + 9894117d-a831-452a-9cc9-45525f5607c6)(content(Whitespace\" \ + \")))))((Secondary((id \ + a3912d0f-4d08-42a5-8211-c4a6f5d44b1b)(content(Whitespace\"\\n\"))))(Secondary((id \ + 63bedade-3a98-4dcd-8195-adcbddda3948)(content(Comment\"# \ + Select `6` then `5` then '4' below: #\"))))(Secondary((id \ + f135f8df-7e21-48c6-b7a0-2cac3f50f077)(content(Whitespace\"\\n\"))))(Tile((id \ + 2f5c0a90-7ee1-4feb-99bd-cd947eeda018)(label(fun \ + ->))(mold((out Exp)(in_(Pat))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 35))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 582e08b3-0e74-4452-8af5-2edbd0c415c5)(content(Whitespace\" \ + \"))))(Projector((id \ + e23d4644-0a1c-4f67-87b9-62231714a828)(kind \ + Probe)(syntax(Tile((id \ + e23d4644-0a1c-4f67-87b9-62231714a828)(label(x))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + f6668a8e-3e2e-476e-97a1-3d8546635b82)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + c30324b4-a927-49c1-bd7e-e13866dd1b3a)(content(Whitespace\" \ + \"))))(Tile((id \ + 86762137-94c7-40cc-9710-4e4aaece92fd)(label(case \ + end))(mold((out Exp)(in_(Rul))(nibs(((shape Convex)(sort \ + Exp))((shape Convex)(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 4678c3eb-9aac-4eb9-adba-a31a59507ca0)(content(Whitespace\" \ + \"))))(Tile((id \ + 0bc2e777-f131-495a-bb1f-de96b072eed4)(label(x))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + a53a3039-d6a5-4160-8539-3485cd29cd37)(content(Whitespace\" \ + \"))))(Secondary((id \ + 54504598-ff5a-47e2-894f-26b95a876260)(content(Whitespace\"\\n\"))))(Secondary((id \ + 296639cd-4894-4fff-ad26-b0b90d43d19d)(content(Comment\"# Note \ + how each activate exactly one branch below: \ + #\"))))(Secondary((id \ + 71afb479-6482-415e-af35-b5c77fa8ab25)(content(Whitespace\"\\n\"))))(Tile((id \ + 4be2d962-7107-4dfa-ba8e-c69a0e5c1058)(label(| =>))(mold((out \ + Rul)(in_(Pat))(nibs(((shape(Concave 41))(sort \ + Exp))((shape(Concave 41))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + a0eea7af-8b0b-4132-92c5-606b74fd9a9b)(content(Whitespace\" \ + \"))))(Tile((id \ + c4e11b3b-070f-4f0a-8919-e62184795f0f)(label(4))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + 811941af-615d-4460-a676-db2a5349339b)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + a7c3b49f-c2b2-45dd-b93a-190b200f3ca5)(content(Whitespace\" \ + \"))))(Projector((id \ + a3403667-1557-4390-8136-786b921ad704)(kind \ + Probe)(syntax(Tile((id \ + a3403667-1557-4390-8136-786b921ad704)(label(true))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 1dc62810-64da-4d13-9dfd-5831dfced3e3)(content(Whitespace\"\\n\"))))(Secondary((id \ + 7273879b-beb7-4071-a7de-fc8452342fbb)(content(Comment\"# \ + Select the `5` above and then the `false` below: \ + #\"))))(Secondary((id \ + 27390f7e-1da9-4d1c-b321-f5cd3d41dcf5)(content(Whitespace\"\\n\"))))(Tile((id \ + 2cb1e61f-de08-46a8-b7c1-3030199bc687)(label(| =>))(mold((out \ + Rul)(in_(Pat))(nibs(((shape(Concave 41))(sort \ + Exp))((shape(Concave 41))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 9ea1b8bd-980d-4e71-9bef-72d8045b64e8)(content(Whitespace\" \ + \"))))(Tile((id \ + e4d33554-fdeb-4b6b-8752-5f76f8a3c5b0)(label(5))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + 3e0d0295-e62d-4c08-b612-a59226953ca0)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + cec3af22-f6d0-4333-8eb7-f8ecb77e3209)(content(Whitespace\" \ + \"))))(Projector((id \ + 5c5878bf-b6fe-4f14-990e-bc6e20c8311c)(kind \ + Probe)(syntax(Tile((id \ + 5c5878bf-b6fe-4f14-990e-bc6e20c8311c)(label(false))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 0551c870-96c1-47c1-a782-a4132a5f6d3c)(content(Whitespace\"\\n\"))))(Secondary((id \ + 6e762ae1-6034-4e36-a285-e033503a1351)(content(Comment\"# Note \ + the same things are highlit as both cells are \ + #\"))))(Secondary((id \ + 0a4afa03-f568-4258-8849-5def9513fe5a)(content(Whitespace\"\\n\"))))(Secondary((id \ + d5ee04dc-b268-48f3-b806-84340f7e23bb)(content(Comment\"# from \ + the same call to cases#\"))))(Secondary((id \ + 639df91a-ad63-474d-95e0-0432fd72eafa)(content(Whitespace\"\\n\"))))(Tile((id \ + cc1776aa-89b4-477b-bbab-127de74af993)(label(| =>))(mold((out \ + Rul)(in_(Pat))(nibs(((shape(Concave 41))(sort \ + Exp))((shape(Concave 41))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + fc3cf54b-42cc-4894-b66f-b741cefe76b0)(content(Whitespace\" \ + \"))))(Tile((id \ + 0115b75e-4bf0-4823-a2b5-5b648c45a8da)(label(_))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + fbd79d5c-94be-44da-ab15-404c19db7e14)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + d9178536-2587-426e-8944-a8061e6373c5)(content(Whitespace\" \ + \"))))(Projector((id \ + 0391b8a0-372a-4287-8066-308dc5acc0e2)(kind \ + Probe)(syntax(Tile((id \ + 0391b8a0-372a-4287-8066-308dc5acc0e2)(label(true))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 47d31cb1-f77a-4e3b-90f4-3913f255486e)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + ef12080f-331e-4c8c-bc23-c542bd1ae245)(content(Whitespace\" \ + \"))))(Secondary((id \ + fce5bf02-e138-44e9-b251-9a770bca056a)(content(Whitespace\" \ + \"))))(Secondary((id \ + face2847-6176-475e-9403-a5394be18871)(content(Whitespace\"\\n\")))))))))(Secondary((id \ + c4ccca77-ab15-41bc-a665-07b6982280ba)(content(Whitespace\" \ + \"))))(Tile((id \ + 9178a18b-9f4c-4657-9075-c95a7352d163)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 16f2722a-bafe-4531-94ba-41e010d5a479)(content(Whitespace\" \ + \"))))(Tile((id \ + f5cb9b60-a132-4f6d-853b-d49a57a8fd5f)(label(_))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + 911b6f8c-2b4a-470f-a3e2-149b69d9c0a1)(content(Whitespace\" \ + \")))))((Secondary((id \ + e4479f5c-6640-41ec-827c-4938006354ba)(content(Whitespace\" \ + \"))))(Tile((id \ + 6d757281-23e5-4eed-94e5-c50171bca21c)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Secondary((id \ + acf8e8e0-c768-4e81-be9a-ec86206670b9)(content(Whitespace\"\\n\"))))(Secondary((id \ + 63d09d0d-6b42-4ccd-98ae-60c1170124d3)(content(Comment\"# \ + Select `true` below and then the `4` cell \ + #\"))))(Secondary((id \ + e413914c-3e8a-464c-9998-49b0fd9008de)(content(Whitespace\"\\n\"))))(Secondary((id \ + f32530b7-90f1-4506-8809-88e96c5979cb)(content(Comment\"# for \ + the argument x to `cases` above. #\"))))(Secondary((id \ + b3476671-b95c-4a3e-83bd-a92d78e0c6fe)(content(Whitespace\"\\n\"))))(Projector((id \ + 45f9c1d8-6ea3-405a-b62a-c5ea1ebf99d9)(kind \ + Probe)(syntax(Tile((id \ + 45f9c1d8-6ea3-405a-b62a-c5ea1ebf99d9)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 2e403537-3961-4787-a743-c227a3105da9)(label(cases))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 53cfcaca-bd07-4c13-b07e-a4a7c0ae42bf)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 6d485cac-18e0-42d9-b668-02ebe48f6fe5)(label(4))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Tile((id \ + 0f5c7e17-6af9-43c9-8992-9aad6fb339e9)(label(,))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 45))(sort \ + Exp))((shape(Concave 45))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + e0928317-f0ea-4269-9f58-a900dc78331d)(content(Whitespace\"\\n\"))))(Secondary((id \ + b1a19cdc-0dad-43b7-8a80-1e5a0d1ad59d)(content(Comment\"# Note \ + how the same cells stay indicated, but the kind \ + #\"))))(Secondary((id \ + 4e44fff2-d51b-4f44-8420-65504ce86dbd)(content(Whitespace\"\\n\"))))(Secondary((id \ + 5fcfb118-c2e8-4a73-b7c6-332b5bdd683e)(content(Comment\"# of \ + indication changes. The `true` below the `4` above \ + #\"))))(Secondary((id \ + 75e5e97e-f0d0-4444-ad6a-c6cd1adcc40d)(content(Whitespace\"\\n\"))))(Secondary((id \ + 9cc83f18-c05a-46a6-bdf4-68a8aaeb8e60)(content(Comment\"# goes \ + from purple outline (created by the cases(4) call) \ + #\"))))(Secondary((id \ + d1e664ac-9e67-4e94-86e9-e568f8ec069f)(content(Whitespace\"\\n\"))))(Secondary((id \ + 6efa56f5-c7aa-489f-b8fd-9d172a2396e9)(content(Comment\"# to \ + green highlighting (part of the same call as `4`). \ + #\"))))(Secondary((id \ + 0873539a-9518-4619-8baa-493f32a883f2)(content(Whitespace\"\\n\"))))(Secondary((id \ + f18db2ec-c13f-45e3-8488-5175363d57f6)(content(Comment\"# The \ + formerly selected lower `true` is now highlit in \ + #\"))))(Secondary((id \ + bab4b3a9-31dd-4a44-92ec-be5f47eb640c)(content(Whitespace\"\\n\"))))(Secondary((id \ + f990f5be-7a6f-445d-8194-239015680b7c)(content(Comment\"# \ + purple since it indicates the call where `4` lives . \ + #\"))))(Secondary((id \ + 49904549-31ef-4039-89c7-936b4bb825b7)(content(Whitespace\"\\n\"))))(Projector((id \ + 3f04dfdd-fbf4-4e57-9e87-285991ed7560)(kind \ + Probe)(syntax(Tile((id \ + 3f04dfdd-fbf4-4e57-9e87-285991ed7560)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + c4d465d9-072c-46f5-87c6-c80c1d4ff20f)(label(cases))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 9d775566-ec89-4d50-ac22-857c93136a05)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + b5147ffa-2884-4570-93ad-afccaed72014)(label(5))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Tile((id \ + aabf7d1d-6a80-4766-ac05-f1e837759de2)(label(,))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 45))(sort \ + Exp))((shape(Concave 45))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 54a03381-eb59-4d73-ae39-4786bc8272e5)(content(Whitespace\"\\n\"))))(Projector((id \ + 417d1c78-cfa8-4277-a454-63c8f895338f)(kind \ + Probe)(syntax(Tile((id \ + 417d1c78-cfa8-4277-a454-63c8f895338f)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 5289e4b3-c220-4645-bf53-a7e1dc1568b2)(label(cases))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 466caa09-a4da-451b-b564-72b3f83d1bd8)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 96f3b987-c9a1-4dff-84dd-a18b2676084e)(label(6))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 2ca79749-48bd-4232-b4fa-0a969ec9d87a)(content(Whitespace\"\\n\")))))))))(Secondary((id \ + 9dd57662-30f4-480d-824d-a9b7d7600944)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + f7239c24-2f02-42ce-9dca-6c4599814f85)(content(Whitespace\"\\n\"))))(Secondary((id \ + 10fdb2c6-6379-4f77-9411-feed6e730480)(content(Whitespace\"\\n\"))))(Secondary((id \ + 234dab0b-9d42-4940-99ce-f0c7a4713c68)(content(Comment\"# \ + FUNCTIONS IN FUNCTIONS #\"))))(Secondary((id \ + 8f0d3403-e3c7-410d-8054-cfccef303571)(content(Whitespace\"\\n\")))))((Secondary((id \ + f0c7ccbd-0a24-46b9-939a-4dafbe1a02e0)(content(Whitespace\" \ + \"))))(Tile((id \ + 31590c3b-df44-4013-a78c-0cca198f1b4a)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 669c8db4-2c50-4950-91a8-3dc7eefbd7c9)(content(Whitespace\" \ + \"))))(Tile((id \ + fc1069d8-b5f7-4b89-9942-8fdcdaee62cf)(label(_))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Secondary((id \ + 445206be-45be-4d9b-b11e-3a5225e8406b)(content(Whitespace\" \ + \")))))((Secondary((id \ + 49b98f40-e962-4440-8595-dac19922ab6e)(content(Whitespace\" \ + \"))))(Secondary((id \ + e179b26c-24ec-40d2-af7a-fca96781e212)(content(Whitespace\" \ + \"))))(Tile((id \ + 116bdadd-1916-4d9a-bee5-1070938cac88)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Secondary((id \ + b1cedbce-5bbb-41e3-9b19-b7c6875d5df6)(content(Whitespace\"\\n\"))))(Projector((id \ + 4c0af00f-fc68-4a0d-86a9-72ec21ca8142)(kind \ + Probe)(syntax(Tile((id \ + 4c0af00f-fc68-4a0d-86a9-72ec21ca8142)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 96f3d011-9e02-4c75-a73f-d1f924baa3d1)(label(outer))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 91b9e262-82b2-4a56-ac6c-ddcb4c62754c)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + a76f065e-380f-4b72-865f-588c68bbcbe2)(label(4))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Tile((id \ + a3532a75-ab50-4760-b986-a19f39892452)(label(,))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 45))(sort \ + Exp))((shape(Concave 45))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + edae6488-6dfa-4876-b435-666b350c6b62)(content(Whitespace\"\\n\"))))(Projector((id \ + 165c1596-820f-4dee-9028-0b4d6eeb3054)(kind \ + Probe)(syntax(Tile((id \ + 165c1596-820f-4dee-9028-0b4d6eeb3054)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 1a39666a-1dca-4d6e-b444-0fda6766fdec)(label(outer))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + a8fd8565-3b2e-45a1-8164-71fa0a54335e)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 4dd547cd-1d0b-4a8e-b524-8c81506d90e0)(label(6))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Tile((id \ + 63e032f6-46c1-4250-8749-57f7c1ffa9f3)(label(,))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 45))(sort \ + Exp))((shape(Concave 45))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 7cde06b6-a558-4b40-9055-4357ac977a32)(content(Whitespace\"\\n\"))))(Projector((id \ + e4b6e723-5940-49ab-9e3e-7f04de2e822c)(kind \ + Probe)(syntax(Tile((id \ + e4b6e723-5940-49ab-9e3e-7f04de2e822c)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 10dc6700-5e21-4721-95e8-6152a8cb1913)(label(outer))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 231add25-cd68-465f-b99f-9becbbf5b7ae)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + d134a9c5-bdce-4088-85cb-37092e366a63)(label(4))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 5a16d272-742b-4feb-96a8-c78a634655c8)(content(Whitespace\" \ + \"))))(Tile((id \ + 1552efae-0c33-4d30-a8dc-30e35f9916be)(label(+))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 27))(sort \ + Exp))((shape(Concave 27))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 1da56a90-47d2-41d4-989e-7ec684250003)(content(Whitespace\" \ + \"))))(Tile((id \ + 92b1c66c-ee14-4e5e-b1c5-654928572d4c)(label(4))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + eab16292-9631-4d40-bac5-f6dfa036f85b)(content(Whitespace\"\\n\")))))))))(Secondary((id \ + ec0d8917-468f-46ac-92ca-a1f50907df0f)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 803b1153-93dc-4548-b67e-63ff592b6c00)(content(Whitespace\"\\n\"))))(Secondary((id \ + ed717a7a-068b-4ef2-a808-0b2b182616ab)(content(Whitespace\"\\n\"))))(Secondary((id \ + 615f17f8-5a69-461e-8b9a-a9539c2b3bcb)(content(Comment\"# \ + RECURSION #\"))))(Secondary((id \ + 5276808f-1f94-49d8-8a68-d35cf4d1375b)(content(Whitespace\"\\n\"))))(Tile((id \ + 953a0f2c-a18e-48ff-b8ab-4363443814bf)(label(let = \ + in))(mold((out Exp)(in_(Pat Exp))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 38))(sort Exp))))))(shards(0 1 \ + 2))(children(((Secondary((id \ + 845a0640-839c-4649-b11c-298ac5e9f741)(content(Whitespace\" \ + \"))))(Tile((id \ + 8412bbd7-b7a6-4391-a4e1-ee6245f36c76)(label(fib))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort Pat))))))(shards(0))(children())))(Tile((id \ + 3fab4407-877d-4759-a203-19efbbe02a37)(label(:))(mold((out \ + Pat)(in_())(nibs(((shape(Concave 23))(sort \ + Pat))((shape(Concave 23))(sort \ + Typ))))))(shards(0))(children())))(Secondary((id \ + 60b7e274-1ed2-4a92-8612-9edfe6387a1a)(content(Whitespace\" \ + \"))))(Tile((id \ + 20e6c6de-efa0-41d1-bbf7-9e9700550c88)(label(Int))(mold((out \ + Typ)(in_())(nibs(((shape Convex)(sort Typ))((shape \ + Convex)(sort Typ))))))(shards(0))(children())))(Secondary((id \ + 6fbbc886-38d3-4b76-8455-66ccce9e2738)(content(Whitespace\" \ + \"))))(Tile((id \ + 6d61eab8-4208-4749-a318-724cd760056f)(label(->))(mold((out \ + Typ)(in_())(nibs(((shape(Concave 13))(sort \ + Typ))((shape(Concave 13))(sort \ + Typ))))))(shards(0))(children())))(Secondary((id \ + b3f2c5b1-66c4-4d3a-9dd6-d9b07da3e121)(content(Whitespace\" \ + \"))))(Tile((id \ + e35cd85b-928e-48b9-9a48-c22ad3e394a9)(label(Int))(mold((out \ + Typ)(in_())(nibs(((shape Convex)(sort Typ))((shape \ + Convex)(sort Typ))))))(shards(0))(children())))(Secondary((id \ + 039cea30-dbd8-481f-926b-b0bfd8d7456e)(content(Whitespace\" \ + \")))))((Secondary((id \ + cc4b9c25-8e26-4187-b770-fed18f904b52)(content(Whitespace\"\\n\"))))(Secondary((id \ + d82e8e94-caaf-48e8-b152-3a6c1e767b57)(content(Comment\"# \ + Recursive calls can complicate probe display due \ + #\"))))(Secondary((id \ + de0d3cbd-f0ea-4bab-8d5a-8ec24da533fd)(content(Whitespace\"\\n\"))))(Secondary((id \ + e4d221fe-b137-41fb-9231-f3a27e823af2)(content(Comment\"# due \ + to overlapping information channels.#\"))))(Secondary((id \ + 55e31bfc-9841-43fe-ba37-c09ab971dad8)(content(Whitespace\"\\n\"))))(Tile((id \ + 64995a45-4dba-4c4e-9382-79f573fba433)(label(fun \ + ->))(mold((out Exp)(in_(Pat))(nibs(((shape Convex)(sort \ + Exp))((shape(Concave 35))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 16d0f9c4-a401-4b0d-beb2-c915134e929a)(content(Whitespace\" \ + \"))))(Projector((id \ + 974da281-9d98-40a5-b7b2-da1ae9227191)(kind \ + Probe)(syntax(Tile((id \ + 974da281-9d98-40a5-b7b2-da1ae9227191)(label(x))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 87db44f1-5880-42ce-8f43-03ca4e33ce18)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 6e314be8-ef4d-48ba-826f-2e6d960e7f0e)(content(Whitespace\" \ + \"))))(Tile((id \ + 7bbb9c8c-989d-4fbd-be33-123a3c2f6d16)(label(case \ + end))(mold((out Exp)(in_(Rul))(nibs(((shape Convex)(sort \ + Exp))((shape Convex)(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 1b488629-e6a2-4865-a401-d387d73eb510)(content(Whitespace\" \ + \"))))(Tile((id \ + ff36ea5f-605f-44cc-a237-cec7d1340378)(label(x))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 6028432f-0dad-4d2e-9c08-616aab9bf8fe)(content(Whitespace\"\\n\"))))(Tile((id \ + 0cdc5e54-71c6-43d3-bd4f-16cf6d94218f)(label(| =>))(mold((out \ + Rul)(in_(Pat))(nibs(((shape(Concave 41))(sort \ + Exp))((shape(Concave 41))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 4eead0ed-8e3b-4524-88c3-6a4fcfcf99e2)(content(Whitespace\" \ + \"))))(Projector((id \ + d8ab6ddf-79bb-4350-9cf0-7d9c5f4fbbf2)(kind \ + Probe)(syntax(Tile((id \ + d8ab6ddf-79bb-4350-9cf0-7d9c5f4fbbf2)(label(0))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 915f9a2e-c34f-4b6c-8912-08dee95c4f49)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + b45c678f-12dc-4c4d-887b-c76cbc4e8704)(content(Whitespace\" \ + \"))))(Tile((id \ + c2443d47-fccf-4682-bacc-e5b90dbb4c9e)(label(1))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 1bad3366-c6a6-4b57-a9ce-ede407c987d6)(content(Whitespace\"\\n\"))))(Tile((id \ + 403bad8b-5549-4264-a2bf-2e8b22564ce2)(label(| =>))(mold((out \ + Rul)(in_(Pat))(nibs(((shape(Concave 41))(sort \ + Exp))((shape(Concave 41))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 4f6b1685-990f-48b4-8f36-d38c8b285223)(content(Whitespace\" \ + \"))))(Projector((id \ + 8891e6db-7340-4bee-8eca-0ae21dfa9ee3)(kind \ + Probe)(syntax(Tile((id \ + 8891e6db-7340-4bee-8eca-0ae21dfa9ee3)(label(1))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + f9c55ed4-5adf-4649-9b61-2b775ad340d7)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 74a7f9d5-6b59-4b2f-a43a-ee064dca5828)(content(Whitespace\" \ + \"))))(Tile((id \ + 107d2dbc-f3cd-4074-8b06-59f6626e53e7)(label(1))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Secondary((id \ + 463fdd55-494f-413f-9ce9-9dc40a55890e)(content(Whitespace\"\\n\"))))(Tile((id \ + d71d3d18-6267-4240-8331-d23114501851)(label(| =>))(mold((out \ + Rul)(in_(Pat))(nibs(((shape(Concave 41))(sort \ + Exp))((shape(Concave 41))(sort Exp))))))(shards(0 \ + 1))(children(((Secondary((id \ + 90667981-258e-4d09-ab33-b03958ad1246)(content(Whitespace\" \ + \"))))(Projector((id \ + b5152244-d625-4b5e-a946-f5ff545efc0d)(kind \ + Probe)(syntax(Tile((id \ + b5152244-d625-4b5e-a946-f5ff545efc0d)(label(n))(mold((out \ + Pat)(in_())(nibs(((shape Convex)(sort Pat))((shape \ + Convex)(sort \ + Pat))))))(shards(0))(children()))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 7d1cadd0-e77f-4410-b644-27fdfb938852)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 39475c8e-4af0-4af1-ad59-6ac043416a44)(content(Whitespace\" \ + \"))))(Secondary((id \ + 237aacb0-5c9d-4d98-9d8a-0978c33c54d7)(content(Whitespace\" \ + \"))))(Secondary((id \ + f9f719ce-0db3-4a90-b2fe-202996b72912)(content(Whitespace\" \ + \"))))(Secondary((id \ + 583884b6-7031-48f1-9bec-7f5471fefd9e)(content(Whitespace\"\\n\"))))(Secondary((id \ + f0f9c7de-181d-440a-ae90-3a6efb931b80)(content(Comment\"# \ + Select the first `1` below: #\"))))(Secondary((id \ + a2e1370d-263c-46ad-b102-e99d9b5fdbd3)(content(Whitespace\"\\n\"))))(Projector((id \ + 92889848-c7f6-48df-bafa-53fec43b90ca)(kind \ + Probe)(syntax(Tile((id \ + 92889848-c7f6-48df-bafa-53fec43b90ca)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + d9de4da9-55d2-4e14-822b-f49dbb46fa1c)(label(fib))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + d8502f58-3b20-4f50-a763-f6bdc272d84c)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + a6bc0c08-b088-40d0-89fb-7835d8990546)(label(x))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + dd9c851b-d4be-4129-9da7-491511610efd)(label(-))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 27))(sort \ + Exp))((shape(Concave 27))(sort \ + Exp))))))(shards(0))(children())))(Tile((id \ + 9c01433b-9d8b-4662-8420-3e7158516380)(label(1))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + dea768f6-5c16-4f5d-87a0-2dc0e4a1e1d7)(content(Whitespace\"\\n\"))))(Secondary((id \ + 2fb0dc74-8a03-4bbf-9ab9-4175ee599bcb)(content(Comment\"# Note \ + the purple `2` below corresponding the call \ + #\"))))(Secondary((id \ + a4d1984f-af90-4f40-8dde-5a8e8d94f292)(content(Whitespace\"\\n\"))))(Secondary((id \ + 5efbec55-a911-4af0-b4f3-b9e9dafac26c)(content(Comment\"# \ + fib(4-2) which contains the above `1`. The `1` below \ + #\"))))(Secondary((id \ + 3602dfd4-0c68-4199-9445-45137f2aa8b4)(content(Whitespace\"\\n\"))))(Secondary((id \ + 6eda1e61-6bfe-4ffe-9f47-788fb6b139f9)(content(Comment\"# OTOH \ + is highlit because when the above call was made, \ + #\"))))(Secondary((id \ + ab635a3a-44f5-4190-885f-28606c9b3b94)(content(Whitespace\"\\n\"))))(Secondary((id \ + 797f467b-7ede-42c9-bc67-3322aa4c8a2e)(content(Comment\"# the \ + call below had that value. The two `1s` outline in \ + #\"))))(Secondary((id \ + fa4f28b9-679d-4a30-968a-ba1680871749)(content(Whitespace\"\\n\"))))(Secondary((id \ + f8befcab-9928-4744-84c9-6795774c272f)(content(Comment\"# \ + purple above come /from/ the indicated call, whereas the \ + #\"))))(Secondary((id \ + 2a3c394c-bb8a-44b1-a068-0afb978ea9ba)(content(Whitespace\"\\n\"))))(Secondary((id \ + cd93b56a-7c9e-4cd5-8315-c743a41d9644)(content(Comment\"# \ + highlit `2`s are from the /same/ call the indicated call \ + #\"))))(Secondary((id \ + 9d4a1042-11e2-4233-9fa0-4179134b6b55)(content(Whitespace\"\\n\"))))(Secondary((id \ + 81f3f6af-b197-4a7d-835f-777603ea4c2b)(content(Comment\"# was \ + evaluated in. #\"))))(Secondary((id \ + 9f5da5f3-8957-4dbd-b760-c2c654406f6c)(content(Whitespace\"\\n\"))))(Tile((id \ + 10ba6aec-a296-4616-9f66-c07bfc457389)(label(+))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 27))(sort \ + Exp))((shape(Concave 27))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + e3805890-f33e-40f3-97a4-9042590b5751)(content(Whitespace\" \ + \"))))(Projector((id \ + 2a82dc5e-8758-4966-a2c7-f0bbe9eb3433)(kind \ + Probe)(syntax(Tile((id \ + 2a82dc5e-8758-4966-a2c7-f0bbe9eb3433)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + f0f88d79-9050-4669-b74b-1f3896ce404d)(label(fib))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 7e963d15-b543-4c12-b97f-1b9daabfc132)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + b72452ba-6de6-43c8-8c58-677d8d7e4c82)(label(x))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 46252f35-e75f-4e1e-97c0-cd970539ae4d)(label(-))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 27))(sort \ + Exp))((shape(Concave 27))(sort \ + Exp))))))(shards(0))(children())))(Tile((id \ + cd459c90-6a46-4cc2-a437-24048f37d510)(label(2))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Secondary((id \ + 969e08d5-4db2-4515-82fb-fb6580de9838)(content(Whitespace\" \ + \")))))))))(Secondary((id \ + 38431bba-98f6-4973-aab9-19718f673035)(content(Whitespace\" \ + \"))))(Secondary((id \ + 74dd9640-2158-46ad-9268-ba1a6f2173f3)(content(Whitespace\"\\n\")))))))))(Secondary((id \ + 1dc924d0-84fb-44ca-abb6-265730fc500b)(content(Whitespace\" \ + \"))))(Tile((id \ + 8973c2ac-1e8e-48bd-9755-37acd220cbc5)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Secondary((id \ + 20f087ad-f08b-44c7-91df-86a49c1628f9)(content(Whitespace\"\\n\"))))(Projector((id \ + a5bc4575-9cfb-4990-97a8-c8c1e3f37b47)(kind \ + Probe)(syntax(Tile((id \ + a5bc4575-9cfb-4990-97a8-c8c1e3f37b47)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 4bd22e68-7383-44b2-a0a9-e43469d927a3)(label(fib))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + fc19962d-3a75-4e2f-af6d-95ad45c95512)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + c8f08840-15b5-4001-a2ba-63f4110a8285)(label(1))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Tile((id \ + b36314dc-8d7f-4943-b4ed-052e0e9ba4cc)(label(,))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 45))(sort \ + Exp))((shape(Concave 45))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + ec75665c-f44b-417b-8c80-1084f7948e7c)(content(Whitespace\"\\n\"))))(Projector((id \ + 18b8ec42-6f93-44b2-9b05-3a74bbd0331c)(kind \ + Probe)(syntax(Tile((id \ + 18b8ec42-6f93-44b2-9b05-3a74bbd0331c)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + a9fb4968-1537-4a44-8bd4-b5553d78ea96)(label(fib))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 9b5c1b4e-389b-4b4f-854d-df134ec80b9b)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 94e01eb2-8664-4538-b814-ea92ff5d7e07)(label(2))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Tile((id \ + 3d1d994e-0e73-4375-a7b9-147ab384fcb2)(label(,))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 45))(sort \ + Exp))((shape(Concave 45))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 4bcfc5c8-bff8-406e-804a-e308e12c58c0)(content(Whitespace\"\\n\"))))(Projector((id \ + 6b42473c-e83a-48fb-963c-34ac3b03e41f)(kind \ + Probe)(syntax(Tile((id \ + 6b42473c-e83a-48fb-963c-34ac3b03e41f)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 8a9685ca-5a3a-4870-86fd-e100f9602139)(label(fib))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 4ef8063e-8088-4532-835b-e12c465eb6e7)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 39c1385b-d3de-4a85-8262-9d0898e729d4)(label(3))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\")))(Tile((id \ + 35f6989e-6717-4141-bcea-48402069219d)(label(,))(mold((out \ + Exp)(in_())(nibs(((shape(Concave 45))(sort \ + Exp))((shape(Concave 45))(sort \ + Exp))))))(shards(0))(children())))(Secondary((id \ + 90bcfb6c-d21c-47f2-a738-485ba16aea96)(content(Whitespace\"\\n\"))))(Projector((id \ + be1fef4e-077b-4e5b-9957-3c5f32400e18)(kind \ + Probe)(syntax(Tile((id \ + be1fef4e-077b-4e5b-9957-3c5f32400e18)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 8c507206-69ff-4adf-b480-5bae74758277)(label(fib))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0))(children())))(Tile((id \ + 80464d32-cae5-4358-a33f-253529f7bf03)(label(\"(\"\")\"))(mold((out \ + Exp)(in_(Exp))(nibs(((shape(Concave 22))(sort Exp))((shape \ + Convex)(sort Exp))))))(shards(0 1))(children(((Tile((id \ + 7654c4a3-164c-4a29-8bea-9381a60b3e08)(label(4))(mold((out \ + Exp)(in_())(nibs(((shape Convex)(sort Exp))((shape \ + Convex)(sort \ + Exp))))))(shards(0))(children()))))))))))))))(model\"((display_lengths())(max_closures \ + 30)(index_offset 0))\"))))))))(Secondary((id \ + 52d4807f-d9fb-4409-b655-d2252754b948)(content(Whitespace\"\\n\"))))(Secondary((id \ + af4c152a-09bc-4bdd-bb82-0bb3f943e19a)(content(Whitespace\"\\n\"))))(Secondary((id \ + 8b23b024-6f92-40f3-ad68-827ebbd72487)(content(Comment\"# \ + ADVANCED FEATURES #\"))))(Secondary((id \ + f4660d47-fb9e-4bdb-b662-d31359e24157)(content(Whitespace\"\\n\"))))(Secondary((id \ + 093e6b59-7951-4c1b-a311-272636c40c76)(content(Comment\"# - \ + You can resize a cell by holding shift and dragging \ + horizontally #\")))))))))))(caret Outer))"; + backup_text = + "# _____ _ #\n\ + # | __ \\ | | #\n\ + # | |__) | __ ___ | |__ ___ ___ #\n\ + # | ___/ '__/ _ \\| '_ \\ / _ \\/ __| #\n\ + # | | | | | (_) | |_) | __/\\__ \\ #\n\ + # |_| |_| \\___/|_.__/ \\___||___/ #\n\ + # INLINE EVAL WITH PROBE PROJECTORS #\n\n\ + # INTRODUCTION #\n\n\ + # Probe projectors are a kind of inline evaluation, #\n\ + # similar to value hints in Emacs or IntelliJ. #\n\n\ + # You can put these on any expression or variable binding to #\n\ + # see a list of all values taken on by that \ + expression/pattern. #\n\ + # By default values are sorted by left-to-right by \ + most-recent. #\n\n\ + # More generally, each cell represents a stack state, #\n\ + # including the top stack frame / closure and hence the #\n\ + # expression's value, the values of environment variables, #\n\ + # as well as the surrounding call stack context. #\n\n\ + # When a cell is selected, you can hover over it to see #\n\ + # relevant environment variables, and all /other/ cells #\n\ + # are decorated according to their relative position in #\n\ + # to the selected cell. in the context #\n\n\ + # Probe are intended mostly as a println replacement #\n\ + # for exposing intermediate values, with the above \ + decorations #\n\ + # as a supporting feature to help maintain context when #\n\ + # navigating between multiple probed expressions, which #\n\ + # may take on many values across nested or recursive \ + functions. #\n\n\n\ + # TUTORIAL #\n\n\ + # The expression (10 * 10) below has a probe. #\n\ + # Its value, 20, is shown in a cell to the right. #\n\ + let sum = (10 + 10) in\n\n\ + # To probe the below expression, put your caret to #\n\ + # left of the `(` and press option/alt-v (for value), #\n\ + # or select `Probe` from the lower right corner menu: #\n\ + let drink_me = (1 + 2 * 3) in\n\ + # The expression should be encased in a green block #\n\ + # and a cell reading `7` should appear to the right. #\n\ + # The same shortcut or menu toggle removes it. #\n\n\ + # Click the below cell (with value 60) to select it. #\n\ + let thrice = (3 * drink_me) in\n\ + # Notice when you hover over a selected cell, it #\n\ + # shows the values of any contained variables. #\n\n\ + # Probes only have cells if the are evaluated. #\n\ + # Below, only the first case branch is evaluated. #\n\ + let maybe = if true then thrice else 0 in\n\ + let _ = case maybe\n\ + | 21 => \"necessary\"\n\ + | _ => \"impossible\"\n\ + end in\n\ + # Note the 2nd branch probe has a zero to the right. #\n\ + # This is the cell's collected closure count, i.e. #\n\ + # the number of times the expression was evaluated #\n\n\ + # Probes can be placed on expressions: #\n\ + let story = (50 ** 2) in\n\ + # And also on patterns (e.g. variables), shown in blue: #\n\ + let story = 50 ** 2 in\n\ + # Expressions currently CAN'T BE EDITED WHILE PROBED #\n\ + # So probing a name instead makes iteration easier. #\n\n\n\ + # FUNCTIONS #\n\ + let _ =\n\ + # Because functions can run multiple times, they can #\n\ + # have multiple cells. Note the closure counts below #\n\ + # are all 2, indicating each probe was evaluated twice. #\n\ + let celcius = fun farenheit ->\n\ + # Click to select the cell above reading 72.5 #\n\ + let diff = (farenheit -. 32.) in\n\ + # This highlights cells below corresponding to the same #\n\ + # function call: the cells reading 40.5 and 22.5) #\n\ + (5./.9. *. diff) in\n\ + let (t1, t2) = 72.5, 103.1 in (\n\ + # It also highlights in purple the cell #\n\ + # of the function's call site#\n\ + (celcius(t1)),\n\ + # Now select the cell above reading 22.5 #\n\ + (celcius(t2))\n\ + # Note the 103.1, 71.1, and 39.5 are no longer green-highlit #\n\ + # as they are not part of the same call as /the expression/ #\n\ + # `celcius(t1)`. However, they are now outlined in purple, as #\n\ + # they are contained in the callstack created by that call #\n\ + ) in\n\n\n\ + # FUNCTIONS CALLING FUNCTIONS #\n\ + let _ =\n\ + let fourth = fun f ->\n\ + (4 * f - 4) in\n\ + # Select `12` below, which reads the argument `t` of `third` #\n\ + let third = fun t -> \n\ + # Note `32` below is highlit as it's from the same call, #\n\ + # and the `10` below is purple because its the cell of #\n\ + # the relevant call to `third`. Now, select `32` below: #\n\ + (fourth(t - 3)) / 3 in\n\ + \ # Note that `10` stays purple as we're still in the same \ + call. #\n\ + \ # `9` and `32` above get purple outlines as they come from \ + the #\n\ + \ # call to `fourth` that we've now selected#\n\ + let second = fun s ->\n\ + # Now select `10` below, which is a call to `third`: #\n\ + (third(2 * s)) + 2 in\n\ + \ # Note that `32` and `9` stay outlined, but are now dimmed, #\n\ + \ # since they come from a call \\in a call\\ to `third`. \ + `12` #\n\ + \ # and the other `32` are outline as they are directly from #\n\ + \ # the call to `third`. #\n\ + let first = fun f -> \n\ + (second(f + 1)) * 2 in\n\ + (first(5)) in\n\n\ + # BRANCHING IN FUNCTIONS #\n\ + let cases =\n\ + # Select `6` then `5` then '4' below: #\n\ + fun x -> case x \n\ + # Note how each activate exactly one branch below: #\n\ + | 4 => true\n\ + # Select the `5` above and then the `false` below: #\n\ + | 5 => false\n\ + # Note the same things are highlit as both cells are #\n\ + # from the same call to cases#\n\ + | _ => true end \n\ + in let _ = (\n\ + # Select `true` below and then the `4` cell #\n\ + # for the argument x to `cases` above. #\n\ + (cases(4)),\n\ + # Note how the same cells stay indicated, but the kind #\n\ + # of indication changes. The `true` below the `4` above #\n\ + # goes from purple outline (created by the cases(4) call) #\n\ + # to green highlighting (part of the same call as `4`). #\n\ + # The formerly selected lower `true` is now highlit in #\n\ + # purple since it indicates the call where `4` lives . #\n\ + (cases(5)),\n\ + (cases(6))\n\ + ) in\n\n\ + # FUNCTIONS IN FUNCTIONS #\n\ + let outer = fun y ->\n\ + # Select `40` then `6` then '4' above. #\n\ + let a =\n\ + 10\n\ + * y in \n\ + # Note how inside the `inner` function below #\n\ + # two cells for each probe gets highlighted. #\n\ + let inner = fun z ->\n\ + (a + 2*z) in\n\ + # This is because there are two calls to `inner` #\n\ + # below for each call to the containing `outer`. #\n\ + (inner(3*a))\n\ + + (inner(5*y)) \n\ + in let _ = (\n\ + (outer(4)),\n\ + (outer(6)),\n\ + (outer(4 + 4))\n\ + ) in\n\n\ + # RECURSION #\n\ + let fib: Int -> Int =\n\ + # Recursive calls can complicate probe display due #\n\ + # due to overlapping information channels.#\n\ + fun x -> case x\n\ + | 0 => 1\n\ + | 1 => 1\n\ + | n => \n\ + # Select the first `1` below: #\n\ + (fib(x-1))\n\ + # Note the purple `2` below corresponding the call #\n\ + # fib(4-2) which contains the above `1`. The `1` below #\n\ + # OTOH is highlit because when the above call was made, #\n\ + # the call below had that value. The two `1s` outline in #\n\ + # purple above come /from/ the indicated call, whereas the #\n\ + # highlit `2`s are from the /same/ call the indicated call #\n\ + # was evaluated in. #\n\ + + (fib(x-2)) end \n\ + in (\n\ + (fib(1)),\n\ + (fib(2)),\n\ + (fib(3)),\n\ + (fib(4)))\n\n\ + # ADVANCED FEATURES #\n\ + # - You can resize a cell by holding shift and dragging \ + horizontally #"; + } ); ( "Casting", { zipper = diff --git a/src/haz3lweb/app/common/ProjectorView.re b/src/haz3lweb/app/common/ProjectorView.re index 30d516fa19..7bb11507f5 100644 --- a/src/haz3lweb/app/common/ProjectorView.re +++ b/src/haz3lweb/app/common/ProjectorView.re @@ -73,7 +73,7 @@ let view_wrapper = ~indication: option(Direction.t), ~selected: bool, p: Base.projector, - view: Node.t, + views: list(Node.t), ) => { let sort = Option.map(Info.sort_of, info.statics) |> Option.value(~default=Sort.Exp); @@ -87,7 +87,7 @@ let view_wrapper = Attr.on_mousedown(focus(info.id)), DecUtil.abs_style(measurement, ~font_metrics), ], - [view, backing_deco(~font_metrics, ~measurement)], + views, ); }; @@ -99,61 +99,94 @@ let handle = (id, action: external_action): Action.project => | SetSyntax(f) => SetSyntax(id, f) }; -/* Position in pixels for the position offset characters to the - * right of the end of the row at measurement.origin. */ -let offside = - ( - ~offset: int, - font_metrics: FontMetrics.t, - measurement: Measured.measurement, - measured: Measured.t, - ) - : float => - font_metrics.col_width - *. float_of_int( - Measured.start_row_width(measurement, measured) - + offset - - measurement.origin.col, - ); +let offside_wrapper = + (font_metrics: FontMetrics.t, offside_base: int, v: Node.t) => + div( + ~attrs=[ + Attr.create( + "style", + Printf.sprintf( + "position: absolute; left: %fpx;", + font_metrics.col_width *. float_of_int(offside_base), + ), + ), + ], + [v], + ); /* Gather utility functions/values to be passed to the projector. * See ProjectorBase.utility definition for more information */ -let collate_utility = - ( - globals: Globals.t, - measurement: Measured.measurement, - cached_syntax: Editor.CachedSyntax.t, - ) - : ProjectorBase.utility => { +let mk_utility = (font_metrics: FontMetrics.t): ProjectorBase.utility => { { - font_metrics: globals.font_metrics, - offside_offset: - offside( - ~offset=4, - globals.font_metrics, - measurement, - cached_syntax.measured, - ), - view: (sort, seg) => - CodeViewable.view_segment( - ~globals, - ~sort, - ~shape_of_proj=Projector.Shape.of_map_default, - seg, - ), + font_metrics, + view_seg: Code.simple_view(font_metrics), exp_to_seg: exp => exp |> DHExp.strip_casts |> ExpToSegment.exp_to_segment( - ~settings={ - inline: false, - fold_case_clauses: false, - fold_fn_bodies: false, - hide_fixpoints: false, - fold_cast_types: false, - }, + ~settings= + ExpToSegment.Settings.of_core(~inline=false, CoreSettings.off), ), + seg_to_exp: seg => MakeTerm.go(seg).term, + }; +}; + +let indication = (z, id) => + switch (Indicated.piece(z)) { + | Some((p, d, _)) when Piece.id(p) == id => Some(Direction.toggle(d)) + | _ => None }; + +/* Find end of row offset position in grid units */ +let offside_base = + (~offset: int, measurement: Measured.measurement, measured: Measured.t) + : int => + Measured.start_row_width(measurement, measured) + + offset + - measurement.origin.col; + +type projector_data = { + p: Piece.projector, + indication: option(Direction.t), + selected: bool, + info: ProjectorBase.info, + measurement: Measured.measurement, + offside_base: int, +}; + +let mk_info = + ( + id: Id.t, + p: Piece.projector, + ~cached_statics: CachedStatics.t, + ~dynamics: Dynamics.Map.t, + ) + : ProjectorBase.info => { + id, + syntax: p.syntax, + statics: Statics.Map.lookup(id, cached_statics.info_map), + dynamics: Dynamics.Map.lookup(id, dynamics), +}; + +let collect_data = + (cached_syntax: Editor.CachedSyntax.t, zipper, cached_statics, dynamics) => { + let projector_ids = cached_syntax.projectors |> Id.Map.bindings |> List.rev; + List.filter_map( + ((id, _)) => { + let* p = Id.Map.find_opt(id, cached_syntax.projectors); + let+ measurement = Measured.find_pr_opt(p, cached_syntax.measured); + { + p, + indication: indication(zipper, id), + selected: List.mem(id, cached_syntax.selection_ids), + measurement, + info: mk_info(id, p, ~cached_statics, ~dynamics), + offside_base: + offside_base(~offset=4, measurement, cached_syntax.measured), + }; + }, + projector_ids, + ); }; /* Extracts projector-instance-specific metadata necessary to @@ -162,35 +195,49 @@ let collate_utility = * correctly with respect to the underyling editor */ let setup_view = ( - id: Id.t, - ~cached_statics: CachedStatics.t, - ~cached_syntax: Editor.CachedSyntax.t, - ~dynamics, - ~inject: Action.t => Ui_effect.t(unit), - ~globals: Globals.t, - ~indication: option(Direction.t), + inject: Action.t => Ui_effect.t(unit), + utility: ProjectorBase.utility, + font_metrics: FontMetrics.t, + {p, info, offside_base, indication, measurement, selected}: projector_data, ) - : option(Node.t) => { - let* p = Id.Map.find_opt(id, cached_syntax.projectors); - let* syntax = Some(p.syntax); - let statics = Statics.Map.lookup(id, cached_statics.info_map); - let dynamics = Dynamics.Map.lookup(id, dynamics); - let info = {id, statics, dynamics, syntax}; - let+ measurement = Measured.find_pr_opt(p, cached_syntax.measured); - let utility = collate_utility(globals, measurement, cached_syntax); + : (Node.t, Node.t, option(Node.t)) => { let (module P) = to_module(p.kind); - let parent = a => inject(Project(handle(id, a))); - let local = a => inject(Project(SetModel(id, P.update(p.model, a)))); - view_wrapper( - ~inject, - ~font_metrics=globals.font_metrics, - ~measurement, - ~indication, - ~info, - ~selected=List.mem(id, cached_syntax.selection_ids), - p, - P.view(p.model, ~info, ~local, ~parent, ~utility), - ); + let parent = a => inject(Project(handle(p.id, a))); + let local = a => + inject(Project(SetModel(p.id, P.update(p.model, info, a)))); + let wrapper = + view_wrapper( + ~inject, + ~font_metrics, + ~measurement, + ~indication, + ~info, + ~selected, + p, + ); + let inline_view = P.view(p.model, info, ~local, ~parent, ~utility); + let offside_view = + Option.map( + v => + offside_wrapper( + font_metrics, + offside_base, + v(p.model, info, ~local, ~parent, ~utility), + ), + P.offside_view, + ); + let overlay_view = + Option.map( + v => wrapper([v(p.model, info, ~local, ~parent, ~utility)]), + P.overlay_view, + ); + let underlay_view = + switch (P.underlay_view) { + | Some(v) => wrapper([v(p.model, info, ~utility)]) + | None => wrapper([backing_deco(~font_metrics, ~measurement)]) + }; + let combined_view = wrapper([inline_view] @ Option.to_list(offside_view)); + (underlay_view, combined_view, overlay_view); }; /* Is the piece with id indicated? If so, where is it wrt the caret? */ @@ -204,30 +251,26 @@ let indication = (z, id) => * be absolutely positioned atop a rendered editor UI */ let all = ( - z, - ~globals: Globals.t, - ~cached_statics: CachedStatics.t, - ~cached_syntax: Editor.CachedSyntax.t, - ~dynamics: Dynamics.Map.t, - ~inject, + inject: Action.t => Ui_effect.t(unit), + utility: ProjectorBase.utility, + font_metrics: FontMetrics.t, + projector_data: list(projector_data), ) => { - div_c( - "projectors", - Id.Map.bindings(cached_syntax.projectors) - |> List.filter_map(((id, _)) => { - let indication = indication(z, id); - setup_view( - id, - ~cached_statics, - ~cached_syntax, - ~dynamics, - ~inject, - ~globals, - ~indication, - ); - }) - |> List.rev, - ); + let (underlay_views, base_views, overlay_views) = + projector_data + |> List.map(setup_view(inject, utility, font_metrics)) + |> ListUtil.split3; + let overlay_views = overlay_views |> List.filter_map(Fun.id); + [ + div_c( + "projectors", + [ + div_c("base", base_views), + div_c("underlays", underlay_views), + div_c("overlays", overlay_views), + ], + ), + ]; }; /* When the caret is directly adjacent to a projector, keyboard commands diff --git a/src/haz3lweb/app/editors/code/Code.re b/src/haz3lweb/app/editors/code/Code.re index e7de89592f..cb9cc8061a 100644 --- a/src/haz3lweb/app/editors/code/Code.re +++ b/src/haz3lweb/app/editors/code/Code.re @@ -179,24 +179,24 @@ let rec holes = ], ); -let simple_view = (~font_metrics, ~segment, ~settings: Settings.t): Node.t => { - let shape_of_proj = Projector.Shape.of_map_default; /* Assume this doesn't contain projectors */ +let simple_view = (font_metrics, sort, segment): Node.t => { + let shape_of_proj = Projector.Shape.of_map_default; let map = Measured.of_segment(segment, shape_of_proj); module Text = Text({ let map = map; - let settings = settings; + let settings = Settings.Model.init; let shape_of_proj = shape_of_proj; }); let holes = holes(~map, ~font_metrics, segment); div( ~attrs=[Attr.class_("code")], [ - span_c("code-text", Text.of_segment([], false, Sort.Any, segment)), + span_c("code-text", Text.of_segment([], false, sort, segment)), ...holes, ], ); -}; +}; /* Assume this doesn't contain projectors */ let of_hole = (~globals: Globals.t, ~measured, g: Grout.t) => // TODO(d) fix sort diff --git a/src/haz3lweb/app/editors/code/CodeEditable.re b/src/haz3lweb/app/editors/code/CodeEditable.re index 800a869689..b5bd7bcaa6 100644 --- a/src/haz3lweb/app/editors/code/CodeEditable.re +++ b/src/haz3lweb/app/editors/code/CodeEditable.re @@ -246,14 +246,20 @@ module View = { }; let projectors = ProjectorView.all( - model.editor.state.zipper, - ~globals, - ~cached_statics=model.statics, - ~cached_syntax=model.editor.syntax, - ~inject=x => inject(Perform(x)), - ~dynamics, + x => inject(Perform(x)), + ProjectorView.mk_utility(globals.font_metrics), + globals.font_metrics, + ProjectorView.collect_data( + model.editor.syntax, + model.editor.state.zipper, + model.statics, + dynamics, + ), ); - let overlays = edit_decos @ overlays @ [projectors]; + let overlays = + [Node.div(~attrs=[Attr.classes(["code-deco"])], edit_decos)] + @ [Node.div(~attrs=[Attr.classes(["overlays"])], overlays)] + @ projectors; let code_view = CodeWithStatics.View.view( ~globals, diff --git a/src/haz3lweb/app/explainthis/ExplainThis.re b/src/haz3lweb/app/explainthis/ExplainThis.re index 84c2a1264b..840e8c4373 100644 --- a/src/haz3lweb/app/explainthis/ExplainThis.re +++ b/src/haz3lweb/app/explainthis/ExplainThis.re @@ -1580,15 +1580,15 @@ let get_doc = | FixF(pat, body, _) => message_single( FixFExp.single( - ~pat_id=UPat.rep_id(pat), - ~body_id=UExp.rep_id(body), + ~pat_id=Pat.rep_id(pat), + ~body_id=Exp.rep_id(body), ), ) | Ap(Reverse, arg, fn) => message_single( PipelineExp.single( - ~arg_id=UExp.rep_id(arg), - ~fn_id=UExp.rep_id(fn), + ~arg_id=Exp.rep_id(arg), + ~fn_id=Exp.rep_id(fn), ), ) | TypAp(f, typ) => @@ -1725,29 +1725,29 @@ let get_doc = | Filter(Filter({act: (Step, One), pat}), body) => message_single( FilterExp.filter_pause( - ~p_id=UExp.rep_id(pat), - ~body_id=UExp.rep_id(body), + ~p_id=Exp.rep_id(pat), + ~body_id=Exp.rep_id(body), ), ) | Filter(Filter({act: (Step, All), pat}), body) => message_single( FilterExp.filter_debug( - ~p_id=UExp.rep_id(pat), - ~body_id=UExp.rep_id(body), + ~p_id=Exp.rep_id(pat), + ~body_id=Exp.rep_id(body), ), ) | Filter(Filter({act: (Eval, All), pat}), body) => message_single( FilterExp.filter_eval( - ~p_id=UExp.rep_id(pat), - ~body_id=UExp.rep_id(body), + ~p_id=Exp.rep_id(pat), + ~body_id=Exp.rep_id(body), ), ) | Filter(Filter({act: (Eval, One), pat}), body) => message_single( FilterExp.filter_hide( - ~p_id=UExp.rep_id(pat), - ~body_id=UExp.rep_id(body), + ~p_id=Exp.rep_id(pat), + ~body_id=Exp.rep_id(body), ), ) | Filter(_) => simple("Internal expression") @@ -1829,7 +1829,7 @@ let get_doc = OpExp.int_un_minus, ); | Meta(Unquote) => - message_single(FilterExp.unquote(~sel_id=UExp.rep_id(exp))) + message_single(FilterExp.unquote(~sel_id=Exp.rep_id(exp))) } | BinOp(op, left, right) => open OpExp; diff --git a/src/haz3lweb/app/inspector/CursorInspector.re b/src/haz3lweb/app/inspector/CursorInspector.re index febb507ada..b33aec0586 100644 --- a/src/haz3lweb/app/inspector/CursorInspector.re +++ b/src/haz3lweb/app/inspector/CursorInspector.re @@ -85,6 +85,7 @@ let common_err_view = (~globals, cls: Cls.t, err: Info.error_common) => { fold_fn_bodies: false, hide_fixpoints: false, fold_cast_types: false, + show_filters: false, }, ~info_map=Id.Map.empty, ) @@ -131,6 +132,7 @@ let common_ok_view = (~globals, cls: Cls.t, ok: Info.ok_pat) => { fold_fn_bodies: false, hide_fixpoints: false, fold_cast_types: false, + show_filters: false, }, ); switch (cls, ok) { @@ -182,6 +184,7 @@ let typ_ok_view = (~globals, cls: Cls.t, ok: Info.ok_typ) => { fold_fn_bodies: false, hide_fixpoints: false, fold_cast_types: false, + show_filters: false, }, ~info_map=Id.Map.empty, ); @@ -215,6 +218,7 @@ let typ_err_view = (~globals, ok: Info.error_typ) => { fold_fn_bodies: false, hide_fixpoints: false, fold_cast_types: false, + show_filters: false, }, ~info_map=Id.Map.empty, ); @@ -247,6 +251,7 @@ let rec exp_view = (~globals, cls: Cls.t, status: Info.status_exp) => { fold_fn_bodies: false, hide_fixpoints: false, fold_cast_types: false, + show_filters: false, }, ~info_map=Id.Map.empty, ); @@ -319,6 +324,7 @@ let tpat_view = (~globals, _: Cls.t, status: Info.status_tpat) => { fold_fn_bodies: false, hide_fixpoints: false, fold_cast_types: false, + show_filters: false, }, ~info_map=Id.Map.empty, ); diff --git a/src/haz3lweb/app/inspector/ProjectorPanel.re b/src/haz3lweb/app/inspector/ProjectorPanel.re index c75822840f..05b4fe7377 100644 --- a/src/haz3lweb/app/inspector/ProjectorPanel.re +++ b/src/haz3lweb/app/inspector/ProjectorPanel.re @@ -104,29 +104,43 @@ let lift = (str, strs) => List.cons(str, List.filter((!=)(str), strs)); * indicated syntax, with the currently applied projection (if any) * lifted to the top of the list */ let applicable_projector_strings = (cursor: Cursor.cursor(Editors.Update.t)) => { - let strs = - applicable_projectors(cursor.info) |> List.map(ProjectorView.name); + let strs = applicable_projectors(cursor.info); switch (kind(cursor.editor)) { | None => strs - | Some(k) => lift(ProjectorView.name(k), strs) + | Some(k) => lift(k, strs) }; }; +let keyboard_shortcut_of = (kind: Base.kind): string => + switch (kind) { + | Fold => "Option-f" + | Probe => "Option-v" + | _ => "" + }; + /* A selection input for contetually applicable projectors */ let select_view = ( ~inject: Action.project => Ui_effect.t(unit), cursor: Cursor.cursor(Editors.Update.t), ) => { - let applicable_projector_strings = + let applicable_projectors = might_project(cursor) ? applicable_projector_strings(cursor) : []; + let applicable_projector_strings = + List.map(ProjectorView.name, applicable_projectors); let value = switch (applicable_projector_strings) { | [] => "" | [hd, ..._] => hd }; + let title = + switch (applicable_projectors) { + | [] => "" + | [hd, ..._] => keyboard_shortcut_of(hd) + }; Node.select( ~attrs=[ + Attr.title(title), Attr.on_change((_, name) => inject(SetIndicated(ProjectorView.of_name(name))) ), diff --git a/src/haz3lweb/debug/DebugConsole.re b/src/haz3lweb/debug/DebugConsole.re index 3a93554eb3..47e0883cc8 100644 --- a/src/haz3lweb/debug/DebugConsole.re +++ b/src/haz3lweb/debug/DebugConsole.re @@ -14,7 +14,7 @@ let print = switch (key) { | "F1" => zipper |> Zipper.show |> print | "F2" => zipper |> Zipper.unselect_and_zip |> Segment.show |> print - | "F3" => term |> UExp.show |> print + | "F3" => term |> Exp.show |> print | "F4" => map |> Statics.Map.show |> print | "F5" => let env_init = Builtins.env_init; diff --git a/src/haz3lweb/exercises/Exercise.re b/src/haz3lweb/exercises/Exercise.re index 613d0d47be..c1eb9ca2b8 100644 --- a/src/haz3lweb/exercises/Exercise.re +++ b/src/haz3lweb/exercises/Exercise.re @@ -419,7 +419,7 @@ let put_stitched = (pos, s: stitched('a), x: 'a): stitched('a) => | HiddenTests => {...s, hidden_tests: x} }; -let wrap_filter = (act: FilterAction.action, term: UExp.t): UExp.t => { +let wrap_filter = (act: FilterAction.action, term: Exp.t): Exp.t => { term: Filter( Filter({ @@ -438,7 +438,7 @@ let wrap_filter = (act: FilterAction.action, term: UExp.t): UExp.t => { let wrap = (term, editor: Editor.t): TermItem.t => {term, editor}; -let term_of = (editor: Editor.t): UExp.t => +let term_of = (editor: Editor.t): Exp.t => MakeTerm.from_zip_for_sem(editor.state.zipper).term; let stitch3 = (ed1: Editor.t, ed2: Editor.t, ed3: Editor.t) => diff --git a/src/haz3lweb/exercises/Grading.re b/src/haz3lweb/exercises/Grading.re index d3256e427e..16bb554aca 100644 --- a/src/haz3lweb/exercises/Grading.re +++ b/src/haz3lweb/exercises/Grading.re @@ -356,7 +356,7 @@ module MutationTestingReport = { // |> Zipper.zip // |> MakeTerm.go // |> fst - // |> UExp.show + // |> Exp.show // |> print_endline // |> (_ => Virtual_dom.Vdom.Effect.Ignore); diff --git a/src/haz3lweb/exercises/SyntaxTest.re b/src/haz3lweb/exercises/SyntaxTest.re index 86d97aede7..041b4cb510 100644 --- a/src/haz3lweb/exercises/SyntaxTest.re +++ b/src/haz3lweb/exercises/SyntaxTest.re @@ -44,8 +44,7 @@ let rec find_var_upat = (name: string, upat: Pat.t): bool => { if name="a", then l=[fun x -> x+1] */ let rec find_in_let = - (name: string, upat: UPat.t, def: UExp.t, l: list(UExp.t)) - : list(UExp.t) => { + (name: string, upat: Pat.t, def: Exp.t, l: list(Exp.t)): list(Exp.t) => { switch (upat.term, def.term) { | (Wrap(up, _), Wrap(ue, _)) => find_in_let(name, up, ue, l) | (Wrap(up, _), _) => find_in_let(name, up, def, l) @@ -80,8 +79,7 @@ let rec find_in_let = /* Find any function expressions in uexp that are bound to variable name */ -let rec find_fn = - (name: string, uexp: UExp.t, l: list(UExp.t)): list(UExp.t) => { +let rec find_fn = (name: string, uexp: Exp.t, l: list(Exp.t)): list(Exp.t) => { switch (uexp.term) { | Let(up, def, body) => l |> find_in_let(name, up, def) |> find_fn(name, body) @@ -378,7 +376,7 @@ let rec tail_check = (name: string, uexp: Exp.t): bool => { /* Check whether all functions bound to variable name are tail recursive. */ -let is_tail_recursive = (name: string, uexp: UExp.t): bool => { +let is_tail_recursive = (name: string, uexp: Exp.t): bool => { let fn_bodies = [] |> find_fn(name, uexp); if (List.length(fn_bodies) == 0) { false; @@ -391,7 +389,7 @@ let is_tail_recursive = (name: string, uexp: UExp.t): bool => { }; }; -let check = (uexp: UExp.t, predicates: list(UExp.t => bool)): syntax_result => { +let check = (uexp: Exp.t, predicates: list(Exp.t => bool)): syntax_result => { let results = List.map(pred => {uexp |> pred}, predicates); let length = List.length(predicates); let passing = Util.ListUtil.count_pred(res => res, results); diff --git a/src/haz3lweb/util/WorkerServer.re b/src/haz3lweb/util/WorkerServer.re index 8c988d7a51..d3a745c63d 100644 --- a/src/haz3lweb/util/WorkerServer.re +++ b/src/haz3lweb/util/WorkerServer.re @@ -28,9 +28,7 @@ module Response = { }; let work = (res: Request.value): Response.value => - switch ( - Haz3lcore.Evaluator.evaluate'(Haz3lcore.Builtins.env_init, {d: res}) - ) { + switch (Haz3lcore.Evaluator.evaluate'(Haz3lcore.Builtins.env_init, res)) { | exception (Haz3lcore.EvaluatorError.Exception(reason)) => print_endline( "EvaluatorError:" ++ Haz3lcore.EvaluatorError.show(reason), diff --git a/src/haz3lweb/view/ContextInspector.re b/src/haz3lweb/view/ContextInspector.re index 3e48eb2109..257afd793a 100644 --- a/src/haz3lweb/view/ContextInspector.re +++ b/src/haz3lweb/view/ContextInspector.re @@ -18,6 +18,7 @@ let context_entry_view = (~globals, entry: Haz3lcore.Ctx.entry): Node.t => { fold_fn_bodies: false, hide_fixpoints: false, fold_cast_types: false, + show_filters: false, }, ); let div_name = div(~attrs=[clss(["name"])]); diff --git a/src/haz3lweb/view/Kind.re b/src/haz3lweb/view/Kind.re index 0eee9db324..345d4bfe01 100644 --- a/src/haz3lweb/view/Kind.re +++ b/src/haz3lweb/view/Kind.re @@ -16,6 +16,7 @@ let view = (~globals, kind: Haz3lcore.Ctx.kind): Node.t => fold_fn_bodies: false, hide_fixpoints: false, fold_cast_types: false, + show_filters: false, }, ~info_map=Haz3lcore.Id.Map.empty, ty, diff --git a/src/haz3lweb/view/ScratchMode.re b/src/haz3lweb/view/ScratchMode.re index 39b1ee511a..8e922aae72 100644 --- a/src/haz3lweb/view/ScratchMode.re +++ b/src/haz3lweb/view/ScratchMode.re @@ -10,11 +10,6 @@ module Model = { scratchpads: list((string, CellEditor.Model.t)), }; - let get_spliced_elabs = model => { - let (key, ed) = List.nth(model.scratchpads, model.current); - [(key, Elaborator.Elaboration.{d: ed.editor.statics.term})]; - }; - [@deriving (show({with_path: false}), sexp, yojson)] type persistent = (int, list((string, CellEditor.Model.persistent))); diff --git a/src/haz3lweb/www/img/noun-pinned-1560993.svg b/src/haz3lweb/www/img/noun-pinned-1560993.svg new file mode 100644 index 0000000000..90b3ce51bc --- /dev/null +++ b/src/haz3lweb/www/img/noun-pinned-1560993.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/haz3lweb/www/style.css b/src/haz3lweb/www/style.css index e38e08bfab..db77860488 100644 --- a/src/haz3lweb/www/style.css +++ b/src/haz3lweb/www/style.css @@ -121,7 +121,6 @@ select:hover { grid-column: 1 / span 2; display: flex; align-items: center; - gap: 1em; justify-content: space-between; align-items: stretch; background-color: var(--ui-bkg); diff --git a/src/haz3lweb/www/style/cursor-inspector.css b/src/haz3lweb/www/style/cursor-inspector.css index a97c0c633b..eef8ba8e26 100644 --- a/src/haz3lweb/www/style/cursor-inspector.css +++ b/src/haz3lweb/www/style/cursor-inspector.css @@ -4,6 +4,9 @@ display: flex; align-items: stretch; gap: 0.5em; + /* Below is to permit access to projector panel if ci is too wide */ + overflow: hidden; + min-width: 0; } #cursor-inspector.no-info { @@ -55,7 +58,7 @@ #cursor-inspector .ci-header .gamma:hover, #cursor-inspector .ci-header.error .gamma:hover, #cursor-inspector .ci-header .gamma.visible, -#cursor-inspector .ci-header.error .gamma.visible, +#cursor-inspector .ci-header.error .gamma.visible, #bottom-bar:has(~ .context-inspector:hover) .gamma { background-color: #dfcc99; outline: 0.3px solid var(--BR3); @@ -94,22 +97,22 @@ #cursor-inspector .ci-header.ok .toggle-switch .toggle-knob { background-color: var(--SAND); } -#cursor-inspector .ci-header.ok.Exp .toggle-switch.active { +#cursor-inspector .ci-header.ok.Exp .toggle-switch.active { background-color: var(--BR3); } -#cursor-inspector .ci-header.ok.Pat .toggle-switch.active { +#cursor-inspector .ci-header.ok.Pat .toggle-switch.active { background-color: var(--token-pat); } -#cursor-inspector .ci-header.ok.Typ .toggle-switch.active { +#cursor-inspector .ci-header.ok.Typ .toggle-switch.active { background-color: var(--token-typ); } -#cursor-inspector .ci-header.ok.TPat .toggle-switch.active { +#cursor-inspector .ci-header.ok.TPat .toggle-switch.active { background-color: var(--token-tpat); } -#cursor-inspector .ci-header.ok.Exp .toggle-switch .toggle-knob { +#cursor-inspector .ci-header.ok.Exp .toggle-switch .toggle-knob { color: var(--BR3); } -#cursor-inspector .ci-header.ok.Pat .toggle-switch .toggle-knob{ +#cursor-inspector .ci-header.ok.Pat .toggle-switch .toggle-knob { color: var(--token-pat); } #cursor-inspector .ci-header.ok.Typ .toggle-switch .toggle-knob { @@ -176,7 +179,6 @@ position: relative; } - #page > .context-inspector { position: absolute; z-index: var(--context-inspector-z); @@ -241,4 +243,4 @@ .context-inspector .context-entry .seperator { color: var(--context-inspector-colon); -} \ No newline at end of file +} diff --git a/src/haz3lweb/www/style/dynamics.css b/src/haz3lweb/www/style/dynamics.css index 822f216481..cdc7759eb2 100644 --- a/src/haz3lweb/www/style/dynamics.css +++ b/src/haz3lweb/www/style/dynamics.css @@ -309,17 +309,17 @@ } .tile-next-step .tile-path.Exp.indicated { - fill: var(--G1) + fill: var(--G1); } .tile-taken-step .tile-path.Exp.indicated { - fill: var(--BR1) + fill: var(--BR1); } svg.tile-next-step { pointer-events: all; cursor: pointer; - z-index: var(--test-result-z); + z-index: var(--projector-overlay-z); filter: drop-shadow(1px 1px var(--G2)); } @@ -337,4 +337,4 @@ svg.tile-taken-step { .cell-result .code { pointer-events: none; -} \ No newline at end of file +} diff --git a/src/haz3lweb/www/style/exercise-mode.css b/src/haz3lweb/www/style/exercise-mode.css index 1dac832e86..98d965b23d 100644 --- a/src/haz3lweb/www/style/exercise-mode.css +++ b/src/haz3lweb/www/style/exercise-mode.css @@ -99,7 +99,7 @@ margin-left: 0.17em; font-size: 64%; vertical-align: super; - z-index: var(--test-result-z); + z-index: var(--projector-overlay-z); } .test-instance.Pass:before, diff --git a/src/haz3lweb/www/style/projectors/base.css b/src/haz3lweb/www/style/projectors/base.css index a4426ffad3..093531afc0 100644 --- a/src/haz3lweb/www/style/projectors/base.css +++ b/src/haz3lweb/www/style/projectors/base.css @@ -5,7 +5,7 @@ @import "cards.css"; /* Turn off caret when a projector is focused */ -#caret:has(~ .projectors .projector *:focus) .caret-path { +.code-deco:has(~ .projectors .projector *:focus) #caret .caret-path { fill: #0000; } @@ -20,15 +20,24 @@ } .projector.indicated { - z-index: var(--code-hovers-z); + z-index: var(--projector-indicated-z); } .projector > *:not(svg) { z-index: var(--projector-z); } +.projectors .overlays .projector { + pointer-events: none; + z-index: var(--projector-overlay-z); +} + +.projectors .underlays .projector { + pointer-events: none; + z-index: var(--projector-underlay-z); +} + .projector > svg { - z-index: var(--projector-backing-z); fill: var(--shard_projector); } diff --git a/src/haz3lweb/www/style/projectors/probe.css b/src/haz3lweb/www/style/projectors/probe.css index b47bde6028..f5fef59bd8 100644 --- a/src/haz3lweb/www/style/projectors/probe.css +++ b/src/haz3lweb/www/style/projectors/probe.css @@ -11,12 +11,14 @@ --exp-indicated: var(--G0); --pat-indicated: var(--PAT); + --exp-ap-indicated: var(--TYP); --exp-base: hsl(120, 40%, 85%); --pat-base: hsl(170, 40%, 85%); --exp-shadow: oklch(0.55 0.15 150); --pat-shadow: oklch(0.5 0.1 245); + --typ-shadow: oklch(0.5 0.1 300); --exp-cell: hsl(115, 30%, 70%); --pat-cell: hsl(165, 30%, 70%); @@ -28,7 +30,7 @@ color: var(--code-text); } -.projector.probe > svg { +.projector.probe.Exp > svg { fill: var(--exp-base); filter: drop-shadow(0.7px 0.7px 0px var(--exp-shadow)); } @@ -63,11 +65,13 @@ .projector.probe .main.ap .icon { background-image: url(../../img/noun-telescope-7245201.svg); } -.projector.probe.indicated .main .icon { +.projector.probe.indicated:not(.selected) .main .icon { filter: invert(1); } -.projector.probe .num-closures { +/* OVERLAY VIEW */ + +.overlays .projector.probe .num-closures { position: absolute; right: -0.5em; top: -0.3em; @@ -77,38 +81,133 @@ font-family: var(--ui-font); display: flex; justify-content: center; - background-color: var(--exp-indicated); border-radius: 2em; font-size: 0.7em; padding: 0.3em; min-width: 1em; - z-index: var(--code-emblems-z); + --original-bg: inherit; + background-color: var(--original-bg); } -.projector.probe.Pat .num-closures { - background-color: var(--pat-indicated); +.overlays .projector.probe.Exp .num-closures { + --original-bg: var(--exp-indicated); } -.projector.probe.selected .num-closures { - background-color: var(--num-closures); +.overlays .projector.probe.Pat .num-closures { + --original-bg: var(--pat-indicated); } -.projector.probe.indicated.Right .num-closures { - background-color: var(--num-closures-indicated); +.overlays .projector.probe.selected .num-closures { + --original-bg: var(--num-closures); } -.projector.probe.indicated.Right .num-closures { - right: -0.8em; - top: -0.5em; + +@keyframes blink-probe-caret { + from, + to { + background-color: var(--R1); + } + + 50% { + background-color: var(--original-bg); + } } +.overlays .projector.probe.indicated.Right .num-closures { + animation: 1s blink-probe-caret step-end infinite; + + /* background-color: var(--num-closures-indicated) !important; */ +} + +.overlays .projector.probe.selected:not(.indicated) .num-closures { + color: var(--STONE); +} + +.overlays .projector.probe .pin { + position: absolute; + left: -0.5em; + top: -0.3em; + height: 0.8em; + width: 0.8em; + border-radius: 2em; + filter: invert(1); + background: url(../../img/noun-pinned-1560993.svg) #33ffff; + background-size: 70%; + background-position: center; + background-repeat: no-repeat; +} +.overlays .projector.probe.indicated.Left .pin { + background-color: oklch(0.8 0.18 201.83); +} + +/* OFFSIDE VIEW */ .projector.probe .live-offside { display: flex; flex-direction: row; + align-items: center; + gap: 6px; +} +.projector.probe .live-offside:empty { + display: none; +} + +.projector.probe .live-equals { + font-weight: 800; + color: var(--exp-base); + opacity: 80%; +} +.projector.probe.indicated .live-equals { + opacity: 100%; + color: var(--exp-indicated); +} + +.projector.probe .live-offside .nav-bar { + display: flex; + flex-direction: row; + align-items: center; + gap: 2px; position: absolute; - left: 10em; + left: -1.75em; +} + +.projector.probe .live-offside .ellipsis { + cursor: pointer; +} +.projector.probe.Exp .live-offside .ellipsis { + color: var(--exp-base); +} +.projector.probe.Pat .live-offside .ellipsis { + color: var(--pat-base); +} +.projector.probe.indicated.Exp .live-offside .ellipsis { + color: var(--exp-indicated); +} +.projector.probe.indicated.Pat .live-offside .ellipsis { + color: var(--pat-indicated); +} +.projector.probe .live-offside .ellipsis:hover { + /* filter: brightness(1.2) saturate(2); */ + scale: 1.4; +} + +.projector.probe .live-offside .closure-groups { + display: flex; + flex-direction: row; + gap: 2px; +} + +.projector.probe .closure-group { + display: flex; gap: 2px; align-items: center; } -.projector.probe .live-offside:empty { - display: none; +.projector.probe .closure-group:not(:last-child)::after { + content: "•"; + font-size: 0.4em; + margin: -1px; +} +.projector.probe.Exp .closure-group:not(:last-child)::after { + color: var(--exp-indicated); +} +.projector.probe.Pat .closure-group:not(:last-child)::after { + color: var(--pat-indicated); } .projector.probe .val-resize { @@ -121,63 +220,93 @@ /* TOKEN BKG */ .projector.probe .val-resize .code-text { + border-radius: 0.05em 0.05em 0.05em 0.2em; +} +.projector.probe.Exp .val-resize .code-text { background-color: var(--exp-base); border-bottom: 1px solid var(--exp-shadow); - border-radius: 0.05em 0.05em 0.05em 0.2em; } .projector.probe.Pat .val-resize .code-text { - border-color: var(--pat-shadow); background-color: var(--pat-base); + border-bottom: 1px solid var(--pat-shadow); } -/* TOKEN OPACITY */ -.projector.probe .val-resize.cursor .code-text { +.projector.probe .val-resize.cursor-lex .code-text { opacity: 100%; } -.projector.probe .val-resize.dyn-cursor.cursor .code-text, -.projector.probe .val-resize.dyn-cursor.top-ap .code-text { +.projector.probe .val-resize.cursor-ap-lex .code-text { opacity: 100%; - outline: 1px solid var(--TYP); + outline: 1px solid var(--exp-ap-indicated); + border-color: var(--exp-ap-indicated); } -.projector.probe .val-resize.dyn-cursor:not(.cursor):not(.top-ap) .code-text { - opacity: 100%; - /* outline: 1px solid var(--TYP); */ - outline: 1px dotted var(--TYP); -} -.projector.probe .val-resize .code-text { - opacity: 51%; +.projector.probe .val-resize.cursor-outer-ap .code-text { + opacity: 100% !important; + outline: 1px solid var(--exp-ap-indicated); + border-color: var(--exp-ap-indicated); + background-color: var(--exp-ap-indicated); } -.projector.probe .val-resize.dyn-cursor.light .code-text { - opacity: 45%; +.projector.probe .val-resize .code-text, +.projector.probe .val-resize.cursor-ap.light .code-text, +.projector.probe .val-resize.cursor-ap-lex.light .code-text { + opacity: 50%; } + +/* TOKEN BKG INDICATED */ .projector.probe.indicated .val-resize .code-text { opacity: 60%; } -.projector.probe.indicated .val-resize.cursor .code-text { +.projector.probe.indicated .closure.cursor .val-resize .code-text { opacity: 100%; - background: none; - outline: 2.8px solid var(--exp-indicated); + background: white; + outline-width: 2.8px; + outline-style: solid; } -.projector.probe.indicated .val-resize.cursor.ap .code-text { - outline-color: var(--TYP); +.projector.probe.indicated.Exp .val-resize.cursor-lex .code-text { + outline-color: var(--exp-indicated); } -/* TOKENS */ -.projector.probe - .val-resize.dyn-cursor:not(.cursor):not(.top-ap) - .code-text - .token { - filter: invert(1) brightness(10); - /* color: var(--TYP); */ +.projector.probe.indicated.Pat .val-resize.cursor-lex .code-text { + outline-color: var(--pat-indicated); +} +.projector.probe.indicated .val-resize.cursor-lex.ap .code-text { + outline-color: var(--exp-ap-indicated); + border-color: var(--exp-ap-indicated); } -.projector.probe .val-resize:not(.dyn-cursor):not(.cursor) .code-text .token { + +/* TOKENS */ +.projector.probe .val-resize.cursor-ap .code-text .token, +.projector.probe .val-resize.cursor-none .code-text .token, +.projector.probe .val-resize.cursor-outer-ap .code-text .token { filter: invert(1) brightness(10); } -.projector.probe .val-resize.dyn-cursor.cursor .code-text .token, -.projector.probe .val-resize.dyn-cursor.top-ap .code-text .token { - color: var(--TYP); +.projector.probe .val-resize.cursor-ap-lex .code-text .token { + color: var(--exp-ap-indicated); } .projector.probe .val-resize .code-text:hover { - filter: brightness(1.05) saturate(1.05); + opacity: 100% !important; + outline-width: 1px; + outline-style: solid; + outline-offset: -0.1px; + border-width: 1.5px; + top: 0.5px; + filter: drop-shadow(1.5px 3px 0 #95876452); +} +.projector.probe.Exp .val-resize .code-text:hover { + outline-color: var(--exp-indicated); + border-color: var(--exp-indicated); +} +.projector.probe.Pat .val-resize .code-text:hover { + outline-color: var(--pat-indicated); + border-color: var(--pat-indicated); +} +.projector.probe.Exp .val-resize.ap .code-text:hover { + outline-color: var(--exp-ap-indicated); + border-color: var(--exp-ap-indicated); +} +.projector.probe .val-resize > .code:hover { + transform-origin: center left; + scale: 1.125; + z-index: var(--projector-indicated-z); + position: relative; } .projector.probe .live-env { @@ -197,23 +326,23 @@ color: var(--GB0); } +.projector.probe.selected > svg { + display: none; +} .projector.probe.indicated > svg { fill: var(--exp-indicated); } .projector.probe.indicated.Pat > svg { fill: var(--pat-indicated); } -.projector.probe.indicated:has(.main.ap) > svg { +.projector.probe:has(.ap.pinned) > svg { + fill: var(--exp-ap-indicated); } -.projector.probe.indicated .main { +.projector.probe.indicated:not(.selected) .main { color: white; } -.projector.probe.indicated.Pat .val-resize.cursor .code-text { - outline-color: var(--pat-indicated); -} - .projector.probe.indicated .closure:hover .live-env { display: block; } @@ -221,33 +350,37 @@ display: none; } -.projector.probe .closures-header, -.projector.probe .closures-tail { +.projector.probe .nav-arrow { width: 10px; height: 13px; - cursor: pointer; + color: var(--exp-indicated); background: url(../../img/noun-arrow-3130579.svg); background-size: cover; color: var(--NONE); - filter: invert(1) brightness(0.5) sepia(1) hue-rotate(61deg); } -.projector.probe .closures-tail { +.projector.probe .nav-arrow:not(.disabled) { + cursor: pointer; +} +.projector.probe .nav-arrow:last-child { transform: rotate(180deg); } +.projector.probe.Exp .nav-arrow { + filter: invert(1) brightness(0.7) sepia(1) hue-rotate(61deg) saturate(0.6); +} +.projector.probe.Pat .nav-arrow { + filter: invert(1) brightness(0.7) sepia(1) hue-rotate(141deg) saturate(0.6); +} -.projector.probe:not(.indicated) .closures-header, -.projector.probe:not(.indicated) .closures-tail { +.projector.probe:not(.indicated) .nav-arrow { pointer-events: none; opacity: 0% !important; } -.projector.probe .closures-header.disabled, -.projector.probe .closures-tail.disabled { +.projector.probe .nav-arrow.disabled { opacity: 15%; } -.projector.probe .closures-header:hover, -.projector.probe .closures-tail:hover { +.projector.probe .nav-arrow:not(.disabled):hover { filter: brightness(1.05) saturate(1.05); } diff --git a/src/haz3lweb/www/style/variables.css b/src/haz3lweb/www/style/variables.css index 8859a25eab..b5d342ed67 100644 --- a/src/haz3lweb/www/style/variables.css +++ b/src/haz3lweb/www/style/variables.css @@ -197,10 +197,9 @@ /* BELOW CODE LEVEL */ --err-hole-arms-z: 0; --tile-z: 1; - --projector-backing-z: 1; --err-hole-z: 2; --select-z: 4; - --test-result-z: 5; + --projector-underlay-z: 9; /* AT CODE LEVEL */ --code-text-z: 10; @@ -209,16 +208,16 @@ /* ABOVE CODE LEVEL */ --backpack-targets-z: 11; - --code-hovers-z: 11; + --projector-indicated-z: 11; --stepper-interactive-z: 15; --caret-z: 20; - --code-emblems-z: 21; + --projector-overlay-z: 29; /* TOP LEVEL UI */ - --context-inspector-z: 29; /* keep below bottom bar */ - --bottom-bar-z: 30; - --top-bar-z: 30; - --explainthis-expander-z: 30; + --context-inspector-z: 30; /* keep below bottom bar */ + --bottom-bar-z: 31; + --top-bar-z: 31; + --explainthis-expander-z: 31; /* HACKS */ --overlay-z: 100; diff --git a/src/util/ListUtil.re b/src/util/ListUtil.re index bc40065201..a3ec1d1e4b 100644 --- a/src/util/ListUtil.re +++ b/src/util/ListUtil.re @@ -608,9 +608,8 @@ let max_common_suffix = (a: list('a), b: list('a)): list('a) => { let common_suffix_length = (s1, s2) => List.length(max_common_suffix(s1, s2)); -let one_is_suffix_of_other = (s1, s2) => - common_suffix_length(s1, s2) == List.length(s1) - || common_suffix_length(s1, s2) == List.length(s2); +let is_suffix_of = (s1, s2) => + common_suffix_length(s1, s2) == List.length(s1); /* list truncated after at most n elementsnts */ let truncate = (n: int, xs: list('a)): list('a) => { @@ -632,6 +631,10 @@ let rec remove_first_n = (n: int, xs: list('a)): list('a) => { }; }; +/* Return at most k elements starting from index i */ +let slice = (i: int, k: int, xs: list('x)): list('x) => + xs |> remove_first_n(i) |> truncate(k); + let rec rotate_n = (n: int, xs: list('a)): list('a) => { let n = IntUtil.modulo(n, List.length(xs)); switch (n) { @@ -639,3 +642,30 @@ let rec rotate_n = (n: int, xs: list('a)): list('a) => { | _ => rotate_n(n - 1, rotate(xs)) }; }; + +let take = (n, xs) => { + let rec loop = (n, xs, acc) => + switch (n, xs) { + | (0, _) => acc + | (_, []) => acc + | (n, [x, ...xs]) => loop(n - 1, xs, [x, ...acc]) + }; + loop(n, xs, []); +}; + +let split3 = (xs: list(('a, 'b, 'c))): (list('a), list('b), list('c)) => { + let rec aux = + ( + xs: list(('a, 'b, 'c)), + acc1: list('a), + acc2: list('b), + acc3: list('c), + ) => { + switch (xs) { + | [] => (List.rev(acc1), List.rev(acc2), List.rev(acc3)) + | [(x, y, z), ...rest] => + aux(rest, [x, ...acc1], [y, ...acc2], [z, ...acc3]) + }; + }; + aux(xs, [], [], []); +}; diff --git a/test/Test_Elaboration.re b/test/Test_Elaboration.re index 9145e6b09a..233c43eed1 100644 --- a/test/Test_Elaboration.re +++ b/test/Test_Elaboration.re @@ -7,206 +7,187 @@ let dhexp_typ = testable(Fmt.using(Exp.show, Fmt.string), DHExp.fast_equal); let ids = List.init(12, _ => Id.mk()); let id_at = x => x |> List.nth(ids); + let mk_map = Statics.mk(CoreSettings.on, Builtins.ctx_init); let dhexp_of_uexp = u => Elaborator.elaborate(mk_map(u), u) |> fst; let alco_check = dhexp_typ |> Alcotest.check; -let u1: Exp.t = {ids: [id_at(0)], term: Int(8), copied: false}; -let single_integer = () => - alco_check("Integer literal 8", u1, dhexp_of_uexp(u1)); +module PlainTests = { + let u1: Exp.t = {ids: [id_at(0)], term: Int(8), copied: false}; + let single_integer = () => + alco_check("Integer literal 8", u1, dhexp_of_uexp(u1)); -let u2: Exp.t = {ids: [id_at(0)], term: EmptyHole, copied: false}; -let empty_hole = () => alco_check("Empty hole", u2, dhexp_of_uexp(u2)); + let u2: Exp.t = {ids: [id_at(0)], term: EmptyHole, copied: false}; + let empty_hole = () => alco_check("Empty hole", u2, dhexp_of_uexp(u2)); -let u3: Exp.t = { - ids: [id_at(0)], - term: Wrap({ids: [id_at(1)], term: Var("y"), copied: false}, Paren), - copied: false, -}; + let u3: Exp.t = { + ids: [id_at(0)], + term: Wrap({ids: [id_at(1)], term: Var("y"), copied: false}, Paren), + copied: false, + }; -let free_var = () => alco_check("free variable", u3, dhexp_of_uexp(u3)); - -let u4: Exp.t = - Let( - Tuple([Var("a") |> Pat.fresh, Var("b") |> Pat.fresh]) |> Pat.fresh, - Tuple([Int(4) |> Exp.fresh, Int(6) |> Exp.fresh]) |> Exp.fresh, - BinOp(Int(Minus), Var("a") |> Exp.fresh, Var("b") |> Exp.fresh) - |> Exp.fresh, - ) - |> Exp.fresh; - -let let_exp = () => - alco_check("Let expression for tuple (a, b)", u4, dhexp_of_uexp(u4)); - -let u5 = - BinOp(Int(Plus), Bool(false) |> Exp.fresh, Var("y") |> Exp.fresh) - |> Exp.fresh; - -let d5 = - BinOp( - Int(Plus), - FailedCast(Bool(false) |> Exp.fresh, Bool |> Typ.fresh, Int |> Typ.fresh) - |> Exp.fresh, - Cast( - Var("y") |> Exp.fresh, - Unknown(Internal) |> Typ.fresh, - Int |> Typ.fresh, + let free_var = () => alco_check("free variable", u3, dhexp_of_uexp(u3)); + + let u4: Exp.t = + Let( + Tuple([Var("a") |> Pat.fresh, Var("b") |> Pat.fresh]) |> Pat.fresh, + Tuple([Int(4) |> Exp.fresh, Int(6) |> Exp.fresh]) |> Exp.fresh, + BinOp(Int(Minus), Var("a") |> Exp.fresh, Var("b") |> Exp.fresh) + |> Exp.fresh, ) - |> Exp.fresh, - ) - |> Exp.fresh; - -let bin_op = () => - alco_check( - "Inconsistent binary integer operation (plus)", - d5, - dhexp_of_uexp(u5), - ); - -let u6: Exp.t = - If(Bool(false) |> Exp.fresh, Int(8) |> Exp.fresh, Int(6) |> Exp.fresh) - |> Exp.fresh; - -let consistent_if = () => - alco_check( - "Consistent case with rules (BoolLit(true), IntLit(8)) and (BoolLit(false), IntLit(6))", - u6, - dhexp_of_uexp(u6), - ); - -// x => 4 + 5 -let f = - Fun( - Var("x") |> Pat.fresh, - BinOp(Int(Plus), Int(4) |> Exp.fresh, Int(5) |> Exp.fresh) |> Exp.fresh, - None, - None, - ) - |> Exp.fresh; -let unapplied_function = () => alco_check("A function", f, dhexp_of_uexp(f)); - -let u7: Exp.t = Ap(Forward, f, Var("y") |> Exp.fresh) |> Exp.fresh; - -let ap_fun = () => - alco_check("Application of a function", u7, dhexp_of_uexp(u7)); - -let u8: Exp.t = - Match( - BinOp(Int(Equals), Int(4) |> Exp.fresh, Int(3) |> Exp.fresh) - |> Exp.fresh, - [ - (Bool(true) |> Pat.fresh, Int(24) |> Exp.fresh), - (Bool(false) |> Pat.fresh, Bool(false) |> Exp.fresh), - ], - ) - |> Exp.fresh; - -let d8: Exp.t = - Match( - BinOp(Int(Equals), Int(4) |> Exp.fresh, Int(3) |> Exp.fresh) - |> Exp.fresh, - [ - ( - Bool(true) |> Pat.fresh, - Cast( - Int(24) |> Exp.fresh, - Int |> Typ.fresh, - Unknown(Internal) |> Typ.fresh, - ) - |> Exp.fresh, - ), - ( - Bool(false) |> Pat.fresh, - Cast( - Bool(false) |> Exp.fresh, - Bool |> Typ.fresh, - Unknown(Internal) |> Typ.fresh, - ) - |> Exp.fresh, - ), - ], - ) - |> Exp.fresh; - -let inconsistent_case = () => - alco_check( - "Inconsistent branches where the first branch is an integer and second branch is a boolean", - d8, - dhexp_of_uexp(u8), - ); - -let u9: Exp.t = - Let( - Cast( - Var("f") |> Pat.fresh, - Arrow(Int |> Typ.fresh, Int |> Typ.fresh) |> Typ.fresh, - Unknown(Internal) |> Typ.fresh, + |> Exp.fresh; + + let let_exp = () => + alco_check("Let expression for tuple (a, b)", u4, dhexp_of_uexp(u4)); + + let u5 = + BinOp(Int(Plus), Bool(false) |> Exp.fresh, Var("y") |> Exp.fresh) + |> Exp.fresh; + + let d5 = + BinOp( + Int(Plus), + FailedCast( + Bool(false) |> Exp.fresh, + Bool |> Typ.fresh, + Int |> Typ.fresh, + ) + |> Exp.fresh, + Cast( + Var("y") |> Exp.fresh, + Unknown(Internal) |> Typ.fresh, + Int |> Typ.fresh, + ) + |> Exp.fresh, ) - |> Pat.fresh, + |> Exp.fresh; + + let bin_op = () => + alco_check( + "Inconsistent binary integer operation (plus)", + d5, + dhexp_of_uexp(u5), + ); + + let u6: Exp.t = + If(Bool(false) |> Exp.fresh, Int(8) |> Exp.fresh, Int(6) |> Exp.fresh) + |> Exp.fresh; + + let consistent_if = () => + alco_check( + "Consistent case with rules (BoolLit(true), IntLit(8)) and (BoolLit(false), IntLit(6))", + u6, + dhexp_of_uexp(u6), + ); + + // x => 4 + 5 + let f = Fun( Var("x") |> Pat.fresh, - BinOp(Int(Plus), Int(1) |> Exp.fresh, Var("x") |> Exp.fresh) + BinOp(Int(Plus), Int(4) |> Exp.fresh, Int(5) |> Exp.fresh) |> Exp.fresh, None, None, ) - |> Exp.fresh, - Int(55) |> Exp.fresh, - ) - |> Exp.fresh; - -let d9: Exp.t = - Let( - Var("f") |> Pat.fresh, - Fun( - Var("x") |> Pat.fresh, - BinOp(Int(Plus), Int(1) |> Exp.fresh, Var("x") |> Exp.fresh) + |> Exp.fresh; + let unapplied_function = () => + alco_check("A function", f, dhexp_of_uexp(f)); + + let u7: Exp.t = Ap(Forward, f, Var("y") |> Exp.fresh) |> Exp.fresh; + + let ap_fun = () => + alco_check("Application of a function", u7, dhexp_of_uexp(u7)); + + let u8: Exp.t = + Match( + BinOp(Int(Equals), Int(4) |> Exp.fresh, Int(3) |> Exp.fresh) |> Exp.fresh, - None, - Some("f"), + [ + (Bool(true) |> Pat.fresh, Int(24) |> Exp.fresh), + (Bool(false) |> Pat.fresh, Bool(false) |> Exp.fresh), + ], ) - |> Exp.fresh, - Int(55) |> Exp.fresh, - ) - |> Exp.fresh; - -let let_fun = () => - alco_check( - "Let expression for function which is not recursive", - d9, - dhexp_of_uexp(u9), - ); - -let deferral = () => - alco_check( - "string_sub(\"hello\", 1, _)", - DeferredAp( - Var("string_sub") |> Exp.fresh, + |> Exp.fresh; + + let d8: Exp.t = + Match( + BinOp(Int(Equals), Int(4) |> Exp.fresh, Int(3) |> Exp.fresh) + |> Exp.fresh, [ - String("hello") |> Exp.fresh, - Int(1) |> Exp.fresh, - Deferral(InAp) |> Exp.fresh, + ( + Bool(true) |> Pat.fresh, + Cast( + Int(24) |> Exp.fresh, + Int |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ) + |> Exp.fresh, + ), + ( + Bool(false) |> Pat.fresh, + Cast( + Bool(false) |> Exp.fresh, + Bool |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ) + |> Exp.fresh, + ), ], ) - |> Exp.fresh, - dhexp_of_uexp( - DeferredAp( - Var("string_sub") |> Exp.fresh, - [ - String("hello") |> Exp.fresh, - Int(1) |> Exp.fresh, - Deferral(InAp) |> Exp.fresh, - ], + |> Exp.fresh; + + let inconsistent_case = () => + alco_check( + "Inconsistent branches where the first branch is an integer and second branch is a boolean", + d8, + dhexp_of_uexp(u8), + ); + + let u9: Exp.t = + Let( + Cast( + Var("f") |> Pat.fresh, + Arrow(Int |> Typ.fresh, Int |> Typ.fresh) |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ) + |> Pat.fresh, + Fun( + Var("x") |> Pat.fresh, + BinOp(Int(Plus), Int(1) |> Exp.fresh, Var("x") |> Exp.fresh) + |> Exp.fresh, + None, + None, ) |> Exp.fresh, - ), - ); + Int(55) |> Exp.fresh, + ) + |> Exp.fresh; -let ap_deferral_single_argument = () => - alco_check( - "string_sub(\"hello\", 1, _)(2)", - Ap( - Forward, + let d9: Exp.t = + Let( + Var("f") |> Pat.fresh, + Fun( + Var("x") |> Pat.fresh, + BinOp(Int(Plus), Int(1) |> Exp.fresh, Var("x") |> Exp.fresh) + |> Exp.fresh, + None, + Some("f"), + ) + |> Exp.fresh, + Int(55) |> Exp.fresh, + ) + |> Exp.fresh; + + let let_fun = () => + alco_check( + "Let expression for function which is not recursive", + d9, + dhexp_of_uexp(u9), + ); + + let deferral = () => + alco_check( + "string_sub(\"hello\", 1, _)", DeferredAp( Var("string_sub") |> Exp.fresh, [ @@ -216,10 +197,22 @@ let ap_deferral_single_argument = () => ], ) |> Exp.fresh, - Int(2) |> Exp.fresh, - ) - |> Exp.fresh, - dhexp_of_uexp( + dhexp_of_uexp( + DeferredAp( + Var("string_sub") |> Exp.fresh, + [ + String("hello") |> Exp.fresh, + Int(1) |> Exp.fresh, + Deferral(InAp) |> Exp.fresh, + ], + ) + |> Exp.fresh, + ), + ); + + let ap_deferral_single_argument = () => + alco_check( + "string_sub(\"hello\", 1, _)(2)", Ap( Forward, DeferredAp( @@ -234,94 +227,107 @@ let ap_deferral_single_argument = () => Int(2) |> Exp.fresh, ) |> Exp.fresh, - ), - ); + dhexp_of_uexp( + Ap( + Forward, + DeferredAp( + Var("string_sub") |> Exp.fresh, + [ + String("hello") |> Exp.fresh, + Int(1) |> Exp.fresh, + Deferral(InAp) |> Exp.fresh, + ], + ) + |> Exp.fresh, + Int(2) |> Exp.fresh, + ) + |> Exp.fresh, + ), + ); -let ap_of_deferral_of_hole = () => - alco_check( - "?(_, _, 3)(1., true)", - Ap( - Forward, - DeferredAp( - Cast( + let ap_of_deferral_of_hole = () => + alco_check( + "?(_, _, 3)(1., true)", + Ap( + Forward, + DeferredAp( Cast( - EmptyHole |> Exp.fresh, - Unknown(Internal) |> Typ.fresh, + Cast( + EmptyHole |> Exp.fresh, + Unknown(Internal) |> Typ.fresh, + Arrow( + Unknown(Internal) |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ) + |> Typ.fresh, + ) + |> Exp.fresh, Arrow( Unknown(Internal) |> Typ.fresh, Unknown(Internal) |> Typ.fresh, ) |> Typ.fresh, - ) - |> Exp.fresh, - Arrow( - Unknown(Internal) |> Typ.fresh, - Unknown(Internal) |> Typ.fresh, - ) - |> Typ.fresh, - Arrow( - Prod([ - Unknown(Internal) |> Typ.fresh, - Unknown(Internal) |> Typ.fresh, + Arrow( + Prod([ + Unknown(Internal) |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ]) + |> Typ.fresh, Unknown(Internal) |> Typ.fresh, - ]) + ) |> Typ.fresh, - Unknown(Internal) |> Typ.fresh, - ) - |> Typ.fresh, - ) - |> Exp.fresh, - [ - Deferral(InAp) |> Exp.fresh, - Deferral(InAp) |> Exp.fresh, - Cast( - Int(3) |> Exp.fresh, - Int |> Typ.fresh, - Unknown(Internal) |> Typ.fresh, ) |> Exp.fresh, - ], - ) - |> Exp.fresh, - Tuple([ - Cast( - Float(1.) |> Exp.fresh, - Float |> Typ.fresh, - Unknown(Internal) |> Typ.fresh, - ) - |> Exp.fresh, - Cast( - Bool(true) |> Exp.fresh, - Bool |> Typ.fresh, - Unknown(Internal) |> Typ.fresh, - ) - |> Exp.fresh, - ]) - |> Exp.fresh, - ) - |> Exp.fresh, - dhexp_of_uexp( - Ap( - Forward, - DeferredAp( - EmptyHole |> Exp.fresh, [ Deferral(InAp) |> Exp.fresh, Deferral(InAp) |> Exp.fresh, - Int(3) |> Exp.fresh, + Cast( + Int(3) |> Exp.fresh, + Int |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ) + |> Exp.fresh, ], ) |> Exp.fresh, - Tuple([Float(1.) |> Exp.fresh, Bool(true) |> Exp.fresh]) + Tuple([ + Cast( + Float(1.) |> Exp.fresh, + Float |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ) + |> Exp.fresh, + Cast( + Bool(true) |> Exp.fresh, + Bool |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ) + |> Exp.fresh, + ]) |> Exp.fresh, ) |> Exp.fresh, - ), - ); + dhexp_of_uexp( + Ap( + Forward, + DeferredAp( + EmptyHole |> Exp.fresh, + [ + Deferral(InAp) |> Exp.fresh, + Deferral(InAp) |> Exp.fresh, + Int(3) |> Exp.fresh, + ], + ) + |> Exp.fresh, + Tuple([Float(1.) |> Exp.fresh, Bool(true) |> Exp.fresh]) + |> Exp.fresh, + ) + |> Exp.fresh, + ), + ); -let elaboration_tests = ( - "Elaboration", - [ + let tests = [ test_case("Single integer", `Quick, single_integer), test_case("Empty hole", `Quick, empty_hole), test_case("Free variable", `Quick, free_var), @@ -347,5 +353,413 @@ let elaboration_tests = ( `Quick, ap_of_deferral_of_hole, ), - ], -); + ]; +}; +module MenhirElaborationTests = { + //dhexp = expected + //uexp = tested + let alco_check_menhir = (name: string, dhexp: string, uexp: Term.Exp.t) => + alco_check( + name, + Haz3lmenhir.Conversion.Exp.of_menhir_ast( + Haz3lmenhir.Interface.parse_program(dhexp), + ), + dhexp_of_uexp(uexp), + ); + + //Test for a let function + let let_fun_uexp: Exp.t = + Let( + Cast( + Var("f") |> Pat.fresh, + Arrow(Int |> Typ.fresh, Int |> Typ.fresh) |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ) + |> Pat.fresh, + Fun( + Var("x") |> Pat.fresh, + BinOp(Int(Plus), Int(1) |> Exp.fresh, Var("x") |> Exp.fresh) + |> Exp.fresh, + None, + None, + ) + |> Exp.fresh, + Int(55) |> Exp.fresh, + ) + |> Exp.fresh; + + let let_fun_str = " +let f = + named_fun f x -> + 1 + x + in +55"; + + let let_fun_menhir = () => + alco_check_menhir( + "Let expression for a function which is not recursive (menhir)", + let_fun_str, + let_fun_uexp, + ); + + //Test for an empty hole + let empty_hole_str = "?"; + let empty_hole_uexp: Exp.t = { + ids: [id_at(0)], + term: EmptyHole, + copied: false, + }; + let empty_hole_menhir = () => + alco_check_menhir("Empty hole (menhir)", empty_hole_str, empty_hole_uexp); + + //Test for a free variable + let free_var_uexp: Exp.t = { + ids: [id_at(0)], + term: Wrap({ids: [id_at(1)], term: Var("y"), copied: false}, Paren), + copied: false, + }; + let free_var_menhir = () => + alco_check_menhir( + "Nonempty hole with free variable (menhir)", + "y", + dhexp_of_uexp(free_var_uexp), + ); + + //Menhir test for a binary operation + let bin_op_uexp: Exp.t = + BinOp(Int(Plus), Bool(false) |> Exp.fresh, Var("y") |> Exp.fresh) + |> Exp.fresh; + + let bin_op_str = "false?{Bool => Int} + y{Unknown Internal => Int}"; + + let bin_op_menhir = () => + alco_check_menhir( + "Inconsistent binary integer operation (plus)", + bin_op_str, + dhexp_of_uexp(bin_op_uexp), + ); + + //Inconsistent branches menhir test + let inconsistent_case_menhir_str = " + case 4 == 3 + | true => 24{Int => Unknown Internal} + | false => false{Bool => Unknown Internal} + end +"; + let inconsistent_case_uexp: Exp.t = + Match( + BinOp(Int(Equals), Int(4) |> Exp.fresh, Int(3) |> Exp.fresh) + |> Exp.fresh, + [ + (Bool(true) |> Pat.fresh, Int(24) |> Exp.fresh), + (Bool(false) |> Pat.fresh, Bool(false) |> Exp.fresh), + ], + ) + |> Exp.fresh; + let inconsistent_case_menhir = () => + alco_check_menhir( + "Inconsistent branches where the first branch is an integer and second branch is a boolean (menhir)", + inconsistent_case_menhir_str, + inconsistent_case_uexp, + ); + + //Function free var application menhir test + let ap_fun_uexp: Exp.t = + Ap( + Forward, + Fun( + Var("x") |> Pat.fresh, + BinOp(Int(Plus), Int(4) |> Exp.fresh, Int(5) |> Exp.fresh) + |> Exp.fresh, + None, + None, + ) + |> Exp.fresh, + Var("y") |> Exp.fresh, + ) + |> Exp.fresh; + let ap_fun_str = " + (fun x -> 4 + 5)(y) +"; + let ap_fun_menhir = () => + alco_check_menhir( + "Application of a function (menhir)", + ap_fun_str, + dhexp_of_uexp(ap_fun_uexp), + ); + + //Consistent if statement menhir test + let consistent_if_uexp: Exp.t = + If(Bool(false) |> Exp.fresh, Int(8) |> Exp.fresh, Int(6) |> Exp.fresh) + |> Exp.fresh; + + let consistent_if_str = " + if false then 8 else 6 +"; + let consistent_if_menhir = () => + alco_check_menhir( + "Consistent case with rules (BoolLit(true), IntLit(8)) and (BoolLit(false), IntLit(6))", + consistent_if_str, + dhexp_of_uexp(consistent_if_uexp), + ); + + //Single integer menhir test + let single_int_str = "8"; + let single_int_uexp: Exp.t = { + ids: [id_at(0)], + term: Int(8), + copied: false, + }; + let single_integer_menhir = () => + alco_check_menhir( + "Single integer test (menhir)", + single_int_str, + single_int_uexp, + ); + + //Menhir let expression test + let let_exp_str = "let (a, b) = (4, 6) in a - b"; + let let_exp_uexp: Exp.t = + Let( + Tuple([Var("a") |> Pat.fresh, Var("b") |> Pat.fresh]) |> Pat.fresh, + Tuple([Int(4) |> Exp.fresh, Int(6) |> Exp.fresh]) |> Exp.fresh, + BinOp(Int(Minus), Var("a") |> Exp.fresh, Var("b") |> Exp.fresh) + |> Exp.fresh, + ) + |> Exp.fresh; + let let_exp_menhir = () => + alco_check_menhir( + "Let expression for tuple (a, b) (menhir)", + let_exp_str, + let_exp_uexp, + ); + + let typ_ap_str = "(typfun x -> 4)@"; + let typ_ap_uexp: Exp.t = + TypAp( + TypFun(Var("x") |> TPat.fresh, Int(4) |> Exp.fresh, None) |> Exp.fresh, + Int |> Typ.fresh, + ) + |> Exp.fresh; + let typ_ap_menhir = () => + alco_check_menhir("Type ap test (menhir)", typ_ap_str, typ_ap_uexp); + + let failed_cast_str = "1 ?{Int => String}"; + let failed_cast_uexp: Exp.t = + FailedCast(Int(1) |> Exp.fresh, Int |> Typ.fresh, String |> Typ.fresh) + |> Exp.fresh; + let failed_cast_menhir = () => + alco_check_menhir( + "Failed cast test (menhir)", + failed_cast_str, + failed_cast_uexp, + ); + + let constructor_str = "X"; + let constructor_uexp: Exp.t = + Constructor("X", Unknown(Internal) |> Typ.fresh) |> Exp.fresh; + let constructor_menhir = () => + alco_check_menhir( + "Constructor test (menhir)", + constructor_str, + constructor_uexp, + ); + + /* + <<1 / 2 ? `a`>> + */ + let dynamic_error_hole_str = "<<(1/0) ? `DivideByZero`>> {Unknown Internal => Int}"; + let dynamic_error_hole_uexp: Exp.t = { + ids: [id_at(0)], + term: + DynamicErrorHole( + BinOp(Int(Divide), Int(1) |> Exp.fresh, Int(0) |> Exp.fresh) + |> Exp.fresh, + InvalidOperationError.DivideByZero, + ), + copied: false, + }; + let dynamic_error_hole_menhir = () => + alco_check_menhir( + "Dynamic error hole (menhir)", + dynamic_error_hole_str, + dynamic_error_hole_uexp, + ); + + let builtin_fun_str = "infinity"; + let builtin_fun_uexp: Exp.t = { + ids: [id_at(0)], + term: BuiltinFun("infinity"), + copied: false, + }; + let builtin_fun_menhir = () => + alco_check_menhir( + "Builtin function test (menhir)", + builtin_fun_str, + builtin_fun_uexp, + ); + + let undef_str = "undef"; + let undef_uexp: Exp.t = {ids: [id_at(0)], term: Undefined, copied: false}; + let undef_menhir = () => + alco_check_menhir("Undef test (menhir)", undef_str, undef_uexp); + + let test_str = "test 1 ?{Int => Bool} end"; + let test_uexp: Exp.t = { + ids: [id_at(0)], + term: Test(Int(1) |> Exp.fresh), + copied: false, + }; + let test_menhir = () => + alco_check_menhir("Test failed (menhir)", test_str, test_uexp); + + let filter_str = "eval 1 in 0"; + let stepper_filter_kind: TermBase.stepper_filter_kind_t = + Filter({ + pat: Int(1) |> Exp.fresh, + act: (FilterAction.Eval, FilterAction.All), + }); + let filter_uexp: Exp.t = { + ids: [id_at(0)], + term: Filter(stepper_filter_kind, Int(0) |> Exp.fresh), + copied: false, + }; + let filter_menhir = () => + alco_check_menhir("Filter test (menhir)", filter_str, filter_uexp); + + let undefined_str = " +undef +"; + let undefined_uexp: Exp.t = Undefined |> Exp.fresh; + let undefined_menhir = () => + alco_check_menhir( + "Undefined test (menhir)", + undefined_str, + undefined_uexp, + ); + + let list_exp_str = "[1, 2, 3]"; + let list_exp_uexp: Exp.t = { + ids: [id_at(0)], + term: + ListLit([ + Int(1) |> Exp.fresh, + Int(2) |> Exp.fresh, + Int(3) |> Exp.fresh, + ]), + copied: false, + }; + let list_exp_menhir = () => + alco_check_menhir("List exp (menhir)", list_exp_str, list_exp_uexp); + + let invalid_str = " +?e \"x\" +"; + let invalid_uexp: Exp.t = Invalid("x") |> Exp.fresh; + let invalid_menhir = () => + alco_check_menhir("Invalid test (menhir)", invalid_str, invalid_uexp); + + let ty_alias_str = " +x +"; + let ty_alias_uexp: Exp.t = { + ids: [id_at(0)], + term: + TyAlias( + Var("x") |> TPat.fresh, + Int |> Typ.fresh, + Var("x") |> Exp.fresh, + ), + copied: false, + }; + let ty_alias_menhir = () => + alco_check_menhir( + "Type alias test (menhir)", + ty_alias_str, + ty_alias_uexp, + ); + + let list_concat_str = "[1, 2] @ [3, 4]"; + let list_concat_uexp: Exp.t = { + ids: [id_at(0)], + term: + ListConcat( + ListLit([Int(1) |> Exp.fresh, Int(2) |> Exp.fresh]) |> Exp.fresh, + ListLit([Int(3) |> Exp.fresh, Int(4) |> Exp.fresh]) |> Exp.fresh, + ), + copied: false, + }; + let list_concat_menhir = () => + alco_check_menhir( + "List concat test (menhir)", + list_concat_str, + list_concat_uexp, + ); + + let unop_str = "-1"; + let unop_uexp: Exp.t = { + ids: [id_at(0)], + term: UnOp(Int(Minus), Int(1) |> Exp.fresh), + copied: false, + }; + let unop_menhir = () => + alco_check_menhir("Unary operation test (menhir)", unop_str, unop_uexp); + + let seq_str = "1; 2"; + let seq_uexp: Exp.t = { + ids: [id_at(0)], + term: Seq(Int(1) |> Exp.fresh, Int(2) |> Exp.fresh), + copied: false, + }; + let seq_menhir = () => + alco_check_menhir("Sequence test (menhir)", seq_str, seq_uexp); + + let fixf_str = "fix x -> 1{Int => Unknown Internal}"; + let fixf_uexp: Exp.t = { + ids: [id_at(0)], + term: FixF(Var("x") |> Pat.fresh, Int(1) |> Exp.fresh, None), + copied: false, + }; + let fixf_menhir = () => + alco_check_menhir("FixF test (menhir)", fixf_str, fixf_uexp); + + let tests = [ + test_case("Filter test (menhir)", `Quick, filter_menhir), + test_case("Test failed (menhir)", `Quick, test_menhir), + test_case("Built-in function (menhir)", `Quick, builtin_fun_menhir), + test_case( + "Dynamic error hole (menhir)", + `Quick, + dynamic_error_hole_menhir, + ), + test_case("Constructor test (menhir)", `Quick, constructor_menhir), + test_case("Failed cast test (menhir)", `Quick, failed_cast_menhir), + test_case("Type ap test (menhir)", `Quick, typ_ap_menhir), + test_case("Let expression for a tuple (menhir)", `Quick, let_exp_menhir), + test_case("Single integer (menhir)", `Quick, single_integer_menhir), + test_case( + "Let expression for a function (menhir)", + `Quick, + let_fun_menhir, + ), + test_case("Empty hole (menhir)", `Quick, empty_hole_menhir), + test_case("Free var (menhir)", `Quick, free_var_menhir), + test_case("Bin op (menhir)", `Quick, bin_op_menhir), + test_case("Inconsistent case (menhir)", `Quick, inconsistent_case_menhir), + test_case("ap fun (menhir)", `Quick, ap_fun_menhir), + test_case("Consistent if (menhir)", `Quick, consistent_if_menhir), + test_case("Undefined test (menhir)", `Quick, undefined_menhir), + test_case("List exp (menhir)", `Quick, list_exp_menhir), + test_case("Invalid test (menhir)", `Quick, invalid_menhir), + test_case("Type alias test (menhir)", `Quick, ty_alias_menhir), + test_case("List concat test (menhir)", `Quick, list_concat_menhir), + test_case("Unary operation test (menhir)", `Quick, unop_menhir), + test_case("Sequence test (menhir)", `Quick, seq_menhir), + test_case("FixF test (menhir)", `Quick, fixf_menhir), + ]; +}; + +let tests = [ + ("Elaboration tests", PlainTests.tests), + ("Menhir elaboration tests", MenhirElaborationTests.tests), +]; diff --git a/test/Test_Evaluator.re b/test/Test_Evaluator.re index d9f8d1d50d..1c1540e9ca 100644 --- a/test/Test_Evaluator.re +++ b/test/Test_Evaluator.re @@ -8,7 +8,7 @@ let evaluation_test = (msg, expected, unevaluated) => msg, expected, ProgramResult.Result.unbox( - snd(Evaluator.evaluate'(Builtins.env_init, {d: unevaluated})), + snd(Evaluator.evaluate'(Builtins.env_init, unevaluated)), ), ); @@ -56,53 +56,45 @@ let tet_ap_of_hole_deferral = () => Ap( Forward, Cast( - Cast( - EmptyHole |> Exp.fresh, - Unknown(Internal) |> Typ.fresh, - Arrow( - Unknown(Internal) |> Typ.fresh, - Unknown(Internal) |> Typ.fresh, - ) - |> Typ.fresh, - ) - |> Exp.fresh, + EmptyHole |> Exp.fresh, + Unknown(Internal) |> Typ.fresh, Arrow( Unknown(Internal) |> Typ.fresh, Unknown(Internal) |> Typ.fresh, ) |> Typ.fresh, - Arrow( - Prod([ + ) + |> Exp.fresh, + Cast( + Tuple([ + Cast( + Float(1.) |> Exp.fresh, + Float |> Typ.fresh, Unknown(Internal) |> Typ.fresh, + ) + |> Exp.fresh, + Cast( + Bool(true) |> Exp.fresh, + Bool |> Typ.fresh, Unknown(Internal) |> Typ.fresh, + ) + |> Exp.fresh, + Cast( + Int(3) |> Exp.fresh, + Int |> Typ.fresh, Unknown(Internal) |> Typ.fresh, - ]) - |> Typ.fresh, - Unknown(Internal) |> Typ.fresh, - ) - |> Typ.fresh, - ) - |> Exp.fresh, - Tuple([ - Cast( - Float(1.) |> Exp.fresh, - Float |> Typ.fresh, - Unknown(Internal) |> Typ.fresh, - ) + ) + |> Exp.fresh, + ]) |> Exp.fresh, - Cast( - Bool(true) |> Exp.fresh, - Bool |> Typ.fresh, + Prod([ Unknown(Internal) |> Typ.fresh, - ) - |> Exp.fresh, - Cast( - Int(3) |> Exp.fresh, - Int |> Typ.fresh, Unknown(Internal) |> Typ.fresh, - ) - |> Exp.fresh, - ]) + Unknown(Internal) |> Typ.fresh, + ]) + |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ) |> Exp.fresh, ) |> Exp.fresh, diff --git a/test/Test_ExpToSegment.re b/test/Test_ExpToSegment.re new file mode 100644 index 0000000000..65bbe18ace --- /dev/null +++ b/test/Test_ExpToSegment.re @@ -0,0 +1,196 @@ +open Alcotest; +open Haz3lcore; + +let segmentize = + ExpToSegment.exp_to_segment( + ~settings={ + inline: true, + fold_case_clauses: false, + fold_fn_bodies: false, + hide_fixpoints: false, + fold_cast_types: false, + show_filters: true, + }, + _, + ); + +let tests = ( + "ExpToSegment", + [ + test_case( + "Empty Ids on ExpToSegment constructor", + `Quick, + () => { + let segment = + segmentize( + Let( + Cast( + ListLit([]) |> Pat.fresh, + Sum([Variant("Jg", [], None)]) |> Typ.fresh, + Float |> Typ.fresh, + ) + |> Pat.fresh, + EmptyHole |> Exp.fresh, + EmptyHole |> Exp.fresh, + ) + |> Exp.fresh, + ); + let serialized = Printer.of_segment(~holes=Some("?"), segment); + + check( + string, + "ascribed sum type constructor in pattern", + "let []: (+ Jg) = ? in ?", + serialized, + ); + }, + ), + test_case( + "Match statement", + `Quick, + () => { + let segment = + segmentize( + Match( + Var("x") |> Exp.fresh, + [ + ( + Constructor("A", Unknown(Internal) |> Typ.fresh) + |> Pat.fresh, + Int(1) |> Exp.fresh, + ), + ( + Constructor("B", Unknown(Internal) |> Typ.fresh) + |> Pat.fresh, + Int(2) |> Exp.fresh, + ), + ], + ) + |> Exp.fresh, + ); + let serialized = Printer.of_segment(~holes=Some("?"), segment); + + check( + string, + "Match statement", + "case x | A => 1| B => 2 end", + serialized, + ); + }, + ), + test_case( + "Deferred application", + `Quick, + () => { + let segment = + segmentize( + DeferredAp( + Var("string_sub") |> Exp.fresh, + [ + String("hello") |> Exp.fresh, + Int(1) |> Exp.fresh, + Deferral(InAp) |> Exp.fresh, + ], + ) + |> Exp.fresh, + ); + let serialized = Printer.of_segment(~holes=Some("?"), segment); + + check( + string, + "deferral in application", + {|string_sub("hello", 1, _)|}, + serialized, + ); + }, + ), + test_case( + "Test", + `Quick, + () => { + let segment = + segmentize(Test(Bool(true) |> Exp.fresh) |> Exp.fresh); + let serialized = Printer.of_segment(~holes=Some("?"), segment); + + check(string, "Test of true", {|test true end|}, serialized); + }, + ), + test_case( + "Filter", + `Quick, + () => { + let segment = + segmentize( + Filter( + Filter({pat: Int(1) |> Exp.fresh, act: (Step, One)}), + Int(2) |> Exp.fresh, + ) + |> Exp.fresh, + ); + let serialized = Printer.of_segment(~holes=Some("?"), segment); + + check(string, "Pause", serialized, {|pause 1 in 2|}); + }, + ), + test_case( + "Right associativity", + `Quick, + () => { + check( + string, + "No parens", + Printer.of_segment( + ~holes=Some("?"), + segmentize( + BinOp( + Int(Power), + Int(2) |> Exp.fresh, + BinOp(Int(Power), Int(3) |> Exp.fresh, Int(4) |> Exp.fresh) + |> Exp.fresh, + ) + |> Exp.fresh, + ), + ), + {|2 ** 3 ** 4|}, + ); + check( + string, + "Parens", + Printer.of_segment( + ~holes=Some("?"), + segmentize( + BinOp( + Int(Power), + BinOp(Int(Power), Int(2) |> Exp.fresh, Int(3) |> Exp.fresh) + |> Exp.fresh, + Int(4) |> Exp.fresh, + ) + |> Exp.fresh, + ), + ), + {|(2 ** 3) ** 4|}, + ); + check( + string, + "Arrow types", + Printer.of_segment( + ~holes=Some("?"), + segmentize( + TyAlias( + Var("x") |> TPat.fresh, + Arrow( + Arrow(Int |> Typ.fresh, Bool |> Typ.fresh) |> Typ.fresh, + Var("x") |> Typ.fresh, + ) + |> Typ.fresh, + Int(1) |> Exp.fresh, + ) + |> Exp.fresh, + ), + ), + {|type x = (Int -> Bool) -> x in 1|}, + ); + }, + ), + ], +); diff --git a/test/Test_Menhir.re b/test/Test_Menhir.re new file mode 100644 index 0000000000..0246a91410 --- /dev/null +++ b/test/Test_Menhir.re @@ -0,0 +1,828 @@ +open Haz3lmenhir; +open Alcotest; +open Haz3lcore; + +let alco_check = + testable( + Fmt.using(Haz3lcore.Exp.show, Fmt.string), + Haz3lcore.DHExp.fast_equal, + ) + |> Alcotest.check; + +let strip_Wrap_and_add_builtins = + Exp.map_term( + ~f_exp= + (cont: TermBase.exp_t => TermBase.exp_t, e: TermBase.exp_t) => + switch (e.term) { + | Wrap(e, _) => cont(e) + | Var(x) => + let builtin = + VarMap.lookup(Haz3lcore.Builtins.Pervasives.builtins, x); + cont( + switch (builtin) { + | Some(Fn(_, _, _)) => cont(BuiltinFun(x) |> Exp.fresh) + | Some(Const(_, _)) + | None => cont(e) + }, + ); + | _ => cont(e) + }, + ~f_pat= + (cont, e) => + switch (e.term) { + | Wrap(e, _) => cont(e) + | _ => cont(e) + }, + ~f_typ= + (cont, e) => + switch (e.term) { + | Wrap(e) => cont(e) + | _ => cont(e) + }, + _, + ); + +// Existing recovering parser +let make_term_parse = (s: string) => + strip_Wrap_and_add_builtins( + MakeTerm.from_zip_for_sem(Option.get(Printer.zipper_of_string(s))).term, + ); + +let menhir_matches = (exp: Term.Exp.t, actual: string) => + alco_check( + "menhir matches expected parse", + exp, + Haz3lmenhir.Conversion.Exp.of_menhir_ast( + Haz3lmenhir.Interface.parse_program(actual), + ), + ); + +let menhir_only_test = (name: string, exp: Term.Exp.t, actual: string) => + test_case(name, `Quick, () => {menhir_matches(exp, actual)}); + +let skip_menhir_maketerm_equivalent_test = + (~speed_level=`Quick, name: string, _actual: string) => + test_case(name, speed_level, () => {Alcotest.skip()}); + +let full_parser_test = (name: string, exp: Term.Exp.t, actual: string) => + test_case( + name, + `Quick, + () => { + alco_check( + "expected parse matches MakeTerm parse", + exp, + make_term_parse(actual), + ); + menhir_matches(exp, actual); + }, + ); + +let menhir_maketerm_equivalent_test = + (~speed_level=`Quick, name: string, actual: string) => + test_case(name, speed_level, () => { + alco_check( + "Menhir parse matches MakeTerm parse", + make_term_parse(actual), + Haz3lmenhir.Conversion.Exp.of_menhir_ast( + Haz3lmenhir.Interface.parse_program(actual), + ), + ) + }); + +/** + * QCheck Test to check the equivalence of the Menhir and MakeTerm parsing. + * We generate an expression, convert it to the core representation, convert it to a segment, + * serialize it, parse it with MakeTerm, and parse it with Menhir. + */ +let qcheck_menhir_maketerm_equivalent_test = + QCheck.Test.make( + ~name="Menhir and maketerm are equivalent", + ~count=100, + QCheck.make(~print=AST.show_exp, AST.gen_exp_sized(7)), + exp => { + let core_exp = Conversion.Exp.of_menhir_ast(exp); + + let segment = + ExpToSegment.exp_to_segment( + ~settings= + ExpToSegment.Settings.of_core(~inline=true, CoreSettings.off), + core_exp, + ); + + let serialized = Printer.of_segment(~holes=Some("?"), segment); + let make_term_parsed = make_term_parse(serialized); + let menhir_parsed = Haz3lmenhir.Interface.parse_program(serialized); + let menhir_parsed_converted = + Haz3lmenhir.Conversion.Exp.of_menhir_ast(menhir_parsed); + + switch ( + Haz3lcore.DHExp.fast_equal(make_term_parsed, menhir_parsed_converted) + ) { + | true => true + | false => false + | exception (Failure(msg)) => + print_endline("Error: " ++ msg); + msg == "Sum type has non-unique constructors"; + }; + }, + ); + +/** + * QCheck Test to check that menhir parses out what ExpToSegment serializes. + * We generate an expression, convert it to the core representation, convert it to a segment, + * serialize it, parse it with Menhir, and compare to the original. + * + * TODO This fails due to types not being serialized on constructors + * and some other ExpToSegment inconsistencies + * + * Filter and Test not implemented + * Deferral serializing as "deferral" + * Right associated operator + * https://github.com/hazelgrove/hazel/issues/1452 + * https://github.com/hazelgrove/hazel/issues/1451 + * https://github.com/hazelgrove/hazel/issues/1445 + */ +let qcheck_menhir_serialized_equivalent_test = + QCheck.Test.make( + ~name="Menhir through ExpToSegment and back", + ~count=1000, + QCheck.make(~print=AST.show_exp, AST.gen_exp_sized(7)), + exp => { + let core_exp = Conversion.Exp.of_menhir_ast(exp); + + let segment = + ExpToSegment.exp_to_segment( + ~settings={ + inline: true, + fold_case_clauses: false, + fold_fn_bodies: false, + hide_fixpoints: false, + fold_cast_types: false, + show_filters: true, + }, + core_exp, + ); + let serialized = Printer.of_segment(~holes=Some("?"), segment); + let menhir_parsed = Haz3lmenhir.Interface.parse_program(serialized); + AST.equal_exp(menhir_parsed, exp); + }, + ); + +let tests = ( + "MenhirParser", + [ + full_parser_test("Integer Literal", Int(8) |> Exp.fresh, "8"), + full_parser_test( + "Fun", + Fun(Var("x") |> Pat.fresh, Var("x") |> Exp.fresh, None, None) + |> Exp.fresh, + "fun x -> x", + ), + full_parser_test( + "String Literal", + String("Hello World") |> Exp.fresh, + {|"Hello World"|}, + ), + full_parser_test("Bool Literal", Bool(true) |> Exp.fresh, "true"), + full_parser_test("Empty Hole", EmptyHole |> Exp.fresh, "?"), + full_parser_test("Var", Var("x") |> Exp.fresh, "x"), + full_parser_test( + "Wrap", + Wrap(Var("y") |> Exp.fresh, Paren) |> Exp.fresh, + "(y)", + ), + full_parser_test( + "BinOp", + BinOp(Int(Plus), Int(4) |> Exp.fresh, Int(5) |> Exp.fresh) + |> Exp.fresh, + "4 + 5", + ), + full_parser_test( + "Let", + Let( + Var("x") |> Pat.fresh, + Int(5) |> Exp.fresh, + Var("x") |> Exp.fresh, + ) + |> Exp.fresh, + "let x = 5 in x", + ), + full_parser_test( + "Tuple", + Tuple([Int(4) |> Exp.fresh, Int(5) |> Exp.fresh]) |> Exp.fresh, + "(4, 5)", + ), + full_parser_test( + "Match", + Match( + Int(4) |> Exp.fresh, + [ + (Int(1) |> Pat.fresh, String("hello") |> Exp.fresh), + (Wild |> Pat.fresh, String("world") |> Exp.fresh), + ], + ) + |> Exp.fresh, + {|case 4 + | 1 => "hello" + | _ => "world" + end|}, + ), + full_parser_test( + "If", + If(Bool(true) |> Exp.fresh, Int(8) |> Exp.fresh, Int(6) |> Exp.fresh) + |> Exp.fresh, + "if true then 8 else 6", + ), + full_parser_test( + "Deferred Ap", + DeferredAp(Var("x") |> Exp.fresh, [Deferral(InAp) |> Exp.fresh]) + |> Exp.fresh, + "x(_)", + ), + full_parser_test( + "Cons", + Cons(Int(1) |> Exp.fresh, ListLit([]) |> Exp.fresh) |> Exp.fresh, + "1 :: []", + ), + full_parser_test( + "ListLit", + ListLit([ + Int(1) |> Exp.fresh, + Int(2) |> Exp.fresh, + Int(3) |> Exp.fresh, + ]) + |> Exp.fresh, + "[1, 2, 3]", + ), + menhir_only_test("Unit", Tuple([]) |> Exp.fresh, "()"), + menhir_only_test( + "Constructor", + Constructor("A", Unknown(Internal) |> Typ.fresh) |> Exp.fresh, + "A", + ), + menhir_only_test( + "Constructor cast", + Cast( + Constructor("A", Unknown(Internal) |> Typ.fresh) |> Exp.fresh, + Unknown(Internal) |> Typ.fresh, + Int |> Typ.fresh, + ) + |> Exp.fresh, + "A : Int", + ), + menhir_only_test( + "Constructor of specific sum type", + Constructor("A", Int |> Typ.fresh) |> Exp.fresh, + "A ~ Int", + ), + // TODO Fix for the tests below + menhir_only_test( + "Constructor with Type Variable", + Constructor("A", Var("T") |> Typ.fresh) |> Exp.fresh, + "A ~ T", + ), + full_parser_test( + "Type Variable", + Let( + Cast( + Var("x") |> Pat.fresh, + Var("T") |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ) + |> Pat.fresh, + EmptyHole |> Exp.fresh, + Var("x") |> Exp.fresh, + ) + |> Exp.fresh, + "let x : T = ? in x", + ), + full_parser_test( + "Type Alias", + TyAlias(Var("x") |> TPat.fresh, Int |> Typ.fresh, Int(1) |> Exp.fresh) + |> Exp.fresh, + "type x = Int in 1", + ), + full_parser_test( + "Test", + Test( + BinOp(Int(Equals), Int(3) |> Exp.fresh, Int(3) |> Exp.fresh) + |> Exp.fresh, + ) + |> Exp.fresh, + "test 3 == 3 end", + ), + full_parser_test( + "Filter", + Filter( + Filter({act: (Eval, All), pat: Int(3) |> Exp.fresh}), + Int(3) |> Exp.fresh, + ) + |> Exp.fresh, + "eval 3 in 3" // TODO Use other filter commands + ), + full_parser_test( + "List Concat", + ListConcat( + ListLit([Int(1) |> Exp.fresh, Int(2) |> Exp.fresh]) |> Exp.fresh, + ListLit([Int(3) |> Exp.fresh, Int(4) |> Exp.fresh]) |> Exp.fresh, + ) + |> Exp.fresh, + "[1, 2] @ [3, 4]", + ), + full_parser_test( + "times and divide precendence", + BinOp( + Int(Divide), + BinOp(Int(Times), Int(1) |> Exp.fresh, Int(2) |> Exp.fresh) + |> Exp.fresh, + Int(3) |> Exp.fresh, + ) + |> Exp.fresh, + "1 * 2 / 3", + ), + full_parser_test( + "plus and minus precendence", + BinOp( + Int(Plus), + BinOp(Int(Minus), Int(1) |> Exp.fresh, Int(2) |> Exp.fresh) + |> Exp.fresh, + Int(3) |> Exp.fresh, + ) + |> Exp.fresh, + "1 - 2 + 3", + ), + full_parser_test( + "Integer Ops", + BinOp( + Int(GreaterThanOrEqual), + BinOp( + Int(Minus), + BinOp( + Int(Plus), + UnOp(Int(Minus), Int(1) |> Exp.fresh) |> Exp.fresh, + Int(2) |> Exp.fresh, + ) + |> Exp.fresh, + BinOp( + Int(Times), + BinOp(Int(Divide), Int(3) |> Exp.fresh, Int(4) |> Exp.fresh) + |> Exp.fresh, + BinOp(Int(Power), Int(5) |> Exp.fresh, Int(6) |> Exp.fresh) + |> Exp.fresh, + ) + |> Exp.fresh, + ) + |> Exp.fresh, + Int(8) |> Exp.fresh, + ) + |> Exp.fresh, + "-1 + 2 - 3 / 4 * 5 ** 6 >= 8", + ), + full_parser_test("Float", Float(1.) |> Exp.fresh, "1."), + full_parser_test( + "Float Ops", + BinOp( + Float(LessThan), + BinOp( + Float(Minus), + Float(2.) |> Exp.fresh, + BinOp( + Float(Times), + BinOp( + Float(Divide), + Float(3.) |> Exp.fresh, + Float(4.) |> Exp.fresh, + ) + |> Exp.fresh, + BinOp( + Float(Power), + Float(5.) |> Exp.fresh, + Float(6.) |> Exp.fresh, + ) + |> Exp.fresh, + ) + |> Exp.fresh, + ) + |> Exp.fresh, + Float(8.) |> Exp.fresh, + ) + |> Exp.fresh, + "2. -. 3. /. 4. *. 5. **. 6. <. 8.", + ), + full_parser_test( + "Let binding with type ascription", + Let( + Cast( + Var("x") |> Pat.fresh, + Int |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ) + |> Pat.fresh, + Int(5) |> Exp.fresh, + Var("x") |> Exp.fresh, + ) + |> Exp.fresh, + "let (x: Int) = 5 in x", + ), + menhir_only_test( + "named_function", + Fun( + (Var("x"): Pat.term) |> Pat.fresh, + BinOp(Int(Plus), Var("x") |> Exp.fresh, Int(5) |> Exp.fresh) + |> Exp.fresh, + None, + Some("f"), + ) + |> Exp.fresh, + "named_fun f x -> x + 5", + ), + full_parser_test( + "basic sum type", + Let( + Cast( + Var("x") |> Pat.fresh, + Sum([ + Variant("A", [], None), + Variant("B", [], None), + Variant("C", [], Some(Int |> Typ.fresh)), + ]) + |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ) + |> Pat.fresh, + Ap( + Forward, + Constructor("C", Unknown(Internal) |> Typ.fresh) |> Exp.fresh, + Int(7) |> Exp.fresh, + ) + |> Exp.fresh, + Var("x") |> Exp.fresh, + ) + |> Exp.fresh, + "let x : +A +B +C(Int) = C(7) in x", + ), + menhir_maketerm_equivalent_test("Empty Type Hole", "let g: ? = 7 in g"), + menhir_maketerm_equivalent_test( + "Pattern with type ascription", + "fun (b : Bool) -> b", + ), + full_parser_test( + "Type Hole in arrow cast", + Fun( + Cast( + Var("b") |> Pat.fresh, + Wrap( + Arrow( + Unknown(Hole(EmptyHole)) |> Typ.fresh, + Unknown(Hole(EmptyHole)) |> Typ.fresh, + ) + |> Typ.fresh, + ) + |> Typ.fresh, + Unknown(Internal) |> Typ.fresh, + ) + |> Pat.fresh, + EmptyHole |> Exp.fresh, + None, + None, + ) + |> Exp.fresh, + "fun (b : ? -> ?) -> ?", + ), + full_parser_test( + "multiargument function", + Ap( + Forward, + Var("f") |> Exp.fresh, + Tuple([Int(1) |> Exp.fresh, Int(2) |> Exp.fresh]) |> Exp.fresh, + ) + |> Exp.fresh, + "f(1, 2)", + ), + menhir_maketerm_equivalent_test( + "partial sum type", + "type Partial = +Ok(?) + ? in ?", + ), + menhir_maketerm_equivalent_test( + "Function with type variable", + "fun (x : a) -> x", + ), + menhir_maketerm_equivalent_test("Sequence addition precedence", "1+2;3"), + menhir_maketerm_equivalent_test( + "And app precedence", + "exp_equal(e1, e3) && exp_equal(e2, e4)", + ), + menhir_maketerm_equivalent_test( + "Negation precedence with multiplication", + "-num*1", + ), + menhir_maketerm_equivalent_test( + "Concatenation association", + "1::2::3::[]", + ), + menhir_maketerm_equivalent_test( + "and less than precedence", + "true && 23 < int_of_float(51.00)" // TODO This looks like a bug in MakeTerm + ), + menhir_maketerm_equivalent_test( + ~speed_level=`Slow, + "Altered Documentation Buffer: Basic Reference", + {| +let empty_hole = ? in + +let non_empty_hole : Int = true in + +let bool: Bool = true in +let operators = !true && false || true in +let conditional = if !true then 1 else 2 in + +let num: Int = 1 in +let arithmetic = -num*1 + 2/3 - 4**5 in +let comparison = + (0 == 0, 0 < 1, 1 <= 1, 2 > 1, 1 >= 1) +in + +let float: Float = 0.1 in +let artihmetic = 0. *. 1. +. 2. /. 3. -. 4. **. 5. in +let comparison = + (0. ==. 0., 0. <. 1., 1. <=. 1., 2. >. 1., 1. >=. 1.) +in + +let string = "Hello, world!" in +let concatenation = string ++ " Goodbye." in +let comparison = string$== "Hello, world!" in + +let tuple : (Int, Bool, (Bool, Int)) = +(1, true, (false, 3)) in +let (a, b, (c, d)) = tuple in + +let y : (Int, Int, Int) -> Int = +fun (m, x, b) -> m * x + b in + +let double_recursively : Int -> Int = + fun n -> + if n == 0 + then 0 + else double_recursively(n - 1) + 2 +in + +let (even : Int -> Bool, odd : Int -> Bool) = + (fun n -> if n == 0 then true else odd(n - 1), + fun n -> if n == 0 then false else even(n - 1)) +in + +let empty_list : [Int] = [] in +let non_empty_list : [Int] = 1::2::3::[] in +let list_literals : [Int] = [1, 2, 3] in +let length : [Int] -> Int = + fun xs -> + case xs + | [] => 0 + | hd::tl => 1 + length(tl) + end +in +let has_at_least_two_elements : [Int] -> Bool = + fun xs -> + case xs + | [] => false + | hd::[] => false + | a::b::[] => true + end +in + +type Exp = + + Var(String) + + Lam(String, Exp) ++ Ap(Exp, Exp) in +let exp_equal: (Exp, Exp) -> Bool = + fun es -> + case es + | (Var(x), Var(y)) => x$== y + | (Lam((x1, e1)), Lam((x2, e2))) => x1$== x2 && exp_equal(e1, e2) + | (Ap((e1, e2)), Ap((e3, e4))) => exp_equal(e1, e3) && exp_equal(e2, e4) + | _ => false + end +in + +let poly_id: (forall a -> (a -> a)) = + (typfun a -> (fun (x : a) -> x)) +in +let apply_both: +forall a -> forall b -> (forall c -> c -> c) -> ((a, b) -> (a, b)) = + typfun a -> typfun b -> + fun (f : forall c -> (c -> c)) -> + fun ((x, y) : (a, b)) -> (f@(x), f@(y)) +in +let list_length: forall a -> ([a] -> Int) = + typfun a -> fun (l : [a]) -> + case l + | [] => 0 + | hd::tl => 1 + list_length@(tl) + end +in + +test 2 + 2 == 4 end; +test 3 + 3 == 6 end; +test 2 + 2 == 5 end; + +2 + 2 + |}, + ), + menhir_maketerm_equivalent_test( + ~speed_level=`Slow, + "Altered Documentation Buffer: Projectors", + {| +let fold = (((((((((((()))))))))))) in +let folds: (Int -> Bool) = ? in +let guard: Bool = true in +let phase: Int = 44 in +let float: Float = 79.00 in +let (a:Int, f: Float) = (true, 28) in +let _ = "" in +let __ = "" in +let ___ = "a" in +let ____ = "shift" in +let _____ = "malicious" in +let ______ = "a shift malicious" in +let box: Int = "malicious" in +if true && (23 < int_of_float(51.00)) +then ______ else "its: " ++ box |}, + ), + menhir_maketerm_equivalent_test( + ~speed_level=`Slow, + "Altered Documentation Buffer: Types & Static Errors", + {| +let _ = unbound in +let Undefined = Undefined in +let true = 2 in + +let ? = if true then 1 else 1. in +let _ = if true then 1 else 1. in +let _: ? = if true then 1 else 1. in +let _: Int = if true then 1 else 1. in +let _: Fake = if true then 1 else true in +let (_, _) = if true then 1 else 1. in +let (_, _) = ((if true then 1 else 1.),?) in +let (_: ?, _) = ((if true then 1 else 1.),?) in +let [_] = [(if true then 1 else 1.)] in +let [_] = (if true then 1 else 1.) in + +(?)(if true then 1 else 1.); +1(if true then 1 else 1.); +(1)(if true then 1 else 1.); +(fun ? -> ?)(if true then 1 else 1.); +(fun _ -> ?)(if true then 1 else 1.); +(fun (_: ?) -> ?)(if true then 1 else 1.); +(fun (_: Int) -> ?)(if true then 1 else 1.); + +let _ = fun x -> if true then 1 else 1. in +let _: ? = fun x -> if true then 1 else 1. in +let _: ? -> ? = fun x -> if true then 1 else 1. in +let _: ? -> Int = fun x -> if true then 1 else 1. in +let _: ? -> [?] = fun x -> if true then 1 else 1. in + +(?)::[(if true then 1 else 1.)]; +1::[(if true then 1 else 1.)]; +(1, 1)::[(if true then 1 else 1.)]; + +let ? = [1, 1., true] in +let _ = [1, 1., true] in +let _: ? = [1, 1., true] in +let _: [?] = [1, 1., true] in +let _: [Int] = [1, 1., true] in + +let _: [Int] = 1::[2] in +let _: [Int] = 1.0::[2] in +let _: [Int] = 1::[2.0] in +"BYE" +|}, + ), + menhir_maketerm_equivalent_test( + ~speed_level=`Slow, + "Altered Documentation Buffer: adt dynamics", + {| +type Exp = + + Var(String) + + Lam(String, Exp) + + Ap(Exp, Exp) in + +let exp_equal: (Exp, Exp) -> Bool = + fun es -> + case es + | (Var(x), Var(y)) => x$== y + | (Lam((x1, e1)), Lam((x2, e2))) => x1$== x2 && exp_equal(e1, e2) + | (Ap((e1, e2)), Ap((e3, e4))) => exp_equal(e1, e3) && exp_equal(e2, e4) + | _ => false end in + +let subst: (Exp, String, Exp) -> Exp= + fun (v, name, f) -> + case f + | Var(n) => + (if n$== name then v else f) + | Lam((x, body)) => + Lam(x, subst(v,name, body)) + | Ap((e1,e2)) => + Ap(subst(v, name, e1), subst(v, name, e2)) end in + +type Result = + + Error(String) + + Ok(Exp) +in + +let result_equal: (Result, Result) -> Bool = + fun rs -> + case rs + | (Ok(e1), Ok(e2)) => exp_equal(e1, e2) + | (Error(e1), Error(e2)) => e1$== e2 +| _ => false end in + +let go: Exp -> Result = + fun f -> + case f + | Var(n) => Error("Free Variable") + | Lam((x, body)) => Ok(Lam(x, body)) + | Ap((e1,e2)) => + case go(e1) + | Ok(Lam((x, body)))=> + case go(e2) + | Error(err) => Error(err) + | Ok(arg) => go(subst(arg, x, body)) end +| _ => Error("Not a Function") end end in + +test result_equal( + go(Var("yo")), +Error("Free Variable")) end; + +test result_equal( + go(Ap(Var("no"), Lam("bro", Var("bro")))), +Error("Not a Function")) end; + +test result_equal( + go(Lam("yo", Var("yo"))), +Ok(Lam("yo", Var("yo")))) end; + +test result_equal( + go(Ap(Lam("yo", Var("yo")), Lam("bro", Var("bro")))), +Ok(Lam("bro", Var("bro")))) end +|}, + ), + menhir_maketerm_equivalent_test( + // Variable names are renamed due to lexing overtaking e, t, p, and tp + ~speed_level=`Slow, + "Altered Documentation Buffer: Polymorphism", + {|let id = typfun A -> (fun (x : A) -> x) in +let ex1 = id@(1) in +let const : forall A -> (forall B -> (A -> B -> A)) = +typfun A -> (typfun B -> (fun x -> fun y -> x)) in +let ex2 = const@@(2)("Hello World") in +let apply_both : forall A -> forall B -> (forall D -> D -> D) -> (A , B) -> (A , B) = +typfun A -> typfun B -> fun f -> fun (x, y) -> (f@(x), f@(y)) in +let ex3 = apply_both@@(id)(3, "Hello World") in +let emptylist : forall A -> [A] = typfun A -> [] in +let map : forall A -> forall B -> (A -> B) -> ([A] -> [B]) = + typfun A -> typfun B -> fun (f : (A -> B)) -> fun (l : [A]) -> + case l + | (h :: a) => f(h) :: map@@(f)(a) + | _ => emptylist@ +end in +let ex4 = map@@(string_of_int)([1,2,3]) in +type MyList = rec A -> (+Nil + Cons(Int, A)) in +let x : MyList = Cons(1, Cons(2, Cons(3, Nil))) in +type MyList2 = +Nil + Cons(Int, MyList2) in +type Broken = Int -> (+HasInt(Int) + HasMore(Int, Broken)) in +let list_of_mylist : (MyList -> [Int]) = fun (myl : MyList) -> + case myl + | Nil => [] + | Cons((h, a)) => h :: list_of_mylist(a) +end in +let ex5 = list_of_mylist(x) in +(ex1, ex2, ex3, ex4, ex5) + |}, + ), + // This fails because MakeTerm can't handle left to right keyword prefixes. + skip_menhir_maketerm_equivalent_test( + "Prefixed keyword parses", + {|let ? = ina in ?|}, + ), + skip_menhir_maketerm_equivalent_test( + "Sum type messed up in make term", + {|type ? = rec ? -> + Aramj -> Bool in ?|}, + ), + skip_menhir_maketerm_equivalent_test( + "List concat and typap", + {|type ? = (+ Ulog, () -> Float) in let (()) = (()) in 0.001536|}, + ), + skip_menhir_maketerm_equivalent_test( + "Sum in product in typeap", + {|((fun _ -> b)) @< [(+ Kfgii, Float)] >|}, + ), + skip_menhir_maketerm_equivalent_test( + "Non-unique constructors currently throws in equality", + {|type ? = ((+ ? + ?)) in []|}, + ), + QCheck_alcotest.to_alcotest(qcheck_menhir_maketerm_equivalent_test), + // Disabled due to bugs in ExpToSegment + QCheck_alcotest.to_alcotest(qcheck_menhir_serialized_equivalent_test), + ], +); diff --git a/test/dune b/test/dune index 3b9dc8bb2e..e16add0dc2 100644 --- a/test/dune +++ b/test/dune @@ -2,7 +2,23 @@ (test (name haz3ltest) - (libraries haz3lcore alcotest junit junit_alcotest bisect_ppx.runtime) + (libraries + haz3lcore + haz3lweb + alcotest + sexplib + base + ptmap + incr_dom + haz3lmenhir + junit + junit_alcotest + bisect_ppx.runtime) (modes js) (preprocess - (pps js_of_ocaml-ppx ppx_deriving.show))) + (pps + js_of_ocaml-ppx + ppx_let + ppx_sexp_conv + ppx_deriving.show + ppx_yojson_conv))) diff --git a/test/haz3ltest.re b/test/haz3ltest.re index f6bcf3cf6f..fc8c52e543 100644 --- a/test/haz3ltest.re +++ b/test/haz3ltest.re @@ -5,13 +5,15 @@ let (suite, _) = ~and_exit=false, "HazelTests", [ - Test_Elaboration.elaboration_tests, + Test_ExpToSegment.tests, + Test_Menhir.tests, Test_StringUtil.tests, Test_Statics.tests, Test_Evaluator.tests, Test_ListUtil.tests, Test_MakeTerm.tests, - ], + ] + @ Test_Elaboration.tests, ); Junit.to_file(Junit.make([suite]), "junit_tests.xml"); Bisect.Runtime.write_coverage_data();