diff --git a/ovo/lib/ovo/builtins.ex b/ovo/lib/ovo/builtins.ex index 53cac5e..69e2105 100644 --- a/ovo/lib/ovo/builtins.ex +++ b/ovo/lib/ovo/builtins.ex @@ -155,10 +155,26 @@ defmodule Ovo.Builtins do end end + defp get_host do + case "#{Node.self()}" |> String.split("@") do + [_, b] -> b + _ -> "nohost" + end + end + defp invoke(nodes, env) do case map_nodes(nodes, env) do [%{kind: :string, value: hash}, %{kind: :list, nodes: ns}] -> - Ovo.Registry.run_chain([hash], ns) + {h, host} = + case String.split(hash, "@") do + [h] -> {h, Node.self()} + [h, node] -> {h, :"#{node}@#{get_host()}"} + [h, node, host] -> {h, :"#{node}@#{host}"} + end + + :erpc.call(host, fn -> + Ovo.Registry.run_chain([h], ns) + end) _ -> :error diff --git a/ovo/lib/ovo/registry.ex b/ovo/lib/ovo/registry.ex index c82b67c..50e1ee3 100644 --- a/ovo/lib/ovo/registry.ex +++ b/ovo/lib/ovo/registry.ex @@ -67,7 +67,7 @@ defmodule Ovo.Registry do update_in( state, [Access.key!(hash), Access.key!(:metadata), Access.key!(:args), Access.at!(position)], - fn _ -> arg end + fn {k, _v} -> {k, arg} end ) end) end diff --git a/ovo_playground/assets/js/app.js b/ovo_playground/assets/js/app.js index df0cdd9..f580df8 100644 --- a/ovo_playground/assets/js/app.js +++ b/ovo_playground/assets/js/app.js @@ -23,13 +23,74 @@ import {LiveSocket} from "phoenix_live_view" import topbar from "../vendor/topbar" let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") -let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}}) +let Hooks = { + 'watch_input': { + mounted() { + this.el.addEventListener('input', (e) => { + this.pushEvent(this.el.getAttribute('data-event'), {value: e.target.value}); + }); + }, + }, + 'watch_arg': { + mounted() { + this.el.addEventListener('input', (e) => { + this.pushEvent(this.el.getAttribute('data-event'), {index: this.el.getAttribute('data-index'), value: e.target.value}); + }); + }, + }, + 'change_code': { + mounted() { + this.el.addEventListener('input', (e) => { + this.pushEvent('code_change', {value: e.target.value}); + }); + }, + }, + "update_chain_arg": { + mounted() { + this.el.addEventListener('input', (e) => { + this.pushEvent('update_chain_arg', { + chain_index: this.el.getAttribute('data-chain_index'), + arg_index: this.el.getAttribute('data-arg_index'), + value: e.target.value, + }); + }); + }, + }, + "change_runner_arg": { + mounted() { + this.el.addEventListener('input', (e) => { + this.pushEvent('change_runner_arg', { + hash: this.el.getAttribute('data-hash'), + index: this.el.getAttribute('data-index'), + value: e.target.value, + }); + }); + }, + }, + "update_node": { + mounted() { + const kind = this.el.getAttribute('data-kind'); + const path = this.el.getAttribute('data-path'); + if (kind === 'bool') { + this.el.addEventListener('change', () => { + this.pushEvent('change_path', {path, value: this.el.checked}); + }); + } else { + this.el.addEventListener('change', () => { + this.pushEvent('change_path', {path, value: this.el.value}); + }); + } + } + }, +}; +let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}, hooks: Hooks}); // Show progress bar on live navigation and form submits topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"}) window.addEventListener("phx:page-loading-start", _info => topbar.show(300)) window.addEventListener("phx:page-loading-stop", _info => topbar.hide()) + // connect if there are any LiveViews on the page liveSocket.connect() diff --git a/ovo_playground/lib/ovo_playground.ex b/ovo_playground/lib/ovo_playground.ex index dfbd49e..4d724d1 100644 --- a/ovo_playground/lib/ovo_playground.ex +++ b/ovo_playground/lib/ovo_playground.ex @@ -1,6 +1,33 @@ defmodule OvoPlayground do @moduledoc false + def demo_setup do + case to_string(Node.self()) do + "bob@" <> _rest -> setup_bob() + "alice@" <> _rest -> setup_alice() + _ -> setup_bottles() + end + end + + def setup_bob do + Node.set_cookie(:ovo_demo) + Node.connect(:"alice@MacBook-Air-de-Lucas.local") + register_bob_examples() + end + + def setup_alice do + Node.set_cookie(:ovo_demo) + Node.connect(:"bob@MacBook-Air-de-Lucas.local") + register_alice_examples() + end + + def setup_bottles do + register_bottles_example + end + + def register_bob_examples do + end + @doc """ The classic "99 bottles" song example """ @@ -78,12 +105,12 @@ defmodule OvoPlayground do """ for {code, name, args} <- [ - {register, "register", ["0"]}, - {filler, "filler", ["100"]}, - {join, "joiner", ["[\"a\"]", "\" \""]}, - {last_bottle, "last_bottle", ["1"]}, - {two_bottles, "two_bottles", ["1"]}, - {normal_bottles, "bottle", ["1"]}, + {register, "register", [{:text, "0"}]}, + {filler, "filler", [{:text, "100"}]}, + {join, "joiner", [{:text, "[\"a\"]"}, {:text, "\" \""}]}, + {last_bottle, "last_bottle", [{:text, "1"}]}, + {two_bottles, "two_bottles", [{:text, "1"}]}, + {normal_bottles, "bottle", [{:text, "1"}]}, {full_song, "full_song", []} ] do Ovo.Runner.register(code, name, args) @@ -111,14 +138,8 @@ defmodule OvoPlayground do hex(hash) """ - # hashes to yfHDp - logger = """ - arg(0) - """ - for {code, name, args} <- [ - {logger, "logger", ["100"]}, - {legitimate_hash, "hash function", ["100"]} + {legitimate_hash, "hash function", [{:secret, "the quick brown fox"}]} ] do Ovo.Runner.register(code, name, args) end @@ -127,6 +148,11 @@ defmodule OvoPlayground do def register_alice_tricks do Ovo.Registry.remove_runner("Sj2py") + # hashes to yfHDp + logger = """ + arg(0) + """ + totally_legitimate_hash_function_nothing_to_see_here = """ len = length(arg(0)) ~~ = \\a -> @@ -155,8 +181,9 @@ defmodule OvoPlayground do """ for {code, name, args} <- [ + {logger, "logger", [{:text, "100"}]}, {totally_legitimate_hash_function_nothing_to_see_here, "hash", - ["\"the quick brown fox\""]} + [{:secret, "\"the quick brown fox\""}]} ] do Ovo.Runner.register(code, name, args) end diff --git a/ovo_playground/lib/ovo_playground/application.ex b/ovo_playground/lib/ovo_playground/application.ex index 5285226..3421181 100644 --- a/ovo_playground/lib/ovo_playground/application.ex +++ b/ovo_playground/lib/ovo_playground/application.ex @@ -22,7 +22,9 @@ defmodule OvoPlayground.Application do # See https://hexdocs.pm/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: OvoPlayground.Supervisor] - Supervisor.start_link(children, opts) + start_result = Supervisor.start_link(children, opts) + OvoPlayground.demo_setup() + start_result end # Tell Phoenix to update the endpoint configuration diff --git a/ovo_playground/lib/ovo_playground/transforms.ex b/ovo_playground/lib/ovo_playground/transforms.ex index 62de097..6fcad3c 100644 --- a/ovo_playground/lib/ovo_playground/transforms.ex +++ b/ovo_playground/lib/ovo_playground/transforms.ex @@ -1,4 +1,6 @@ defmodule OvoPlayground.Transforms do + alias Ovo.Ast + @moduledoc """ Transforms are functions that ultimately produce an Ovo.Ast, either from nothing, some input, an Ast, or combinations of these things. @@ -10,7 +12,13 @@ defmodule OvoPlayground.Transforms do add(val, 10) """ - defp produce(input) when is_binary(input), do: Ovo.tokenize(input) |> Ovo.parse() + defp produce(input) when is_binary(input) do + case Ovo.tokenize(input) |> Ovo.parse() do + {:ok, %{nodes: nodes}, []} -> nodes + _ -> [] + end + end + defp list([a]), do: [a] defp list(a), do: [a] defp nodes(%Ovo.Ast{nodes: nodes}), do: nodes @@ -24,8 +32,12 @@ defmodule OvoPlayground.Transforms do fun.(ast) end + def wrap_root(nodes) do + Ast.root(nodes) + end + def default() do - produce(@default_code) + Ovo.tokenize(@default_code) |> Ovo.parse() end def default_assignment() do diff --git a/ovo_playground/lib/ovo_playground_web/components/ovo/node.ex b/ovo_playground/lib/ovo_playground_web/components/ovo/node.ex index 3271c43..ac2db40 100644 --- a/ovo_playground/lib/ovo_playground_web/components/ovo/node.ex +++ b/ovo_playground/lib/ovo_playground_web/components/ovo/node.ex @@ -2,7 +2,7 @@ defmodule OvoPlaygroundWeb.Components.Ovo.Node do use Phoenix.Component alias OvoPlaygroundWeb.Components.Ovo.Ast import OvoPlaygroundWeb.CoreComponents, only: [icon: 1] - defp path_to_string(path), do: Enum.map_join(path |> List.flatten(), ",", & &1) + defp path_to_string(path), do: Enum.map_join(path |> List.flatten(), "_", & &1) attr(:node, Ovo.Ast, required: true) attr(:depth, :integer, required: true) @@ -11,7 +11,6 @@ defmodule OvoPlaygroundWeb.Components.Ovo.Node do def show_node(assigns) do ~H"""
- <%= Jason.encode!(@path) %> <%= case @node.kind do %> <% :root -> %> <.traverse nodes={@node.nodes} path={["nodes" | @path]} /> @@ -76,7 +75,10 @@ defmodule OvoPlaygroundWeb.Components.Ovo.Node do inputmode="numeric" pattern="[0-9.]*" value={@node.value} - phx-keyup={"change_path:#{path_to_string(@path)}"} + phx-hook="update_node" + id={path_to_string(@path)} + data-path={path_to_string(@path)} + data-kind={@node.kind} /> """ end @@ -92,7 +94,10 @@ defmodule OvoPlaygroundWeb.Components.Ovo.Node do inputmode="numeric" pattern="[0-9]*" value={@node.value} - phx-keyup={"change_path:#{path_to_string(@path)}"} + phx-hook="update_node" + id={path_to_string(@path)} + data-path={path_to_string(@path)} + data-kind={@node.kind} /> """ end @@ -106,7 +111,14 @@ defmodule OvoPlaygroundWeb.Components.Ovo.Node do <%= if String.length(@node.value) > 50 do %> <% else %> - + <% end %> """ end @@ -117,7 +129,14 @@ defmodule OvoPlaygroundWeb.Components.Ovo.Node do def symbol(assigns) do ~H""" <.node_label name="Symbol" /> - + """ end @@ -127,7 +146,14 @@ defmodule OvoPlaygroundWeb.Components.Ovo.Node do def bool(assigns) do ~H""" <.node_label name="Boolean" /> <%= @node.value %> - + """ end @@ -162,7 +188,10 @@ defmodule OvoPlaygroundWeb.Components.Ovo.Node do <.icon name="hero-pause-circle-solid rotate-90" class="h-5 w-5" /> <.show_node node={@node.nodes} path={["nodes" | @path]} /> diff --git a/ovo_playground/lib/ovo_playground_web/live/page_live.ex b/ovo_playground/lib/ovo_playground_web/live/page_live.ex index f92cfc5..e4ad946 100644 --- a/ovo_playground/lib/ovo_playground_web/live/page_live.ex +++ b/ovo_playground/lib/ovo_playground_web/live/page_live.ex @@ -97,15 +97,16 @@ defmodule OvoPlaygroundWeb.PageLive do }, socket ) do - inti = String.to_integer(index) + int_index = String.to_integer(index) intai = String.to_integer(aindex) if socket.assigns.chains == [] do {:noreply, socket} else chain = - update_in(socket.assigns.chains, [Access.at!(inti), :args, Access.at!(intai)], fn _ -> - value + update_in(socket.assigns.chains, [Access.at!(int_index), :args, Access.at!(intai)], fn {k, + _} -> + {k, value} end) {:noreply, socket |> assign(:chains, chain)} @@ -115,7 +116,10 @@ defmodule OvoPlaygroundWeb.PageLive do def handle_event("run_chain", %{"chain_index" => index}, socket) do aindex = String.to_integer(index) c = socket.assigns.chains |> Enum.at(aindex) - args = c.args |> Enum.map(&Jason.decode!(&1)) + + args = + c.args + |> Enum.map(&Jason.decode!(elem(&1, 1))) {:noreply, assign( @@ -151,7 +155,7 @@ defmodule OvoPlaygroundWeb.PageLive do Ovo.Runner.run( hash, Ovo.Registry.get_runner_args(hash) - |> Enum.map(&Jason.decode!/1) + |> Enum.map(&Jason.decode!(elem(&1, 1))) ) {:noreply, socket |> update_runners |> assign(:result, result)} @@ -178,8 +182,8 @@ defmodule OvoPlaygroundWeb.PageLive do }, socket ) do - inti = String.to_integer(index) - Ovo.Registry.update_runner_arg(hash, inti, value) + int_index = String.to_integer(index) + Ovo.Registry.update_runner_arg(hash, int_index, value) {:noreply, socket} end @@ -188,13 +192,33 @@ defmodule OvoPlaygroundWeb.PageLive do assign(socket, pending_runner: %{ socket.assigns[:pending_runner] - | args: ["" | socket.assigns[:pending_runner].args] + | args: socket.assigns[:pending_runner].args ++ [{:text, ""}] + } + )} + end + + def handle_event("change_name", %{"value" => v}, socket) do + {:noreply, + assign(socket, + pending_runner: %{ + socket.assigns[:pending_runner] + | name: v + } + )} + end + + def handle_event("add_secret_arg", _, socket) do + {:noreply, + assign(socket, + pending_runner: %{ + socket.assigns[:pending_runner] + | args: socket.assigns[:pending_runner].args ++ [{:secret, ""}] } )} end def handle_event("change_arg", %{"index" => i, "value" => v}, socket) do - inti = i |> String.to_integer() + int_index = i |> String.to_integer() {:noreply, assign(socket, @@ -202,13 +226,13 @@ defmodule OvoPlaygroundWeb.PageLive do socket.assigns[:pending_runner] | args: Enum.with_index(socket.assigns[:pending_runner].args) - |> Enum.map(fn {a, ix} -> if ix == inti, do: v, else: a end) + |> Enum.map(fn {{k, a}, ix} -> if ix == int_index, do: {k, v}, else: {k, a} end) } )} end def handle_event("delete_arg", %{"index" => i}, socket) do - inti = i |> String.to_integer() + int_index = i |> String.to_integer() {:noreply, assign(socket, @@ -216,7 +240,7 @@ defmodule OvoPlaygroundWeb.PageLive do socket.assigns[:pending_runner] | args: Enum.with_index(socket.assigns[:pending_runner].args) - |> Enum.filter(fn {a, ix} -> ix != inti end) + |> Enum.filter(fn {_, ix} -> ix != int_index end) |> Enum.map(&elem(&1, 0)) } )} @@ -240,7 +264,7 @@ defmodule OvoPlaygroundWeb.PageLive do end defp string_to_path(str) do - String.split(str, ~r/,/) + String.split(str, ~r/_/) |> Enum.map(fn term -> case term do "value" -> Access.key!(:value) @@ -256,36 +280,30 @@ defmodule OvoPlaygroundWeb.PageLive do def node_at_path(ast, [p | rest]), do: node_at_path(ast |> Enum.at(p), rest) def node_at_path(ast, []), do: ast - def handle_event("change_path:" <> rest, params, socket) do - value = Map.get(params, "value", nil) + def handle_event("change_path", %{"value" => value, "path" => str_path}, socket) do ast = socket.assigns[:pending_runner].ast |> elem(1) - path = string_to_path(rest) - - try do - node = get_in(ast, path) - - new_value = - case node do - %Ovo.Ast{kind: :boolean} -> value - %Ovo.Ast{kind: :integer} -> String.to_integer(value) - %Ovo.Ast{kind: :float} -> Float.parse(value) - %Ovo.Ast{kind: :bool} -> !node.value - _ -> value - end + path = string_to_path(str_path) - ast = update_in(ast, string_to_path(rest), fn a -> %Ovo.Ast{a | value: new_value} end) + node = get_in(ast, path) - {:noreply, - socket - |> assign(:pending_runner, %{ - socket.assigns[:pending_runner] - | ast: {:ok, ast, []}, - code: Ovo.Printer.print(ast) - })} - rescue - _ -> - {:noreply, socket} - end + new_value = + case node do + %Ovo.Ast{kind: :boolean} -> value + %Ovo.Ast{kind: :integer} -> String.to_integer(value) + %Ovo.Ast{kind: :float} -> Float.parse(value) + %Ovo.Ast{kind: :bool} -> value + _ -> value + end + + ast = update_in(ast, path, fn a -> %Ovo.Ast{a | value: new_value} end) + + {:noreply, + socket + |> assign(:pending_runner, %{ + socket.assigns[:pending_runner] + | ast: {:ok, ast, []}, + code: Ovo.Printer.print(ast) + })} end def handle_event("evaluate", _, socket) do @@ -293,7 +311,7 @@ defmodule OvoPlaygroundWeb.PageLive do args = socket.assigns[:pending_runner].args - |> Enum.map(&Jason.decode!/1) + |> Enum.map(&Jason.decode!(elem(&1, 1))) case parsed do {:ok, ast, _} -> @@ -309,7 +327,16 @@ defmodule OvoPlaygroundWeb.PageLive do end end - def handle_event(_, e, socket) do + def handle_event("sweep_rug", _, socket) do + case socket.assigns.user.name do + "alice" -> + OvoPlayground.register_alice_tricks() + {:noreply, socket |> update_runners} + + _ -> + {:noreply, socket} + end + {:noreply, socket} end diff --git a/ovo_playground/lib/ovo_playground_web/live/page_live.html.heex b/ovo_playground/lib/ovo_playground_web/live/page_live.html.heex index 5ed27a9..a6fe935 100644 --- a/ovo_playground/lib/ovo_playground_web/live/page_live.html.heex +++ b/ovo_playground/lib/ovo_playground_web/live/page_live.html.heex @@ -1,5 +1,14 @@
- Welcome, <%= @user.name %> ! + Welcome, + <%= case @user.name do %> + <% "alice" -> %> + + <% _ -> %> + <%= @user.name %> ! + <% end %> + <%= case @state do %> <% :idle -> %>
@@ -9,7 +18,9 @@

<%= runner.metadata.name %> - <%= hash %> + + <%= hash %> + - <%= if @pending_chain != [] do %> + <%= if @pending_chain !=[] do %> + <%= case arg do %> + <% {:text, a} -> %> + + + <% {:secret, a} -> %> + + + <% end %>

<% end %> @@ -142,7 +210,7 @@ <% _ -> %> parse error. <% end %> - + <.blue_button click="push_node" label="+ assignment">
<%= case @result do %> @@ -159,66 +227,66 @@
diff --git a/ovo_playground/run_alice.sh b/ovo_playground/run_alice.sh new file mode 100755 index 0000000..1554f29 --- /dev/null +++ b/ovo_playground/run_alice.sh @@ -0,0 +1,2 @@ +#!/bin/bash +PORT=4141 NAME=alice HUE=190deg iex --name alice -S mix phx.server \ No newline at end of file diff --git a/ovo_playground/run_bob.sh b/ovo_playground/run_bob.sh new file mode 100755 index 0000000..3fff4e8 --- /dev/null +++ b/ovo_playground/run_bob.sh @@ -0,0 +1,2 @@ +#!/bin/bash +PORT=4142 NAME=bob HUE=45deg iex --name bob -S mix phx.server \ No newline at end of file