Files
2026-03-09 23:09:46 -04:00

104 lines
2.7 KiB
Elixir

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] <file.clje>")
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