Skip to content

Commit 7ea62a9

Browse files
committed
wip
1 parent 3fa3663 commit 7ea62a9

File tree

6 files changed

+97
-46
lines changed

6 files changed

+97
-46
lines changed
Lines changed: 18 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
defmodule ElixirTour do
22
use GenServer
3+
34
import Popcorn.Wasm, only: [is_wasm_message: 1]
5+
6+
alias ElixirTour.Evaluator
47
alias Popcorn.Wasm
58

69
@process_name :main
710

811
@type editor_id :: String.t()
9-
@type bindings :: Keyword.t()
1012
@type state :: %{
1113
editor_order: [editor_id()],
12-
bindings: %{editor_id() => bindings()}
14+
bindings: %{editor_id() => Evaluator.bindings()}
1315
}
1416
@type wasm_result :: {:resolve, String.t(), state()} | {:reject, String.t(), state()}
1517

@@ -21,7 +23,7 @@ defmodule ElixirTour do
2123
def init(_init_arg) do
2224
Wasm.register(@process_name)
2325
:application.set_env(:elixir, :ansi_enabled, false)
24-
{:ok, %{editor_order: [], bindings: %{}}}
26+
{:ok, %{editor_order: [], bindings: %{}, evaluator: Evaluator.start!()}}
2527
end
2628

2729
@impl GenServer
@@ -38,37 +40,30 @@ defmodule ElixirTour do
3840
end
3941

4042
@spec handle_wasm({:wasm_call, list()}, state()) :: wasm_result()
41-
defp handle_wasm({:wasm_call, ["set_editor_order", editor_order]}, _state) do
42-
{:resolve, "ok", %{editor_order: editor_order, bindings: %{}}}
43+
defp handle_wasm({:wasm_call, ["set_editor_order", editor_order]}, state) do
44+
{:resolve, "ok", %{state | editor_order: editor_order, bindings: %{}}}
4345
end
4446

4547
defp handle_wasm({:wasm_call, ["eval_elixir", editor_id, code]}, state) do
46-
editor_order = Map.get(state, :editor_order, [])
47-
bindings_map = Map.get(state, :bindings, %{})
48+
%{evaluator: evaluator, bindings: bindings_map} = state
4849

49-
preceding_editor_ids = get_preceding_editors(editor_order, editor_id)
50+
preceding_editor_ids = get_preceding_editors(state.editor_order, editor_id)
5051

5152
preceding_bindings =
5253
preceding_editor_ids
5354
|> Enum.map(&Map.get(bindings_map, &1, []))
5455
|> Enum.reduce([], &Keyword.merge(&2, &1))
5556

56-
try do
57-
case eval(code, preceding_bindings) do
58-
{:ok, result, new_bindings} ->
59-
editor_bindings = get_changed(preceding_bindings, new_bindings)
60-
updated_bindings = Map.put(bindings_map, editor_id, editor_bindings)
61-
62-
{:resolve, inspect(result), %{state | bindings: updated_bindings}}
57+
case Evaluator.eval(state.evaluator, code, preceding_bindings) do
58+
{:ok, result, new_bindings} ->
59+
editor_bindings = get_changed(preceding_bindings, new_bindings)
60+
updated_bindings = Map.put(bindings_map, editor_id, editor_bindings)
6361

64-
{:error, error_message} ->
65-
{:reject, error_message, state}
66-
end
67-
rescue
68-
e ->
69-
error_message = Exception.format(:error, e)
62+
{:resolve, inspect(result), %{state | bindings: updated_bindings}}
7063

71-
{:reject, error_message, state}
64+
{:error, error_message} ->
65+
evaluator = if Process.alive?(state.evaluator), do: evaluator, else: Evaluator.start!()
66+
{:reject, error_message, %{state | evaluator: evaluator}}
7267
end
7368
end
7469

@@ -77,31 +72,12 @@ defmodule ElixirTour do
7772
Enum.take_while(editor_order, &(&1 != editor_id))
7873
end
7974

80-
@spec get_changed(bindings(), bindings()) :: bindings()
75+
@spec get_changed(Evaluator.bindings(), Evaluator.bindings()) :: Evaluator.bindings()
8176
defp get_changed(base_kw, new_kw) do
8277
unchanged? = fn {key, value} ->
8378
Keyword.get(base_kw, key) == value
8479
end
8580

