From 3af8aa158c05dd40c6b469d7221667b842d64a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20N=2EC=2E=20van=20=C2=B4t=20Hooft?= Date: Wed, 19 Jul 2023 14:00:47 -0300 Subject: [PATCH] Add erlang-module support The code is deemed a module definition if the first line start with "-module(". In this case the entire code-block is interpreted as erlang-module and if there are no errors the module is compiled and loaded. --- lib/livebook/runtime/evaluator.ex | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/lib/livebook/runtime/evaluator.ex b/lib/livebook/runtime/evaluator.ex index 7043eb5b7021..5781857a5047 100644 --- a/lib/livebook/runtime/evaluator.ex +++ b/lib/livebook/runtime/evaluator.ex @@ -675,6 +675,47 @@ defmodule Livebook.Runtime.Evaluator do end defp eval(:erlang, code, binding, env) do + is_module = String.starts_with?(code, "-module(") + + case is_module do + true -> eval_module(:erlang, code, binding, env) + false -> eval_statements(:erlang, code, binding, env) + end + end + + # Create module - tokens from string + # Based on: https://stackoverflow.com/questions/2160660/how-to-compile-erlang-code-loaded-into-a-string + # The function will first assume that code starting with -module( is a erlang module definition + + # Step 1: Split the module in \n + # Step 2: Scan and convert to froms + # Step 3: Extract module name + # Step 4: Compile and load + defp eval_module(:erlang, code, binding, env) do + statements = :binary.split(code, "\n", [:global, :trim_all]) + + forms = + Enum.map( + statements, + fn str -> + {:ok, result, _} = :erl_scan.string(String.to_charlist(str)) + {:ok, form} = :erl_parse.parse_form(result) + form + end + ) + + # First statement - form = module definition + {:attribute, 1, :module, module_name} = hd(forms) + + # Compile froms to binary + {:ok, _, binary_module} = :compile.forms(forms) + + :code.load_binary(module_name, ~c"nofile", binary_module) + + {{:ok, ~c"erlang module successfully compiled", binding, env}, []} + end + + defp eval_statements(:erlang, code, binding, env) do try do erl_binding = Enum.reduce(binding, %{}, fn {name, value}, erl_binding ->