defmodule Mix.Tasks.Clje.Run do @moduledoc """ Compile and run a CljElixir file. ## Usage mix clje.run examples/chat_room.clje mix clje.run -e '(println "hello")' script.clje Like `elixir script.exs` or `bb script.clj`. The file is compiled and evaluated. Modules defined in the file become available. ## Options * `-e` / `--eval` - evaluate expression before running the file * `--no-halt` - keep the system running after execution (useful for spawned processes) Arguments after `--` are available via `System.argv()`. """ use Mix.Task @shortdoc "Run a CljElixir file" @impl Mix.Task def run(args) do # Split on "--" to separate mix opts from script args {before_dashdash, script_args} = split_on_dashdash(args) {opts, positional, _} = OptionParser.parse(before_dashdash, switches: [eval: :keep, no_halt: :boolean], aliases: [e: :eval] ) Mix.Task.run("compile") Mix.Task.run("app.start") # Make script args available via System.argv() System.argv(script_args) # Evaluate any -e expressions first bindings = opts |> Keyword.get_values(:eval) |> Enum.reduce([], fn expr, bindings -> case CljElixir.Compiler.eval_string(expr, bindings: bindings) do {:ok, result, new_bindings} -> IO.puts(CljElixir.Printer.pr_str(result)) new_bindings {:error, diagnostics} -> print_diagnostics(diagnostics) System.halt(1) end end) # Run file(s) case positional do [] -> unless Keyword.has_key?(opts, :eval) do Mix.shell().error("Usage: mix clje.run [options] ") System.halt(1) end files -> Enum.reduce(files, bindings, fn file, bindings -> case CljElixir.Compiler.eval_file(file, bindings: bindings) do {:ok, _result, new_bindings} -> new_bindings {:error, diagnostics} -> print_diagnostics(diagnostics) System.halt(1) end end) end if opts[:no_halt] do Process.sleep(:infinity) end end defp split_on_dashdash(args) do case Enum.split_while(args, &(&1 != "--")) do {before, ["--" | rest]} -> {before, rest} {before, []} -> {before, []} end end defp print_diagnostics(diagnostics) do Enum.each(diagnostics, fn diag -> loc = case {Map.get(diag, :file), Map.get(diag, :line, 0)} do {nil, _} -> "" {_f, 0} -> "#{diag.file}: " {_f, l} -> "#{diag.file}:#{l}: " end Mix.shell().error("#{loc}#{diag.severity}: #{diag.message}") end) end end