8681
Enum.reject(new_kw, unchanged?)
8782
end
88-
89-
@spec eval(String.t(), bindings()) :: {:ok, any(), bindings()} | {:error, String.t()}
90-
defp eval(code, bindings) do
91-
try do
92-
{evaluated, new_bindings} =
93-
Code.eval_string(code, bindings, %Macro.Env{
94-
__ENV__
95-
| file: "playground",
96-
line: 1,
97-
module: nil
98-
})
99-
100-
{:ok, evaluated, new_bindings}
101-
rescue
102-
e ->
103-
error_message = Exception.format(:error, e)
104-
{:error, error_message}
105-
end
106-
end
10783
end
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
defmodule ElixirTour.Evaluator do
2+
use GenServer
3+
4+
@type bindings :: Keyword.t()
5+
6+
@spec start!() :: pid()
7+
def start!() do
8+
{:ok, pid} = GenServer.start(__MODULE__, [])
9+
pid
10+
end
11+
12+
@spec eval(pid(), String.t(), bindings()) ::
13+
{:ok, result :: any(), bindings()} | {:error, message :: String.t()}
14+
def eval(runner, code, bindings) do
15+
try do
16+
GenServer.call(runner, {:eval, code, bindings}, :infinity)
17+
catch
18+
:exit, _reason -> {:error, "error"}
19+
end
20+
end
21+
22+
@impl true
23+
def init(_opts) do
24+
{:ok, %{}}
25+
end
26+
27+
@impl true
28+
def handle_call({:eval, code, bindings}, _from, state) do
29+
try do
30+
{result, new_bindings} =
31+
Code.eval_string(code, bindings, %Macro.Env{
32+
__ENV__
33+
| file: "playground",
34+
line: 1,
35+
module: nil
36+
})
37+
38+
{:reply, {:ok, result, new_bindings}, state}
39+
rescue
40+
error ->
41+
error_message = Exception.format(:error, error)
42+
{:reply, {:error, error_message}, state}
43+
end
44+
end
45+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Links
2+
3+
```elixir
4+
{:ok, a} = Agent.start_link(fn -> %{} end)
5+
6+
Agent.get(a, fn _s -> raise "dupa" end)
7+
```

misc/language-tour-guide/src/content/8-processes/message_passing.livemd

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ send(self(), {:hello, "world"})
4747

4848
receive do
4949
x when is_integer(x) ->
50-
IO.inspect x, label: "This won't match"
50+
"Received an integer: #{x}"
5151

5252
{:hello, addressee} ->
53-
IO.inspect addressee, label: "Received hello to"
53+
"Received hello to: #{addressee}"
5454
end
5555
```
5656

@@ -62,9 +62,9 @@ send(self(), :bye)
6262

6363
receive do
6464
{:hello, addressee} ->
65-
IO.inspect addressee, label: "This won't be received"
65+
"Received hello to: #{addressee}"
6666
after
67-
1_000 -> "Nothing after 1s"
67+
100 -> "Nothing after 100 milliseconds"
6868
end
6969
```
7070

misc/language-tour-guide/src/utils/content/configuration.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ export const navigationConfig: NavigationConfig = [
163163
{
164164
name: "Message passing",
165165
component: lazyLoadLiveMd("8-processes/message_passing")
166+
},
167+
{
168+
name: "Links",
169+
component: lazyLoadLiveMd("8-processes/links")
166170
}
167171
]
168172
},

patches/otp/stdlib/gen_server.erl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-module(gen_server).
2+
3+
-compile({popcorn_patch_private, client_stacktrace/1}).
4+
5+
client_stacktrace(undefined) ->
6+
undefined;
7+
client_stacktrace({From, _Tag}) ->
8+
client_stacktrace(From);
9+
client_stacktrace(From) when is_pid(From), node(From) =:= node() ->
10+
case process_info(From, registered_name) of
11+
undefined ->
12+
{From, dead};
13+
[] ->
14+
{From, {From, []}};
15+
{registered_name, Name} ->
16+
{From, {Name, []}}
17+
end;
18+
client_stacktrace(From) when is_pid(From) ->
19+
{From, remote}.

0 commit comments

Comments
 (0)