From b695ad75fa299bfc068d78a3369930cdb0043af5 Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 10 Oct 2022 20:18:58 +0200 Subject: [PATCH] try a tree shaking algorithm --- eyg/src/eyg/entry.gleam | 20 +++-- eyg/test/eyg/analysis_test.gleam | 130 +++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 8 deletions(-) create mode 100644 eyg/test/eyg/analysis_test.gleam diff --git a/eyg/src/eyg/entry.gleam b/eyg/src/eyg/entry.gleam index eeb268a9a..776974bbe 100644 --- a/eyg/src/eyg/entry.gleam +++ b/eyg/src/eyg/entry.gleam @@ -15,7 +15,6 @@ import eyg/typer import eyg/typer/monotype as t import eyg/editor/editor - fn update(page, interrupt, display, on_click) { io.debug(page) display(page) @@ -25,9 +24,14 @@ fn b(args) { Ok(args) } -fn tree() { - r.Function(p.Variable("x"), e.tuple_([e.variable("x"),e.variable("x")]), map.new(), None) - } +fn tree() { + r.Function( + p.Variable("x"), + e.tuple_([e.variable("x"), e.variable("x")]), + map.new(), + None, + ) +} // uses default builtin that need moving out of effectful // has an entry point key should eventually be a hash @@ -44,12 +48,12 @@ pub fn interpret_client(source, key, display, on_click) { let #(xtyped, typer) = typer.expand_providers(typed, typer, []) // Running the interpreter kills the client - // assert Ok(term) = effectful.eval(editor.untype(xtyped)) - // io.debug(term) + // assert Ok(term) = effectful.eval(editor.untype(xtyped)) + // io.debug(term) |> io.debug io.debug("expanded") - effectful.eval_call(tree(), r.Binary("nothing exciting"), effectful.real_log) - |> io.debug + effectful.eval_call(tree(), r.Binary("nothing exciting"), effectful.real_log) + |> io.debug // // TODO make an AST the requires rendering // term } diff --git a/eyg/test/eyg/analysis_test.gleam b/eyg/test/eyg/analysis_test.gleam new file mode 100644 index 000000000..456f0ebed --- /dev/null +++ b/eyg/test/eyg/analysis_test.gleam @@ -0,0 +1,130 @@ +import gleam/io +import gleam/option.{Some} +import gleam/list +import gleam/string +import eyg/ast/expression as e +import eyg/ast/pattern as p +import eyg/typer/monotype as t +import eyg/analysis +import eyg/typer + +fn variables_needed(source) { + do_variables_needed(source) + |> list.unique + |> list.sort(string.compare) +} + +fn do_variables_needed(tree) { + let #(_, exp) = tree + case exp { + e.Binary(_) | e.Hole -> [] + e.Variable(label) -> [label] + e.Tuple(elems) -> + list.fold( + elems, + [], + fn(acc, e) { list.append(acc, do_variables_needed(e)) }, + ) + e.Record(fields) -> + list.fold( + fields, + [], + fn(acc, f) { + let #(_, e) = f + list.append(acc, do_variables_needed(e)) + }, + ) + e.Access(e, _) | e.Tagged(_, e) -> do_variables_needed(e) + e.Call(f, arg) -> + list.append(do_variables_needed(f), do_variables_needed(arg)) + e.Let(_p, value, then) -> + list.append(do_variables_needed(value), do_variables_needed(then)) + // TODO pattern + e.Function(_p, body) -> do_variables_needed(body) + e.Case(value, branches) -> + list.fold( + branches, + do_variables_needed(value), + fn(acc, b) { + // TODO remove pattern + let #(_, _, then) = b + list.append(acc, do_variables_needed(then)) + }, + ) + // TODO these shouldn't add anything new or we will change them to be eval'd functions + e.Provider(_, _, _) -> [] + } + // _ -> todo +} + +fn by_type(tree, scope) { + assert Ok(typer) = do_type(tree, scope, typer.init()) + t.resolve(scope, typer.substitutions) +} + +fn do_type(tree, scope, state) -> Result(typer.Typer, _) { + let #(_, exp) = tree + case exp { + e.Variable(label) -> { + let #(row, state) = typer.next_unbound(state) + let #(type_, state) = typer.next_unbound(state) + typer.unify( + t.Record([#(label, t.Unbound(type_))], Some(row)), + scope, + state, + ) + } + e.Let(_, value, then) -> do_type(value, scope, state) + e.Access(term, key) -> + // This is on the type of the term does nothing to scope. I think need to run through checking the typer + todo + _ -> { + io.debug(exp) + todo + } + } +} + +pub fn slim_records_test() -> Nil { + // e.let_(p.Record([]), e.record([]), e.hole()) + + let source = e.variable("x") + assert _ = + by_type(source, t.Unbound(-1)) + |> io.debug + + let source = + e.let_( + p.Variable(""), + e.access(e.variable("x"), "foo"), + e.let_(p.Variable(""), e.access(e.variable("x"), "foo"), e.hole()), + ) + + assert _ = + by_type(source, t.Unbound(-1)) + |> io.debug + todo("slim") +} + +pub fn capturing_all_type_variables_test() { + // If there is a function from string -> string could be a -> b a -> string b -> string + // how do providers work over this + let source = e.variable("x") + assert ["x"] = variables_needed(source) + + let source = e.binary("hello") + assert [] = variables_needed(source) + + let source = e.call(e.variable("x"), e.variable("y")) + assert ["x", "y"] = variables_needed(source) + + let source = e.tuple_([e.variable("x"), e.variable("y")]) + assert ["x", "y"] = variables_needed(source) + + let source = e.access(e.variable("x"), "y") + assert ["x"] = variables_needed(source) + + let source = e.tagged("Y", e.variable("x")) + assert ["x"] = variables_needed(source) + // todo("test") +}