init commit

This commit is contained in:
2026-03-09 23:09:46 -04:00
parent 5cbc493cc5
commit 5da77e3360
73 changed files with 9935 additions and 103 deletions
+2
View File
@@ -8,4 +8,6 @@ erl_crash.dump
*.beam
/tmp/
.elixir_ls/
.clj-kondo/
.lsp/
.nrepl-port
+13 -10
View File
@@ -1,12 +1,9 @@
{:tasks
{compile {:doc "Compile all .clje and .ex files"
:task (shell "mix compile")}
:task (apply shell "mix compile" *command-line-args*)}
test {:doc "Run all tests"
:task (shell "mix test")}
test:trace {:doc "Run all tests with trace output"
:task (shell "mix test --trace")}
test {:doc "Run tests (accepts mix test args, e.g. bb test --only phase5)"
:task (apply shell "mix test" *command-line-args*)}
repl {:doc "Start interactive CljElixir REPL"
:task (shell "rlwrap mix clje.repl")}
@@ -14,11 +11,17 @@
repl:basic {:doc "Start REPL without rlwrap"
:task (shell "mix clje.repl")}
nrepl {:doc "Start nREPL server (random port)"
:task (shell "mix clje.nrepl")}
nrepl {:doc "Start nREPL server (bb nrepl --port 7888)"
:task (apply shell "mix clje.nrepl" *command-line-args*)}
nrepl:port {:doc "Start nREPL on port 7888"
:task (shell "mix clje.nrepl --port 7888")}
eval {:doc "Evaluate expression (bb eval '(+ 1 2)')"
:task (apply shell "mix clje.eval" *command-line-args*)}
run {:doc "Run a .clje file (bb run examples/chat_room.clje)"
:task (apply shell "mix clje.run" *command-line-args*)}
build {:doc "Compile .clje to BEAM (bb build src/foo.clje [-o dir])"
:task (apply shell "mix clje.build" *command-line-args*)}
clean {:doc "Clean build artifacts"
:task (shell "mix clean")}
+138
View File
@@ -0,0 +1,138 @@
# PersistentVector vs Erlang Tuple vs List benchmarks
# Run: mix run bench/persistent_vector_bench.exs
defmodule PVBench do
def run do
sizes = [10, 100, 1_000, 10_000]
IO.puts("=" |> String.duplicate(70))
IO.puts("PersistentVector vs Tuple vs List — Benchmarks")
IO.puts("=" |> String.duplicate(70))
for size <- sizes do
IO.puts("\n--- Size: #{size} ---\n")
list = Enum.to_list(1..size)
tuple = List.to_tuple(list)
pv = CljElixir.PersistentVector.from_list(list)
bench_construction(size, list)
bench_indexed_access(size, list, tuple, pv)
bench_append(size, list, tuple, pv)
bench_update(size, list, tuple, pv)
bench_iteration(size, list, tuple, pv)
end
end
defp bench_construction(size, list) do
{pv_us, _} = :timer.tc(fn ->
CljElixir.PersistentVector.from_list(list)
end)
{tuple_us, _} = :timer.tc(fn ->
List.to_tuple(list)
end)
IO.puts(" Construction:")
IO.puts(" PersistentVector.from_list: #{format_us(pv_us)}")
IO.puts(" List.to_tuple: #{format_us(tuple_us)}")
IO.puts(" List (identity): ~0 µs")
end
defp bench_indexed_access(size, list, tuple, pv) do
indices = for _ <- 1..1000, do: :rand.uniform(size) - 1
{pv_us, _} = :timer.tc(fn ->
Enum.each(indices, fn i -> CljElixir.PersistentVector.pv_nth(pv, i) end)
end)
{tuple_us, _} = :timer.tc(fn ->
Enum.each(indices, fn i -> elem(tuple, i) end)
end)
{list_us, _} = :timer.tc(fn ->
Enum.each(indices, fn i -> Enum.at(list, i) end)
end)
IO.puts(" Indexed access (1000 random):")
IO.puts(" PersistentVector.pv_nth: #{format_us(pv_us)}")
IO.puts(" elem(tuple, i): #{format_us(tuple_us)}")
IO.puts(" Enum.at(list, i): #{format_us(list_us)}")
end
defp bench_append(size, _list, tuple, pv) do
iters = min(1000, size)
{pv_us, _} = :timer.tc(fn ->
Enum.reduce(1..iters, pv, fn x, acc ->
CljElixir.PersistentVector.pv_conj(acc, x)
end)
end)
{tuple_us, _} = :timer.tc(fn ->
Enum.reduce(1..iters, tuple, fn x, acc ->
:erlang.append_element(acc, x)
end)
end)
{list_us, _} = :timer.tc(fn ->
# Prepend (O(1)) — append would be O(n) per op
Enum.reduce(1..iters, [], fn x, acc -> [x | acc] end)
end)
IO.puts(" Append #{iters} elements:")
IO.puts(" PersistentVector.pv_conj: #{format_us(pv_us)}")
IO.puts(" :erlang.append_element: #{format_us(tuple_us)}")
IO.puts(" [x | list] (prepend): #{format_us(list_us)}")
end
defp bench_update(size, _list, tuple, pv) do
indices = for _ <- 1..1000, do: :rand.uniform(size) - 1
{pv_us, _} = :timer.tc(fn ->
Enum.each(indices, fn i ->
CljElixir.PersistentVector.pv_assoc(pv, i, :updated)
end)
end)
{tuple_us, _} = :timer.tc(fn ->
Enum.each(indices, fn i ->
put_elem(tuple, i, :updated)
end)
end)
IO.puts(" Update (1000 random):")
IO.puts(" PersistentVector.pv_assoc: #{format_us(pv_us)}")
IO.puts(" put_elem(tuple, i, v): #{format_us(tuple_us)}")
IO.puts(" List: N/A (O(n) per update)")
end
defp bench_iteration(size, list, tuple, pv) do
{pv_us, _} = :timer.tc(fn ->
cnt = CljElixir.PersistentVector.pv_count(pv)
Enum.reduce(0..(cnt - 1), 0, fn i, acc ->
acc + CljElixir.PersistentVector.pv_nth(pv, i)
end)
end)
{tuple_us, _} = :timer.tc(fn ->
Enum.reduce(0..(size - 1), 0, fn i, acc ->
acc + elem(tuple, i)
end)
end)
{list_us, _} = :timer.tc(fn ->
Enum.reduce(list, 0, fn x, acc -> acc + x end)
end)
IO.puts(" Iteration (sum all):")
IO.puts(" PersistentVector by index: #{format_us(pv_us)}")
IO.puts(" Tuple by index: #{format_us(tuple_us)}")
IO.puts(" List reduce: #{format_us(list_us)}")
end
defp format_us(us) when us < 1_000, do: "#{us} µs"
defp format_us(us) when us < 1_000_000, do: "#{Float.round(us / 1_000, 1)} ms"
defp format_us(us), do: "#{Float.round(us / 1_000_000, 2)} s"
end
PVBench.run()
+142
View File
@@ -0,0 +1,142 @@
;;; ChatRoom — process-based chat room using spawn/send/receive
;;;
;;; This is a single-VM demo showing BEAM concurrency primitives.
;;; Run with: mix clje.run examples/chat_room.clje
;;;
;;; For a multi-terminal chat experience, see:
;;; examples/tcp_chat_server.clje and examples/tcp_chat_client.clje
(ns ChatRoom
(:require [clje.core :refer :all]
[Enum] [IO] [Map] [Process] [String]))
;; ── Room loop ─────────────────────────────────────────────────────
;; Manages members map of {username → pid} and broadcasts messages.
(defn run-loop [state]
(receive
[:join username pid]
(let [members (assoc (:members state) username pid)]
(send pid #el[:welcome username (count members)])
;; Notify existing members
(doseq [[_name member-pid] (:members state)]
(send member-pid #el[:system (str username " joined the room")]))
(recur (assoc state :members members)))
[:message from body] :guard [(is-binary body)]
(do
(doseq [[_name pid] (:members state)]
(send pid #el[:chat from body]))
(recur state))
[:leave username]
(let [new-members (dissoc (:members state) username)]
(doseq [[_name pid] new-members]
(send pid #el[:system (str username " left the room")]))
(recur (assoc state :members new-members)))
[:who reply-pid]
(do
(send reply-pid #el[:members (Map/keys (:members state))])
(recur state))
:shutdown
(do
(doseq [[_name pid] (:members state)]
(send pid :room-closed))
:ok)
:after 5000
(if (== (count (:members state)) 0)
:empty-timeout
(recur state))))
;; ── User listener ─────────────────────────────────────────────────
;; Collects messages received by a user and prints them.
(defn listen [username messages]
(receive
[:welcome _name member-count]
(do
(println (str " [" username " sees: Welcome! " member-count " user(s) here]"))
(ChatRoom/listen username messages))
[:chat from body]
(do
(println (str " [" username " sees: " from "> " body "]"))
(ChatRoom/listen username (cons #el[from body] messages)))
[:system text]
(do
(println (str " [" username " sees: * " text "]"))
(ChatRoom/listen username messages))
:room-closed
(println (str " [" username " sees: room closed]"))
:dump
messages
:after 2000
(do
(println (str " [" username " done listening]"))
messages)))
;; ── Run the demo ────────────────────────────────────────────────────
(println "=== ChatRoom Demo ===\n")
;; Start the room
(let [room (spawn (fn [] (ChatRoom/run-loop {:owner "system" :members {}})))
alice-listener (spawn (fn [] (ChatRoom/listen "alice" (list))))
bob-listener (spawn (fn [] (ChatRoom/listen "bob" (list))))
carol-listener (spawn (fn [] (ChatRoom/listen "carol" (list))))]
;; Alice joins
(println "Alice joins...")
(send room #el[:join "alice" alice-listener])
(Process/sleep 100)
;; Bob joins
(println "\nBob joins...")
(send room #el[:join "bob" bob-listener])
(Process/sleep 100)
;; Alice sends a message
(println "\nAlice sends a message...")
(send room #el[:message "alice" "Hello everyone!"])
(Process/sleep 100)
;; Carol joins
(println "\nCarol joins...")
(send room #el[:join "carol" carol-listener])
(Process/sleep 100)
;; Bob sends a message
(println "\nBob sends a message...")
(send room #el[:message "bob" "Hey Alice! Welcome Carol!"])
(Process/sleep 100)
;; Carol sends a message
(println "\nCarol sends a message...")
(send room #el[:message "carol" "Thanks Bob!"])
(Process/sleep 100)
;; Check who's online
(println "\nWho's online?")
(send room #el[:who *self*])
(receive
[:members names]
(println (str " Online: " (Enum/join names ", "))))
;; Bob leaves
(println "\nBob leaves...")
(send room #el[:leave "bob"])
(Process/sleep 100)
;; Shutdown the room
(println "\nShutting down room...")
(send room :shutdown)
(Process/sleep 200)
(println "\n=== Demo complete ==="))
+130
View File
@@ -0,0 +1,130 @@
;;; TCP Chat Client — connects to the TCP chat server
;;;
;;; Usage:
;;; mix clje.run --no-halt examples/tcp_chat_client.clje -- <username>
;;;
;;; The server must be running first:
;;; mix clje.run --no-halt examples/tcp_chat_server.clje
;;;
;;; Commands:
;;; Type a message and press Enter to send
;;; /who — list online users
;;; /quit — disconnect and exit
(ns TcpChatClient
(:require [clje.core :refer :all]
[IO] [String] [System] [erlang] [gen_tcp]))
;; ── Receiver process ──────────────────────────────────────────────
;; Listens for TCP messages from the server and prints them.
(defn receiver [socket parent]
(receive
[:tcp _sock data]
(let [line (String/trim data)]
(cond
(String/starts-with? line "MSG:")
(let [rest (String/slice line 4 (String/length line))
parts (String/split rest ":" (list #el[:parts 2]))
from (hd parts)
text (hd (tl parts))]
(IO/puts (str from "> " text))
(TcpChatClient/receiver socket parent))
(String/starts-with? line "SYS:")
(do
(IO/puts (str "* " (String/slice line 4 (String/length line))))
(TcpChatClient/receiver socket parent))
(String/starts-with? line "JOIN:")
(do
(IO/puts (str "* " (String/slice line 5 (String/length line)) " joined"))
(TcpChatClient/receiver socket parent))
(String/starts-with? line "QUIT:")
(do
(IO/puts (str "* " (String/slice line 5 (String/length line)) " left"))
(TcpChatClient/receiver socket parent))
:else
(do
(IO/puts line)
(TcpChatClient/receiver socket parent))))
[:tcp_closed _sock]
(do
(IO/puts "* Connection closed by server.")
(System/halt 0))
[:tcp_error _sock _reason]
(do
(IO/puts "* Connection error.")
(System/halt 1))))
;; ── Input loop ────────────────────────────────────────────────────
;; Reads from stdin and sends to the server.
(defn send-line [socket line]
(gen_tcp/send socket line))
(defn input-loop [socket]
(let [line (IO/gets "")]
(cond
(== line :eof)
(do
(TcpChatClient/send-line socket "QUIT\n")
(gen_tcp/close socket)
(System/halt 0))
:else
(let [trimmed (String/trim line)]
(cond
(== trimmed "/quit")
(do
(TcpChatClient/send-line socket "QUIT\n")
(gen_tcp/close socket)
(IO/puts "Goodbye!")
(System/halt 0))
(== trimmed "")
(TcpChatClient/input-loop socket)
:else
(do
(TcpChatClient/send-line socket (str "MSG:" trimmed "\n"))
(TcpChatClient/input-loop socket)))))))
;; ── Connect ───────────────────────────────────────────────────────
(defn start [username]
(case (gen_tcp/connect (erlang/binary-to-list "127.0.0.1") 4040
(list :binary #el[:active true] #el[:packet :line]))
[:ok socket]
(do
;; Send JOIN
(TcpChatClient/send-line socket (str "JOIN:" username "\n"))
;; Spawn receiver and hand it the socket
(let [me *self*
recv-pid (spawn (fn [] (TcpChatClient/receiver socket me)))]
(gen_tcp/controlling-process socket recv-pid)
;; Run input loop in the main process
(IO/puts (str "Connected as " username ". Type a message or /quit to exit."))
(TcpChatClient/input-loop socket)))
[:error reason]
(do
(IO/puts (str "Could not connect: " reason))
(IO/puts "Is the server running? Start it with:")
(IO/puts " mix clje.run --no-halt examples/tcp_chat_server.clje")
(System/halt 1))))
;; ── Entry point ─────────────────────────────────────────────────────
(let [args (System/argv)]
(if (== (count args) 0)
(do
(IO/puts "Usage: mix clje.run --no-halt examples/tcp_chat_client.clje -- <username>")
(System/halt 1))
(TcpChatClient/start (hd args))))
+131
View File
@@ -0,0 +1,131 @@
;;; TCP Chat Server — multi-terminal chat using gen_tcp
;;;
;;; Start the server:
;;; mix clje.run --no-halt examples/tcp_chat_server.clje
;;;
;;; Then connect clients in separate terminals:
;;; mix clje.run --no-halt examples/tcp_chat_client.clje -- alice
;;; mix clje.run --no-halt examples/tcp_chat_client.clje -- bob
;;;
;;; Protocol (line-based):
;;; Client → Server: JOIN:<username> | MSG:<text> | QUIT
;;; Server → Client: SYS:<text> | MSG:<user>:<text> | JOIN:<user> | QUIT:<user>
(ns TcpChatServer
(:require [clje.core :refer :all]
[Map] [String] [gen_tcp] [inet]))
;; ── Room manager ──────────────────────────────────────────────────
;; Holds {username → socket} map, broadcasts messages to all members.
(defn room-loop [members]
(receive
[:join username socket handler-pid]
(do
(TcpChatServer/broadcast members (str "JOIN:" username "\n"))
(TcpChatServer/send-line socket (str "SYS:Welcome " username "! " (count members) " user(s) online.\n"))
(TcpChatServer/room-loop (assoc members username #el[socket handler-pid])))
[:msg username text]
(do
(TcpChatServer/broadcast members (str "MSG:" username ":" text "\n"))
(TcpChatServer/room-loop members))
[:quit username]
(let [new-members (dissoc members username)]
(TcpChatServer/broadcast new-members (str "QUIT:" username "\n"))
(TcpChatServer/room-loop new-members))
[:list reply-pid]
(do
(send reply-pid #el[:members (Map/keys members)])
(TcpChatServer/room-loop members))))
(defn broadcast [members line]
(doseq [[_name [socket _pid]] members]
(gen_tcp/send socket line)))
(defn send-line [socket line]
(gen_tcp/send socket line))
;; ── Per-client handler ────────────────────────────────────────────
;; Receives TCP data via active mode and forwards to room manager.
(defn client-handler [socket room username]
(receive
[:tcp _sock data]
(let [line (String/trim data)]
(cond
(String/starts-with? line "MSG:")
(do
(send room #el[:msg username (String/slice line 4 (String/length line))])
(TcpChatServer/client-handler socket room username))
(== line "QUIT")
(do
(send room #el[:quit username])
(gen_tcp/close socket)
(println (str " [" username " quit]")))
:else
(do
(TcpChatServer/send-line socket (str "SYS:Unknown command. Send MSG:<text> or QUIT\n"))
(TcpChatServer/client-handler socket room username))))
[:tcp_closed _sock]
(do
(send room #el[:quit username])
(println (str " [" username " disconnected]")))
[:tcp_error _sock _reason]
(do
(send room #el[:quit username])
(println (str " [" username " error]")))))
;; ── Accept loop ───────────────────────────────────────────────────
(defn accept-loop [listen-socket room]
(case (gen_tcp/accept listen-socket)
[:ok client-socket]
(do
(TcpChatServer/handle-new-client client-socket room)
(TcpChatServer/accept-loop listen-socket room))
[:error reason]
(println (str "Accept error: " reason))))
(defn handle-new-client [socket room]
;; First message must be JOIN:<username>
;; Socket starts in passive mode so we can recv synchronously
(case (gen_tcp/recv socket 0 5000)
[:ok data]
(let [line (String/trim data)]
(if (String/starts-with? line "JOIN:")
(let [username (String/slice line 5 (String/length line))
;; Switch to active mode for the handler
_ (inet/setopts socket (list #el[:active true]))
handler (spawn (fn []
(TcpChatServer/client-handler socket room username)))]
(gen_tcp/controlling-process socket handler)
(send room #el[:join username socket handler])
(println (str " [" username " connected]")))
(do
(TcpChatServer/send-line socket "SYS:First message must be JOIN:<username>\n")
(gen_tcp/close socket))))
[:error _reason]
(gen_tcp/close socket)))
;; ── Start ─────────────────────────────────────────────────────────
(defn start [port]
(case (gen_tcp/listen port (list :binary #el[:active false] #el[:packet :line] #el[:reuseaddr true]))
[:ok listen-socket]
(do
(println (str "TCP Chat Server listening on port " port))
(println "Connect with: mix clje.run --no-halt examples/tcp_chat_client.clje -- <username>")
(let [room (spawn (fn [] (TcpChatServer/room-loop {})))]
(TcpChatServer/accept-loop listen-socket room)))
[:error reason]
(println (str "Failed to listen on port " port ": " reason))))
;; Start the server on port 4040
(TcpChatServer/start 4040)
+25
View File
@@ -100,6 +100,10 @@ defmodule CljElixir.Analyzer do
validate_loop(args, meta, ctx)
end
defp validate_form({:list, _meta, [{:symbol, _, "receive"} | args]}, ctx) do
validate_receive(args, ctx)
end
defp validate_form({:list, meta, [{:symbol, _, "recur"} | _args]}, ctx) do
validate_recur(meta, ctx)
end
@@ -529,6 +533,27 @@ defmodule CljElixir.Analyzer do
end
end
# receive propagates tail position into clause bodies
defp validate_receive(clauses, ctx) do
validate_receive_clauses(clauses, ctx)
end
defp validate_receive_clauses([], _ctx), do: []
defp validate_receive_clauses([:after, _timeout, body | rest], ctx) do
validate_form(body, ctx) ++ validate_receive_clauses(rest, ctx)
end
defp validate_receive_clauses([_pattern, :guard, _guard, body | rest], ctx) do
validate_form(body, ctx) ++ validate_receive_clauses(rest, ctx)
end
defp validate_receive_clauses([_pattern, body | rest], ctx) do
validate_form(body, ctx) ++ validate_receive_clauses(rest, ctx)
end
defp validate_receive_clauses([_], _ctx), do: []
defp validate_recur(meta, ctx) do
line = meta_line(meta)
col = meta_col(meta)
+39 -19
View File
@@ -83,26 +83,46 @@ defmodule CljElixir.Compiler do
@spec eval_string(String.t(), keyword()) :: {:ok, term(), keyword()} | {:error, list()}
def eval_string(source, opts \\ []) do
with {:ok, ast} <- compile_string(source, opts) do
try do
bindings = opts[:bindings] || []
env_opts = build_eval_opts(opts)
{result, new_bindings} = Code.eval_quoted(ast, bindings, env_opts)
{:ok, result, new_bindings}
rescue
e ->
file = opts[:file] || "nofile"
eval_ast(ast, opts)
end
end
{:error,
[
%{
severity: :error,
message: format_eval_error(e),
file: file,
line: extract_line(e),
col: 0
}
]}
end
@doc """
Evaluate pre-parsed CljElixir AST forms.
Runs analyze → transform → eval, skipping the read step.
Used by the REPL for incremental re-evaluation of accumulated definitions.
Returns `{:ok, result, bindings}` on success, or `{:error, diagnostics}` on failure.
"""
@spec eval_forms(list(), keyword()) :: {:ok, term(), keyword()} | {:error, list()}
def eval_forms(forms, opts \\ []) do
with {:ok, forms} <- analyze(forms, opts),
{:ok, ast} <- transform(forms, opts) do
eval_ast(ast, opts)
end
end
defp eval_ast(ast, opts) do
try do
bindings = opts[:bindings] || []
env_opts = build_eval_opts(opts)
{result, new_bindings} = Code.eval_quoted(ast, bindings, env_opts)
{:ok, result, new_bindings}
rescue
e ->
file = opts[:file] || "nofile"
{:error,
[
%{
severity: :error,
message: format_eval_error(e),
file: file,
line: extract_line(e),
col: 0
}
]}
end
end
+2 -2
View File
@@ -35,7 +35,7 @@ defmodule CljElixir.NRepl.Handler do
code = Map.get(msg, "code", "")
# Capture stdout inside the Agent process where eval actually runs
{output, result} = SessionManager.eval_with_capture(manager, session, code)
{output, result, ns} = SessionManager.eval_with_capture(manager, session, code)
responses = []
@@ -51,7 +51,7 @@ defmodule CljElixir.NRepl.Handler do
responses =
case result do
{:ok, value} ->
responses ++ [%{"id" => id, "session" => session, "value" => value, "ns" => "user"}]
responses ++ [%{"id" => id, "session" => session, "value" => value, "ns" => ns}]
{:error, error} ->
responses ++
+5 -4
View File
@@ -87,21 +87,22 @@ defmodule CljElixir.NRepl.SessionManager do
def handle_call({:eval_with_capture, id, code}, _from, state) do
case Map.get(state.sessions, id) do
nil ->
{:reply, {"", {:error, "unknown session"}}, state}
{:reply, {"", {:error, "unknown session"}, "user"}, state}
pid ->
{output, result} =
{output, result, ns} =
Agent.get_and_update(
pid,
fn repl_state ->
{output, eval_result, new_state} = eval_capturing_output(code, repl_state)
ns = CljElixir.REPL.current_ns(new_state)
{{output, eval_result}, new_state}
{{output, eval_result, ns}, new_state}
end,
:infinity
)
{:reply, {output, result}, state}
{:reply, {output, result, ns}, state}
end
end
+4
View File
@@ -36,6 +36,10 @@ defmodule CljElixir.Printer do
do_print_str(value)
end
@doc "Clojure-compatible str: nil→\"\", strings pass through, else print representation"
def str_value(nil), do: ""
def str_value(value), do: print_str(value)
# Check if IPrintWithWriter is compiled and implemented for this value
defp protocol_implemented?(value) do
case Code.ensure_loaded(CljElixir.IPrintWithWriter) do
+168 -22
View File
@@ -4,44 +4,52 @@ defmodule CljElixir.REPL do
Maintains state across evaluations: bindings persist,
modules defined in one evaluation are available in the next.
Tracks the current namespace (`ns`) so that bare `defn`/`def` forms
are merged into the active module and the module is recompiled
incrementally.
"""
defstruct bindings: [],
history: [],
counter: 1,
env: nil
env: nil,
current_ns: nil,
module_defs: %{}
@doc "Create a new REPL state"
def new do
Code.compiler_options(ignore_module_conflict: true)
%__MODULE__{}
end
@doc "Return the current namespace name (defaults to \"user\")"
def current_ns(%__MODULE__{current_ns: ns}), do: ns || "user"
@doc """
Evaluate a CljElixir source string in the given REPL state.
Returns {:ok, result_string, new_state} or {:error, error_string, new_state}.
"""
def eval(source, state) do
opts = [
bindings: state.bindings,
file: "repl"
]
case CljElixir.Reader.read_string(source) do
{:ok, forms} ->
has_ns = Enum.any?(forms, &ns_form?/1)
has_defs = Enum.any?(forms, &def_form?/1)
case CljElixir.Compiler.eval_string(source, opts) do
{:ok, result, new_bindings} ->
result_str = CljElixir.Printer.pr_str(result)
cond do
has_ns ->
eval_with_ns(forms, source, state)
new_state = %{state |
bindings: new_bindings,
history: [source | state.history],
counter: state.counter + 1
}
has_defs and state.current_ns != nil ->
eval_in_ns(forms, source, state)
{:ok, result_str, new_state}
true ->
eval_plain(source, state)
end
{:error, errors} ->
error_str = format_errors(errors)
new_state = %{state | counter: state.counter + 1}
{:error, error_str, new_state}
{:error, reason} ->
error_msg = if is_binary(reason), do: reason, else: inspect(reason)
{:error, "Read error: #{error_msg}", %{state | counter: state.counter + 1}}
end
end
@@ -52,15 +60,150 @@ defmodule CljElixir.REPL do
|> count_delimiters(0, 0, 0, false, false)
end
# Count open/close delimiters, respecting strings and comments
# ---------------------------------------------------------------------------
# Eval strategies
# ---------------------------------------------------------------------------
# Full ns block: set namespace, capture defs, compile normally
defp eval_with_ns(forms, source, state) do
ns_name = extract_ns_name(forms)
new_defs = collect_defs(forms)
opts = [bindings: state.bindings, file: "repl"]
case CljElixir.Compiler.eval_string(source, opts) do
{:ok, result, new_bindings} ->
new_state = %{state |
bindings: new_bindings,
current_ns: ns_name,
module_defs: new_defs,
history: [source | state.history],
counter: state.counter + 1
}
{:ok, CljElixir.Printer.pr_str(result), new_state}
{:error, errors} ->
{:error, format_errors(errors), %{state | counter: state.counter + 1}}
end
end
# Bare defs in active namespace: merge into module_defs and recompile module
defp eval_in_ns(forms, source, state) do
{new_def_forms, exprs} = Enum.split_with(forms, &def_form?/1)
# Merge new defs into accumulated module_defs (keyed by name)
merged_defs =
Enum.reduce(new_def_forms, state.module_defs, fn form, acc ->
name = extract_def_name(form)
Map.put(acc, name, form)
end)
# Reconstruct: ns + all accumulated defs + current expressions
ns_form = make_ns_form(state.current_ns)
all_forms = [ns_form | Map.values(merged_defs)] ++ exprs
opts = [bindings: state.bindings, file: "repl"]
case CljElixir.Compiler.eval_forms(all_forms, opts) do
{:ok, result, new_bindings} ->
result_str =
if exprs == [] do
# Def-only: show var-like representation
new_def_forms
|> Enum.map(&extract_def_name/1)
|> Enum.map_join(" ", &"#'#{state.current_ns}/#{&1}")
else
CljElixir.Printer.pr_str(result)
end
new_state = %{state |
bindings: new_bindings,
module_defs: merged_defs,
history: [source | state.history],
counter: state.counter + 1
}
{:ok, result_str, new_state}
{:error, errors} ->
{:error, format_errors(errors), %{state | counter: state.counter + 1}}
end
end
# No ns context: eval as-is (legacy / ad-hoc expressions)
defp eval_plain(source, state) do
opts = [bindings: state.bindings, file: "repl"]
case CljElixir.Compiler.eval_string(source, opts) do
{:ok, result, new_bindings} ->
new_state = %{state |
bindings: new_bindings,
history: [source | state.history],
counter: state.counter + 1
}
{:ok, CljElixir.Printer.pr_str(result), new_state}
{:error, errors} ->
{:error, format_errors(errors), %{state | counter: state.counter + 1}}
end
end
# ---------------------------------------------------------------------------
# Form classification helpers
# ---------------------------------------------------------------------------
defp ns_form?({:list, _, [{:symbol, _, "ns"} | _]}), do: true
defp ns_form?(_), do: false
defp def_form?({:list, _, [{:symbol, _, name} | _]})
when name in ~w(defn defn- def defprotocol defrecord extend-type
extend-protocol reify defmacro use),
do: true
defp def_form?({:list, _, [{:symbol, _, "m/=>"} | _]}), do: true
defp def_form?(_), do: false
defp extract_ns_name(forms) do
Enum.find_value(forms, fn
{:list, _, [{:symbol, _, "ns"}, {:symbol, _, name} | _]} -> name
_ -> nil
end)
end
defp collect_defs(forms) do
forms
|> Enum.filter(&def_form?/1)
|> Enum.reduce(%{}, fn form, acc ->
name = extract_def_name(form)
Map.put(acc, name, form)
end)
end
defp extract_def_name({:list, _, [{:symbol, _, _}, {:symbol, _, name} | _]}), do: name
defp extract_def_name(form), do: "anon_#{:erlang.phash2(form)}"
defp make_ns_form(ns_name) do
{:list, %{line: 0, col: 0}, [
{:symbol, %{line: 0, col: 0}, "ns"},
{:symbol, %{line: 0, col: 0}, ns_name}
]}
end
# ---------------------------------------------------------------------------
# Delimiter balancing
# ---------------------------------------------------------------------------
defp count_delimiters([], parens, brackets, braces, _in_string, _escape) do
parens == 0 and brackets == 0 and braces == 0
# Negative counts mean excess closing delimiters — let the reader report the error
parens < 0 or brackets < 0 or braces < 0 or
(parens == 0 and brackets == 0 and braces == 0)
end
defp count_delimiters([char | rest], p, b, br, in_string, escape) do
cond do
escape ->
# Previous char was \, skip this one
count_delimiters(rest, p, b, br, in_string, false)
char == "\\" and in_string ->
@@ -76,7 +219,6 @@ defmodule CljElixir.REPL do
count_delimiters(rest, p, b, br, true, false)
char == ";" ->
# Comment - skip rest of line
rest_after_newline = Enum.drop_while(rest, &(&1 != "\n"))
count_delimiters(rest_after_newline, p, b, br, false, false)
@@ -91,6 +233,10 @@ defmodule CljElixir.REPL do
end
end
# ---------------------------------------------------------------------------
# Error formatting
# ---------------------------------------------------------------------------
defp format_errors(errors) when is_list(errors) do
Enum.map_join(errors, "\n", fn
%{message: msg, line: line} when is_integer(line) and line > 0 ->
+119 -33
View File
@@ -53,14 +53,51 @@ defmodule CljElixir.Transformer do
def transform(forms, ctx \\ %Context{})
def transform(forms, ctx) when is_list(forms) do
{elixir_forms, _ctx} =
Enum.map_reduce(forms, ctx, fn form, acc ->
{ast, new_ctx} = transform_form(form, acc)
{ast, new_ctx}
# Check if file has explicit defmodule forms (ns won't auto-wrap if so)
has_defmodule =
Enum.any?(forms, fn
{:list, _, [{:symbol, _, "defmodule"} | _]} -> true
_ -> false
end)
# Filter out nil (from defmacro which produces no runtime code)
elixir_forms = Enum.filter(elixir_forms, &(&1 != nil))
{elixir_forms, final_ctx} =
Enum.map_reduce(forms, ctx, fn form, acc ->
{ast, new_ctx} = transform_form(form, acc)
# Tag each transformed form with whether the source was a def-like form
{{ast, def_form?(form)}, new_ctx}
end)
# Filter out nil (from ns, defmacro which produce no runtime code)
elixir_forms = Enum.filter(elixir_forms, fn {ast, _} -> ast != nil end)
# If ns declared a module and there are no explicit defmodule forms,
# separate def-forms (inside module) from expressions (after module)
elixir_forms =
if final_ctx.module_name != nil and ctx.module_name == nil and not has_defmodule do
{defs, exprs} =
Enum.split_with(elixir_forms, fn {_ast, is_def} -> is_def end)
def_asts = Enum.map(defs, fn {ast, _} -> ast end)
expr_asts = Enum.map(exprs, fn {ast, _} -> ast end)
block =
case def_asts do
[] -> nil
[single] -> single
multiple -> {:__block__, [], multiple}
end
module_ast =
if block do
[{:defmodule, [context: Elixir], [final_ctx.module_name, [do: block]]}]
else
[]
end
module_ast ++ expr_asts
else
Enum.map(elixir_forms, fn {ast, _} -> ast end)
end
case elixir_forms do
[] -> nil
@@ -74,6 +111,30 @@ defmodule CljElixir.Transformer do
ast
end
# Transform a list of guard forms into a single ANDed Elixir guard AST.
# [:guard [(> x 0) (< x 10)]] → {:and, [], [guard1, guard2]}
defp transform_guards(guard_forms, ctx) do
guard_asts = Enum.map(guard_forms, &transform(&1, ctx))
case guard_asts do
[single] -> single
[first | rest] -> Enum.reduce(rest, first, fn g, acc ->
{:and, [context: Elixir], [acc, g]}
end)
end
end
# Is this CljElixir AST form a definition (goes inside defmodule)?
defp def_form?({:list, _, [{:symbol, _, name} | _]})
when name in ~w(defn defn- def defprotocol defrecord extend-type
extend-protocol reify defmacro use),
do: true
# m/=> schema annotations
defp def_form?({:list, _, [{:symbol, _, "m/=>"} | _]}), do: true
defp def_form?(_), do: false
# ---------------------------------------------------------------------------
# Main dispatch
# ---------------------------------------------------------------------------
@@ -258,6 +319,7 @@ defmodule CljElixir.Transformer do
defp transform_list([head | args], meta, ctx) do
case head do
# --- Special forms (symbols) ---
{:symbol, _, "ns"} -> transform_ns(args, meta, ctx)
{:symbol, _, "defmodule"} -> transform_defmodule(args, meta, ctx)
{:symbol, _, "defn"} -> transform_defn(args, meta, ctx, :def)
{:symbol, _, "defn-"} -> transform_defn(args, meta, ctx, :defp)
@@ -419,6 +481,17 @@ defmodule CljElixir.Transformer do
end
end
# ---------------------------------------------------------------------------
# 0. ns — module declaration (sets ctx.module_name for auto-wrapping)
# ---------------------------------------------------------------------------
defp transform_ns([name_form | _rest], _meta, ctx) do
mod_alias = module_name_ast(name_form)
{nil, %{ctx | module_name: mod_alias}}
end
defp transform_ns([], _meta, ctx), do: {nil, ctx}
# ---------------------------------------------------------------------------
# 1. defmodule
# ---------------------------------------------------------------------------
@@ -527,8 +600,8 @@ defmodule CljElixir.Transformer do
nil ->
{def_kind, em, [call_with_args(fun_name, param_asts), [do: body_ast]]}
guard_form ->
guard_ast = transform(guard_form, fn_ctx)
guard_forms ->
guard_ast = transform_guards(guard_forms, fn_ctx)
{def_kind, em,
[
@@ -568,18 +641,18 @@ defmodule CljElixir.Transformer do
{required, rest_param, nil, body}
{:list, _, clause_elements} ->
# Might have guard: ([params] :when guard body)
parse_clause_with_guard(clause_elements)
# Might have guard: ([params] :guard guard body)
parse_clause_with_guards(clause_elements)
end)
end
end
defp parse_clause_with_guard([{:vector, _, params}, :when, guard | body]) do
defp parse_clause_with_guards([{:vector, _, params}, :guard, {:vector, _, guards} | body]) do
{required, rest_param} = split_rest_params(params)
{required, rest_param, guard, body}
{required, rest_param, guards, body}
end
defp parse_clause_with_guard([{:vector, _, params} | body]) do
defp parse_clause_with_guards([{:vector, _, params} | body]) do
{required, rest_param} = split_rest_params(params)
{required, rest_param, nil, body}
end
@@ -630,8 +703,8 @@ defmodule CljElixir.Transformer do
nil ->
{:->, [], [all_param_asts, body_ast]}
guard_form ->
guard_ast = transform(guard_form, ctx)
guard_forms ->
guard_ast = transform_guards(guard_forms, ctx)
guard_params = [{:when, [], all_param_asts ++ [guard_ast]}]
{:->, [], [guard_params, body_ast]}
end
@@ -657,7 +730,7 @@ defmodule CljElixir.Transformer do
{required, rest_param, nil, body}
{:list, _, clause_elements} ->
parse_clause_with_guard(clause_elements)
parse_clause_with_guards(clause_elements)
end)
end
end
@@ -823,8 +896,8 @@ defmodule CljElixir.Transformer do
clauses
|> Enum.chunk_every(2)
|> Enum.map(fn
[pattern, :when | rest] ->
# pattern :when guard body — need to re-chunk
[pattern, :guard | rest] ->
# pattern :guard guard body — need to re-chunk
# This won't happen with chunk_every(2), handle differently
pat_ast = transform(pattern, pattern_ctx)
body_ast = transform(List.last(rest), ctx)
@@ -1550,8 +1623,8 @@ defmodule CljElixir.Transformer do
nil ->
{:->, [], [[pat_ast], body_ast]}
guard_form ->
guard_ast = transform(guard_form, ctx)
guard_forms ->
guard_ast = transform_guards(guard_forms, ctx)
{:->, [], [[{:when, [], [pat_ast, guard_ast]}], body_ast]}
end
end)
@@ -1585,8 +1658,8 @@ defmodule CljElixir.Transformer do
parse_receive_clauses(rest, acc, {timeout, body})
end
defp parse_receive_clauses([pattern, :when, guard, body | rest], acc, after_clause) do
parse_receive_clauses(rest, [{pattern, guard, body} | acc], after_clause)
defp parse_receive_clauses([pattern, :guard, {:vector, _, guards}, body | rest], acc, after_clause) do
parse_receive_clauses(rest, [{pattern, guards, body} | acc], after_clause)
end
defp parse_receive_clauses([pattern, body | rest], acc, after_clause) do
@@ -1959,23 +2032,24 @@ defmodule CljElixir.Transformer do
{{:-, [], [a_ast, 1]}, ctx}
end
# str — concatenate with <> using to_string
# str — Clojure-compatible: nil→"", strings pass through, collections use print repr
defp transform_str(args, ctx) do
t_args = Enum.map(args, fn a -> transform(a, ctx) end)
str_call = fn arg ->
{{:., [], [{:__aliases__, [alias: false], [:CljElixir, :Printer]}, :str_value]}, [], [arg]}
end
ast =
case t_args do
[] ->
""
[single] ->
{{:., [], [{:__aliases__, [alias: false], [:Kernel]}, :to_string]}, [], [single]}
str_call.(single)
_ ->
stringified =
Enum.map(t_args, fn a ->
{{:., [], [{:__aliases__, [alias: false], [:Kernel]}, :to_string]}, [], [a]}
end)
stringified = Enum.map(t_args, str_call)
Enum.reduce(tl(stringified), hd(stringified), fn arg, acc ->
{:<>, [], [acc, arg]}
@@ -1985,12 +2059,12 @@ defmodule CljElixir.Transformer do
{ast, ctx}
end
# println → IO.puts
# println → IO.puts, returns nil (Clojure convention)
defp transform_println(args, ctx) do
t_args = Enum.map(args, fn a -> transform(a, ctx) end)
# If multiple args, join with str first
ast =
io_call =
case t_args do
[single] ->
{{:., [], [{:__aliases__, [alias: false], [:IO]}, :puts]}, [], [single]}
@@ -2001,6 +2075,7 @@ defmodule CljElixir.Transformer do
{{:., [], [{:__aliases__, [alias: false], [:IO]}, :puts]}, [], [str_ast]}
end
ast = {:__block__, [], [io_call, nil]}
{ast, ctx}
end
@@ -2052,11 +2127,11 @@ defmodule CljElixir.Transformer do
end
end)
ast = {:__block__, [], writes}
ast = {:__block__, [], writes ++ [nil]}
{ast, ctx}
end
# (prn val) -> IO.puts(CljElixir.Printer.pr_str(val))
# (prn val) -> IO.puts(CljElixir.Printer.pr_str(val)), returns nil
# Multiple args joined with spaces, then newline
defp transform_prn(args, ctx) do
t_args = Enum.map(args, fn a -> transform(a, ctx) end)
@@ -2079,7 +2154,8 @@ defmodule CljElixir.Transformer do
end)
end
ast = {{:., [], [io_mod, :puts]}, [], [joined]}
io_call = {{:., [], [io_mod, :puts]}, [], [joined]}
ast = {:__block__, [], [io_call, nil]}
{ast, ctx}
end
@@ -2535,6 +2611,16 @@ defmodule CljElixir.Transformer do
{ast, ctx}
end
# update - (update m k f x y ...) => rewrite to (assoc m k (f (get m k) x y ...))
# Rewritten at AST level so f goes through builtin dispatch (e.g. dissoc)
defp transform_update([m, k, f | extra_args], ctx) do
meta = %{line: 0, col: 0}
get_call = {:list, meta, [{:symbol, meta, "get"}, m, k]}
f_call = {:list, meta, [f, get_call | extra_args]}
assoc_call = {:list, meta, [{:symbol, meta, "assoc"}, m, k, f_call]}
do_transform(assoc_call, ctx)
end
# conj
defp transform_conj([c, x], ctx) do
c_ast = transform(c, ctx)
+72
View File
@@ -0,0 +1,72 @@
defmodule Mix.Tasks.Clje.Build do
@moduledoc """
Compile CljElixir files to BEAM bytecode.
## Usage
mix clje.build src/my_module.clje
mix clje.build src/foo.clje src/bar.clje
mix clje.build src/foo.clje -o _build/dev/lib/clj_elixir/ebin
Like `elixirc`. Compiles `.clje` files to `.beam` files without running them.
## Options
* `-o` / `--output` - output directory for .beam files (default: `_build/dev/lib/<app>/ebin`)
"""
use Mix.Task
@shortdoc "Compile .clje files to BEAM bytecode"
@impl Mix.Task
def run(args) do
{opts, files, _} =
OptionParser.parse(args,
switches: [output: :string],
aliases: [o: :output]
)
if files == [] do
Mix.shell().error("Usage: mix clje.build <file.clje> [...] [-o output_dir]")
System.halt(1)
end
Mix.Task.run("compile")
Mix.Task.run("app.start")
output_dir = opts[:output] || Mix.Project.compile_path()
results =
Enum.map(files, fn file ->
Mix.shell().info("Compiling #{file}")
case CljElixir.Compiler.compile_file_to_beam(file, output_dir: output_dir) do
{:ok, modules} ->
Enum.each(modules, fn {mod, _binary} ->
Mix.shell().info(" -> #{mod}")
end)
:ok
{:error, diagnostics} ->
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)
:error
end
end)
if Enum.any?(results, &(&1 == :error)) do
System.halt(1)
end
end
end
+45
View File
@@ -0,0 +1,45 @@
defmodule Mix.Tasks.Clje.Eval do
@moduledoc """
Evaluate a CljElixir expression from the command line.
## Usage
mix clje.eval '(+ 1 2)'
mix clje.eval '(defn greet [name] (str "hello " name))' '(greet "world")'
Multiple expressions are evaluated in sequence, with bindings persisting.
The result of the last expression is printed.
"""
use Mix.Task
@shortdoc "Evaluate CljElixir expressions"
@impl Mix.Task
def run([]) do
Mix.shell().error("Usage: mix clje.eval '<expression>' [...]")
System.halt(1)
end
def run(exprs) do
Mix.Task.run("compile")
Mix.Task.run("app.start")
{result, _bindings} =
Enum.reduce(exprs, {nil, []}, fn expr, {_prev, bindings} ->
case CljElixir.Compiler.eval_string(expr, bindings: bindings) do
{:ok, result, new_bindings} ->
{result, new_bindings}
{:error, diagnostics} ->
Enum.each(diagnostics, fn diag ->
Mix.shell().error("#{diag.severity}: #{diag.message}")
end)
System.halt(1)
end
end)
IO.puts(CljElixir.Printer.pr_str(result))
end
end
+40 -13
View File
@@ -38,7 +38,8 @@ defmodule Mix.Tasks.Clje.Repl do
end
defp loop(state) do
prompt = "clje:#{state.counter}> "
ns = CljElixir.REPL.current_ns(state)
prompt = "#{ns}:#{state.counter}> "
case read_input(prompt) do
:eof ->
@@ -80,10 +81,28 @@ defmodule Mix.Tasks.Clje.Repl do
end
defp read_input(prompt) do
case IO.gets(prompt) do
:eof -> :eof
{:error, _} -> :eof
data -> data
IO.write(prompt)
read_line()
end
# Read a line character-by-character, treating both \r and \n as line terminators.
# This avoids IO.gets hanging when the terminal sends \r without \n.
defp read_line, do: read_line([])
defp read_line(acc) do
case IO.getn("", 1) do
:eof ->
if acc == [], do: :eof, else: acc |> Enum.reverse() |> IO.iodata_to_binary()
{:error, _} ->
if acc == [], do: :eof, else: acc |> Enum.reverse() |> IO.iodata_to_binary()
<<c>> when c in [?\r, ?\n] ->
IO.write("\n")
acc |> Enum.reverse() |> IO.iodata_to_binary()
char ->
read_line([char | acc])
end
end
@@ -96,16 +115,24 @@ defmodule Mix.Tasks.Clje.Repl do
end
defp read_continuation(acc) do
case IO.gets(" ") do
:eof -> acc
{:error, _} -> acc
line ->
new_acc = acc <> "\n" <> String.trim_trailing(line, "\n")
IO.write(" ")
if CljElixir.REPL.balanced?(new_acc) do
new_acc
case read_line() do
:eof -> acc
line ->
trimmed = String.trim(line)
if trimmed == "" do
# Empty Enter in continuation mode: submit what we have
acc
else
read_continuation(new_acc)
new_acc = acc <> "\n" <> trimmed
if CljElixir.REPL.balanced?(new_acc) do
new_acc
else
read_continuation(new_acc)
end
end
end
end
+103
View File
@@ -0,0 +1,103 @@
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
+1572
View File
File diff suppressed because it is too large Load Diff
+75
View File
@@ -0,0 +1,75 @@
(ns Access
"Elixir Access module — nested data access and update.
In CljElixir: Provides access functions used with get-in, update-in, etc.
Access functions create composable paths for nested data manipulation.")
(defn key
"Accesses a key in a map. Returns `default` if key is missing.
(get-in data [(Access/key :user) (Access/key :name)])"
([key])
([key default]))
(defn key!
"Accesses a key in a map. Raises if key is missing."
[key])
(defn elem
"Accesses an element in a tuple by index.
(get-in data [(Access/elem 0)])"
[index])
(defn at
"Accesses an element in a list by index.
(get-in data [(Access/at 0)])"
([index])
([index default]))
(defn at!
"Accesses a list element by index. Raises on out of bounds."
[index])
(defn all
"Accesses all elements in a list.
(get-in data [(Access/all)])"
[])
(defn filter
"Filters elements in a list.
(update-in data [(Access/filter (fn [x] (> x 2)))] inc)"
[fun])
(defn find
"Finds the first element matching `fun`.
(get-in data [(Access/find (fn [x] (> x 2)))])"
[fun])
(defn slice
"Accesses a slice of a list.
(get-in data [(Access/slice 1 3)])"
[range-or-index])
(defn pop
"Pops a key from a map or index from a list."
[key])
(defn fetch
"Fetches a value with {:ok value} or :error semantics."
[container key])
(defn fetch!
"Fetches a value. Raises on missing key."
[container key])
(defn get
"Gets a value from a container with optional default."
([container key])
([container key default]))
(defn get-and-update
"Gets and updates a key in one operation."
[container key fun])
(defn get-and-update!
"Gets and updates. Raises if container doesn't implement Access."
[container key fun])
+60
View File
@@ -0,0 +1,60 @@
(ns Agent
"Elixir Agent module — simple state wrapper around a process.
In CljElixir: (Agent/start-link (fn [] initial-state)), etc.
Agents are a simpler alternative to GenServer for pure state management.")
(defn start
"Starts an Agent without linking. Returns {:ok pid}.
(Agent/start (fn [] {:count 0}))"
([fun])
([fun opts])
([module fun args])
([module fun args opts]))
(defn start-link
"Starts an Agent linked to the current process. Returns {:ok pid}.
(Agent/start-link (fn [] {:count 0}))
(Agent/start-link (fn [] {:count 0}) :name :my-agent)"
([fun])
([fun opts])
([module fun args])
([module fun args opts]))
(defn get
"Gets the agent state by applying `fun`. Blocks until result.
(Agent/get pid (fn [state] (:count state))) ;=> 0
(Agent/get pid (fn [state] state) 5000) ;=> with 5s timeout"
([agent fun])
([agent fun timeout])
([agent module fun args])
([agent module fun args timeout]))
(defn update
"Updates the agent state by applying `fun`. Returns :ok.
(Agent/update pid (fn [state] (update state :count inc))) ;=> :ok"
([agent fun])
([agent fun timeout])
([agent module fun args])
([agent module fun args timeout]))
(defn get-and-update
"Gets and updates in one operation. `fun` returns {get_value, new_state}.
(Agent/get-and-update pid (fn [state] {(:count state) (update state :count inc)}))"
([agent fun])
([agent fun timeout])
([agent module fun args])
([agent module fun args timeout]))
(defn cast
"Async update. Returns :ok immediately.
(Agent/cast pid (fn [state] (assoc state :key \"val\"))) ;=> :ok"
([agent fun])
([agent module fun args]))
(defn stop
"Stops the agent.
(Agent/stop pid) ;=> :ok"
([agent])
([agent reason])
([agent reason timeout]))
+104
View File
@@ -0,0 +1,104 @@
(ns Application
"Elixir Application module — OTP application management.
In CljElixir: (Application/get-env :my-app :key), etc.
Applications are the unit of deployment on the BEAM.")
(defn get-env
"Gets application environment value.
(Application/get-env :my-app :key) ;=> value or nil
(Application/get-env :my-app :key :default) ;=> value or :default"
([app key])
([app key default]))
(defn fetch-env
"Gets app env. Returns {:ok value} or :error.
(Application/fetch-env :my-app :key) ;=> {:ok value}"
[app key])
(defn fetch-env!
"Gets app env. Raises if missing."
[app key])
(defn get-all-env
"Returns all environment for an application.
(Application/get-all-env :my-app) ;=> [[:key1 val1] [:key2 val2]]"
[app])
(defn put-env
"Sets an application environment value.
(Application/put-env :my-app :key \"value\")"
([app key value])
([app key value opts]))
(defn put-all-env
"Sets multiple app env values."
([app config])
([app config opts]))
(defn delete-env
"Deletes an application environment value."
[app key])
(defn start
"Starts an application and its dependencies.
(Application/start :my-app) ;=> :ok
(Application/start :my-app :permanent) ;=> with restart type"
([app])
([app type]))
(defn stop
"Stops an application.
(Application/stop :my-app) ;=> :ok"
[app])
(defn ensure-started
"Ensures an application is started. No-op if already running.
(Application/ensure-started :logger) ;=> :ok"
([app])
([app type]))
(defn ensure-all-started
"Starts an application and all dependencies.
(Application/ensure-all-started :my-app) ;=> {:ok [:dep1 :dep2 :my-app]}"
([app])
([app type]))
(defn started-applications
"Returns a list of all started applications.
(Application/started-applications) ;=> [[:kernel \"...\" '1.0'] ...]"
([])
([timeout]))
(defn loaded-applications
"Returns a list of all loaded applications."
([])
([timeout]))
(defn which-applications
"Returns running applications.
(Application/which-applications) ;=> [...]"
([])
([timeout]))
(defn spec
"Returns the application specification.
(Application/spec :my-app) ;=> full spec
(Application/spec :my-app :vsn) ;=> version"
([app])
([app key]))
(defn app-dir
"Returns the application directory.
(Application/app-dir :my-app) ;=> \"/path/to/my_app\""
([app])
([app path]))
(defn compile-env
"Reads compile-time environment values."
([app key])
([app key default]))
(defn compile-env!
"Reads compile-time environment. Raises if missing."
[app key])
+16
View File
@@ -0,0 +1,16 @@
(ns Atom
"Elixir Atom module — atom operations.
In CljElixir: (Atom/to-string :hello), etc.
Atoms are constants whose value is their name.")
(defn to-string
"Converts an atom to a string.
(Atom/to-string :hello) ;=> \"hello\"
(Atom/to-string :Elixir.MyModule) ;=> \"Elixir.MyModule\""
[atom])
(defn to-charlist
"Converts an atom to a charlist.
(Atom/to-charlist :hello) ;=> 'hello'"
[atom])
+88
View File
@@ -0,0 +1,88 @@
(ns Base
"Elixir Base module — encoding/decoding (base16, base32, base64).
In CljElixir: (Base/encode64 data), (Base/decode16 hex-string), etc.")
(defn encode16
"Encodes binary to base-16 (hex) string.
(Base/encode16 \"hello\") ;=> \"68656C6C6F\"
(Base/encode16 \"hello\" :case :lower) ;=> \"68656c6c6f\""
([data])
([data opts]))
(defn decode16
"Decodes a base-16 string. Returns {:ok binary} or :error.
(Base/decode16 \"68656C6C6F\") ;=> {:ok \"hello\"}"
([string])
([string opts]))
(defn decode16!
"Decodes base-16. Raises on error.
(Base/decode16! \"68656C6C6F\") ;=> \"hello\""
([string])
([string opts]))
(defn encode32
"Encodes binary to base-32 string.
(Base/encode32 \"hello\") ;=> \"NBSWY3DP\""
([data])
([data opts]))
(defn decode32
"Decodes a base-32 string. Returns {:ok binary} or :error."
([string])
([string opts]))
(defn decode32!
"Decodes base-32. Raises on error."
([string])
([string opts]))
(defn encode64
"Encodes binary to base-64 string.
(Base/encode64 \"hello\") ;=> \"aGVsbG8=\""
([data])
([data opts]))
(defn decode64
"Decodes a base-64 string. Returns {:ok binary} or :error.
(Base/decode64 \"aGVsbG8=\") ;=> {:ok \"hello\"}"
([string])
([string opts]))
(defn decode64!
"Decodes base-64. Raises on error.
(Base/decode64! \"aGVsbG8=\") ;=> \"hello\""
([string])
([string opts]))
(defn url-encode64
"Encodes binary to URL-safe base-64.
(Base/url-encode64 data) ;=> URL-safe base64 string"
([data])
([data opts]))
(defn url-decode64
"Decodes URL-safe base-64. Returns {:ok binary} or :error."
([string])
([string opts]))
(defn url-decode64!
"Decodes URL-safe base-64. Raises on error."
([string])
([string opts]))
(defn hex-encode32
"Encodes binary to hex-base-32 (Extended Hex)."
([data])
([data opts]))
(defn hex-decode32
"Decodes hex-base-32."
([string])
([string opts]))
(defn hex-decode32!
"Decodes hex-base-32. Raises on error."
([string])
([string opts]))
+133
View File
@@ -0,0 +1,133 @@
(ns Code
"Elixir Code module — code loading, compilation, and evaluation.
In CljElixir: (Code/eval-string \"1 + 2\"), (Code/compile-file \"mod.ex\"), etc.")
(defn eval-string
"Evaluates Elixir code from a string. Returns {result, binding}.
(Code/eval-string \"1 + 2\") ;=> {3 []}"
([string])
([string binding])
([string binding opts]))
(defn eval-quoted
"Evaluates a quoted Elixir AST.
(Code/eval-quoted ast binding)"
([quoted])
([quoted binding])
([quoted binding opts]))
(defn compile-string
"Compiles Elixir code from a string."
([string])
([string file]))
(defn compile-file
"Compiles an Elixir source file."
([file])
([file relative-to]))
(defn compile-quoted
"Compiles a quoted AST."
([quoted])
([quoted file]))
(defn require-file
"Requires a file, compiling it if needed. No-op if already loaded."
([file])
([file relative-to]))
(defn ensure-loaded
"Ensures the module is loaded. Returns {:module module} or {:error reason}.
(Code/ensure-loaded Enum) ;=> {:module Enum}"
[module])
(defn ensure-loaded!
"Ensures the module is loaded. Raises on error."
[module])
(defn ensure-compiled
"Ensures the module is compiled. Returns {:module module} or {:error reason}."
[module])
(defn ensure-compiled!
"Ensures the module is compiled. Raises on error."
[module])
(defn loaded?
"Returns true if the module is loaded.
(Code/loaded? Enum) ;=> true"
[module])
(defn available?
"Returns true if the module is available."
[module])
(defn fetch-docs
"Returns documentation for a module."
[module])
(defn get-docs
"Returns docs for a module, function, or callback."
([module spec]))
(defn string-to-quoted
"Parses an Elixir string to AST. Returns {:ok ast} or {:error reason}.
(Code/string-to-quoted \"1 + 2\") ;=> {:ok {:+ [line: 1] [1 2]}}"
([string])
([string opts]))
(defn string-to-quoted!
"Parses to AST. Raises on error."
([string])
([string opts]))
(defn quoted-to-algebra
"Converts quoted AST to formatted algebra document."
([quoted])
([quoted opts]))
(defn format-string!
"Formats Elixir code string.
(Code/format-string! \"1+2\") ;=> \"1 + 2\""
([string])
([string opts]))
(defn format-file!
"Formats an Elixir source file."
([file])
([file opts]))
(defn purge
"Purges a module (removes old code)."
[module])
(defn delete
"Deletes a module from the VM."
[module])
(defn compiler-options
"Gets or sets compiler options.
(Code/compiler-options) ;=> current options"
([])
([opts]))
(defn put-compiler-option
"Sets a single compiler option."
[key value])
(defn unrequire-files
"Un-requires files so they can be required again."
[files])
(defn required-files
"Returns list of required files."
[])
(defn append-path
"Appends a path to the code path."
[path])
(defn prepend-path
"Prepends a path to the code path."
[path])
+161
View File
@@ -0,0 +1,161 @@
(ns Date
"Elixir Date module — calendar date operations.
In CljElixir: (Date/utc-today), (Date/new 2024 3 9), etc.")
(defn utc-today
"Returns today's date in UTC.
(Date/utc-today) ;=> ~D[2024-03-09]"
([])
([calendar]))
(defn new
"Creates a new date.
(Date/new 2024 3 9) ;=> {:ok ~D[2024-03-09]}"
([year month day])
([year month day calendar]))
(defn new!
"Creates a new date. Raises on error.
(Date/new! 2024 3 9) ;=> ~D[2024-03-09]"
([year month day])
([year month day calendar]))
(defn from-iso8601
"Parses ISO 8601 date. Returns {:ok date} or {:error reason}.
(Date/from-iso8601 \"2024-03-09\") ;=> {:ok ~D[2024-03-09]}"
([string])
([string calendar]))
(defn from-iso8601!
"Parses ISO 8601. Raises on error."
([string])
([string calendar]))
(defn to-iso8601
"Converts to ISO 8601 string.
(Date/to-iso8601 date) ;=> \"2024-03-09\""
([date])
([date format]))
(defn to-string
"Converts to string.
(Date/to-string date) ;=> \"2024-03-09\""
[date])
(defn add
"Adds days to a date.
(Date/add date 7) ;=> one week later
(Date/add date 1 :month)"
([date days])
([date amount unit]))
(defn diff
"Returns the difference in days between two dates.
(Date/diff date1 date2) ;=> 7"
[date1 date2])
(defn day-of-week
"Returns the day of the week (1=Monday, 7=Sunday).
(Date/day-of-week date) ;=> 6 (Saturday)"
([date])
([date starting-on]))
(defn day-of-year
"Returns the day of the year (1-366).
(Date/day-of-year date) ;=> 69"
[date])
(defn day-of-era
"Returns the day of the era and era number."
[date])
(defn days-in-month
"Returns the number of days in the month.
(Date/days-in-month date) ;=> 31"
[date])
(defn months-in-year
"Returns the number of months in the year."
[date])
(defn quarter-of-year
"Returns the quarter (1-4).
(Date/quarter-of-year date) ;=> 1"
[date])
(defn year-of-era
"Returns the year of the era."
[date])
(defn leap-year?
"Returns true if the date's year is a leap year.
(Date/leap-year? date) ;=> true"
[date])
(defn beginning-of-month
"Returns the first day of the month.
(Date/beginning-of-month date)"
[date])
(defn end-of-month
"Returns the last day of the month.
(Date/end-of-month date)"
[date])
(defn beginning-of-week
"Returns the first day of the week."
([date])
([date starting-on]))
(defn end-of-week
"Returns the last day of the week."
([date])
([date starting-on]))
(defn range
"Creates a date range.
(Date/range date1 date2) ;=> %Date.Range{}"
([first last])
([first last step]))
(defn compare
"Compares two dates. Returns :lt, :eq, or :gt."
[date1 date2])
(defn before?
"Returns true if `date1` is before `date2`."
[date1 date2])
(defn after?
"Returns true if `date1` is after `date2`."
[date1 date2])
(defn shift
"Shifts date by a duration.
(Date/shift date :month 1 :day -3)"
[date duration])
(defn convert
"Converts a date to a different calendar."
([date calendar]))
(defn convert!
"Converts to a different calendar. Raises on error."
([date calendar]))
(defn from-erl
"Converts Erlang date tuple to Date.
(Date/from-erl {2024 3 9}) ;=> {:ok ~D[2024-03-09]}"
([tuple])
([tuple calendar]))
(defn from-erl!
"Converts Erlang date tuple. Raises on error."
([tuple])
([tuple calendar]))
(defn to-erl
"Converts Date to Erlang date tuple.
(Date/to-erl date) ;=> {2024 3 9}"
[date])
+155
View File
@@ -0,0 +1,155 @@
(ns DateTime
"Elixir DateTime module — date and time with timezone.
In CljElixir: (DateTime/utc-now), (DateTime/to-iso8601 dt), etc.")
(defn utc-now
"Returns the current UTC datetime.
(DateTime/utc-now) ;=> ~U[2024-03-09 12:00:00Z]
(DateTime/utc-now Calendar.ISO)"
([])
([calendar]))
(defn now
"Returns the current datetime for a timezone.
(DateTime/now \"Etc/UTC\") ;=> {:ok datetime}"
([timezone])
([timezone calendar]))
(defn now!
"Returns the current datetime. Raises on error.
(DateTime/now! \"Etc/UTC\")"
([timezone])
([timezone calendar]))
(defn new
"Creates a new DateTime.
(DateTime/new 2024 3 9 12 0 0)"
([date time])
([date time timezone])
([date time timezone database]))
(defn new!
"Creates a new DateTime. Raises on error."
([date time])
([date time timezone])
([date time timezone database]))
(defn from-unix
"Converts Unix timestamp to DateTime.
(DateTime/from-unix 1709985600) ;=> {:ok datetime}
(DateTime/from-unix 1709985600000 :millisecond)"
([integer])
([integer unit])
([integer unit calendar]))
(defn from-unix!
"Converts Unix timestamp. Raises on error.
(DateTime/from-unix! 1709985600)"
([integer])
([integer unit])
([integer unit calendar]))
(defn to-unix
"Converts DateTime to Unix timestamp.
(DateTime/to-unix datetime) ;=> 1709985600
(DateTime/to-unix datetime :millisecond)"
([datetime])
([datetime unit]))
(defn from-iso8601
"Parses ISO 8601 string. Returns {:ok datetime utc-offset}.
(DateTime/from-iso8601 \"2024-03-09T12:00:00Z\") ;=> {:ok datetime 0}"
([string])
([string calendar-or-format]))
(defn from-iso8601!
"Parses ISO 8601. Raises on error."
([string])
([string calendar-or-format]))
(defn to-iso8601
"Converts to ISO 8601 string.
(DateTime/to-iso8601 datetime) ;=> \"2024-03-09T12:00:00Z\""
([datetime])
([datetime format])
([datetime format offset]))
(defn to-string
"Converts to human-readable string.
(DateTime/to-string datetime) ;=> \"2024-03-09 12:00:00Z\""
[datetime])
(defn to-date
"Extracts the Date part.
(DateTime/to-date datetime) ;=> ~D[2024-03-09]"
[datetime])
(defn to-time
"Extracts the Time part.
(DateTime/to-time datetime) ;=> ~T[12:00:00]"
[datetime])
(defn to-naive
"Converts to NaiveDateTime (drops timezone info).
(DateTime/to-naive datetime) ;=> ~N[2024-03-09 12:00:00]"
[datetime])
(defn from-naive
"Converts NaiveDateTime to DateTime with timezone.
(DateTime/from-naive naive \"Etc/UTC\") ;=> {:ok datetime}"
([naive-datetime timezone])
([naive-datetime timezone database]))
(defn from-naive!
"Converts NaiveDateTime. Raises on error."
([naive-datetime timezone])
([naive-datetime timezone database]))
(defn add
"Adds `amount` of time to a datetime.
(DateTime/add datetime 3600) ;=> +1 hour
(DateTime/add datetime 1 :hour)"
([datetime amount])
([datetime amount unit]))
(defn diff
"Returns the difference between two datetimes.
(DateTime/diff dt1 dt2) ;=> seconds
(DateTime/diff dt1 dt2 :hour) ;=> hours"
([datetime1 datetime2])
([datetime1 datetime2 unit]))
(defn shift-zone
"Shifts datetime to a different timezone.
(DateTime/shift-zone datetime \"America/New_York\")"
([datetime timezone])
([datetime timezone database]))
(defn shift-zone!
"Shifts timezone. Raises on error."
([datetime timezone])
([datetime timezone database]))
(defn truncate
"Truncates datetime to given precision.
(DateTime/truncate datetime :second)"
[datetime precision])
(defn compare
"Compares two datetimes. Returns :lt, :eq, or :gt.
(DateTime/compare dt1 dt2) ;=> :lt"
[datetime1 datetime2])
(defn before?
"Returns true if `datetime1` is before `datetime2`."
[datetime1 datetime2])
(defn after?
"Returns true if `datetime1` is after `datetime2`."
[datetime1 datetime2])
(defn shift
"Shifts datetime by a duration.
(DateTime/shift datetime :hour 1 :minute 30)"
[datetime duration])
+378
View File
@@ -0,0 +1,378 @@
(ns Enum
"Elixir Enum module — eager operations on enumerables.
In CljElixir: (Enum/map coll f), (Enum/reduce coll acc f), etc.
Works on lists, maps, ranges, and any Enumerable.")
;; --- Mapping & Transformation ---
(defn map
"Returns a list with `f` applied to each element.
(Enum/map [1 2 3] (fn [x] (* x 2))) ;=> [2 4 6]"
[enumerable f])
(defn map-every
"Applies `f` to every `nth` element, starting with the first.
(Enum/map-every [1 2 3 4 5] 2 (fn [x] (* x 2))) ;=> [2 2 6 4 10]"
[enumerable nth f])
(defn flat-map
"Maps `f` over `enumerable` and flattens the result.
(Enum/flat-map [[1 2] [3 4]] (fn [x] x)) ;=> [1 2 3 4]"
[enumerable f])
(defn map-reduce
"Maps and reduces in one pass. Returns {mapped_list, acc}.
(Enum/map-reduce [1 2 3] 0 (fn [x acc] {(* x 2) (+ acc x)})) ;=> {[2 4 6] 6}"
[enumerable acc f])
(defn scan
"Returns a list of successive reduced values from the left.
(Enum/scan [1 2 3 4] (fn [x acc] (+ x acc))) ;=> [1 3 6 10]"
([enumerable f])
([enumerable acc f]))
;; --- Filtering ---
(defn filter
"Returns elements for which `f` returns a truthy value.
(Enum/filter [1 2 3 4] (fn [x] (> x 2))) ;=> [3 4]"
[enumerable f])
(defn reject
"Returns elements for which `f` returns a falsy value.
(Enum/reject [1 2 3 4] (fn [x] (> x 2))) ;=> [1 2]"
[enumerable f])
(defn uniq
"Returns unique elements, preserving order.
(Enum/uniq [1 2 1 3 2]) ;=> [1 2 3]"
[enumerable])
(defn uniq-by
"Returns elements unique by the result of `f`.
(Enum/uniq-by [{:x 1 :y 2} {:x 1 :y 3}] (fn [m] (:x m))) ;=> [{:x 1 :y 2}]"
[enumerable f])
(defn dedup
"Removes consecutive duplicate elements.
(Enum/dedup [1 1 2 2 3 1]) ;=> [1 2 3 1]"
[enumerable])
(defn dedup-by
"Removes consecutive elements where `f` returns the same value."
[enumerable f])
;; --- Reducing ---
(defn reduce
"Invokes `f` for each element with the accumulator.
(Enum/reduce [1 2 3] 0 (fn [x acc] (+ acc x))) ;=> 6
Note: Elixir callback order is (element, acc) not (acc, element)."
([enumerable f])
([enumerable acc f]))
(defn reduce-while
"Reduces while `f` returns {:cont, acc}. Halts on {:halt, acc}.
(Enum/reduce-while [1 2 3 4] 0 (fn [x acc] (if (< acc 5) {:cont (+ acc x)} {:halt acc})))"
[enumerable acc f])
(defn map-join
"Maps `f` over `enumerable` then joins results with `joiner`.
(Enum/map-join [1 2 3] \", \" (fn [x] (str x))) ;=> \"1, 2, 3\""
([enumerable f])
([enumerable joiner f]))
;; --- Sorting ---
(defn sort
"Sorts the enumerable. Uses Erlang term ordering or a custom comparator.
(Enum/sort [3 1 2]) ;=> [1 2 3]
(Enum/sort [3 1 2] (fn [a b] (> a b))) ;=> [3 2 1]"
([enumerable])
([enumerable sorter]))
(defn sort-by
"Sorts by the result of applying `mapper` to each element.
(Enum/sort-by [{:name \"b\"} {:name \"a\"}] (fn [x] (:name x))) ;=> [{:name \"a\"} {:name \"b\"}]"
([enumerable mapper])
([enumerable mapper sorter]))
;; --- Grouping & Partitioning ---
(defn group-by
"Groups elements by the result of `f`.
(Enum/group-by [\"ant\" \"bee\" \"ape\"] (fn [s] (String/at s 0)))
;=> %{\"a\" => [\"ant\" \"ape\"], \"b\" => [\"bee\"]}"
[enumerable f])
(defn chunk-by
"Splits into chunks where consecutive elements return the same value for `f`.
(Enum/chunk-by [1 1 2 2 3] (fn [x] x)) ;=> [[1 1] [2 2] [3]]"
[enumerable f])
(defn chunk-every
"Splits into chunks of `count` elements.
(Enum/chunk-every [1 2 3 4 5] 2) ;=> [[1 2] [3 4] [5]]"
([enumerable count])
([enumerable count step])
([enumerable count step leftover]))
(defn frequencies
"Returns a map with keys as unique elements and values as counts.
(Enum/frequencies [\"a\" \"b\" \"a\" \"c\" \"b\" \"a\"]) ;=> %{\"a\" => 3, \"b\" => 2, \"c\" => 1}"
[enumerable])
(defn split-with
"Splits into two lists: elements satisfying `f` and the rest.
(Enum/split-with [1 2 3 4] (fn [x] (< x 3))) ;=> {[1 2] [3 4]}"
[enumerable f])
(defn partition-by
"Splits when `f` returns a new value.
(Enum/partition-by [1 1 2 2 3] (fn [x] x)) ;=> [[1 1] [2 2] [3]]"
[enumerable f])
;; --- Lookup & Search ---
(defn find
"Returns the first element for which `f` returns truthy.
(Enum/find [1 2 3 4] (fn [x] (> x 2))) ;=> 3"
([enumerable f])
([enumerable default f]))
(defn find-index
"Returns the index of the first element for which `f` returns truthy.
(Enum/find-index [1 2 3] (fn [x] (= x 2))) ;=> 1"
[enumerable f])
(defn find-value
"Returns the first truthy return value of `f`.
(Enum/find-value [1 2 3] (fn [x] (if (> x 2) (* x 10) nil))) ;=> 30"
([enumerable f])
([enumerable default f]))
(defn member?
"Returns true if `element` exists in the enumerable.
(Enum/member? [1 2 3] 2) ;=> true"
[enumerable element])
(defn any?
"Returns true if any element satisfies `f` (or if any element is truthy).
(Enum/any? [false nil true]) ;=> true
(Enum/any? [1 2 3] (fn [x] (> x 2))) ;=> true"
([enumerable])
([enumerable f]))
(defn all?
"Returns true if all elements satisfy `f` (or all are truthy).
(Enum/all? [1 2 3] (fn [x] (> x 0))) ;=> true"
([enumerable])
([enumerable f]))
(defn count
"Returns the count of elements, optionally only those satisfying `f`.
(Enum/count [1 2 3]) ;=> 3
(Enum/count [1 2 3] (fn [x] (> x 1))) ;=> 2"
([enumerable])
([enumerable f]))
(defn empty?
"Returns true if the enumerable is empty.
(Enum/empty? []) ;=> true"
[enumerable])
;; --- Subsequences ---
(defn take
"Takes the first `amount` elements.
(Enum/take [1 2 3 4 5] 3) ;=> [1 2 3]"
[enumerable amount])
(defn take-while
"Takes elements while `f` returns truthy.
(Enum/take-while [1 2 3 4] (fn [x] (< x 3))) ;=> [1 2]"
[enumerable f])
(defn take-every
"Takes every `nth` element (0-indexed).
(Enum/take-every [1 2 3 4 5 6] 2) ;=> [1 3 5]"
[enumerable nth])
(defn drop
"Drops the first `amount` elements.
(Enum/drop [1 2 3 4 5] 2) ;=> [3 4 5]"
[enumerable amount])
(defn drop-while
"Drops elements while `f` returns truthy.
(Enum/drop-while [1 2 3 4] (fn [x] (< x 3))) ;=> [3 4]"
[enumerable f])
(defn slice
"Returns a subset of the enumerable.
(Enum/slice [1 2 3 4 5] 1 3) ;=> [2 3 4]
(Enum/slice [1 2 3 4 5] 1..3) ;=> [2 3 4]"
([enumerable index-range])
([enumerable start amount]))
(defn reverse
"Reverses the enumerable.
(Enum/reverse [1 2 3]) ;=> [3 2 1]"
([enumerable])
([enumerable tail]))
(defn shuffle
"Returns a list with elements in random order.
(Enum/shuffle [1 2 3 4 5]) ;=> [3 1 5 2 4]"
[enumerable])
;; --- Aggregation ---
(defn sum
"Returns the sum of all elements.
(Enum/sum [1 2 3]) ;=> 6"
[enumerable])
(defn product
"Returns the product of all elements.
(Enum/product [1 2 3 4]) ;=> 24"
[enumerable])
(defn min
"Returns the minimum element.
(Enum/min [3 1 2]) ;=> 1"
([enumerable])
([enumerable empty-fallback]))
(defn max
"Returns the maximum element.
(Enum/max [3 1 2]) ;=> 3"
([enumerable])
([enumerable empty-fallback]))
(defn min-by
"Returns the element for which `f` returns the smallest value.
(Enum/min-by [\"aaa\" \"b\" \"cc\"] (fn [s] (String/length s))) ;=> \"b\""
([enumerable f])
([enumerable f sorter]))
(defn max-by
"Returns the element for which `f` returns the largest value.
(Enum/max-by [\"aaa\" \"b\" \"cc\"] (fn [s] (String/length s))) ;=> \"aaa\""
([enumerable f])
([enumerable f sorter]))
(defn min-max
"Returns a tuple with the minimum and maximum elements.
(Enum/min-max [3 1 2]) ;=> {1 3}"
([enumerable])
([enumerable empty-fallback]))
(defn min-max-by
"Returns a tuple with the min/max elements by `f`."
([enumerable f])
([enumerable f sorter-or-empty]))
;; --- Joining & Conversion ---
(defn join
"Joins elements into a string with an optional separator.
(Enum/join [1 2 3] \", \") ;=> \"1, 2, 3\"
(Enum/join [1 2 3]) ;=> \"123\""
([enumerable])
([enumerable joiner]))
(defn into
"Inserts each element into a collectable.
(Enum/into [1 2 3] []) ;=> [1 2 3]
(Enum/into %{a: 1} %{b: 2}) ;=> %{a: 1, b: 2}
(Enum/into [1 2 3] [] (fn [x] (* x 2))) ;=> [2 4 6]"
([enumerable collectable])
([enumerable collectable transform]))
(defn to-list
"Converts an enumerable to a list.
(Enum/to-list (1..5)) ;=> [1 2 3 4 5]"
[enumerable])
(defn zip
"Zips corresponding elements from a finite collection of enumerables.
(Enum/zip [1 2 3] [:a :b :c]) ;=> [{1 :a} {2 :b} {3 :c}]"
([enumerables])
([enum1 enum2]))
(defn zip-with
"Zips with a merge function.
(Enum/zip-with [1 2 3] [4 5 6] (fn [a b] (+ a b))) ;=> [5 7 9]"
([enumerables zip-fun])
([enum1 enum2 zip-fun]))
(defn unzip
"Opposite of zip. Takes a list of two-element tuples and returns two lists.
(Enum/unzip [{1 :a} {2 :b}]) ;=> {[1 2] [:a :b]}"
[list])
(defn with-index
"Wraps each element in a tuple with its index.
(Enum/with-index [:a :b :c]) ;=> [{:a 0} {:b 1} {:c 2}]"
([enumerable])
([enumerable fun-or-offset]))
(defn flat-map-reduce
"Maps and reduces, emitting lists that get flattened. Returns {[flat_mapped], acc}."
[enumerable acc f])
;; --- Element Access ---
(defn at
"Returns the element at `index`. Returns `default` if out of bounds.
(Enum/at [1 2 3] 1) ;=> 2
(Enum/at [1 2 3] 5 :none) ;=> :none"
([enumerable index])
([enumerable index default]))
(defn fetch
"Returns {:ok element} or :error for index lookup.
(Enum/fetch [1 2 3] 1) ;=> {:ok 2}
(Enum/fetch [1 2 3] 5) ;=> :error"
[enumerable index])
(defn fetch!
"Returns the element at `index`. Raises if out of bounds.
(Enum/fetch! [1 2 3] 1) ;=> 2"
[enumerable index])
(defn random
"Returns a random element.
(Enum/random [1 2 3 4 5]) ;=> 3"
([enumerable])
([enumerable count]))
;; --- List Operations ---
(defn concat
"Concatenates enumerables.
(Enum/concat [1 2] [3 4]) ;=> [1 2 3 4]
(Enum/concat [[1 2] [3 4]]) ;=> [1 2 3 4]"
([enumerables])
([left right]))
(defn intersperse
"Inserts `separator` between each element.
(Enum/intersperse [1 2 3] 0) ;=> [1 0 2 0 3]"
[enumerable separator])
(defn each
"Invokes `f` for each element (side effects). Returns :ok.
(Enum/each [1 2 3] (fn [x] (IO/puts x)))"
[enumerable f])
(defn map-intersperse
"Maps and intersperses in one pass."
[enumerable separator mapper])
(defn split
"Splits into two lists at `count`.
(Enum/split [1 2 3 4 5] 3) ;=> {[1 2 3] [4 5]}"
[enumerable count])
+223
View File
@@ -0,0 +1,223 @@
(ns File
"Elixir File module — file system operations.
In CljElixir: (File/read \"path\"), (File/write \"path\" content), etc.
Returns {:ok result} or {:error reason} for most operations.")
(defn read
"Reads the contents of `path`. Returns {:ok binary} or {:error reason}.
(File/read \"myfile.txt\") ;=> {:ok \"contents\"}"
[path])
(defn read!
"Reads the contents of `path`. Raises on error.
(File/read! \"myfile.txt\") ;=> \"contents\""
[path])
(defn write
"Writes `content` to `path`. Returns :ok or {:error reason}.
(File/write \"myfile.txt\" \"hello\")
(File/write \"myfile.txt\" \"hello\" [:append])"
([path content])
([path content modes]))
(defn write!
"Writes `content` to `path`. Raises on error.
(File/write! \"myfile.txt\" \"hello\")"
([path content])
([path content modes]))
(defn exists?
"Returns true if `path` exists.
(File/exists? \"myfile.txt\") ;=> true"
[path])
(defn dir?
"Returns true if `path` is a directory.
(File/dir? \"/tmp\") ;=> true"
[path])
(defn regular?
"Returns true if `path` is a regular file.
(File/regular? \"myfile.txt\") ;=> true"
[path])
(defn mkdir
"Creates a directory at `path`.
(File/mkdir \"mydir\") ;=> :ok"
[path])
(defn mkdir-p
"Creates a directory and all parent directories.
(File/mkdir-p \"a/b/c\") ;=> :ok"
[path])
(defn rm
"Removes a file at `path`.
(File/rm \"myfile.txt\") ;=> :ok"
[path])
(defn rm!
"Removes a file at `path`. Raises on error."
[path])
(defn rm-rf
"Removes files and directories recursively.
(File/rm-rf \"mydir\") ;=> {:ok [\"mydir/a\" \"mydir\"]}"
[path])
(defn rmdir
"Removes an empty directory.
(File/rmdir \"mydir\") ;=> :ok"
[path])
(defn cp
"Copies `source` to `destination`.
(File/cp \"src.txt\" \"dst.txt\") ;=> :ok"
([source destination])
([source destination callback]))
(defn cp!
"Copies. Raises on error."
([source destination])
([source destination callback]))
(defn cp-r
"Copies recursively.
(File/cp-r \"src_dir\" \"dst_dir\")"
([source destination])
([source destination callback]))
(defn cp-r!
"Copies recursively. Raises on error."
([source destination])
([source destination callback]))
(defn rename
"Renames/moves `source` to `destination`.
(File/rename \"old.txt\" \"new.txt\") ;=> :ok"
[source destination])
(defn rename!
"Renames/moves. Raises on error."
[source destination])
(defn ln-s
"Creates a symbolic link.
(File/ln-s \"target\" \"link_name\") ;=> :ok"
[existing new-link])
(defn ls
"Lists files in a directory. Returns {:ok [filenames]} or {:error reason}.
(File/ls \".\") ;=> {:ok [\"mix.exs\" \"lib\"]}"
([path])
([]))
(defn ls!
"Lists files. Raises on error.
(File/ls! \".\") ;=> [\"mix.exs\" \"lib\"]"
([path])
([]))
(defn stat
"Returns file info. Returns {:ok stat} or {:error reason}.
(File/stat \"myfile.txt\") ;=> {:ok %File.Stat{...}}"
([path])
([path opts]))
(defn stat!
"Returns file info. Raises on error."
([path])
([path opts]))
(defn lstat
"Like stat but doesn't follow symlinks."
([path])
([path opts]))
(defn lstat!
"Like stat! but doesn't follow symlinks."
([path])
([path opts]))
(defn cwd
"Returns the current working directory.
(File/cwd) ;=> {:ok \"/Users/ajet/repos/clje\"}"
[])
(defn cwd!
"Returns the current working directory. Raises on error."
[])
(defn cd
"Changes the current working directory.
(File/cd \"/tmp\") ;=> :ok"
[path])
(defn cd!
"Changes directory. Raises on error."
[path])
(defn open
"Opens a file. Returns {:ok io-device} or {:error reason}.
(File/open \"myfile.txt\" [:read :utf8])
(File/open \"myfile.txt\" [:write :append])"
([path])
([path modes]))
(defn open!
"Opens a file. Raises on error."
([path])
([path modes]))
(defn close
"Closes a file IO device.
(File/close io-device) ;=> :ok"
[io-device])
(defn stream
"Returns a File.Stream for lazy reading.
(File/stream \"bigfile.txt\") ;=> %File.Stream{...}
(File/stream \"bigfile.txt\" [:read] :line) ;=> line-by-line"
([path])
([path modes])
([path modes line-or-bytes]))
(defn stream!
"Returns a File.Stream. Raises on error."
([path])
([path modes])
([path modes line-or-bytes]))
(defn touch
"Updates file timestamps, creating the file if it doesn't exist.
(File/touch \"myfile.txt\") ;=> :ok"
([path])
([path time]))
(defn touch!
"Touch. Raises on error."
([path])
([path time]))
(defn chmod
"Changes file permissions.
(File/chmod \"script.sh\" 0o755) ;=> :ok"
[path mode])
(defn chown
"Changes file ownership."
[path uid])
(defn chgrp
"Changes file group."
[path gid])
(defn read-link
"Reads the target of a symbolic link.
(File/read-link \"my-link\") ;=> {:ok \"target\"}"
[path])
(defn read-link!
"Reads symlink target. Raises on error."
[path])
+63
View File
@@ -0,0 +1,63 @@
(ns Float
"Elixir Float module — float operations.
In CljElixir: (Float/round 3.14159 2), (Float/parse \"3.14\"), etc.")
(defn parse
"Parses a string into a float. Returns {float rest} or :error.
(Float/parse \"3.14\") ;=> {3.14 \"\"}
(Float/parse \"3.14abc\") ;=> {3.14 \"abc\"}
(Float/parse \"nope\") ;=> :error"
[string])
(defn round
"Rounds to the given number of decimal places.
(Float/round 3.14159 2) ;=> 3.14
(Float/round 3.5) ;=> 4.0"
([float])
([float precision]))
(defn ceil
"Rounds up to given decimal precision.
(Float/ceil 3.14 1) ;=> 3.2
(Float/ceil 3.14) ;=> 4.0"
([float])
([float precision]))
(defn floor
"Rounds down to given decimal precision.
(Float/floor 3.14 1) ;=> 3.1
(Float/floor 3.14) ;=> 3.0"
([float])
([float precision]))
(defn to-string
"Converts float to string.
(Float/to-string 3.14) ;=> \"3.14\"
(Float/to-string 3.14 :compact) ;=> compact decimal
(Float/to-string 3.14 [:decimals 2]) ;=> \"3.14\""
([float])
([float opts]))
(defn to-charlist
"Converts float to charlist."
([float])
([float opts]))
(defn ratio
"Returns {numerator denominator} for the given float.
(Float/ratio 0.75) ;=> {3 4}"
[float])
(defn min-finite
"Returns the minimum finite float value."
[])
(defn max-finite
"Returns the maximum finite float value."
[])
(defn pow
"Returns `base` raised to `exponent` as a float.
(Float/pow 2.0 10) ;=> 1024.0"
[base exponent])
+64
View File
@@ -0,0 +1,64 @@
(ns GenServer
"Elixir GenServer module — generic server (OTP behaviour).
In CljElixir: (GenServer/start-link MyModule init-arg opts), etc.
GenServer is the core abstraction for stateful processes on the BEAM.
Callbacks to implement in your module:
init/1, handle-call/3, handle-cast/2, handle-info/2, terminate/2")
(defn start
"Starts a GenServer without linking. Returns {:ok pid} or {:error reason}.
(GenServer/start MyModule init-arg [])
(GenServer/start MyModule init-arg :name :my-server)"
([module init-arg])
([module init-arg opts]))
(defn start-link
"Starts a GenServer linked to the current process. Returns {:ok pid}.
(GenServer/start-link MyModule init-arg [])
(GenServer/start-link MyModule init-arg :name :my-server)"
([module init-arg])
([module init-arg opts]))
(defn call
"Makes a synchronous call to the server. Blocks until reply. Default timeout 5000ms.
(GenServer/call pid :get-state) ;=> server's reply
(GenServer/call pid {:set \"value\"} 10000) ;=> with 10s timeout"
([server request])
([server request timeout]))
(defn cast
"Sends an asynchronous request to the server. Returns :ok immediately.
(GenServer/cast pid {:update \"value\"}) ;=> :ok"
[server request])
(defn reply
"Replies to a client from within handle-call (for delayed replies).
(GenServer/reply from {:ok result}) ;=> :ok"
[client reply])
(defn stop
"Stops the GenServer.
(GenServer/stop pid) ;=> :ok
(GenServer/stop pid :normal) ;=> with reason
(GenServer/stop pid :normal :infinity) ;=> with timeout"
([server])
([server reason])
([server reason timeout]))
(defn whereis
"Returns the PID of a named GenServer, or nil.
(GenServer/whereis :my-server) ;=> #PID<0.123.0>"
[name])
(defn multi-call
"Calls all locally registered servers on all connected nodes."
([name request])
([nodes name request])
([nodes name request timeout]))
(defn abcast
"Casts to all locally registered servers on all connected nodes."
([name request])
([nodes name request]))
+83
View File
@@ -0,0 +1,83 @@
(ns IO
"Elixir IO module — input/output operations.
In CljElixir: (IO/puts msg), (IO/inspect val), etc.
Handles reading/writing to stdio, files, and IO devices.")
(defn puts
"Writes `item` to the device followed by a newline. Returns :ok.
(IO/puts \"hello\") ;=> prints 'hello\\n', returns :ok
(IO/puts :stderr \"error!\") ;=> prints to stderr"
([item])
([device item]))
(defn write
"Writes `item` to the device without a trailing newline.
(IO/write \"hello\") ;=> prints 'hello', returns :ok"
([item])
([device item]))
(defn inspect
"Inspects the given value and prints it. Returns the value (pass-through).
(IO/inspect {:a 1}) ;=> prints '%{a: 1}', returns {:a 1}
(IO/inspect val :label \"debug\") ;=> prints 'debug: ...'
Useful for debugging — can be inserted anywhere in a pipeline."
([item])
([item opts])
([device item opts]))
(defn gets
"Reads a line from the IO device. Shows `prompt` and returns user input.
(IO/gets \"Enter name: \") ;=> \"Alice\\n\""
([prompt])
([device prompt]))
(defn read
"Reads from the IO device.
(IO/read :stdio :line) ;=> reads one line
(IO/read :stdio 10) ;=> reads 10 characters"
([device count-or-line])
([device count-or-line opts]))
(defn warn
"Writes `message` to stderr followed by a newline.
(IO/warn \"deprecation warning\")"
[message])
(defn iodata-to-binary
"Converts iodata (a list of binaries/integers/iolists) to a single binary.
(IO/iodata-to-binary [\"hello\" \" \" \"world\"]) ;=> \"hello world\""
[iodata])
(defn iodata-length
"Returns the length of iodata without converting to binary.
(IO/iodata-length [\"hello\" \" \" \"world\"]) ;=> 11"
[iodata])
(defn chardata-to-string
"Converts chardata to a string."
[chardata])
(defn getn
"Gets a number of bytes from IO device.
(IO/getn \"prompt> \" 3)"
([prompt])
([prompt count])
([device prompt count]))
(defn binread
"Reads `count` bytes from IO device as binary.
(IO/binread :stdio 10)"
([device count])
([count]))
(defn binwrite
"Writes binary data to IO device.
(IO/binwrite :stdio <<1 2 3>>)"
([device iodata])
([iodata]))
(defn stream
"Converts an IO device into a Stream. Useful for lazy line-by-line reading.
(IO/stream :stdio :line)"
([device mode]))
+80
View File
@@ -0,0 +1,80 @@
(ns Integer
"Elixir Integer module — integer operations.
In CljElixir: (Integer/to-string 255 16), (Integer/digits 123), etc.")
(defn to-string
"Converts integer to string, optionally in a given base.
(Integer/to-string 123) ;=> \"123\"
(Integer/to-string 255 16) ;=> \"FF\""
([integer])
([integer base]))
(defn to-charlist
"Converts integer to charlist.
(Integer/to-charlist 123) ;=> '123'"
([integer])
([integer base]))
(defn parse
"Parses a string into an integer. Returns {integer rest} or :error.
(Integer/parse \"123abc\") ;=> {123 \"abc\"}
(Integer/parse \"FF\" 16) ;=> {255 \"\"}
(Integer/parse \"nope\") ;=> :error"
([string])
([string base]))
(defn digits
"Returns the digits of `integer` as a list.
(Integer/digits 123) ;=> [1 2 3]
(Integer/digits 255 16) ;=> [15 15]"
([integer])
([integer base]))
(defn undigits
"Converts a list of digits back to an integer.
(Integer/undigits [1 2 3]) ;=> 123
(Integer/undigits [15 15] 16) ;=> 255"
([digits])
([digits base]))
(defn pow
"Returns `base` raised to `exponent` (integer exponentiation).
(Integer/pow 2 10) ;=> 1024"
[base exponent])
(defn gcd
"Returns the greatest common divisor.
(Integer/gcd 12 8) ;=> 4"
[integer1 integer2])
(defn mod
"Computes modulo (always non-negative for positive divisor).
(Integer/mod 10 3) ;=> 1
(Integer/mod -5 3) ;=> 1 (differs from rem)"
[dividend divisor])
(defn floor-div
"Integer division rounded towards negative infinity.
(Integer/floor-div 10 3) ;=> 3
(Integer/floor-div -5 3) ;=> -2"
[dividend divisor])
(defn is-odd
"Returns true if `integer` is odd. Allowed in guards.
(Integer/is-odd 3) ;=> true"
[integer])
(defn is-even
"Returns true if `integer` is even. Allowed in guards.
(Integer/is-even 4) ;=> true"
[integer])
(defn extended-gcd
"Returns {gcd, s, t} such that gcd = s*a + t*b (Bezout's identity).
(Integer/extended-gcd 12 8) ;=> {4 1 -1}"
[a b])
(defn to-string-padded
"Converts integer to string with zero-padding."
[integer width])
+336
View File
@@ -0,0 +1,336 @@
(ns Kernel
"Elixir Kernel module — core functions auto-imported into every module.
In CljElixir: most Kernel functions are available as builtins (e.g., +, -, if, etc.).
Use (Kernel/function ...) for less common ones.
Note: many of these are already available as CljElixir builtins without the Kernel/ prefix.")
;; --- Type Checking ---
(defn is-atom
"Returns true if `term` is an atom.
(Kernel/is-atom :hello) ;=> true"
[term])
(defn is-binary
"Returns true if `term` is a binary (string).
(Kernel/is-binary \"hello\") ;=> true"
[term])
(defn is-bitstring
"Returns true if `term` is a bitstring."
[term])
(defn is-boolean
"Returns true if `term` is a boolean.
(Kernel/is-boolean true) ;=> true"
[term])
(defn is-float
"Returns true if `term` is a float.
(Kernel/is-float 1.0) ;=> true"
[term])
(defn is-function
"Returns true if `term` is a function, optionally with given `arity`.
(Kernel/is-function f) ;=> true
(Kernel/is-function f 2) ;=> true if f accepts 2 args"
([term])
([term arity]))
(defn is-integer
"Returns true if `term` is an integer.
(Kernel/is-integer 42) ;=> true"
[term])
(defn is-list
"Returns true if `term` is a list.
(Kernel/is-list [1 2 3]) ;=> true"
[term])
(defn is-map
"Returns true if `term` is a map.
(Kernel/is-map {:a 1}) ;=> true"
[term])
(defn is-map-key
"Returns true if `key` exists in `map`. Allowed in guard expressions."
[map key])
(defn is-nil
"Returns true if `term` is nil.
(Kernel/is-nil nil) ;=> true"
[term])
(defn is-number
"Returns true if `term` is a number (integer or float).
(Kernel/is-number 42) ;=> true"
[term])
(defn is-pid
"Returns true if `term` is a PID.
(Kernel/is-pid (Process/self)) ;=> true"
[term])
(defn is-port
"Returns true if `term` is a port."
[term])
(defn is-reference
"Returns true if `term` is a reference."
[term])
(defn is-tuple
"Returns true if `term` is a tuple.
(Kernel/is-tuple #el[1 2 3]) ;=> true"
[term])
(defn is-struct
"Returns true if `term` is a struct.
(Kernel/is-struct term)
(Kernel/is-struct term MyStruct) ;=> checks specific struct type"
([term])
([term name]))
(defn is-exception
"Returns true if `term` is an exception."
([term])
([term name]))
;; --- Arithmetic (also available as +, -, *, / builtins) ---
(defn div
"Integer division (truncated).
(Kernel/div 10 3) ;=> 3"
[dividend divisor])
(defn rem
"Integer remainder (same sign as dividend).
(Kernel/rem 10 3) ;=> 1"
[dividend divisor])
(defn abs
"Returns the absolute value.
(Kernel/abs -5) ;=> 5"
[number])
(defn max
"Returns the maximum of two terms.
(Kernel/max 1 2) ;=> 2"
[first second])
(defn min
"Returns the minimum of two terms.
(Kernel/min 1 2) ;=> 1"
[first second])
(defn ceil
"Returns the smallest integer >= number.
(Kernel/ceil 1.2) ;=> 2"
[number])
(defn floor
"Returns the largest integer <= number.
(Kernel/floor 1.8) ;=> 1"
[number])
(defn round
"Rounds to the nearest integer.
(Kernel/round 1.5) ;=> 2"
[number])
(defn trunc
"Truncates the float to an integer.
(Kernel/trunc 1.9) ;=> 1"
[number])
;; --- Comparison ---
(defn ==
"Structural equality. 1 == 1.0 is true.
(Kernel/== 1 1.0) ;=> true"
[left right])
(defn ===
"Strict equality. 1 === 1.0 is false.
(Kernel/=== 1 1.0) ;=> false"
[left right])
(defn !=
"Not equal (structural).
(Kernel/!= 1 2) ;=> true"
[left right])
(defn !==
"Strict not equal.
(Kernel/!== 1 1.0) ;=> true"
[left right])
;; --- String & Binary ---
(defn to-string
"Converts `term` to a string via the String.Chars protocol.
(Kernel/to-string 123) ;=> \"123\"
(Kernel/to-string :hello) ;=> \"hello\""
[term])
(defn inspect
"Returns a string representation of `term` (via Inspect protocol).
(Kernel/inspect {:a 1}) ;=> \"%{a: 1}\""
([term])
([term opts]))
(defn byte-size
"Returns the number of bytes in a binary.
(Kernel/byte-size \"hello\") ;=> 5"
[binary])
(defn bit-size
"Returns the number of bits in a bitstring."
[bitstring])
(defn binary-part
"Extracts a binary part.
(Kernel/binary-part \"hello\" 1 3) ;=> \"ell\""
([binary start length]))
;; --- Process & Node ---
(defn self
"Returns the PID of the calling process.
(Kernel/self) ;=> #PID<0.123.0>"
[])
(defn node
"Returns the current node name, or the node for a given PID/ref.
(Kernel/node) ;=> :nonode@nohost"
([])
([pid-or-ref]))
(defn spawn
"Spawns a new process. Returns PID.
(Kernel/spawn (fn [] (IO/puts \"hello\")))"
([fun])
([module fun args]))
(defn spawn-link
"Spawns a linked process.
(Kernel/spawn-link (fn [] (IO/puts \"linked\")))"
([fun])
([module fun args]))
(defn spawn-monitor
"Spawns a monitored process. Returns {pid ref}."
([fun])
([module fun args]))
(defn send
"Sends a message. Returns the message.
(Kernel/send pid :hello)"
[dest msg])
(defn exit
"Sends an exit signal.
(Kernel/exit :normal)"
[reason])
;; --- Tuple Operations ---
(defn elem
"Gets element at `index` from a tuple (0-based).
(Kernel/elem #el[:a :b :c] 1) ;=> :b"
[tuple index])
(defn put-elem
"Puts `value` at `index` in a tuple.
(Kernel/put-elem #el[:a :b :c] 1 :x) ;=> #el[:a :x :c]"
[tuple index value])
(defn tuple-size
"Returns the number of elements in a tuple.
(Kernel/tuple-size #el[1 2 3]) ;=> 3"
[tuple])
(defn make-ref
"Creates a unique reference.
(Kernel/make-ref) ;=> #Reference<0.0.0.1>"
[])
;; --- List Operations ---
(defn hd
"Returns the head of a list.
(Kernel/hd [1 2 3]) ;=> 1"
[list])
(defn tl
"Returns the tail of a list.
(Kernel/tl [1 2 3]) ;=> [2 3]"
[list])
(defn length
"Returns the length of a list.
(Kernel/length [1 2 3]) ;=> 3"
[list])
(defn in
"Membership test (for use in guards). Checks if `elem` is in `list`.
(Kernel/in x [1 2 3])"
[elem list])
;; --- Misc ---
(defn apply
"Applies `fun` with `args`.
(Kernel/apply Enum :map [[1 2 3] inc])"
([fun args])
([module fun args]))
(defn function-exported?
"Returns true if `module` exports `function` with given `arity`.
(Kernel/function-exported? Enum :map 2) ;=> true"
[module function arity])
(defn struct
"Creates a struct from a module.
(Kernel/struct MyStruct {:field \"value\"})"
([module])
([module fields]))
(defn struct!
"Like struct/2 but raises on invalid keys."
([module])
([module fields]))
(defn raise
"Raises a RuntimeError or specific exception.
(Kernel/raise \"something went wrong\")
(Kernel/raise ArgumentError :message \"bad arg\")"
([message])
([exception attrs]))
(defn reraise
"Re-raises an exception preserving the original stacktrace."
([message stacktrace])
([exception attrs stacktrace]))
(defn throw
"Throws a value to be caught with try/catch.
(Kernel/throw :some-value)"
[value])
(defn tap
"Pipes `value` into `fun` for side effects, returns `value`.
(Kernel/tap {:a 1} (fn [x] (IO/inspect x))) ;=> {:a 1}"
[value fun])
(defn then
"Pipes `value` into `fun`, returns the result of `fun`.
(Kernel/then 5 (fn [x] (* x 2))) ;=> 10"
[value fun])
(defn dbg
"Debug macro. Prints the code and its result. Returns the result.
(Kernel/dbg (+ 1 2)) ;=> prints '(+ 1 2) #=> 3', returns 3"
([code])
([code opts]))
+165
View File
@@ -0,0 +1,165 @@
(ns Keyword
"Elixir Keyword module — operations on keyword lists (list of {atom, value} tuples).
In CljElixir: (Keyword/get kw :key), (Keyword/put kw :key val), etc.
Keyword lists allow duplicate keys and preserve insertion order.")
(defn get
"Gets the value for `key`. Returns `default` if not found.
(Keyword/get [[:a 1] [:b 2]] :a) ;=> 1"
([keywords key])
([keywords key default]))
(defn get-lazy
"Gets value for `key`, calling `fun` for default if missing."
[keywords key fun])
(defn fetch
"Returns {:ok value} or :error.
(Keyword/fetch [[:a 1]] :a) ;=> {:ok 1}"
[keywords key])
(defn fetch!
"Gets value for `key`. Raises if missing."
[keywords key])
(defn get-values
"Gets all values for `key` (keyword lists allow duplicates).
(Keyword/get-values [[:a 1] [:a 2] [:b 3]] :a) ;=> [1 2]"
[keywords key])
(defn has-key?
"Returns true if `key` exists.
(Keyword/has-key? [[:a 1]] :a) ;=> true"
[keywords key])
(defn keys
"Returns all keys.
(Keyword/keys [[:a 1] [:b 2]]) ;=> [:a :b]"
[keywords])
(defn values
"Returns all values.
(Keyword/values [[:a 1] [:b 2]]) ;=> [1 2]"
[keywords])
(defn put
"Puts `value` under `key`, replacing any existing.
(Keyword/put [[:a 1]] :b 2) ;=> [[:a 1] [:b 2]]"
[keywords key value])
(defn put-new
"Puts `value` under `key` only if `key` doesn't exist."
[keywords key value])
(defn put-new-lazy
"Like put-new but calls `fun` only if key is absent."
[keywords key fun])
(defn delete
"Deletes all entries for `key`.
(Keyword/delete [[:a 1] [:b 2] [:a 3]] :a) ;=> [[:b 2]]"
[keywords key])
(defn delete-first
"Deletes only the first entry for `key`."
[keywords key])
(defn pop
"Returns {value, rest} for `key`.
(Keyword/pop [[:a 1] [:b 2]] :a) ;=> {1 [[:b 2]]}"
([keywords key])
([keywords key default]))
(defn pop-first
"Pops only the first entry for `key`."
([keywords key])
([keywords key default]))
(defn pop-lazy
"Like pop but calls `fun` for default."
[keywords key fun])
(defn pop-values
"Pops all values for `key`. Returns {values, rest}."
[keywords key])
(defn update
"Updates `key` by applying `fun` to current value.
(Keyword/update [[:a 1]] :a (fn [v] (+ v 1))) ;=> [[:a 2]]"
([keywords key fun])
([keywords key default fun]))
(defn update!
"Updates `key`. Raises if missing."
[keywords key fun])
(defn replace
"Replaces value at `key` only if it exists."
[keywords key value])
(defn replace!
"Replaces value at `key`. Raises if missing."
[keywords key value])
(defn merge
"Merges two keyword lists.
(Keyword/merge [[:a 1]] [[:b 2]]) ;=> [[:a 1] [:b 2]]
(Keyword/merge kw1 kw2 (fn [k v1 v2] v2)) ;=> with resolver"
([keywords1 keywords2])
([keywords1 keywords2 fun]))
(defn split
"Splits keyword list into two based on `keys`.
(Keyword/split [[:a 1] [:b 2] [:c 3]] [:a :c]) ;=> {[[:a 1] [:c 3]] [[:b 2]]}"
[keywords keys])
(defn take
"Takes only the given `keys`.
(Keyword/take [[:a 1] [:b 2] [:c 3]] [:a :c]) ;=> [[:a 1] [:c 3]]"
[keywords keys])
(defn drop
"Drops the given `keys`."
[keywords keys])
(defn filter
"Filters entries where `fun` returns truthy."
[keywords fun])
(defn reject
"Rejects entries where `fun` returns truthy."
[keywords fun])
(defn map
"Maps over entries. `fun` receives {key, value}."
[keywords fun])
(defn new
"Creates a new keyword list.
(Keyword/new) ;=> []
(Keyword/new [[:a 1] [:b 2]]) ;=> [[:a 1] [:b 2]]"
([])
([pairs])
([pairs transform]))
(defn keyword?
"Returns true if `term` is a keyword list."
[term])
(defn equal?
"Returns true if two keyword lists are equal."
[keywords1 keywords2])
(defn to-list
"Converts keyword list to a list of tuples (identity for keyword lists)."
[keywords])
(defn validate
"Validates keyword list against a set of allowed keys.
(Keyword/validate [[:a 1] [:b 2]] [:a :b :c]) ;=> {:ok [[:a 1] [:b 2]]}"
[keywords values])
(defn validate!
"Validates. Raises on invalid keys."
[keywords values])
+171
View File
@@ -0,0 +1,171 @@
(ns List
"Elixir List module — operations on linked lists.
In CljElixir: (List/flatten nested), (List/to-tuple lst), etc.
Lists are the fundamental sequence type on the BEAM (singly-linked).")
(defn first
"Returns the first element, or `default` if empty.
(List/first [1 2 3]) ;=> 1
(List/first [] :none) ;=> :none"
([list])
([list default]))
(defn last
"Returns the last element, or `default` if empty.
(List/last [1 2 3]) ;=> 3
(List/last [] :none) ;=> :none"
([list])
([list default]))
(defn flatten
"Flattens nested lists.
(List/flatten [[1 [2]] [3 4]]) ;=> [1 2 3 4]
(List/flatten [1 [2 [3]]] 1) ;=> [1 2 [3]] (one level only)"
([list])
([list tail]))
(defn foldl
"Left fold. Invokes `fun` for each element with accumulator.
(List/foldl [1 2 3] 0 (fn [elem acc] (+ acc elem))) ;=> 6"
[list acc fun])
(defn foldr
"Right fold.
(List/foldr [1 2 3] 0 (fn [elem acc] (+ acc elem))) ;=> 6"
[list acc fun])
(defn wrap
"Wraps a non-list value in a list. nil becomes []. Lists pass through.
(List/wrap 1) ;=> [1]
(List/wrap nil) ;=> []
(List/wrap [1 2]) ;=> [1 2]"
[term])
(defn duplicate
"Creates a list with `elem` repeated `n` times.
(List/duplicate :ok 3) ;=> [:ok :ok :ok]"
[elem n])
(defn zip
"Zips corresponding elements from multiple lists into tuples.
(List/zip [[1 2 3] [:a :b :c]]) ;=> [{1 :a} {2 :b} {3 :c}]"
[list-of-lists])
(defn insert-at
"Inserts `value` at `index`.
(List/insert-at [1 2 3] 1 :a) ;=> [1 :a 2 3]"
[list index value])
(defn replace-at
"Replaces element at `index` with `value`.
(List/replace-at [1 2 3] 1 :a) ;=> [1 :a 3]"
[list index value])
(defn update-at
"Updates element at `index` by applying `fun`.
(List/update-at [1 2 3] 1 (fn [x] (* x 10))) ;=> [1 20 3]"
[list index fun])
(defn delete-at
"Deletes element at `index`.
(List/delete-at [1 2 3] 1) ;=> [1 3]"
[list index])
(defn delete
"Deletes the first occurrence of `element`.
(List/delete [1 2 1 3] 1) ;=> [2 1 3]"
[list element])
(defn pop-at
"Returns the element at `index` and the list without it.
(List/pop-at [1 2 3] 1) ;=> {2 [1 3]}"
([list index])
([list index default]))
(defn starts-with?
"Returns true if `list` starts with `prefix`.
(List/starts-with? [1 2 3] [1 2]) ;=> true"
[list prefix])
(defn myers-difference
"Returns the edit steps to transform `list1` into `list2`.
(List/myers-difference [1 2 3] [1 3 4]) ;=> [eq: [1], del: [2], eq: [3], ins: [4]]"
([list1 list2])
([list1 list2 diff-script]))
(defn to-tuple
"Converts a list to a tuple.
(List/to-tuple [1 2 3]) ;=> #el[1 2 3]"
[list])
(defn to-string
"Converts a charlist to a string.
(List/to-string [104 101 108 108 111]) ;=> \"hello\""
[charlist])
(defn to-charlist
"Converts a list of codepoints to a charlist."
[list])
(defn to-integer
"Converts a charlist to integer.
(List/to-integer '123') ;=> 123"
([charlist])
([charlist base]))
(defn to-float
"Converts a charlist to float."
[charlist])
(defn to-atom
"Converts a charlist to atom."
[charlist])
(defn to-existing-atom
"Converts a charlist to an existing atom."
[charlist])
(defn ascii-printable?
"Returns true if all chars are ASCII printable.
(List/ascii-printable? 'hello') ;=> true"
([list])
([list limit]))
(defn improper?
"Returns true if the list is improper (doesn't end in []).
(List/improper? [1 | 2]) ;=> true"
[list])
(defn keyfind
"Finds a tuple in a list of tuples where element at `position` matches `key`.
(List/keyfind [{:a 1} {:b 2}] :b 0) ;=> {:b 2}
(List/keyfind [{:a 1}] :c 0 :default) ;=> :default"
([list key position])
([list key position default]))
(defn keystore
"Replaces or inserts tuple in list based on key at position."
[list position key new-tuple])
(defn keydelete
"Deletes tuple from list where element at position matches key."
[list key position])
(defn keymember?
"Returns true if any tuple has `key` at `position`.
(List/keymember? [{:a 1} {:b 2}] :a 0) ;=> true"
[list key position])
(defn keyreplace
"Replaces the first tuple with matching key at position."
[list key position new-tuple])
(defn keysort
"Sorts list of tuples by element at `position`.
(List/keysort [{:b 2} {:a 1}] 0) ;=> [{:a 1} {:b 2}]"
[list position])
(defn keytake
"Takes all tuples from list where element at position matches key."
[list key position])
+127
View File
@@ -0,0 +1,127 @@
(ns Logger
"Elixir Logger module — structured logging.
In CljElixir: (Logger/info \"message\"), (Logger/debug (fn [] \"lazy\")), etc.
Log levels: :emergency :alert :critical :error :warning :notice :info :debug")
(defn debug
"Logs a debug message. Accepts string or zero-arity fn (lazy evaluation).
(Logger/debug \"detailed info\")
(Logger/debug (fn [] (str \"user=\" user-id)))"
([message-or-fun])
([message-or-fun metadata]))
(defn info
"Logs an info message.
(Logger/info \"server started on port 4000\")"
([message-or-fun])
([message-or-fun metadata]))
(defn notice
"Logs a notice message.
(Logger/notice \"configuration changed\")"
([message-or-fun])
([message-or-fun metadata]))
(defn warning
"Logs a warning message.
(Logger/warning \"disk usage above 80%\")"
([message-or-fun])
([message-or-fun metadata]))
(defn error
"Logs an error message.
(Logger/error \"failed to connect to database\")"
([message-or-fun])
([message-or-fun metadata]))
(defn critical
"Logs a critical message.
(Logger/critical \"system overload detected\")"
([message-or-fun])
([message-or-fun metadata]))
(defn alert
"Logs an alert message.
(Logger/alert \"database corruption detected\")"
([message-or-fun])
([message-or-fun metadata]))
(defn emergency
"Logs an emergency message.
(Logger/emergency \"system is going down\")"
([message-or-fun])
([message-or-fun metadata]))
(defn log
"Logs at the specified level.
(Logger/log :info \"dynamic level logging\")"
([level message-or-fun])
([level message-or-fun metadata]))
(defn configure
"Configures the logger at runtime.
(Logger/configure :level :debug)"
[options])
(defn level
"Returns the current log level.
(Logger/level) ;=> :info"
[])
(defn put-module-level
"Sets log level for specific modules.
(Logger/put-module-level MyModule :debug)"
[module level])
(defn delete-module-level
"Resets module-level logging to global.
(Logger/delete-module-level MyModule)"
[module])
(defn put-process-level
"Sets log level for the current process.
(Logger/put-process-level (Process/self) :debug)"
[pid level])
(defn delete-process-level
"Resets process-level logging.
(Logger/delete-process-level pid)"
[pid])
(defn enable
"Enables logging for the current process."
[pid])
(defn disable
"Disables logging for the current process."
[pid])
(defn metadata
"Gets or sets logger metadata for the current process.
(Logger/metadata) ;=> []
(Logger/metadata :request-id \"abc123\")"
([])
([keyword-list]))
(defn reset-metadata
"Resets all logger metadata for the current process.
(Logger/reset-metadata) ;=> :ok"
([])
([keys]))
(defn flush
"Flushes the logger, ensuring all messages are processed.
(Logger/flush) ;=> :ok"
[])
(defn add-backend
"Adds a logging backend.
(Logger/add-backend :console)"
([backend])
([backend opts]))
(defn remove-backend
"Removes a logging backend."
([backend])
([backend opts]))
+176
View File
@@ -0,0 +1,176 @@
(ns Map
"Elixir Map module — operations on maps.
In CljElixir: (Map/get m :key), (Map/put m :key val), etc.
Maps are the fundamental key-value data structure on the BEAM.")
;; --- Access ---
(defn get
"Gets the value for `key` in `map`. Returns `default` if key is missing.
(Map/get {:a 1 :b 2} :a) ;=> 1
(Map/get {:a 1} :c :not-found) ;=> :not-found"
([map key])
([map key default]))
(defn get-lazy
"Gets `key` from `map`, calling `fun` for default if missing.
(Map/get-lazy m :key (fn [] (expensive-computation)))"
[map key fun])
(defn fetch
"Returns {:ok value} if `key` exists, :error otherwise.
(Map/fetch {:a 1} :a) ;=> {:ok 1}
(Map/fetch {:a 1} :b) ;=> :error"
[map key])
(defn fetch!
"Gets the value for `key`. Raises if key is missing.
(Map/fetch! {:a 1} :a) ;=> 1"
[map key])
(defn has-key?
"Returns true if `map` contains `key`.
(Map/has-key? {:a 1 :b 2} :a) ;=> true"
[map key])
(defn keys
"Returns all keys in the map.
(Map/keys {:a 1 :b 2}) ;=> [:a :b]"
[map])
(defn values
"Returns all values in the map.
(Map/values {:a 1 :b 2}) ;=> [1 2]"
[map])
;; --- Modification ---
(defn put
"Puts the given `value` under `key` in `map`.
(Map/put {:a 1} :b 2) ;=> {:a 1 :b 2}"
[map key value])
(defn put-new
"Puts `value` under `key` only if `key` doesn't exist yet.
(Map/put-new {:a 1} :a 99) ;=> {:a 1}
(Map/put-new {:a 1} :b 2) ;=> {:a 1 :b 2}"
[map key value])
(defn put-new-lazy
"Like put-new but calls `fun` only if key is absent.
(Map/put-new-lazy m :key (fn [] (expensive-computation)))"
[map key fun])
(defn delete
"Deletes `key` from `map`. No-op if key doesn't exist.
(Map/delete {:a 1 :b 2} :a) ;=> {:b 2}"
[map key])
(defn drop
"Drops the given `keys` from `map`.
(Map/drop {:a 1 :b 2 :c 3} [:a :c]) ;=> {:b 2}"
[map keys])
(defn take
"Takes only the given `keys` from `map`.
(Map/take {:a 1 :b 2 :c 3} [:a :c]) ;=> {:a 1 :c 3}"
[map keys])
(defn pop
"Returns the value for `key` and the map without `key`.
(Map/pop {:a 1 :b 2} :a) ;=> {1 {:b 2}}
(Map/pop {:a 1} :c :default) ;=> {:default {:a 1}}"
([map key])
([map key default]))
(defn pop-lazy
"Like pop but calls `fun` for default if key is absent."
[map key fun])
(defn update
"Updates the value at `key` by applying `fun` to the current value.
(Map/update {:a 1} :a (fn [v] (+ v 1))) ;=> {:a 2}"
([map key fun])
([map key default fun]))
(defn update!
"Updates `key` by applying `fun`. Raises if `key` doesn't exist.
(Map/update! {:a 1} :a (fn [v] (* v 2))) ;=> {:a 2}"
[map key fun])
(defn replace
"Replaces value at `key` only if it already exists. No-op otherwise.
(Map/replace {:a 1} :a 99) ;=> {:a 99}
(Map/replace {:a 1} :b 99) ;=> {:a 1}"
[map key value])
(defn replace!
"Replaces value at `key`. Raises if `key` doesn't exist."
[map key value])
;; --- Merging ---
(defn merge
"Merges two maps. Values from `map2` take precedence.
(Map/merge {:a 1 :b 2} {:b 3 :c 4}) ;=> {:a 1 :b 3 :c 4}
With resolver: (Map/merge m1 m2 (fn [k v1 v2] (+ v1 v2)))"
([map1 map2])
([map1 map2 resolver]))
(defn split
"Splits `map` into two maps based on the given `keys`.
(Map/split {:a 1 :b 2 :c 3} [:a :c]) ;=> {%{a: 1, c: 3} %{b: 2}}"
[map keys])
;; --- Conversion ---
(defn new
"Creates a new empty map or from an enumerable.
(Map/new) ;=> {}
(Map/new [[:a 1] [:b 2]]) ;=> {:a 1 :b 2}
(Map/new [1 2 3] (fn [x] {x (* x x)})) ;=> {1 1, 2 4, 3 9}"
([])
([enumerable])
([enumerable transform]))
(defn from-struct
"Converts a struct to a plain map (removes __struct__ key).
(Map/from-struct my-struct) ;=> {...}"
[struct])
(defn to-list
"Converts a map to a keyword list (list of {key, value} tuples).
(Map/to-list {:a 1 :b 2}) ;=> [[:a 1] [:b 2]]"
[map])
(defn equal?
"Returns true if two maps are equal.
(Map/equal? {:a 1} {:a 1}) ;=> true"
[map1 map2])
;; --- Filtering ---
(defn filter
"Filters map entries where `f` returns truthy. `f` receives {key, value}.
(Map/filter {:a 1 :b 2 :c 3} (fn [{k v}] (> v 1))) ;=> {:b 2 :c 3}"
[map f])
(defn reject
"Rejects map entries where `f` returns truthy.
(Map/reject {:a 1 :b 2 :c 3} (fn [{k v}] (> v 1))) ;=> {:a 1}"
[map f])
(defn map
"Maps over entries, `f` receives {key, value} and must return {key, value}.
(Map/map {:a 1 :b 2} (fn [{k v}] {k (* v 10)})) ;=> {:a 10 :b 20}"
[map f])
(defn intersect
"Returns entries common to both maps (using `map2` values)."
([map1 map2])
([map1 map2 resolver]))
(defn diff
"Returns {entries_only_in_map1, entries_only_in_map2, entries_in_both}."
[map1 map2])
+87
View File
@@ -0,0 +1,87 @@
(ns MapSet
"Elixir MapSet module — set operations backed by maps.
In CljElixir: (MapSet/new [1 2 3]), (MapSet/member? s 2), etc.
Use #{1 2 3} literal syntax for set creation in CljElixir.")
(defn new
"Creates a new set, optionally from an enumerable.
(MapSet/new) ;=> #{}
(MapSet/new [1 2 3]) ;=> #{1 2 3}
(MapSet/new [1 2 3] (fn [x] (* x 2))) ;=> #{2 4 6}"
([])
([enumerable])
([enumerable transform]))
(defn put
"Inserts `value` into the set.
(MapSet/put #{1 2} 3) ;=> #{1 2 3}"
[set value])
(defn delete
"Deletes `value` from the set.
(MapSet/delete #{1 2 3} 2) ;=> #{1 3}"
[set value])
(defn member?
"Returns true if `value` is in `set`.
(MapSet/member? #{1 2 3} 2) ;=> true"
[set value])
(defn size
"Returns the number of elements.
(MapSet/size #{1 2 3}) ;=> 3"
[set])
(defn to-list
"Converts the set to a list.
(MapSet/to-list #{1 2 3}) ;=> [1 2 3]"
[set])
(defn equal?
"Returns true if two sets are equal.
(MapSet/equal? #{1 2} #{2 1}) ;=> true"
[set1 set2])
(defn union
"Returns the union of two sets.
(MapSet/union #{1 2} #{2 3}) ;=> #{1 2 3}"
[set1 set2])
(defn intersection
"Returns the intersection of two sets.
(MapSet/intersection #{1 2 3} #{2 3 4}) ;=> #{2 3}"
[set1 set2])
(defn difference
"Returns elements in `set1` not in `set2`.
(MapSet/difference #{1 2 3} #{2 3}) ;=> #{1}"
[set1 set2])
(defn symmetric-difference
"Returns elements in either set but not both.
(MapSet/symmetric-difference #{1 2 3} #{2 3 4}) ;=> #{1 4}"
[set1 set2])
(defn subset?
"Returns true if `set1` is a subset of `set2`.
(MapSet/subset? #{1 2} #{1 2 3}) ;=> true"
[set1 set2])
(defn disjoint?
"Returns true if `set1` and `set2` have no elements in common.
(MapSet/disjoint? #{1 2} #{3 4}) ;=> true"
[set1 set2])
(defn filter
"Returns elements for which `fun` returns truthy.
(MapSet/filter #{1 2 3 4} (fn [x] (> x 2))) ;=> #{3 4}"
[set fun])
(defn reject
"Returns elements for which `fun` returns falsy."
[set fun])
(defn map
"Maps `fun` over the set, returning a new set."
[set fun])
+76
View File
@@ -0,0 +1,76 @@
(ns Module
"Elixir Module module — module introspection and manipulation.
In CljElixir: (Module/defines? MyModule :my-func 2), etc.")
(defn concat
"Concatenates atoms/strings into a module name.
(Module/concat [\"Elixir\" \"MyApp\" \"Router\"]) ;=> MyApp.Router
(Module/concat Elixir.MyApp :Router) ;=> MyApp.Router"
([list])
([left right]))
(defn split
"Splits a module name into parts.
(Module/split MyApp.Router) ;=> [\"MyApp\" \"Router\"]"
[module])
(defn defines?
"Returns true if `module` defines `function` with given `arity`.
(Module/defines? Enum :map 2) ;=> true
(Module/defines? Enum :map) ;=> true"
([module function-name])
([module function-name arity]))
(defn definitions-in
"Returns all functions/macros defined in `module`.
(Module/definitions-in Enum) ;=> [{:map 2} {:filter 2} ...]"
([module])
([module kind]))
(defn has-attribute?
"Returns true if `module` has `attribute`.
(Module/has-attribute? MyModule :behaviour) ;=> true"
[module attribute])
(defn get-attribute
"Gets a module attribute.
(Module/get-attribute MyModule :moduledoc)"
([module attribute])
([module attribute default]))
(defn put-attribute
"Sets a module attribute during compilation."
[module attribute value])
(defn delete-attribute
"Deletes a module attribute during compilation."
[module attribute])
(defn register-attribute
"Registers a module attribute during compilation."
[module attribute opts])
(defn spec-to-callback
"Converts a spec to a callback."
[module spec])
(defn open?
"Returns true if the module is open (being compiled)."
[module])
(defn overridable?
"Returns true if function is overridable in module."
[module function-arity])
(defn make-overridable
"Makes functions overridable."
[module function-arities])
(defn safe-concat
"Safely concatenates module atoms without creating new atoms."
[list])
(defn create
"Creates a module at runtime."
[module quoted opts])
+126
View File
@@ -0,0 +1,126 @@
(ns NaiveDateTime
"Elixir NaiveDateTime module — datetime without timezone.
In CljElixir: (NaiveDateTime/utc-now), (NaiveDateTime/new 2024 3 9 12 0 0), etc.
'Naive' because it has no timezone information.")
(defn utc-now
"Returns the current UTC naive datetime.
(NaiveDateTime/utc-now) ;=> ~N[2024-03-09 12:00:00]"
([])
([calendar]))
(defn local-now
"Returns the current local naive datetime."
([])
([calendar]))
(defn new
"Creates a new NaiveDateTime.
(NaiveDateTime/new 2024 3 9 12 0 0) ;=> {:ok ~N[2024-03-09 12:00:00]}"
([year month day hour minute second])
([year month day hour minute second microsecond])
([date time]))
(defn new!
"Creates a new NaiveDateTime. Raises on error."
([year month day hour minute second])
([year month day hour minute second microsecond])
([date time]))
(defn from-iso8601
"Parses ISO 8601 string.
(NaiveDateTime/from-iso8601 \"2024-03-09T12:00:00\") ;=> {:ok naive}"
([string])
([string calendar]))
(defn from-iso8601!
"Parses ISO 8601. Raises on error."
([string])
([string calendar]))
(defn to-iso8601
"Converts to ISO 8601 string.
(NaiveDateTime/to-iso8601 ndt) ;=> \"2024-03-09T12:00:00\""
([naive-datetime])
([naive-datetime format]))
(defn to-string
"Converts to string."
[naive-datetime])
(defn to-date
"Extracts the Date part."
[naive-datetime])
(defn to-time
"Extracts the Time part."
[naive-datetime])
(defn add
"Adds time.
(NaiveDateTime/add ndt 3600) ;=> +1 hour"
([naive-datetime amount])
([naive-datetime amount unit]))
(defn diff
"Returns difference.
(NaiveDateTime/diff ndt1 ndt2) ;=> seconds"
([naive-datetime1 naive-datetime2])
([naive-datetime1 naive-datetime2 unit]))
(defn truncate
"Truncates to precision."
[naive-datetime precision])
(defn compare
"Compares two NaiveDateTimes. Returns :lt, :eq, or :gt."
[naive-datetime1 naive-datetime2])
(defn before?
"Returns true if first is before second."
[naive-datetime1 naive-datetime2])
(defn after?
"Returns true if first is after second."
[naive-datetime1 naive-datetime2])
(defn shift
"Shifts by a duration."
[naive-datetime duration])
(defn beginning-of-day
"Returns midnight of the same day."
[naive-datetime])
(defn end-of-day
"Returns 23:59:59.999999 of the same day."
[naive-datetime])
(defn from-erl
"Converts from Erlang datetime tuple.
(NaiveDateTime/from-erl {{2024 3 9} {12 0 0}}) ;=> {:ok naive}"
([tuple])
([tuple microsecond])
([tuple microsecond calendar]))
(defn from-erl!
"Converts from Erlang tuple. Raises on error."
([tuple])
([tuple microsecond])
([tuple microsecond calendar]))
(defn to-erl
"Converts to Erlang datetime tuple.
(NaiveDateTime/to-erl ndt) ;=> {{2024 3 9} {12 0 0}}"
[naive-datetime])
(defn from-gregorian-seconds
"Creates from seconds since year 0."
([seconds])
([seconds microsecond])
([seconds microsecond calendar]))
(defn to-gregorian-seconds
"Returns seconds since year 0."
[naive-datetime])
+88
View File
@@ -0,0 +1,88 @@
(ns Node
"Elixir Node module — distributed Erlang node operations.
In CljElixir: (Node/self), (Node/connect :other@host), etc.
For building distributed BEAM applications.")
(defn self
"Returns the current node name.
(Node/self) ;=> :mynode@myhost"
[])
(defn alive?
"Returns true if the local node is alive (part of a distributed system).
(Node/alive?) ;=> true"
[])
(defn list
"Returns a list of all connected nodes.
(Node/list) ;=> [:node1@host :node2@host]
(Node/list :known) ;=> includes known but disconnected nodes"
([])
([type]))
(defn connect
"Connects to a remote node.
(Node/connect :other@hostname) ;=> true"
[node])
(defn disconnect
"Disconnects from a remote node.
(Node/disconnect :other@hostname) ;=> true"
[node])
(defn monitor
"Monitors a remote node. Sends {:nodedown node} on disconnect.
(Node/monitor :other@hostname true)"
([node flag])
([node flag opts]))
(defn ping
"Pings a remote node.
(Node/ping :other@hostname) ;=> :pong or :pang"
[node])
(defn spawn
"Spawns a process on a remote node.
(Node/spawn :other@hostname (fn [] (IO/puts \"remote!\")))"
([node fun])
([node fun opts])
([node module fun args])
([node module fun args opts]))
(defn spawn-link
"Spawns a linked process on a remote node."
([node fun])
([node fun opts])
([node module fun args])
([node module fun args opts]))
(defn spawn-monitor
"Spawns a monitored process on a remote node."
([node fun])
([node fun opts])
([node module fun args])
([node module fun args opts]))
(defn get-cookie
"Returns the magic cookie for the node.
(Node/get-cookie) ;=> :nocookie or :somecookie"
[])
(defn set-cookie
"Sets the magic cookie.
(Node/set-cookie :my-secret-cookie)"
([cookie])
([node cookie]))
(defn start
"Starts distribution. Returns {:ok pid} or {:error reason}.
(Node/start :mynode :shortnames)"
([name])
([name type])
([name type tick-time]))
(defn stop
"Stops distribution.
(Node/stop) ;=> :ok"
[])
+90
View File
@@ -0,0 +1,90 @@
(ns Path
"Elixir Path module — file path manipulation.
In CljElixir: (Path/join \"a\" \"b\"), (Path/expand \"~/file\"), etc.
All functions work with forward slashes on all platforms.")
(defn join
"Joins path segments.
(Path/join \"a\" \"b\") ;=> \"a/b\"
(Path/join [\"a\" \"b\" \"c\"]) ;=> \"a/b/c\""
([paths])
([left right]))
(defn expand
"Expands a path to an absolute path, resolving ~ and relative components.
(Path/expand \"~/file.txt\") ;=> \"/Users/ajet/file.txt\"
(Path/expand \"../other\" \"/base\") ;=> \"/other\""
([path])
([path relative-to]))
(defn absname
"Converts to absolute path.
(Path/absname \"file.txt\") ;=> \"/Users/ajet/repos/clje/file.txt\""
([path])
([path relative-to]))
(defn relative-to
"Returns the relative path from `path` to `from`.
(Path/relative-to \"/a/b/c\" \"/a\") ;=> \"b/c\""
([path from])
([path from opts]))
(defn relative
"Forces path to be relative.
(Path/relative \"/a/b\") ;=> \"a/b\""
[path])
(defn basename
"Returns the last component of the path.
(Path/basename \"/a/b/c.txt\") ;=> \"c.txt\"
(Path/basename \"/a/b/c.txt\" \".txt\") ;=> \"c\""
([path])
([path extension]))
(defn dirname
"Returns the directory component.
(Path/dirname \"/a/b/c.txt\") ;=> \"/a/b\""
[path])
(defn extname
"Returns the file extension.
(Path/extname \"file.ex\") ;=> \".ex\"
(Path/extname \"file\") ;=> \"\""
[path])
(defn rootname
"Returns the path without extension.
(Path/rootname \"file.ex\") ;=> \"file\"
(Path/rootname \"file.tar.gz\" \".tar.gz\") ;=> \"file\""
([path])
([path extension]))
(defn split
"Splits a path into its components.
(Path/split \"/a/b/c\") ;=> [\"/\" \"a\" \"b\" \"c\"]"
[path])
(defn type
"Returns the path type: :absolute, :relative, or :volumerelative.
(Path/type \"/a/b\") ;=> :absolute
(Path/type \"a/b\") ;=> :relative"
[path])
(defn wildcard
"Expands a glob pattern. Returns matching paths.
(Path/wildcard \"lib/**/*.ex\") ;=> [\"lib/my_app.ex\" ...]
(Path/wildcard \"*.{ex,exs}\") ;=> matches .ex and .exs files"
([glob])
([glob opts]))
(defn safe-relative
"Returns a safe relative path (no ..). Returns {:ok path} or :error.
(Path/safe-relative \"a/b/c\") ;=> {:ok \"a/b/c\"}
(Path/safe-relative \"../etc/passwd\") ;=> :error"
([path])
([path cwd]))
(defn safe-relative-to
"Returns a safe relative path from `path` to `cwd`. Returns {:ok path} or :error."
[path cwd])
+49
View File
@@ -0,0 +1,49 @@
(ns Port
"Elixir Port module — external program interaction.
In CljElixir: (Port/open {:spawn \"cmd\"} opts), etc.
Ports allow communication with external OS processes via stdin/stdout.")
(defn open
"Opens a port to an external program. Returns a port.
(Port/open {:spawn \"cat\"} [:binary])
(Port/open {:spawn-executable \"/usr/bin/python3\"} [:binary {:args [\"-c\" \"print(1)\"]}])
(Port/open {:fd 0 1} [:binary]) ;=> stdin/stdout"
[name settings])
(defn command
"Sends data to a port.
(Port/command port data)"
([port data])
([port data opts]))
(defn close
"Closes a port.
(Port/close port) ;=> true"
[port])
(defn connect
"Changes the owner of a port.
(Port/connect port new-owner-pid)"
[port pid])
(defn info
"Returns information about a port.
(Port/info port) ;=> [{:name ...} {:links [...]} ...]
(Port/info port :name) ;=> port name"
([port])
([port item]))
(defn list
"Returns all open ports.
(Port/list) ;=> [#Port<0.5> ...]"
[])
(defn monitor
"Monitors a port. Returns reference."
[type port])
(defn demonitor
"Stops monitoring a port."
([ref])
([ref opts]))
+166
View File
@@ -0,0 +1,166 @@
(ns Process
"Elixir Process module — BEAM process management.
In CljElixir: (Process/sleep 1000), (Process/send pid msg), etc.
Provides utilities for working with BEAM processes (lightweight actors).")
(defn sleep
"Sleeps the current process for `timeout` milliseconds or :infinity.
(Process/sleep 1000) ;=> :ok (sleeps 1 second)
(Process/sleep :infinity) ;=> blocks forever"
[timeout])
(defn send
"Sends `msg` to `dest` (pid, port, or registered name). Returns `msg`.
(Process/send pid {:hello \"world\"})
(Process/send pid msg [:noconnect]) ;=> with options"
([dest msg])
([dest msg opts]))
(defn spawn
"Spawns a new process. Returns the PID.
(Process/spawn (fn [] (IO/puts \"hi\")) [])
(Process/spawn MyModule :my-func [arg1 arg2] [:link])"
([fun opts])
([module function args opts]))
(defn spawn-link
"Spawns a linked process. If the child crashes, the parent crashes too.
(Process/spawn-link (fn [] (IO/puts \"linked\")) [])"
([fun opts])
([module function args opts]))
(defn spawn-monitor
"Spawns a monitored process. Returns {pid, ref}.
(Process/spawn-monitor (fn [] (do-work)))"
([fun])
([module function args]))
(defn alive?
"Returns true if the process identified by `pid` is alive.
(Process/alive? pid) ;=> true"
[pid])
(defn self
"Returns the PID of the calling process.
(Process/self) ;=> #PID<0.123.0>"
[])
(defn whereis
"Returns the PID registered under `name`, or nil.
(Process/whereis :my-server) ;=> #PID<0.456.0>"
[name])
(defn register
"Registers a PID under the given atom `name`.
(Process/register pid :my-server) ;=> true"
[pid name])
(defn unregister
"Unregisters the given `name`.
(Process/unregister :my-server) ;=> true"
[name])
(defn registered
"Returns a list of all registered process names.
(Process/registered) ;=> [:logger :code-server ...]"
[])
(defn link
"Creates a link between the calling process and `pid_or_port`.
(Process/link pid)"
[pid-or-port])
(defn unlink
"Removes a link between the calling process and `pid_or_port`.
(Process/unlink pid)"
[pid-or-port])
(defn monitor
"Starts monitoring `item`. Returns a reference.
(Process/monitor :process pid)"
([type item])
([type item opts]))
(defn demonitor
"Stops monitoring. The `ref` is from a previous `monitor` call.
(Process/demonitor ref)
(Process/demonitor ref [:flush])"
([ref])
([ref opts]))
(defn exit
"Sends an exit signal to `pid`.
(Process/exit pid :normal) ;=> normal shutdown
(Process/exit pid :kill) ;=> forceful kill (untrappable)"
[pid reason])
(defn flag
"Sets process flags. Returns the old value.
(Process/flag :trap-exit true) ;=> false (previous value)
(Process/flag :priority :high)"
([flag value])
([pid flag value]))
(defn info
"Returns info about a process.
(Process/info pid) ;=> keyword list of process info
(Process/info pid :message-queue-len) ;=> message queue length"
([pid])
([pid item]))
(defn list
"Returns a list of all running process PIDs.
(Process/list) ;=> [#PID<0.0.0> #PID<0.1.0> ...]"
[])
(defn group-leader
"Returns or sets the group leader.
(Process/group-leader) ;=> #PID<0.64.0>
(Process/group-leader pid gl)"
([])
([pid leader]))
(defn hibernate
"Puts the process into hibernation (frees memory). Resumes in `fun`.
(Process/hibernate Module :function [args])"
[module function args])
(defn send-after
"Sends `msg` to `dest` after `time` milliseconds. Returns a timer ref.
(Process/send-after self :tick 1000)"
([dest msg time])
([dest msg time opts]))
(defn cancel-timer
"Cancels a timer created by send-after."
([timer-ref])
([timer-ref opts]))
(defn read-timer
"Returns the time remaining for a timer, or false."
([timer-ref])
([timer-ref opts]))
(defn put
"Puts a key-value pair in the process dictionary. Returns old value or nil.
(Process/put :my-key \"my value\") ;=> nil"
[key value])
(defn get
"Gets a value from the process dictionary.
(Process/get :my-key) ;=> \"my value\"
(Process/get :missing :default) ;=> :default"
([key])
([key default]))
(defn delete
"Deletes a key from the process dictionary. Returns old value.
(Process/delete :my-key) ;=> \"my value\""
[key])
(defn get-keys
"Returns all keys in the process dictionary (or keys with given value).
(Process/get-keys) ;=> [:my-key ...]"
([])
([value]))
+37
View File
@@ -0,0 +1,37 @@
(ns Range
"Elixir Range module — integer ranges.
In CljElixir: Ranges use first..last or first..last//step syntax.
(Range/new 1 10) ;=> 1..10")
(defn new
"Creates a new range.
(Range/new 1 10) ;=> 1..10
(Range/new 1 10 2) ;=> 1..10//2 (step of 2)"
([first last])
([first last step]))
(defn size
"Returns the number of elements in the range.
(Range/size (Range/new 1 10)) ;=> 10"
[range])
(defn disjoint?
"Returns true if two ranges don't overlap.
(Range/disjoint? (Range/new 1 5) (Range/new 6 10)) ;=> true"
[range1 range2])
(defn shift
"Shifts a range by `steps`.
(Range/shift (Range/new 1 5) 2) ;=> 3..7"
[range steps])
(defn split
"Splits a range at position `split`.
(Range/split (Range/new 1 5) 3) ;=> {1..3 4..5}"
[range split])
(defn to-list
"Converts range to a list.
(Range/to-list (Range/new 1 5)) ;=> [1 2 3 4 5]"
[range])
+85
View File
@@ -0,0 +1,85 @@
(ns Regex
"Elixir Regex module — regular expression operations (wraps Erlang :re).
In CljElixir: (Regex/match? ~r/pattern/ string), etc.
Regex literals use ~r/pattern/flags syntax.")
(defn compile
"Compiles a regex pattern string. Returns {:ok regex} or {:error reason}.
(Regex/compile \"^hello\") ;=> {:ok ~r/^hello/}
(Regex/compile \"hello\" \"i\") ;=> case-insensitive"
([source])
([source opts]))
(defn compile!
"Compiles a regex. Raises on invalid pattern.
(Regex/compile! \"^hello\") ;=> ~r/^hello/"
([source])
([source opts]))
(defn match?
"Returns true if `string` matches `regex`.
(Regex/match? ~r/\\d+/ \"hello123\") ;=> true"
[regex string])
(defn run
"Runs the regex against `string`. Returns list of matches or nil.
(Regex/run ~r/(\\w+)@(\\w+)/ \"user@host\") ;=> [\"user@host\" \"user\" \"host\"]
(Regex/run ~r/\\d+/ \"hello\") ;=> nil"
([regex string])
([regex string opts]))
(defn scan
"Scans the string for all matches.
(Regex/scan ~r/\\d+/ \"a1b2c3\") ;=> [[\"1\"] [\"2\"] [\"3\"]]"
([regex string])
([regex string opts]))
(defn named-captures
"Returns a map of named captures.
(Regex/named-captures ~r/(?P<year>\\d{4})-(?P<month>\\d{2})/ \"2024-03\")
;=> %{\"year\" => \"2024\", \"month\" => \"03\"}"
([regex string])
([regex string opts]))
(defn replace
"Replaces regex matches in `string` with `replacement`.
(Regex/replace ~r/\\d+/ \"a1b2\" \"X\") ;=> \"aXbX\"
(Regex/replace ~r/(\\w+)/ \"hello\" (fn [match] (String/upcase match)))"
([regex string replacement])
([regex string replacement opts]))
(defn split
"Splits `string` by `regex`.
(Regex/split ~r/\\s+/ \"hello world\") ;=> [\"hello\" \"world\"]
(Regex/split ~r/,/ \"a,b,c\" :parts 2) ;=> [\"a\" \"b,c\"]"
([regex string])
([regex string opts]))
(defn source
"Returns the source string of a compiled regex.
(Regex/source ~r/hello/) ;=> \"hello\""
[regex])
(defn opts
"Returns the options string of a compiled regex.
(Regex/opts ~r/hello/i) ;=> \"i\""
[regex])
(defn re-pattern
"Returns the underlying compiled pattern."
[regex])
(defn names
"Returns a list of named capture group names.
(Regex/names ~r/(?P<name>\\w+)/) ;=> [\"name\"]"
[regex])
(defn escape
"Escapes a string for use in a regex.
(Regex/escape \"hello.world\") ;=> \"hello\\\\.world\""
[string])
(defn regex?
"Returns true if `term` is a compiled regex."
[term])
+86
View File
@@ -0,0 +1,86 @@
(ns Registry
"Elixir Registry module — process registry for local name lookup.
In CljElixir: (Registry/start-link :keys :unique :name :my-registry), etc.
Registries allow processes to be found by key (unique or duplicate).")
(defn start-link
"Starts a registry. Returns {:ok pid}.
(Registry/start-link :keys :unique :name MyApp.Registry)
(Registry/start-link :keys :duplicate :name MyApp.PubSub)"
[opts])
(defn register
"Registers the current process under `key`.
(Registry/register MyApp.Registry :my-key \"value\") ;=> {:ok pid}"
[registry key value])
(defn unregister
"Unregisters the current process from `key`.
(Registry/unregister MyApp.Registry :my-key)"
[registry key])
(defn lookup
"Looks up processes registered under `key`. Returns [{pid, value} ...].
(Registry/lookup MyApp.Registry :my-key) ;=> [{#PID<0.123.0> \"value\"}]"
[registry key])
(defn dispatch
"Dispatches to all registered processes for `key`.
(Registry/dispatch MyApp.PubSub :topic (fn [{pid val}] (send pid :msg)))"
([registry key fun])
([registry key fun opts]))
(defn keys
"Returns all keys registered by the current process.
(Registry/keys MyApp.Registry (Process/self)) ;=> [:key1 :key2]"
[registry pid])
(defn values
"Returns all {pid, value} pairs for `key`.
(Registry/values MyApp.Registry :my-key (Process/self))"
[registry key pid])
(defn count
"Returns the number of registered entries.
(Registry/count MyApp.Registry) ;=> 5"
[registry])
(defn count-match
"Returns count of entries matching `key` and `pattern`.
(Registry/count-match MyApp.Registry :my-key :_)"
([registry key pattern])
([registry key pattern guards]))
(defn match
"Matches entries by key and value pattern.
(Registry/match MyApp.Registry :my-key :_) ;=> all values for key"
([registry key pattern])
([registry key pattern guards]))
(defn select
"Selects entries using match specifications."
[registry spec])
(defn update-value
"Updates the value for the current process's registration.
(Registry/update-value MyApp.Registry :my-key (fn [old] (inc old)))"
[registry key callback])
(defn unregister-match
"Unregisters entries matching key and pattern."
([registry key pattern])
([registry key pattern guards]))
(defn meta
"Gets metadata for a registry key.
(Registry/meta MyApp.Registry :my-key)"
[registry key])
(defn put-meta
"Sets metadata for a registry key."
[registry key value])
(defn child-spec
"Returns a child spec for starting under a supervisor."
[opts])
+168
View File
@@ -0,0 +1,168 @@
(ns Stream
"Elixir Stream module — lazy, composable enumerables.
In CljElixir: (Stream/map coll f), (Stream/filter coll f), etc.
Streams are lazy: operations are only executed when the stream is consumed
(e.g., by Enum/to-list, Enum/take, etc.).")
(defn map
"Lazily maps `fun` over `enumerable`.
(-> [1 2 3] (Stream/map (fn [x] (* x 2))) (Enum/to-list)) ;=> [2 4 6]"
[enumerable fun])
(defn filter
"Lazily filters elements.
(-> [1 2 3 4] (Stream/filter (fn [x] (> x 2))) (Enum/to-list)) ;=> [3 4]"
[enumerable fun])
(defn reject
"Lazily rejects elements where `fun` returns truthy."
[enumerable fun])
(defn flat-map
"Lazily maps and flattens."
[enumerable fun])
(defn take
"Lazily takes `count` elements.
(-> (Stream/iterate 0 inc) (Stream/take 5) (Enum/to-list)) ;=> [0 1 2 3 4]"
[enumerable count])
(defn take-while
"Lazily takes while `fun` returns truthy."
[enumerable fun])
(defn take-every
"Lazily takes every `nth` element."
[enumerable nth])
(defn drop
"Lazily drops `count` elements."
[enumerable count])
(defn drop-while
"Lazily drops while `fun` returns truthy."
[enumerable fun])
(defn chunk-by
"Lazily chunks by result of `fun`."
[enumerable fun])
(defn chunk-every
"Lazily chunks into groups of `count`.
(-> [1 2 3 4 5] (Stream/chunk-every 2) (Enum/to-list)) ;=> [[1 2] [3 4] [5]]"
([enumerable count])
([enumerable count step])
([enumerable count step leftover]))
(defn chunk-while
"Lazily chunks with custom accumulator logic."
[enumerable acc chunk-fun after-fun])
(defn concat
"Lazily concatenates enumerables.
(-> (Stream/concat [1 2] [3 4]) Enum/to-list) ;=> [1 2 3 4]"
([enumerables])
([first rest]))
(defn dedup
"Lazily removes consecutive duplicates."
([enumerable]))
(defn dedup-by
"Lazily removes consecutive duplicates by `fun`."
[enumerable fun])
(defn each
"Lazily invokes `fun` for side effects. Elements pass through.
(-> [1 2 3] (Stream/each (fn [x] (IO/puts x))) Enum/to-list)"
[enumerable fun])
(defn scan
"Lazily emits successive reduced values.
(-> [1 2 3 4] (Stream/scan 0 +) Enum/to-list) ;=> [1 3 6 10]"
([enumerable fun])
([enumerable acc fun]))
(defn transform
"Lazily transforms with an accumulator."
[enumerable acc reducer after-fun])
(defn uniq
"Lazily removes duplicates."
[enumerable])
(defn uniq-by
"Lazily removes duplicates by `fun`."
[enumerable fun])
(defn with-index
"Lazily adds indices.
(-> [:a :b :c] Stream/with-index Enum/to-list) ;=> [{:a 0} {:b 1} {:c 2}]"
([enumerable])
([enumerable offset]))
(defn zip
"Lazily zips enumerables.
(-> (Stream/zip [1 2 3] [:a :b :c]) Enum/to-list) ;=> [{1 :a} {2 :b} {3 :c}]"
([enumerables])
([enum1 enum2]))
(defn zip-with
"Lazily zips with a merge function."
([enumerables zip-fun])
([enum1 enum2 zip-fun]))
;; --- Generators ---
(defn iterate
"Generates an infinite stream by repeatedly applying `fun`.
(-> (Stream/iterate 0 inc) (Stream/take 5) Enum/to-list) ;=> [0 1 2 3 4]"
[start-value next-fun])
(defn repeatedly
"Generates a stream by calling `fun` repeatedly.
(-> (Stream/repeatedly (fn [] (rand 10))) (Stream/take 3) Enum/to-list)"
([fun])
([count fun]))
(defn unfold
"Generates a stream with an accumulator. `fun` returns {emit, next-acc} or nil.
(-> (Stream/unfold 5 (fn [n] (if (> n 0) {n (- n 1)} nil)))
Enum/to-list) ;=> [5 4 3 2 1]"
[acc fun])
(defn resource
"Creates a stream with setup/cleanup. Useful for external resources.
(Stream/resource
(fn [] (File/open! \"file.txt\"))
(fn [file] ...)
(fn [file] (File/close file)))"
[start-fun next-fun after-fun])
(defn cycle
"Repeats an enumerable infinitely.
(-> (Stream/cycle [1 2 3]) (Stream/take 7) Enum/to-list) ;=> [1 2 3 1 2 3 1]"
[enumerable])
(defn interval
"Emits incrementing integers at `interval` milliseconds.
(-> (Stream/interval 1000) (Stream/take 3) Enum/to-list) ;=> [0 1 2] (1s apart)"
[interval])
(defn timer
"Emits a single value of 0 after `delay` milliseconds.
(-> (Stream/timer 1000) Enum/to-list) ;=> [0]"
[delay])
(defn run
"Consumes the stream for side effects. Returns :ok."
[stream])
(defn intersperse
"Lazily intersperses `separator` between elements."
[enumerable separator])
(defn map-every
"Lazily maps `fun` over every `nth` element."
[enumerable nth fun])
+242
View File
@@ -0,0 +1,242 @@
(ns String
"Elixir String module — UTF-8 string operations.
In CljElixir: (String/split s \" \"), (String/trim s), etc.
Strings in Elixir are UTF-8 encoded binaries.")
;; --- Searching ---
(defn contains?
"Returns true if `string` contains `pattern`.
(String/contains? \"hello world\" \"world\") ;=> true
(String/contains? \"hello\" [\"x\" \"e\"]) ;=> true (any match)"
[string pattern])
(defn starts-with?
"Returns true if `string` starts with `prefix`.
(String/starts-with? \"hello\" \"he\") ;=> true"
[string prefix])
(defn ends-with?
"Returns true if `string` ends with `suffix`.
(String/ends-with? \"hello\" \"lo\") ;=> true"
[string suffix])
(defn match?
"Returns true if `string` matches the regex `pattern`.
(String/match? \"hello123\" ~r/\\d+/) ;=> true"
[string pattern])
;; --- Splitting & Joining ---
(defn split
"Splits `string` by `pattern`. Without a pattern splits on whitespace.
(String/split \"a,b,c\" \",\") ;=> [\"a\" \"b\" \"c\"]
(String/split \"a,b,c\" \",\" 2) ;=> [\"a\" \"b,c\"]"
([string])
([string pattern])
([string pattern parts]))
(defn split-at
"Splits `string` at `position`.
(String/split-at \"hello\" 3) ;=> {\"hel\" \"lo\"}"
[string position])
;; --- Transformation ---
(defn replace
"Replaces occurrences of `pattern` in `string` with `replacement`.
(String/replace \"hello world\" \"world\" \"elixir\") ;=> \"hello elixir\"
(String/replace \"aabba\" ~r/a/ \"x\") ;=> \"xxbbx\""
[string pattern replacement])
(defn replace-prefix
"Replaces prefix if it matches.
(String/replace-prefix \"hello\" \"he\" \"HE\") ;=> \"HEllo\""
[string match replacement])
(defn replace-suffix
"Replaces suffix if it matches.
(String/replace-suffix \"hello\" \"lo\" \"LO\") ;=> \"helLO\""
[string match replacement])
(defn replace-leading
"Replaces all leading occurrences of `match` with `replacement`."
[string match replacement])
(defn replace-trailing
"Replaces all trailing occurrences of `match` with `replacement`."
[string match replacement])
(defn upcase
"Converts string to uppercase.
(String/upcase \"hello\") ;=> \"HELLO\""
[string])
(defn downcase
"Converts string to lowercase.
(String/downcase \"HELLO\") ;=> \"hello\""
[string])
(defn capitalize
"Capitalizes the first character, downcases the rest.
(String/capitalize \"hello world\") ;=> \"Hello world\""
[string])
(defn reverse
"Reverses the string (grapheme-aware for Unicode).
(String/reverse \"hello\") ;=> \"olleh\""
[string])
(defn duplicate
"Repeats `string` `n` times.
(String/duplicate \"ha\" 3) ;=> \"hahaha\""
[string n])
(defn pad-leading
"Pads `string` on the left to `count` characters.
(String/pad-leading \"13\" 5 \"0\") ;=> \"00013\""
([string count])
([string count padding]))
(defn pad-trailing
"Pads `string` on the right to `count` characters.
(String/pad-trailing \"hi\" 5) ;=> \"hi \""
([string count])
([string count padding]))
;; --- Trimming ---
(defn trim
"Removes leading and trailing whitespace (or specified characters).
(String/trim \" hello \") ;=> \"hello\""
([string])
([string to-trim]))
(defn trim-leading
"Removes leading whitespace.
(String/trim-leading \" hello\") ;=> \"hello\""
([string])
([string to-trim]))
(defn trim-trailing
"Removes trailing whitespace.
(String/trim-trailing \"hello \") ;=> \"hello\""
([string])
([string to-trim]))
;; --- Slicing & Access ---
(defn slice
"Returns a substring starting at `start` for `length` characters.
(String/slice \"hello\" 1 3) ;=> \"ell\"
(String/slice \"hello\" 1..3) ;=> \"ell\""
([string range])
([string start length]))
(defn at
"Returns the grapheme at `position`. Negative indices count from end.
(String/at \"hello\" 1) ;=> \"e\"
(String/at \"hello\" -1) ;=> \"o\""
[string position])
(defn first
"Returns the first grapheme.
(String/first \"hello\") ;=> \"h\""
[string])
(defn last
"Returns the last grapheme.
(String/last \"hello\") ;=> \"o\""
[string])
(defn length
"Returns the number of Unicode graphemes.
(String/length \"héllo\") ;=> 5"
[string])
(defn byte-size
"Returns the number of bytes in the string.
(String/byte-size \"héllo\") ;=> 6 (é is 2 bytes in UTF-8)"
[string])
(defn graphemes
"Returns a list of grapheme clusters.
(String/graphemes \"hello\") ;=> [\"h\" \"e\" \"l\" \"l\" \"o\"]"
[string])
(defn codepoints
"Returns a list of codepoints.
(String/codepoints \"hello\") ;=> [\"h\" \"e\" \"l\" \"l\" \"o\"]"
[string])
(defn next-grapheme
"Returns tuple {grapheme rest} or nil.
(String/next-grapheme \"abc\") ;=> {\"a\" \"bc\"}"
[string])
(defn next-codepoint
"Returns tuple {codepoint rest} or nil."
[string])
;; --- Conversion ---
(defn to-integer
"Converts string to integer.
(String/to-integer \"123\") ;=> 123
(String/to-integer \"FF\" 16) ;=> 255"
([string])
([string base]))
(defn to-float
"Converts string to float.
(String/to-float \"3.14\") ;=> 3.14"
[string])
(defn to-atom
"Converts string to an existing atom.
(String/to-atom \"hello\") ;=> :hello"
[string])
(defn to-existing-atom
"Converts string to an existing atom. Raises if atom doesn't exist.
(String/to-existing-atom \"hello\") ;=> :hello"
[string])
(defn to-charlist
"Converts string to a charlist.
(String/to-charlist \"hello\") ;=> 'hello'"
[string])
(defn myers-difference
"Returns a keyword list of edit steps to transform string1 into string2.
(String/myers-difference \"abc\" \"adc\") ;=> [[:eq \"a\"] [:del \"b\"] [:ins \"d\"] [:eq \"c\"]]"
[string1 string2])
(defn valid?
"Returns true if `string` is a valid UTF-8 string.
(String/valid? \"hello\") ;=> true"
[string])
(defn printable?
"Returns true if `string` consists only of printable characters.
(String/printable? \"hello\") ;=> true"
[string])
(defn equivalent?
"Returns true if two strings are equivalent ignoring Unicode normalization differences."
[string1 string2])
(defn bag-distance
"Returns the bag distance between two strings (simple edit distance metric)."
[string1 string2])
(defn jaro-distance
"Returns the Jaro distance between two strings (0.0 to 1.0).
(String/jaro-distance \"Dwayne\" \"Duane\") ;=> 0.822..."
[string1 string2])
(defn chunk
"Splits string into chunks by character type.
(String/chunk \"abc123def\" :valid) ;=> [\"abc123def\"]"
[string mode])
+57
View File
@@ -0,0 +1,57 @@
(ns Supervisor
"Elixir Supervisor module — OTP supervisor for fault-tolerant process trees.
In CljElixir: (Supervisor/start-link children opts), etc.
Strategies: :one-for-one, :one-for-all, :rest-for-one.")
(defn start-link
"Starts a supervisor with child specifications.
(Supervisor/start-link [{MyWorker [arg1]}] :strategy :one-for-one)
(Supervisor/start-link MyApp [init-arg])"
([children-or-module opts-or-arg])
([module arg opts]))
(defn start-child
"Dynamically starts a child under the supervisor.
(Supervisor/start-child sup child-spec)"
[supervisor child-spec])
(defn terminate-child
"Terminates a child process.
(Supervisor/terminate-child sup child-id)"
[supervisor child-id])
(defn restart-child
"Restarts a terminated child.
(Supervisor/restart-child sup child-id)"
[supervisor child-id])
(defn delete-child
"Deletes a terminated child specification.
(Supervisor/delete-child sup child-id)"
[supervisor child-id])
(defn which-children
"Returns a list of all children with their info.
(Supervisor/which-children sup) ;=> [{id, pid, type, modules} ...]"
[supervisor])
(defn count-children
"Returns a map with child counts by status.
(Supervisor/count-children sup) ;=> %{active: 2, specs: 2, supervisors: 0, workers: 2}"
[supervisor])
(defn stop
"Stops the supervisor.
(Supervisor/stop sup) ;=> :ok"
([supervisor])
([supervisor reason])
([supervisor reason timeout]))
(defn child-spec
"Builds a child specification map."
[module overrides])
(defn init
"Returns supervisor init spec. For use in module-based supervisors."
[children-and-opts])
+176
View File
@@ -0,0 +1,176 @@
(ns System
"Elixir System module — system-level information and operations.
In CljElixir: (System/argv), (System/halt 0), etc.")
(defn argv
"Returns command line arguments as a list of strings.
(System/argv) ;=> [\"--flag\" \"value\"]"
[])
(defn halt
"Halts the Erlang runtime. `status` is 0 (success) or positive integer.
(System/halt 0) ;=> exits with code 0
(System/halt 1) ;=> exits with code 1
(System/halt \"crash message\") ;=> prints message and exits"
([])
([status]))
(defn stop
"Gracefully stops the system. Runs all shutdown hooks.
(System/stop 0)"
([status]))
(defn cmd
"Runs an external command. Returns {output, exit_status}.
(System/cmd \"echo\" [\"hello\"]) ;=> {\"hello\\n\" 0}
(System/cmd \"ls\" [\"-la\"] :cd \"/tmp\") ;=> with options"
([command args])
([command args opts]))
(defn shell
"Runs a shell command. Returns {output, exit_status}.
(System/shell \"echo hello && echo world\") ;=> {\"hello\\nworld\\n\" 0}"
([command])
([command opts]))
(defn get-env
"Gets an environment variable. Returns nil if not set.
(System/get-env \"HOME\") ;=> \"/Users/ajet\"
(System/get-env \"MISSING\" \"default\") ;=> \"default\"
(System/get-env) ;=> all env vars as a map"
([])
([varname])
([varname default]))
(defn put-env
"Sets an environment variable.
(System/put-env \"MY_VAR\" \"value\") ;=> :ok
(System/put-env %{\"KEY1\" \"val1\" \"KEY2\" \"val2\"}) ;=> :ok"
([varname value])
([env-map]))
(defn delete-env
"Deletes an environment variable.
(System/delete-env \"MY_VAR\") ;=> :ok"
[varname])
(defn fetch-env
"Fetches an env var. Returns {:ok value} or :error.
(System/fetch-env \"HOME\") ;=> {:ok \"/Users/ajet\"}"
[varname])
(defn fetch-env!
"Fetches an env var. Raises if not set.
(System/fetch-env! \"HOME\") ;=> \"/Users/ajet\""
[varname])
(defn cwd
"Returns the current working directory.
(System/cwd) ;=> \"/Users/ajet/repos/clje\""
[])
(defn cwd!
"Returns the current working directory. Raises on error."
[])
(defn tmp-dir
"Returns the system temporary directory.
(System/tmp-dir) ;=> \"/tmp\""
[])
(defn tmp-dir!
"Returns the system temporary directory. Raises on error."
[])
(defn user-home
"Returns the user's home directory.
(System/user-home) ;=> \"/Users/ajet\""
[])
(defn user-home!
"Returns the user's home directory. Raises on error."
[])
(defn monotonic-time
"Returns monotonic time. Useful for measuring elapsed time.
(System/monotonic-time) ;=> nanoseconds
(System/monotonic-time :millisecond) ;=> milliseconds"
([])
([unit]))
(defn system-time
"Returns system time (wall clock, not monotonic).
(System/system-time) ;=> nanoseconds
(System/system-time :second) ;=> seconds since epoch"
([])
([unit]))
(defn os-time
"Returns OS time.
(System/os-time :second) ;=> seconds since epoch"
([])
([unit]))
(defn unique-integer
"Returns a unique integer.
(System/unique-integer) ;=> -576460752303423485
(System/unique-integer [:positive :monotonic]) ;=> 1"
([])
([modifiers]))
(defn version
"Returns the Elixir version string.
(System/version) ;=> \"1.19.5\""
[])
(defn otp-release
"Returns the OTP release number as a string.
(System/otp-release) ;=> \"28\""
[])
(defn build-info
"Returns a map with build info."
[])
(defn schedulers
"Returns the number of schedulers.
(System/schedulers) ;=> 8"
[])
(defn schedulers-online
"Returns the number of online schedulers.
(System/schedulers-online) ;=> 8"
[])
(defn pid
"Returns the PID of the OS process running the VM.
(System/pid) ;=> \"12345\""
[])
(defn at-exit
"Registers a function to run at VM exit.
(System/at-exit (fn [status] (IO/puts \"bye\")))"
[fun])
(defn stacktrace
"Returns the last exception stacktrace for the calling process."
[])
(defn no-halt
"Configures whether the system halts on script completion."
([status]))
(defn trap-signal
"Traps an OS signal. Returns {:ok fun} or {:already-registered fun}.
(System/trap-signal :sigterm (fn [] (System/stop 0)))"
[signal fun])
(defn untrap-signal
"Removes a previously registered signal trap."
[signal id])
(defn compiled-endianness
"Returns the endianness (:big or :little).
(System/compiled-endianness) ;=> :little"
[])
+94
View File
@@ -0,0 +1,94 @@
(ns Task
"Elixir Task module — convenient process abstraction for async computation.
In CljElixir: (Task/async (fn [] (expensive-work))), etc.
Tasks are processes meant to run a single action and return a result.")
(defn async
"Starts a task linked to the caller. Returns a Task struct.
(let [t (Task/async (fn [] (compute-result)))]
(Task/await t)) ;=> result"
([fun])
([module function-name args]))
(defn await
"Awaits a task reply. Raises on timeout (default 5000ms).
(Task/await task) ;=> result
(Task/await task 10000) ;=> with 10s timeout"
([task])
([task timeout]))
(defn await-many
"Awaits multiple tasks. Returns a list of results.
(Task/await-many [task1 task2 task3]) ;=> [result1 result2 result3]
(Task/await-many tasks 10000) ;=> with 10s timeout"
([tasks])
([tasks timeout]))
(defn yield
"Temporarily yields for a reply. Returns {:ok result}, {:exit reason}, or nil.
(Task/yield task 5000) ;=> {:ok result} or nil (if still running)"
([task])
([task timeout]))
(defn yield-many
"Yields on multiple tasks. Returns [{:ok result} | {:exit reason} | nil ...].
(Task/yield-many [task1 task2] 5000)"
([tasks])
([tasks opts-or-timeout]))
(defn start
"Starts a task that is not linked. Useful for fire-and-forget.
(Task/start (fn [] (send-email)))"
([fun])
([module function-name args]))
(defn start-link
"Starts a linked task. Useful under a supervisor.
(Task/start-link (fn [] (do-work)))"
([fun])
([module function-name args]))
(defn shutdown
"Shuts down a task. Returns {:ok reply}, {:exit reason}, or nil.
(Task/shutdown task) ;=> {:ok result}
(Task/shutdown task :brutal-kill) ;=> force shutdown"
([task])
([task timeout-or-brutal]))
(defn ignore
"Ignores an existing task. The task continues but its result is discarded.
(Task/ignore task)"
[task])
(defn completed
"Creates an already-completed task with the given `result`.
(Task/completed {:ok \"cached\"}) ;=> a task that immediately resolves"
[result])
(defn async-stream
"Returns a stream that runs the given function concurrently on each element.
(Task/async-stream [1 2 3] (fn [x] (expensive x)) :max-concurrency 4)"
([enumerable fun])
([enumerable fun opts])
([enumerable module function-name opts]))
(defn Supervisor.start-link
"Starts a Task.Supervisor. Use with async_nolink for fault-tolerant tasks."
([opts]))
(defn Supervisor.async
"Starts an async task under a Task.Supervisor.
(Task/Supervisor.async supervisor (fn [] (do-work)))"
([supervisor fun])
([supervisor fun opts])
([supervisor module fun args])
([supervisor module fun args opts]))
(defn Supervisor.async-nolink
"Like Supervisor.async but doesn't link. Safe for untrusted work.
(Task/Supervisor.async-nolink supervisor (fn [] (risky-work)))"
([supervisor fun])
([supervisor fun opts])
([supervisor module fun args])
([supervisor module fun args opts]))
+111
View File
@@ -0,0 +1,111 @@
(ns Time
"Elixir Time module — time-of-day operations.
In CljElixir: (Time/utc-now), (Time/new 12 30 0), etc.")
(defn utc-now
"Returns the current UTC time.
(Time/utc-now) ;=> ~T[12:30:00]"
([])
([calendar]))
(defn new
"Creates a new time.
(Time/new 12 30 0) ;=> {:ok ~T[12:30:00]}"
([hour minute second])
([hour minute second microsecond])
([hour minute second microsecond calendar]))
(defn new!
"Creates a new time. Raises on error.
(Time/new! 12 30 0) ;=> ~T[12:30:00]"
([hour minute second])
([hour minute second microsecond])
([hour minute second microsecond calendar]))
(defn from-iso8601
"Parses ISO 8601 time. Returns {:ok time} or {:error reason}.
(Time/from-iso8601 \"12:30:00\") ;=> {:ok ~T[12:30:00]}"
([string])
([string calendar]))
(defn from-iso8601!
"Parses ISO 8601 time. Raises on error."
([string])
([string calendar]))
(defn to-iso8601
"Converts to ISO 8601 string.
(Time/to-iso8601 time) ;=> \"12:30:00\""
([time])
([time format]))
(defn to-string
"Converts to string.
(Time/to-string time) ;=> \"12:30:00\""
[time])
(defn add
"Adds `amount` of time.
(Time/add time 3600) ;=> +1 hour
(Time/add time 30 :minute)"
([time amount])
([time amount unit]))
(defn diff
"Returns difference between two times.
(Time/diff t1 t2) ;=> seconds"
([time1 time2])
([time1 time2 unit]))
(defn truncate
"Truncates to given precision.
(Time/truncate time :second)"
[time precision])
(defn compare
"Compares two times. Returns :lt, :eq, or :gt."
[time1 time2])
(defn before?
"Returns true if `time1` is before `time2`."
[time1 time2])
(defn after?
"Returns true if `time1` is after `time2`."
[time1 time2])
(defn shift
"Shifts time by a duration.
(Time/shift time :hour 1 :minute -15)"
[time duration])
(defn from-erl
"Converts Erlang time tuple.
(Time/from-erl {12 30 0}) ;=> {:ok ~T[12:30:00]}"
([tuple])
([tuple microsecond])
([tuple microsecond calendar]))
(defn from-erl!
"Converts Erlang time tuple. Raises on error."
([tuple])
([tuple microsecond])
([tuple microsecond calendar]))
(defn to-erl
"Converts to Erlang time tuple.
(Time/to-erl time) ;=> {12 30 0}"
[time])
(defn from-seconds-after-midnight
"Creates time from seconds after midnight.
(Time/from-seconds-after-midnight 45000) ;=> {:ok ~T[12:30:00]}"
([seconds])
([seconds microsecond])
([seconds microsecond calendar]))
(defn to-seconds-after-midnight
"Returns seconds since midnight.
(Time/to-seconds-after-midnight time) ;=> 45000"
[time])
+39
View File
@@ -0,0 +1,39 @@
(ns Tuple
"Elixir Tuple module — operations on tuples.
In CljElixir: (Tuple/to-list tup), (Tuple/append tup elem), etc.
Tuples are fixed-size, contiguous containers on the BEAM.
Create tuples with #el[...] syntax in CljElixir.")
(defn to-list
"Converts a tuple to a list.
(Tuple/to-list #el[1 2 3]) ;=> [1 2 3]"
[tuple])
(defn append
"Appends `value` to `tuple`.
(Tuple/append #el[1 2] 3) ;=> #el[1 2 3]"
[tuple value])
(defn insert-at
"Inserts `value` at `index` in `tuple`.
(Tuple/insert-at #el[1 2 3] 1 :a) ;=> #el[1 :a 2 3]"
[tuple index value])
(defn delete-at
"Deletes element at `index` from `tuple`.
(Tuple/delete-at #el[1 2 3] 1) ;=> #el[1 3]"
[tuple index])
(defn duplicate
"Creates a tuple with `data` repeated `size` times.
(Tuple/duplicate :ok 3) ;=> #el[:ok :ok :ok]"
[data size])
(defn product
"Returns the product of all numeric elements in the tuple."
[tuple])
(defn sum
"Returns the sum of all numeric elements in the tuple."
[tuple])
+101
View File
@@ -0,0 +1,101 @@
(ns binary
"Erlang :binary module — binary data operations.
In CljElixir: (binary/split data pattern), (binary/part data pos len), etc.
Efficient binary search, split, and manipulation.")
(defn split
"Splits binary by pattern. Returns list of parts.
(binary/split \"a,b,c\" \",\") ;=> [\"a\" \"b,c\"]
(binary/split \"a,b,c\" \",\" [:global]) ;=> [\"a\" \"b\" \"c\"]"
([subject pattern])
([subject pattern opts]))
(defn part
"Extracts a part of a binary.
(binary/part \"hello\" 1 3) ;=> \"ell\"
(binary/part \"hello\" {1 3}) ;=> \"ell\""
([subject pos-len])
([subject pos len]))
(defn match
"Finds the first/all occurrences of pattern in binary.
(binary/match \"hello world\" \"world\") ;=> {6 5}
(binary/match \"abcabc\" \"a\" [:global]) ;=> [... all positions ...]"
([subject pattern])
([subject pattern opts]))
(defn matches
"Returns all matches (like match with :global).
(binary/matches \"abcabc\" \"a\") ;=> [{0 1} {3 1}]"
([subject pattern])
([subject pattern opts]))
(defn replace
"Replaces pattern in binary.
(binary/replace \"hello\" \"l\" \"L\" [:global]) ;=> \"heLLo\""
([subject pattern replacement])
([subject pattern replacement opts]))
(defn at
"Returns the byte at position.
(binary/at \"hello\" 0) ;=> 104"
[subject position])
(defn first
"Returns the first byte.
(binary/first \"hello\") ;=> 104"
[subject])
(defn last
"Returns the last byte.
(binary/last \"hello\") ;=> 111"
[subject])
(defn bin-to-list
"Converts binary to list of bytes.
(binary/bin-to-list \"hello\") ;=> [104 101 108 108 111]"
([subject])
([subject pos-len])
([subject pos len]))
(defn list-to-bin
"Converts byte list to binary.
(binary/list-to-bin [104 101 108 108 111]) ;=> \"hello\""
[byte-list])
(defn copy
"Copies a binary, optionally repeating it.
(binary/copy \"ab\" 3) ;=> \"ababab\""
([subject])
([subject n]))
(defn decode-unsigned
"Decodes an unsigned integer from binary.
(binary/decode-unsigned <<0 0 0 42>>) ;=> 42"
([subject])
([subject endianness]))
(defn encode-unsigned
"Encodes an unsigned integer to binary.
(binary/encode-unsigned 42) ;=> <<42>>"
([value])
([value endianness]))
(defn longest-common-prefix
"Returns the length of the longest common prefix of binaries.
(binary/longest-common-prefix [\"abc\" \"abd\"]) ;=> 2"
[binaries])
(defn longest-common-suffix
"Returns the length of the longest common suffix."
[binaries])
(defn compile-pattern
"Pre-compiles a search pattern for repeated use.
(let [pat (binary/compile-pattern \",\")] (binary/split data pat))"
[pattern])
(defn referenced-byte-size
"Returns the size of the referenced binary (before sub-binary optimization)."
[binary])
+123
View File
@@ -0,0 +1,123 @@
(ns calendar
"Erlang :calendar module — date and time calculations.
In CljElixir: (calendar/local-time), (calendar/universal-time), etc.
Works with Erlang date/time tuples: {Year Month Day} and {Hour Min Sec}.")
(defn local-time
"Returns the current local datetime as {{Y M D} {H M S}}.
(calendar/local-time) ;=> {{2024 3 9} {12 30 0}}"
[])
(defn universal-time
"Returns the current UTC datetime as {{Y M D} {H M S}}.
(calendar/universal-time) ;=> {{2024 3 9} {12 30 0}}"
[])
(defn local-time-to-universal-time-dst
"Converts local time to UTC, handling DST. Returns list of possible results."
[datetime])
(defn universal-time-to-local-time
"Converts UTC to local time."
[datetime])
(defn now-to-datetime
"Converts erlang:now/0 tuple to datetime tuple."
[now])
(defn now-to-local-time
"Converts erlang:now/0 to local datetime."
[now])
(defn now-to-universal-time
"Converts erlang:now/0 to UTC datetime."
[now])
(defn datetime-to-gregorian-seconds
"Converts {{Y M D} {H M S}} to Gregorian seconds (since year 0).
(calendar/datetime-to-gregorian-seconds {{2024 1 1} {0 0 0}}) ;=> 63871..."
[datetime])
(defn gregorian-seconds-to-datetime
"Converts Gregorian seconds back to {{Y M D} {H M S}}.
(calendar/gregorian-seconds-to-datetime 63871...)"
[seconds])
(defn date-to-gregorian-days
"Converts {Year Month Day} to Gregorian day count.
(calendar/date-to-gregorian-days {2024 1 1})"
([date])
([year month day]))
(defn gregorian-days-to-date
"Converts Gregorian day count to {Year Month Day}."
[days])
(defn day-of-the-week
"Returns day of week (1=Monday, 7=Sunday).
(calendar/day-of-the-week {2024 3 9}) ;=> 6"
([date])
([year month day]))
(defn is-leap-year
"Returns true if year is a leap year.
(calendar/is-leap-year 2024) ;=> true"
[year])
(defn last-day-of-the-month
"Returns the last day of the month.
(calendar/last-day-of-the-month 2024 2) ;=> 29"
[year month])
(defn valid-date
"Returns true if the date is valid.
(calendar/valid-date {2024 2 29}) ;=> true"
([date])
([year month day]))
(defn iso-week-number
"Returns {year week} for a date.
(calendar/iso-week-number {2024 3 9}) ;=> {2024 10}"
([date])
([year month day]))
(defn time-difference
"Returns the time difference between two datetimes.
(calendar/time-difference dt1 dt2) ;=> {days {hours mins secs}}"
[datetime1 datetime2])
(defn seconds-to-daystime
"Converts seconds to {days {hours minutes seconds}}.
(calendar/seconds-to-daystime 90061) ;=> {1 {1 1 1}}"
[seconds])
(defn seconds-to-time
"Converts seconds to {hours minutes seconds}.
(calendar/seconds-to-time 3661) ;=> {1 1 1}"
[seconds])
(defn time-to-seconds
"Converts {hours minutes seconds} to seconds.
(calendar/time-to-seconds {1 1 1}) ;=> 3661"
[time])
(defn system-time-to-local-time
"Converts system time to local datetime."
[time unit])
(defn system-time-to-universal-time
"Converts system time to UTC datetime."
[time unit])
(defn rfc3339-to-system-time
"Parses RFC 3339 timestamp to system time.
(calendar/rfc3339-to-system-time \"2024-03-09T12:00:00Z\") ;=> integer"
([string])
([string opts]))
(defn system-time-to-rfc3339
"Converts system time to RFC 3339 string.
(calendar/system-time-to-rfc3339 time) ;=> \"2024-03-09T12:00:00Z\""
([time])
([time opts]))
+367
View File
@@ -0,0 +1,367 @@
(ns clje.core
"CljElixir core — stubs for clj-kondo/clojure-lsp.
These are builtin functions and special forms handled directly by the
CljElixir transformer. Standard Clojure forms (defn, let, fn, if, etc.)
are mapped via :lint-as in .clj-kondo/config.edn."
(:refer-clojure :exclude [case send pr pr-str prn print-str
vec vector subvec vector?
str println cons list
inc dec
map filter concat take drop
sort sort-by group-by frequencies distinct
mapcat partition
keys vals select-keys merge into
get assoc dissoc update get-in assoc-in update-in
count first rest seq
conj nth peek pop
reduce reduce-kv
contains? empty? nil?
not= with-open throw]))
;; ===== Special Forms (hooks handle these) =====
(defmacro receive
"BEAM message receive with pattern matching.
(receive
[:hello sender] (send sender :hi)
[:quit] (System/halt 0)
:after 5000 (println \"timeout\"))"
[& clauses])
(defmacro defmodule
"Defines an Elixir module.
(defmodule MyModule
(defn my-func [x] (* x 2)))"
[name & body])
(defmacro case
"Pattern matching (not constant matching like Clojure).
(case val
[a b] (+ a b)
{:key v} v
_ :default)"
[expr & clauses])
(defmacro with
"Monadic binding — chains pattern matches, short-circuits on mismatch.
(with [{:ok val1} (step1)
{:ok val2} (step2 val1)]
(+ val1 val2))"
[bindings & body])
(defmacro ns
"Declares the module namespace (primary module declaration).
(ns MyApp.Router
(require [Logger])
(use [CljElixir.Core]))"
[name & body])
;; ===== BEAM Concurrency =====
(defn spawn
"Spawns a new BEAM process. Returns PID.
(spawn (fn [] (IO/puts \"hello from new process\")))"
[f])
(defn send
"Sends a message to a process. Returns the message.
(send pid {:hello \"world\"})"
[pid msg])
(defn monitor
"Monitors a process. Returns a reference.
(monitor pid)
(monitor :process pid)"
([pid]) ([type pid]))
(defn link
"Creates a bidirectional link between processes.
(link pid)"
[pid])
(defn unlink
"Removes a link between processes.
(unlink pid)"
[pid])
(defn alive?
"Returns true if the process is alive.
(alive? pid) ;=> true"
[pid])
;; ===== Arithmetic =====
(defn inc
"Increments by 1.
(inc 5) ;=> 6"
[x])
(defn dec
"Decrements by 1.
(dec 5) ;=> 4"
[x])
;; ===== String & Output =====
(defn str
"Concatenates arguments into a string.
(str \"hello\" \" \" \"world\") ;=> \"hello world\""
[& args])
(defn println
"Prints arguments followed by newline.
(println \"hello\")"
[& args])
(defn pr-str
"Returns a string representation (EDN-like).
(pr-str {:a 1}) ;=> \"{:a 1}\""
[& args])
(defn prn
"Prints EDN representation followed by newline."
[& args])
(defn pr
"Prints EDN representation (no newline)."
[& args])
(defn print-str
"Returns a human-readable string."
[& args])
;; ===== BEAM Types & Interop =====
(defn tuple
"Creates a BEAM tuple from arguments.
(tuple :ok \"value\") ;=> #el[:ok \"value\"]"
[& args])
(defn clojurify
"Converts Elixir types to Clojure equivalents (keyword lists → maps, etc.).
(clojurify elixir-val)"
[val])
(defn elixirify
"Converts Clojure types to Elixir equivalents (maps → keyword lists, etc.).
(elixirify clj-val)"
[val])
(defn hd
"Returns the head (first element) of a list.
(hd [1 2 3]) ;=> 1"
[coll])
(defn tl
"Returns the tail (rest) of a list.
(tl [1 2 3]) ;=> [2 3]"
[coll])
(defn cons
"Prepends an element to a list.
(cons 1 [2 3]) ;=> [1 2 3]"
[head tail])
(defn list
"Creates a list from arguments.
(list 1 2 3) ;=> [1 2 3]"
[& args])
;; ===== BEAM Type Guards =====
(defn is-binary
"Returns true if `x` is a binary (string). Allowed in guards.
(is-binary \"hello\") ;=> true"
[x])
(defn is-integer
"Returns true if `x` is an integer. Allowed in guards."
[x])
(defn is-float
"Returns true if `x` is a float. Allowed in guards."
[x])
(defn is-number
"Returns true if `x` is a number. Allowed in guards."
[x])
(defn is-atom
"Returns true if `x` is an atom. Allowed in guards."
[x])
(defn is-list
"Returns true if `x` is a list. Allowed in guards."
[x])
(defn is-map
"Returns true if `x` is a map. Allowed in guards."
[x])
(defn is-tuple
"Returns true if `x` is a tuple. Allowed in guards."
[x])
(defn is-pid
"Returns true if `x` is a PID. Allowed in guards."
[x])
(defn is-boolean
"Returns true if `x` is a boolean. Allowed in guards."
[x])
(defn is-nil
"Returns true if `x` is nil. Allowed in guards."
[x])
(defn is-function
"Returns true if `x` is a function, optionally with given `arity`.
(is-function f) ;=> true
(is-function f 2) ;=> true if f takes 2 args"
([x]) ([x arity]))
;; ===== Vectors =====
(defn vec
"Converts a collection to a PersistentVector.
(vec [1 2 3])"
[coll])
(defn vector
"Creates a PersistentVector from arguments.
(vector 1 2 3)"
[& args])
(defn subvec
"Returns a subvector from `start` to `end`.
(subvec v 1 3)"
([v start]) ([v start end]))
(defn vector?
"Returns true if `x` is a PersistentVector.
(vector? (vector 1 2 3)) ;=> true"
[x])
;; ===== Core Data Operations =====
(defn get
"Gets value at `key` from collection. Returns `default` if missing.
(get {:a 1} :a) ;=> 1
(get {:a 1} :b :not-found) ;=> :not-found"
([coll key]) ([coll key default]))
(defn assoc
"Associates `key` with `val` in collection.
(assoc {:a 1} :b 2) ;=> {:a 1 :b 2}"
[coll key val])
(defn dissoc
"Dissociates `key` from collection.
(dissoc {:a 1 :b 2} :a) ;=> {:b 2}"
[coll key])
(defn update
"Updates value at `key` by applying `f`.
(update {:a 1} :a inc) ;=> {:a 2}"
([coll key f]) ([coll key f & args]))
(defn get-in
"Gets value at nested `path`.
(get-in {:a {:b 1}} [:a :b]) ;=> 1"
([coll path]) ([coll path default]))
(defn assoc-in
"Associates value at nested `path`.
(assoc-in {} [:a :b] 1) ;=> {:a {:b 1}}"
[coll path val])
(defn update-in
"Updates value at nested `path` by applying `f`.
(update-in {:a {:b 1}} [:a :b] inc) ;=> {:a {:b 2}}"
[coll path f])
(defn contains?
"Returns true if `coll` contains `key`.
(contains? {:a 1} :a) ;=> true"
[coll key])
(defn empty?
"Returns true if `coll` is empty.
(empty? []) ;=> true"
[coll])
(defn nil?
"Returns true if `x` is nil.
(nil? nil) ;=> true"
[x])
(defn count
"Returns the number of elements.
(count [1 2 3]) ;=> 3"
[coll])
(defn first
"Returns the first element.
(first [1 2 3]) ;=> 1"
[coll])
(defn rest
"Returns all but the first element.
(rest [1 2 3]) ;=> [2 3]"
[coll])
(defn seq
"Returns a seq on the collection, or nil if empty.
(seq [1 2 3]) ;=> (1 2 3)"
[coll])
(defn conj
"Adds element to collection (position depends on type).
(conj [1 2] 3) ;=> [1 2 3]"
[coll x])
(defn nth
"Returns element at `index`.
(nth [10 20 30] 1) ;=> 20"
([coll index]) ([coll index not-found]))
(defn peek
"Returns the most accessible element.
(peek [1 2 3]) ;=> 3"
[coll])
(defn pop
"Returns collection without the most accessible element.
(pop [1 2 3]) ;=> [1 2]"
[coll])
;; ===== Reducing =====
(defn reduce
"Reduces collection with `f`.
(reduce + [1 2 3]) ;=> 6
(reduce + 0 [1 2 3]) ;=> 6"
([f coll]) ([f init coll]))
(defn reduce-kv
"Reduces a map with `f` receiving (acc, key, value).
(reduce-kv (fn [acc k v] (+ acc v)) 0 {:a 1 :b 2}) ;=> 3"
[f init coll])
;; ===== Sequence Operations =====
(defn map
"Applies `f` to each element, returns a list.
(map inc [1 2 3]) ;=> [2 3 4]"
[f coll])
(defn filter
"Returns elements where `f` returns truthy.
(filter (fn [x] (> x 2)) [1 2 3 4]) ;=> [3 4]"
[f coll])
(defn concat
"Concatenates collections.
(concat [1 2] [3 4]) ;=> [1 2 3 4]"
[& colls])
(defn take
"Returns first `n` elements.
(take 2 [1 2 3 4]) ;=> [1 2]"
[n coll])
(defn drop
"Drops first `n` elements.
(drop 2 [1 2 3 4]) ;=> [3 4]"
[n coll])
(defn sort
"Sorts a collection.
(sort [3 1 2]) ;=> [1 2 3]
(sort > [3 1 2]) ;=> [3 2 1]"
([coll]) ([comp coll]))
(defn sort-by
"Sorts by the result of `keyfn`.
(sort-by :name [{:name \"b\"} {:name \"a\"}])"
([keyfn coll]) ([keyfn comp coll]))
(defn group-by
"Groups elements by the result of `f`.
(group-by even? [1 2 3 4]) ;=> {false [1 3] true [2 4]}"
[f coll])
(defn frequencies
"Returns a map of element → count.
(frequencies [:a :b :a]) ;=> {:a 2 :b 1}"
[coll])
(defn distinct
"Returns unique elements.
(distinct [1 2 1 3]) ;=> [1 2 3]"
[coll])
(defn mapcat
"Maps then concatenates results.
(mapcat (fn [x] [x x]) [1 2 3]) ;=> [1 1 2 2 3 3]"
[f coll])
(defn partition
"Partitions collection into groups of `n`.
(partition 2 [1 2 3 4]) ;=> [[1 2] [3 4]]"
([n coll]) ([n step coll]) ([n step pad coll]))
(defn keys
"Returns keys of a map.
(keys {:a 1 :b 2}) ;=> [:a :b]"
[m])
(defn vals
"Returns values of a map.
(vals {:a 1 :b 2}) ;=> [1 2]"
[m])
(defn select-keys
"Selects only specified `ks` from map.
(select-keys {:a 1 :b 2 :c 3} [:a :c]) ;=> {:a 1 :c 3}"
[m ks])
(defn merge
"Merges maps. Later maps take precedence.
(merge {:a 1} {:b 2}) ;=> {:a 1 :b 2}"
[& maps])
(defn into
"Pours elements from `from` into `to`.
(into {} [[:a 1] [:b 2]]) ;=> {:a 1 :b 2}"
[to from])
(defn not=
"Returns true if arguments are not equal.
(not= 1 2) ;=> true"
[& args])
(defn throw
"Throws a value (caught by try/catch :throw).
(throw :some-error)"
[value])
+120
View File
@@ -0,0 +1,120 @@
(ns crypto
"Erlang :crypto module — cryptographic functions.
In CljElixir: (crypto/hash :sha256 data), (crypto/strong-rand-bytes 16), etc.
Wraps OpenSSL for hashing, encryption, and random number generation.")
(defn hash
"Computes a hash digest.
(crypto/hash :sha256 \"hello\") ;=> <<binary hash>>
Algorithms: :md5, :sha, :sha224, :sha256, :sha384, :sha512, :sha3-256, etc."
[type data])
(defn mac
"Computes a Message Authentication Code.
(crypto/mac :hmac :sha256 key data)"
([type sub-type key data])
([type sub-type key data mac-length]))
(defn hash-init
"Initializes incremental hashing.
(crypto/hash-init :sha256)"
[type])
(defn hash-update
"Updates incremental hash with more data.
(crypto/hash-update state data)"
[state data])
(defn hash-final
"Finalizes incremental hash. Returns the digest."
[state])
(defn strong-rand-bytes
"Generates `n` cryptographically strong random bytes.
(crypto/strong-rand-bytes 16) ;=> <<16 random bytes>>"
[n])
(defn crypto-one-time
"One-shot symmetric encryption/decryption.
(crypto/crypto-one-time :aes-256-ctr key iv data true) ;=> encrypted"
([cipher key iv data encrypt-flag])
([cipher key data encrypt-flag]))
(defn crypto-one-time-aead
"One-shot AEAD encryption/decryption (e.g., AES-GCM).
(crypto/crypto-one-time-aead :aes-256-gcm key iv data aad true)"
([cipher key iv data aad encrypt-flag])
([cipher key iv data aad tag-length encrypt-flag]))
(defn crypto-init
"Initializes streaming encryption/decryption.
3-arity: (crypto/crypto-init cipher key encrypt-flag)
4-arity: (crypto/crypto-init cipher key iv encrypt-flag-or-opts)"
([cipher key encrypt-flag])
([cipher key iv encrypt-flag-or-opts]))
(defn crypto-update
"Updates streaming encryption with more data."
[state data])
(defn crypto-final
"Finalizes streaming encryption."
[state])
(defn sign
"Creates a digital signature.
(crypto/sign :rsa :sha256 data private-key)"
([algorithm digest-type data key])
([algorithm digest-type data key opts]))
(defn verify
"Verifies a digital signature.
(crypto/verify :rsa :sha256 data signature public-key)"
([algorithm digest-type data signature key])
([algorithm digest-type data signature key opts]))
(defn generate-key
"Generates a key pair.
(crypto/generate-key :ecdh :secp256r1)"
([type params])
([type params private-key]))
(defn compute-key
"Computes shared secret from key exchange."
([type others-public-key my-private-key params])
([type others-public-key my-private-key shared-info params]))
(defn supports
"Returns lists of supported algorithms.
(crypto/supports) ;=> [{:ciphers [...]}, {:hashs [...]}, ...]"
([])
([category]))
(defn hash-info
"Returns information about a hash algorithm."
[type])
(defn cipher-info
"Returns information about a cipher."
[cipher])
(defn ec-curves
"Returns supported elliptic curves."
[])
(defn rand-seed
"Seeds the random number generator.
(crypto/rand-seed seed)"
([seed])
([alg-or-state seed]))
(defn rand-uniform
"Returns a random integer in 1..n.
(crypto/rand-uniform 100) ;=> 42"
[n])
(defn exor
"XORs two equal-length binaries.
(crypto/exor bin1 bin2)"
[bin1 bin2])
+93
View File
@@ -0,0 +1,93 @@
(ns io
"Erlang :io module — I/O protocol operations.
In CljElixir: (io/format \"Hello ~s!~n\" [\"world\"]), etc.
Lower-level than Elixir's IO module. Uses charlists and format strings.")
(defn format
"Formatted output (like C printf).
(io/format \"Hello ~s!~n\" [\"world\"]) ;=> prints 'Hello world!\\n'
(io/format device \"~p~n\" [term]) ;=> pretty-print to device
Common format specs:
~s string ~w write (Erlang term)
~p pretty-print ~f float
~e scientific ~b integer base 10
~.Xb integer base X ~n newline
~c character ~i ignore"
([format args])
([device format args]))
(defn fwrite
"Like format but returns :ok or {:error reason}."
([format args])
([device format args]))
(defn get-line
"Reads a line from standard input. Returns charlist or :eof.
(io/get-line \"prompt> \")"
([prompt])
([device prompt]))
(defn put-chars
"Writes characters to the IO device.
(io/put-chars \"hello\")"
([chars])
([device chars]))
(defn nl
"Writes a newline.
(io/nl)"
([])
([device]))
(defn read
"Reads an Erlang term from input. Returns {:ok term} or {:error reason}.
(io/read \"enter term> \")"
([prompt])
([device prompt]))
(defn write
"Writes an Erlang term.
(io/write {:a 1}) ;=> prints '{a,1}'"
([term])
([device term]))
(defn scan-erl-form
"Scans an Erlang form from input."
([prompt])
([device prompt])
([device prompt start-line]))
(defn parse-erl-form
"Parses an Erlang form from input."
([prompt])
([device prompt])
([device prompt start-line]))
(defn setopts
"Sets IO device options.
(io/setopts [{:encoding :unicode}])"
([opts])
([device opts]))
(defn getopts
"Gets IO device options."
([])
([device]))
(defn columns
"Returns the terminal column count.
(io/columns) ;=> {:ok 120}"
([])
([device]))
(defn rows
"Returns the terminal row count.
(io/rows) ;=> {:ok 40}"
([])
([device]))
(defn printable-range
"Returns the printable character range (:unicode or :latin1)."
[])
+558
View File
@@ -0,0 +1,558 @@
(ns erlang
"Erlang :erlang module — BEAM runtime BIFs (Built-In Functions).
In CljElixir: (erlang/self), (erlang/band x y), etc.
These are the lowest-level BEAM operations, many are used internally.")
;; --- Process ---
(defn self
"Returns the PID of the calling process.
(erlang/self) ;=> #PID<0.123.0>"
[])
(defn spawn
"Spawns a new process. Returns PID.
(erlang/spawn (fn [] (do-work)))
(erlang/spawn Module :fun [args])"
([fun])
([module function args]))
(defn spawn-link
"Spawns and links a process."
([fun])
([module function args]))
(defn spawn-monitor
"Spawns and monitors a process. Returns {pid ref}."
([fun])
([module function args]))
(defn send
"Sends a message. Same as `!` operator.
(erlang/send pid :hello)"
([dest msg])
([dest msg opts]))
(defn exit
"Sends an exit signal.
(erlang/exit :normal)
(erlang/exit pid :kill)"
([reason])
([pid reason]))
(defn link
"Creates a bidirectional link.
(erlang/link pid)"
[pid])
(defn unlink
"Removes a link.
(erlang/unlink pid)"
[pid])
(defn monitor
"Starts monitoring a process.
(erlang/monitor :process pid)"
[type item])
(defn demonitor
"Stops monitoring.
(erlang/demonitor ref)"
([ref])
([ref opts]))
(defn process-flag
"Sets process flags.
(erlang/process-flag :trap-exit true)"
([flag value])
([pid flag value]))
(defn process-info
"Returns info about a process.
(erlang/process-info pid)
(erlang/process-info pid :message-queue-len)"
([pid])
([pid item]))
(defn processes
"Returns a list of all process PIDs."
[])
(defn is-process-alive
"Returns true if `pid` is alive."
[pid])
(defn register
"Registers a process under a name.
(erlang/register :my-server (erlang/self))"
[name pid])
(defn unregister
"Unregisters a name."
[name])
(defn whereis
"Returns PID for registered name, or :undefined."
[name])
(defn registered
"Returns list of all registered names."
[])
(defn group-leader
"Gets or sets the group leader.
(erlang/group-leader)
(erlang/group-leader new-leader pid)"
([])
([leader pid]))
(defn hibernate
"Puts process in hibernate mode (frees heap)."
[module function args])
;; --- Bitwise Operations ---
(defn band
"Bitwise AND.
(erlang/band 0xFF 0x0F) ;=> 15"
[int1 int2])
(defn bor
"Bitwise OR.
(erlang/bor 0x0F 0xF0) ;=> 255"
[int1 int2])
(defn bxor
"Bitwise XOR.
(erlang/bxor 0xFF 0x0F) ;=> 240"
[int1 int2])
(defn bnot
"Bitwise NOT.
(erlang/bnot 0) ;=> -1"
[int])
(defn bsl
"Bitwise shift left.
(erlang/bsl 1 5) ;=> 32"
[int shift])
(defn bsr
"Bitwise shift right.
(erlang/bsr 32 5) ;=> 1"
[int shift])
;; --- Arithmetic ---
(defn abs
"Returns absolute value.
(erlang/abs -5) ;=> 5"
[number])
(defn div
"Integer division (truncated towards zero).
(erlang/div 10 3) ;=> 3"
[a b])
(defn rem
"Integer remainder.
(erlang/rem 10 3) ;=> 1"
[a b])
(defn float
"Converts to float.
(erlang/float 42) ;=> 42.0"
[number])
(defn round
"Rounds to nearest integer.
(erlang/round 3.5) ;=> 4"
[number])
(defn trunc
"Truncates to integer.
(erlang/trunc 3.9) ;=> 3"
[number])
(defn ceil
"Ceiling (smallest integer >= number).
(erlang/ceil 3.1) ;=> 4"
[number])
(defn floor
"Floor (largest integer <= number).
(erlang/floor 3.9) ;=> 3"
[number])
;; --- Tuple Operations ---
(defn element
"Gets element at 1-based `index` from tuple.
(erlang/element 1 #el[:a :b :c]) ;=> :a"
[index tuple])
(defn setelement
"Sets element at 1-based `index` in tuple.
(erlang/setelement 1 #el[:a :b] :x) ;=> #el[:x :b]"
[index tuple value])
(defn tuple-size
"Returns the size of a tuple.
(erlang/tuple-size #el[1 2 3]) ;=> 3"
[tuple])
(defn make-tuple
"Creates a tuple of `arity` filled with `init-value`.
(erlang/make-tuple 3 0) ;=> #el[0 0 0]"
([arity init-value])
([arity default-value init-list]))
(defn append-element
"Appends `element` to `tuple`.
(erlang/append-element #el[1 2] 3) ;=> #el[1 2 3]"
[tuple element])
(defn tuple-to-list
"Converts a tuple to a list.
(erlang/tuple-to-list #el[1 2 3]) ;=> [1 2 3]"
[tuple])
(defn list-to-tuple
"Converts a list to a tuple.
(erlang/list-to-tuple [1 2 3]) ;=> #el[1 2 3]"
[list])
;; --- Type Conversion ---
(defn atom-to-list
"Converts atom to charlist.
(erlang/atom-to-list :hello) ;=> 'hello'"
[atom])
(defn list-to-atom
"Converts charlist to atom."
[charlist])
(defn atom-to-binary
"Converts atom to binary string.
(erlang/atom-to-binary :hello) ;=> \"hello\""
([atom])
([atom encoding]))
(defn binary-to-atom
"Converts binary to atom."
([binary])
([binary encoding]))
(defn binary-to-existing-atom
"Converts binary to existing atom."
([binary])
([binary encoding]))
(defn integer-to-list
"Converts integer to charlist.
(erlang/integer-to-list 123) ;=> '123'
(erlang/integer-to-list 255 16) ;=> 'FF'"
([integer])
([integer base]))
(defn list-to-integer
"Converts charlist to integer."
([charlist])
([charlist base]))
(defn integer-to-binary
"Converts integer to binary string.
(erlang/integer-to-binary 123) ;=> \"123\""
([integer])
([integer base]))
(defn binary-to-integer
"Converts binary to integer."
([binary])
([binary base]))
(defn float-to-list
"Converts float to charlist."
([float])
([float opts]))
(defn float-to-binary
"Converts float to binary."
([float])
([float opts]))
(defn list-to-float
"Converts charlist to float."
[charlist])
(defn binary-to-float
"Converts binary to float."
[binary])
(defn binary-to-list
"Converts binary to list of bytes.
(erlang/binary-to-list \"hello\") ;=> [104 101 108 108 111]"
([binary])
([binary start stop]))
(defn list-to-binary
"Converts iolist to binary.
(erlang/list-to-binary [104 101 108 108 111]) ;=> \"hello\""
[iolist])
(defn iolist-to-binary
"Converts iolist to binary.
(erlang/iolist-to-binary [\"hello\" \" \" \"world\"]) ;=> \"hello world\""
[iolist])
(defn iolist-size
"Returns byte size of an iolist."
[iolist])
(defn term-to-binary
"Serializes a term to binary (External Term Format).
(erlang/term-to-binary {:key \"value\"}) ;=> <<131, ...>>"
([term])
([term opts]))
(defn binary-to-term
"Deserializes binary (ETF) to a term.
(erlang/binary-to-term bin) ;=> {:key \"value\"}"
([binary])
([binary opts]))
;; --- Type Checks ---
(defn is-atom
"Returns true if term is an atom."
[term])
(defn is-binary
"Returns true if term is a binary."
[term])
(defn is-boolean
"Returns true if term is a boolean."
[term])
(defn is-float
"Returns true if term is a float."
[term])
(defn is-function
"Returns true if term is a function."
([term])
([term arity]))
(defn is-integer
"Returns true if term is an integer."
[term])
(defn is-list
"Returns true if term is a list."
[term])
(defn is-map
"Returns true if term is a map."
[term])
(defn is-map-key
"Returns true if key exists in map. Guard-safe."
[map key])
(defn is-number
"Returns true if term is a number."
[term])
(defn is-pid
"Returns true if term is a PID."
[term])
(defn is-port
"Returns true if term is a port."
[term])
(defn is-reference
"Returns true if term is a reference."
[term])
(defn is-tuple
"Returns true if term is a tuple."
[term])
;; --- List Operations ---
(defn hd
"Returns the head of a list.
(erlang/hd [1 2 3]) ;=> 1"
[list])
(defn tl
"Returns the tail of a list.
(erlang/tl [1 2 3]) ;=> [2 3]"
[list])
(defn length
"Returns the length of a list.
(erlang/length [1 2 3]) ;=> 3"
[list])
;; --- Binary/Bitstring ---
(defn byte-size
"Returns byte size of a binary.
(erlang/byte-size \"hello\") ;=> 5"
[binary])
(defn bit-size
"Returns bit size of a bitstring."
[bitstring])
(defn binary-part
"Extracts a part of a binary. Guard-safe.
(erlang/binary-part \"hello\" 1 3) ;=> \"ell\""
[binary start length])
(defn split-binary
"Splits binary at position.
(erlang/split-binary \"hello\" 3) ;=> {\"hel\" \"lo\"}"
[binary pos])
;; --- Hash ---
(defn phash2
"Portable hash function. Returns integer in 0..(range-1).
(erlang/phash2 :my-term) ;=> 12345678
(erlang/phash2 :my-term 100) ;=> 78"
([term])
([term range]))
;; --- Time ---
(defn monotonic-time
"Returns monotonic time.
(erlang/monotonic-time) ;=> native time units
(erlang/monotonic-time :millisecond)"
([])
([unit]))
(defn system-time
"Returns system (wall-clock) time."
([])
([unit]))
(defn unique-integer
"Returns a unique integer.
(erlang/unique-integer [:positive :monotonic])"
([])
([modifiers]))
(defn timestamp
"Returns {megasecs secs microsecs} tuple."
[])
(defn convert-time-unit
"Converts time between units.
(erlang/convert-time-unit time :native :millisecond)"
[time from-unit to-unit])
;; --- Node ---
(defn node
"Returns the current node name.
(erlang/node) ;=> :nonode@nohost"
([])
([arg]))
(defn nodes
"Returns list of connected nodes.
(erlang/nodes) ;=> [:node1@host ...]"
([])
([type]))
(defn is-alive
"Returns true if the node is part of a distributed system."
[])
;; --- System ---
(defn apply
"Applies function. Like Kernel.apply.
(erlang/apply Enum :map [[1 2 3] inc])
(erlang/apply fun args)"
([fun args])
([module function args]))
(defn error
"Raises an error.
(erlang/error :badarg)
(erlang/error {:my-error \"reason\"})"
([reason])
([reason args]))
(defn throw
"Throws a term (caught by try/catch :throw)."
[term])
(defn halt
"Halts the runtime.
(erlang/halt 0)"
([])
([status])
([status opts]))
(defn garbage-collect
"Forces garbage collection on current (or specified) process.
(erlang/garbage-collect)"
([])
([pid]))
(defn memory
"Returns memory usage info.
(erlang/memory) ;=> [{:total N} {:processes N} ...]
(erlang/memory :total) ;=> bytes"
([])
([type]))
(defn system-info
"Returns system information.
(erlang/system-info :process-count)
(erlang/system-info :otp-release)"
[item])
(defn statistics
"Returns runtime statistics.
(erlang/statistics :wall-clock)"
[type])
(defn make-ref
"Creates a unique reference."
[])
(defn md5
"Computes MD5 hash of binary. Returns 16-byte binary."
[data])
(defn crc32
"Computes CRC32 checksum."
([data])
([old-crc data]))
(defn adler32
"Computes Adler-32 checksum."
([data])
([old-adler data]))
;; --- Map Operations ---
(defn map-get
"Gets a value from a map. Guard-safe.
(erlang/map-get :key my-map)"
[key map])
(defn map-size
"Returns the number of entries in a map. Guard-safe.
(erlang/map-size {:a 1 :b 2}) ;=> 2"
[map])
+181
View File
@@ -0,0 +1,181 @@
(ns ets
"Erlang :ets module — Erlang Term Storage (in-memory tables).
In CljElixir: (ets/new :my-table [:set :public]), etc.
ETS tables are mutable, concurrent, in-memory key-value stores.")
(defn new
"Creates a new ETS table. Returns table ID.
(ets/new :my-table [:set :public :named-table])
Types: :set, :ordered-set, :bag, :duplicate-bag
Access: :public, :protected (default), :private"
[name opts])
(defn insert
"Inserts one or more tuples. Returns true.
(ets/insert table {:key \"value\"})
(ets/insert table [{:a 1} {:b 2}])"
[table objects])
(defn insert-new
"Inserts only if key doesn't exist. Returns true/false."
[table objects])
(defn lookup
"Returns all objects matching `key`. Returns list.
(ets/lookup table :key) ;=> [{:key \"value\"}]"
[table key])
(defn lookup-element
"Returns specific element at `pos` for `key`.
(ets/lookup-element table :key 2) ;=> \"value\""
([table key pos])
([table key pos default]))
(defn member
"Returns true if `key` exists.
(ets/member table :key) ;=> true"
[table key])
(defn delete
"Deletes a table or entries matching `key`.
(ets/delete table) ;=> deletes entire table
(ets/delete table :key) ;=> deletes entry"
([table])
([table key]))
(defn delete-object
"Deletes a specific object from the table."
[table object])
(defn delete-all-objects
"Deletes all objects from the table."
[table])
(defn update-counter
"Atomically updates a counter. Returns new value.
(ets/update-counter table :hits 1) ;=> increments by 1
(ets/update-counter table :hits {2 1}) ;=> increments pos 2 by 1"
([table key update-op])
([table key update-op default]))
(defn update-element
"Atomically updates specific elements of a tuple."
([table key element-spec])
([table key element-spec default]))
(defn select
"Selects objects matching a match specification.
(ets/select table match-spec)"
([table match-spec])
([table match-spec limit]))
(defn match
"Pattern matches objects in the table.
(ets/match table {:_ :$1}) ;=> extracts matched values"
([table pattern])
([table pattern limit]))
(defn match-object
"Returns full objects matching the pattern.
(ets/match-object table {:_ :_}) ;=> all objects"
([table pattern])
([table pattern limit]))
(defn match-delete
"Deletes all objects matching the pattern.
(ets/match-delete table {:_ :_}) ;=> deletes all"
[table pattern])
(defn select-delete
"Deletes objects matching a match specification. Returns count."
[table match-spec])
(defn select-count
"Counts objects matching a match specification."
[table match-spec])
(defn select-replace
"Replaces objects matching a match specification."
[table match-spec])
(defn first
"Returns the first key in the table.
(ets/first table) ;=> :some-key or :'$end_of_table'"
[table])
(defn last
"Returns the last key (only for ordered-set)."
[table])
(defn next
"Returns the key after `key`.
(ets/next table :key) ;=> :next-key"
[table key])
(defn prev
"Returns the key before `key` (only for ordered-set)."
[table key])
(defn tab2list
"Converts entire table to a list.
(ets/tab2list table) ;=> [{:key1 \"val1\"} ...]"
[table])
(defn info
"Returns information about the table.
(ets/info table) ;=> keyword list
(ets/info table :size) ;=> number of objects"
([table])
([table item]))
(defn rename
"Renames a named table."
[table name])
(defn give-away
"Transfers table ownership to another process."
[table pid gift-data])
(defn i
"Prints brief info about all ETS tables."
([])
([table]))
(defn foldl
"Folds left over table entries."
[fun acc table])
(defn foldr
"Folds right over table entries."
[fun acc table])
(defn tab2file
"Dumps table to a file."
([table filename])
([table filename opts]))
(defn file2tab
"Loads table from a file."
([filename])
([filename opts]))
(defn whereis
"Returns the table ID for a named table."
[name])
(defn safe-fixtable
"Fixes/unfixes a table for safe traversal."
[table fix])
(defn fun2ms
"Converts a literal fun to a match specification."
[fun])
(defn test-ms
"Tests a match specification against a tuple."
[tuple match-spec])
(defn all
"Returns a list of all ETS table IDs."
[])
+73
View File
@@ -0,0 +1,73 @@
(ns filename
"Erlang :filename module — file name manipulation.
In CljElixir: (filename/join [\"a\" \"b\"]), (filename/basename path), etc.
Works with charlists (unlike Elixir's Path which uses binaries).")
(defn join
"Joins path components.
(filename/join [\"a\" \"b\" \"c\"]) ;=> 'a/b/c'
(filename/join \"a\" \"b\") ;=> 'a/b'"
([components])
([name1 name2]))
(defn split
"Splits a path into components.
(filename/split \"/a/b/c\") ;=> [\"/\" \"a\" \"b\" \"c\"]"
[name])
(defn basename
"Returns the last component of the path.
(filename/basename \"/a/b/c.txt\") ;=> 'c.txt'
(filename/basename \"/a/b/c.txt\" \".txt\") ;=> 'c'"
([name])
([name ext]))
(defn dirname
"Returns the directory component.
(filename/dirname \"/a/b/c\") ;=> '/a/b'"
[name])
(defn extension
"Returns the file extension including the dot.
(filename/extension \"file.ex\") ;=> '.ex'"
[name])
(defn rootname
"Returns path without extension.
(filename/rootname \"file.ex\") ;=> 'file'"
([name])
([name ext]))
(defn absname
"Converts to absolute path.
(filename/absname \"file.txt\")"
([name])
([name dir]))
(defn flatten
"Flattens a filename (resolves deep lists)."
[name])
(defn nativename
"Converts to native OS path format."
[name])
(defn pathtype
"Returns path type: :absolute, :relative, or :volumerelative.
(filename/pathtype \"/a/b\") ;=> :absolute"
[name])
(defn safe-relative-path
"Returns a safe relative path or :unsafe."
[name])
(defn find-src
"Finds source file from object file name."
([beam])
([beam rules]))
(defn find-file
"Finds a file in the given paths."
([name paths])
([name paths extensions]))
+79
View File
@@ -0,0 +1,79 @@
(ns gen_server
"Erlang :gen_server module — generic server (low-level OTP API).
In CljElixir: prefer Elixir's GenServer module for most use cases.
Use this for direct Erlang OTP interop.")
(defn start
"Starts a gen_server. Returns {:ok pid} or {:error reason}.
(gen_server/start module init-arg opts)"
([module init-arg opts])
([server-name module init-arg opts]))
(defn start-link
"Starts a linked gen_server."
([module init-arg opts])
([server-name module init-arg opts]))
(defn call
"Makes a synchronous call.
(gen_server/call pid request) ;=> reply
(gen_server/call pid request 5000)"
([server-ref request])
([server-ref request timeout]))
(defn cast
"Sends an async message.
(gen_server/cast pid message) ;=> :ok"
[server-ref request])
(defn reply
"Replies to a caller from within handle_call.
(gen_server/reply from reply-value)"
[client reply])
(defn stop
"Stops the server.
(gen_server/stop pid)"
([server-ref])
([server-ref reason])
([server-ref reason timeout]))
(defn multi-call
"Calls all registered servers on all/specified nodes."
([name request])
([nodes name request])
([nodes name request timeout]))
(defn abcast
"Casts to all registered servers on all/specified nodes."
([name request])
([nodes name request]))
(defn enter-loop
"Makes calling process enter the gen_server receive loop."
([module opts state])
([module opts state server-name])
([module opts state server-name timeout]))
(defn wait-response
"Waits for a gen_server response."
([request-id timeout])
([request-id]))
(defn send-request
"Sends an async request. Returns request-id.
(gen_server/send-request pid request)"
([server-ref request])
([server-ref request label req-id-collection]))
(defn receive-response
"Receives a response from send-request.
(gen_server/receive-response request-id timeout)"
([request-id timeout])
([request-id]))
(defn check-response
"Checks if a response has arrived (non-blocking).
(gen_server/check-response msg request-id-or-collection)"
[msg request-id-or-collection])
+52
View File
@@ -0,0 +1,52 @@
(ns gen_tcp
"Erlang :gen_tcp module — TCP socket interface.
In CljElixir: (gen_tcp/listen port opts), (gen_tcp/accept socket), etc.
Core networking module for TCP servers and clients on the BEAM.")
(defn listen
"Starts listening on `port`. Returns {:ok listen-socket} or {:error reason}.
(gen_tcp/listen 4000 [:binary {:active false} {:reuseaddr true}])"
[port opts])
(defn accept
"Accepts an incoming connection. Blocks until a connection arrives.
(gen_tcp/accept listen-socket) ;=> {:ok socket}
(gen_tcp/accept listen-socket 5000) ;=> with 5s timeout"
([listen-socket])
([listen-socket timeout]))
(defn connect
"Connects to a TCP server. Returns {:ok socket} or {:error reason}.
(gen_tcp/connect \"localhost\" 4000 [:binary {:active false}])
(gen_tcp/connect {127 0 0 1} 4000 opts 5000) ;=> with timeout"
([address port opts])
([address port opts timeout]))
(defn send
"Sends data over a TCP socket. Returns :ok or {:error reason}.
(gen_tcp/send socket \"hello\")"
[socket packet])
(defn recv
"Receives data from a socket. Blocks until data arrives.
(gen_tcp/recv socket 0) ;=> {:ok data} (0 = any amount)
(gen_tcp/recv socket 1024 5000) ;=> with timeout"
([socket length])
([socket length timeout]))
(defn close
"Closes a TCP socket.
(gen_tcp/close socket) ;=> :ok"
[socket])
(defn controlling-process
"Transfers socket ownership to another process.
(gen_tcp/controlling-process socket new-owner-pid) ;=> :ok"
[socket pid])
(defn shutdown
"Shuts down one or both directions of a socket.
(gen_tcp/shutdown socket :write) ;=> :ok
(gen_tcp/shutdown socket :read-write)"
[socket how])
+33
View File
@@ -0,0 +1,33 @@
(ns gen_udp
"Erlang :gen_udp module — UDP socket interface.
In CljElixir: (gen_udp/open port opts), (gen_udp/send socket addr port data), etc.")
(defn open
"Opens a UDP socket. Returns {:ok socket} or {:error reason}.
(gen_udp/open 0 [:binary {:active false}]) ;=> {:ok socket}"
([port])
([port opts]))
(defn send
"Sends a UDP packet.
(gen_udp/send socket \"localhost\" 4000 \"hello\")
(gen_udp/send socket destination packet)"
([socket destination packet])
([socket host port packet]))
(defn recv
"Receives a UDP packet. Returns {:ok {address port data}} or {:error reason}.
(gen_udp/recv socket 0) ;=> {:ok {addr port data}}
(gen_udp/recv socket 1024 5000) ;=> with timeout"
([socket length])
([socket length timeout]))
(defn close
"Closes a UDP socket.
(gen_udp/close socket) ;=> :ok"
[socket])
(defn controlling-process
"Transfers socket ownership to another process."
[socket pid])
+68
View File
@@ -0,0 +1,68 @@
(ns inet
"Erlang :inet module — network interface configuration.
In CljElixir: (inet/setopts socket opts), (inet/port socket), etc.")
(defn setopts
"Sets socket options.
(inet/setopts socket [{:active true}])
(inet/setopts socket [{:packet :line}])"
[socket opts])
(defn getopts
"Gets socket options.
(inet/getopts socket [:active :packet])"
[socket opts])
(defn port
"Returns the port number of a socket.
(inet/port socket) ;=> {:ok 4000}"
[socket])
(defn peername
"Returns {address port} of the remote end.
(inet/peername socket) ;=> {:ok {{127 0 0 1} 4000}}"
[socket])
(defn sockname
"Returns {address port} of the local end.
(inet/sockname socket) ;=> {:ok {{0 0 0 0} 4000}}"
[socket])
(defn getaddr
"Resolves hostname to IP address.
(inet/getaddr \"localhost\" :inet) ;=> {:ok {127 0 0 1}}"
[hostname family])
(defn gethostname
"Returns the hostname of the local machine.
(inet/gethostname) ;=> {:ok \"myhost\"}"
[])
(defn getifaddrs
"Returns network interface addresses.
(inet/getifaddrs) ;=> {:ok [...]}"
[])
(defn parse-address
"Parses an IP address string.
(inet/parse-address \"192.168.1.1\") ;=> {:ok {192 168 1 1}}"
[address])
(defn ntoa
"Converts IP tuple to string.
(inet/ntoa {192 168 1 1}) ;=> '192.168.1.1'"
[ip])
(defn close
"Closes a socket."
[socket])
(defn controlling-process
"Transfers socket control to another process."
[socket pid])
(defn i
"Prints information about all open sockets."
([])
([proto]))
+249
View File
@@ -0,0 +1,249 @@
(ns lists
"Erlang :lists module — list processing functions.
In CljElixir: (lists/reverse lst), (lists/sort lst), etc.
Lower-level than Enum; operates directly on Erlang lists.")
(defn reverse
"Reverses a list.
(lists/reverse [3 1 2]) ;=> [2 1 3]"
([list])
([list tail]))
(defn sort
"Sorts a list using Erlang term ordering.
(lists/sort [3 1 2]) ;=> [1 2 3]
(lists/sort (fn [a b] (> a b)) [3 1 2]) ;=> [3 2 1]"
([list])
([fun list]))
(defn usort
"Sorts and removes duplicates.
(lists/usort [3 1 2 1]) ;=> [1 2 3]"
([list])
([fun list]))
(defn keysort
"Sorts list of tuples by element at `n`-th position (1-based).
(lists/keysort 1 [{:b 2} {:a 1}]) ;=> [{:a 1} {:b 2}]"
[n tuple-list])
(defn keyfind
"Finds first tuple where element at `n` matches `key`. Returns tuple or false.
(lists/keyfind :a 1 [{:a 1} {:b 2}]) ;=> {:a 1}"
[key n tuple-list])
(defn keystore
"Replaces or appends tuple in list based on key at position `n`."
[key n tuple-list new-tuple])
(defn keydelete
"Deletes first tuple with matching key at position `n`."
[key n tuple-list])
(defn keymember
"Returns true if any tuple has `key` at position `n`."
[key n tuple-list])
(defn keyreplace
"Replaces first tuple with matching key at position `n`."
[key n tuple-list new-tuple])
(defn keytake
"Takes first tuple with matching key. Returns {value rest} or false."
[key n tuple-list])
(defn map
"Applies `fun` to each element, returns new list.
(lists/map (fn [x] (* x 2)) [1 2 3]) ;=> [2 4 6]"
[fun list])
(defn flatmap
"Maps and flattens one level.
(lists/flatmap (fn [x] [x x]) [1 2 3]) ;=> [1 1 2 2 3 3]"
[fun list])
(defn filter
"Returns elements for which `pred` returns true.
(lists/filter (fn [x] (> x 2)) [1 2 3 4]) ;=> [3 4]"
[pred list])
(defn filtermap
"Filters and maps in one pass. `fun` returns true/false/{true, value}.
(lists/filtermap (fn [x] (if (> x 2) {:true (* x 10)} false)) [1 2 3 4]) ;=> [30 40]"
[fun list])
(defn foldl
"Left fold.
(lists/foldl (fn [elem acc] (+ acc elem)) 0 [1 2 3]) ;=> 6"
[fun acc list])
(defn foldr
"Right fold.
(lists/foldr (fn [elem acc] (+ acc elem)) 0 [1 2 3]) ;=> 6"
[fun acc list])
(defn foreach
"Calls `fun` on each element for side effects. Returns :ok.
(lists/foreach (fn [x] (IO/puts x)) [1 2 3])"
[fun list])
(defn flatten
"Flattens nested lists.
(lists/flatten [[1 [2]] [3]]) ;=> [1 2 3]"
([list])
([list tail]))
(defn append
"Appends lists.
(lists/append [1 2] [3 4]) ;=> [1 2 3 4]
(lists/append [[1] [2] [3]]) ;=> [1 2 3]"
([list-of-lists])
([list1 list2]))
(defn concat
"Concatenates a list of things to a flat list."
[list])
(defn merge
"Merges sorted lists.
(lists/merge [1 3 5] [2 4 6]) ;=> [1 2 3 4 5 6]"
([list1 list2])
([fun list1 list2])
([list-of-lists]))
(defn zip
"Zips two lists into a list of tuples.
(lists/zip [1 2 3] [:a :b :c]) ;=> [{1 :a} {2 :b} {3 :c}]"
[list1 list2])
(defn unzip
"Unzips a list of tuples into two lists.
(lists/unzip [{1 :a} {2 :b}]) ;=> {[1 2] [:a :b]}"
[tuple-list])
(defn zipwith
"Zips with a combining function.
(lists/zipwith (fn [a b] (+ a b)) [1 2 3] [4 5 6]) ;=> [5 7 9]"
[fun list1 list2])
(defn member
"Returns true if `elem` is in `list`.
(lists/member 2 [1 2 3]) ;=> true"
[elem list])
(defn nth
"Returns the `n`-th element (1-based).
(lists/nth 2 [10 20 30]) ;=> 20"
[n list])
(defn nthtail
"Returns the tail starting at position `n` (1-based).
(lists/nthtail 2 [10 20 30]) ;=> [30]"
[n list])
(defn last
"Returns the last element.
(lists/last [1 2 3]) ;=> 3"
[list])
(defn delete
"Deletes first occurrence of `elem`.
(lists/delete 2 [1 2 3 2]) ;=> [1 3 2]"
[elem list])
(defn subtract
"Subtracts elements of `list2` from `list1`.
(lists/subtract [1 2 3 2] [2]) ;=> [1 3 2]"
[list1 list2])
(defn duplicate
"Creates a list with `elem` repeated `n` times.
(lists/duplicate 3 :ok) ;=> [:ok :ok :ok]"
[n elem])
(defn seq
"Generates a sequence from `from` to `to`.
(lists/seq 1 5) ;=> [1 2 3 4 5]
(lists/seq 1 10 2) ;=> [1 3 5 7 9]"
([from to])
([from to step]))
(defn sum
"Returns the sum of all elements."
[list])
(defn max
"Returns the maximum element."
[list])
(defn min
"Returns the minimum element."
[list])
(defn prefix
"Returns true if `list1` is a prefix of `list2`."
[list1 list2])
(defn suffix
"Returns true if `list1` is a suffix of `list2`."
[list1 list2])
(defn splitwith
"Splits list into two at the point where `pred` first returns false."
[pred list])
(defn partition
"Partitions list into two: elements satisfying `pred` and those that don't."
[pred list])
(defn takewhile
"Takes elements while `pred` returns true."
[pred list])
(defn dropwhile
"Drops elements while `pred` returns true."
[pred list])
(defn droplast
"Drops the last element of a list.
(lists/droplast [1 2 3]) ;=> [1 2]"
[list])
(defn sublist
"Returns a sublist.
(lists/sublist [1 2 3 4 5] 3) ;=> [1 2 3]
(lists/sublist [1 2 3 4 5] 2 3) ;=> [2 3 4]"
([list len])
([list start len]))
(defn search
"Searches for an element matching `pred`. Returns {:value elem} or false."
[pred list])
(defn enumerate
"Returns a list of {index, element} tuples.
(lists/enumerate [\"a\" \"b\" \"c\"]) ;=> [{1 \"a\"} {2 \"b\"} {3 \"c\"}]"
([list])
([index list])
([index step list]))
(defn all
"Returns true if `pred` returns true for all elements."
[pred list])
(defn any
"Returns true if `pred` returns true for any element."
[pred list])
(defn join
"Joins list elements into a string (Elixir extension)."
[list separator])
(defn map-foldl
"Combined map and foldl."
[fun acc list])
(defn map-foldr
"Combined map and foldr."
[fun acc list])
+148
View File
@@ -0,0 +1,148 @@
(ns maps
"Erlang :maps module — map operations.
In CljElixir: (maps/get key map), (maps/put key value map), etc.
Lower-level than Elixir's Map module.")
(defn get
"Gets value for `key`. Raises if missing (or returns `default`).
(maps/get :a {:a 1}) ;=> 1
(maps/get :b {:a 1} :default) ;=> :default"
([key map])
([key map default]))
(defn find
"Returns {:ok value} or :error.
(maps/find :a {:a 1}) ;=> {:ok 1}"
[key map])
(defn is-key
"Returns true if `key` exists in `map`.
(maps/is-key :a {:a 1}) ;=> true"
[key map])
(defn keys
"Returns all keys.
(maps/keys {:a 1 :b 2}) ;=> [:a :b]"
[map])
(defn values
"Returns all values.
(maps/values {:a 1 :b 2}) ;=> [1 2]"
[map])
(defn size
"Returns the number of entries.
(maps/size {:a 1 :b 2}) ;=> 2"
[map])
(defn put
"Puts `key`/`value` into `map`.
(maps/put :c 3 {:a 1 :b 2}) ;=> {:a 1 :b 2 :c 3}"
[key value map])
(defn remove
"Removes `key` from `map`.
(maps/remove :a {:a 1 :b 2}) ;=> {:b 2}"
[key map])
(defn update
"Updates `key` by applying `fun` to current value. Raises if key missing.
(maps/update :a (fn [v] (+ v 1)) {:a 1}) ;=> {:a 2}"
[key fun map])
(defn update-with
"Updates `key` with `fun`, using `init` if key is missing.
(maps/update-with :a (fn [v] (+ v 1)) 0 {:a 1}) ;=> {:a 2}
(maps/update-with :b (fn [v] (+ v 1)) 0 {:a 1}) ;=> {:a 1 :b 0}"
([key fun map])
([key fun init map]))
(defn merge
"Merges two maps. `map2` values take precedence.
(maps/merge {:a 1} {:b 2}) ;=> {:a 1 :b 2}"
[map1 map2])
(defn merge-with
"Merges with a conflict resolver.
(maps/merge-with (fn [v1 v2] (+ v1 v2)) {:a 1} {:a 2}) ;=> {:a 3}"
[fun map1 map2])
(defn intersect
"Returns intersection of two maps."
([map1 map2])
([combiner map1 map2]))
(defn intersect-with
"Intersects with a combining function."
[combiner map1 map2])
(defn from-list
"Creates a map from a list of {key, value} tuples.
(maps/from-list [[:a 1] [:b 2]]) ;=> {:a 1 :b 2}"
[list])
(defn from-keys
"Creates a map from a list of keys, all with the same value.
(maps/from-keys [:a :b :c] 0) ;=> {:a 0 :b 0 :c 0}"
[keys value])
(defn to-list
"Converts map to list of {key, value} tuples.
(maps/to-list {:a 1 :b 2}) ;=> [[:a 1] [:b 2]]"
[map])
(defn new
"Creates an empty map.
(maps/new) ;=> {}"
[])
(defn map
"Maps `fun` over map entries. `fun` receives (key, value).
(maps/map (fn [k v] (* v 2)) {:a 1 :b 2}) ;=> {:a 2 :b 4}"
[fun map])
(defn filter
"Filters entries where `pred` returns true.
(maps/filter (fn [k v] (> v 1)) {:a 1 :b 2 :c 3}) ;=> {:b 2 :c 3}"
[pred map])
(defn filtermap
"Filters and maps in one pass."
[fun map])
(defn fold
"Folds over map entries.
(maps/fold (fn [k v acc] (+ acc v)) 0 {:a 1 :b 2}) ;=> 3"
[fun acc map])
(defn foreach
"Calls `fun` on each entry for side effects.
(maps/foreach (fn [k v] (IO/puts (str k \": \" v))) my-map)"
[fun map])
(defn with
"Takes only the given `keys` from `map`.
(maps/with [:a :c] {:a 1 :b 2 :c 3}) ;=> {:a 1 :c 3}"
[keys map])
(defn without
"Drops the given `keys` from `map`.
(maps/without [:a] {:a 1 :b 2}) ;=> {:b 2}"
[keys map])
(defn groups-from-list
"Groups list elements by key function.
(maps/groups-from-list (fn [x] (rem x 2)) [1 2 3 4]) ;=> {1 [1 3], 0 [2 4]}"
([key-fun list])
([key-fun value-fun list]))
(defn iterator
"Returns a map iterator for lazy traversal.
(maps/iterator my-map)"
([map])
([map order]))
(defn next
"Advances a map iterator. Returns {key value iterator} or :none."
[iterator])
+141
View File
@@ -0,0 +1,141 @@
(ns math
"Erlang :math module — mathematical functions.
In CljElixir: (math/sqrt 2), (math/pow 2 10), etc.
All functions return floats.")
(defn pi
"Returns the value of pi.
(math/pi) ;=> 3.141592653589793"
[])
(defn e
"Returns Euler's number.
(math/e) ;=> 2.718281828459045"
[])
(defn tau
"Returns tau (2*pi).
(math/tau) ;=> 6.283185307179586"
[])
;; --- Powers & Roots ---
(defn pow
"Returns `x` raised to `y`.
(math/pow 2 10) ;=> 1024.0"
[x y])
(defn sqrt
"Returns the square root.
(math/sqrt 4) ;=> 2.0"
[x])
(defn exp
"Returns e^x.
(math/exp 1) ;=> 2.718281828459045"
[x])
(defn log
"Returns the natural logarithm (base e).
(math/log 2.718281828459045) ;=> 1.0"
[x])
(defn log2
"Returns the base-2 logarithm.
(math/log2 1024) ;=> 10.0"
[x])
(defn log10
"Returns the base-10 logarithm.
(math/log10 1000) ;=> 3.0"
[x])
;; --- Trigonometry ---
(defn sin
"Returns the sine (radians).
(math/sin (/ (math/pi) 2)) ;=> 1.0"
[x])
(defn cos
"Returns the cosine (radians).
(math/cos 0) ;=> 1.0"
[x])
(defn tan
"Returns the tangent (radians).
(math/tan 0) ;=> 0.0"
[x])
(defn asin
"Returns the arcsine (radians).
(math/asin 1) ;=> 1.5707963267948966"
[x])
(defn acos
"Returns the arccosine (radians).
(math/acos 1) ;=> 0.0"
[x])
(defn atan
"Returns the arctangent (radians).
(math/atan 1) ;=> 0.7853981633974483"
[x])
(defn atan2
"Returns the two-argument arctangent (radians).
(math/atan2 1 1) ;=> 0.7853981633974483"
[y x])
;; --- Hyperbolic ---
(defn sinh
"Returns the hyperbolic sine."
[x])
(defn cosh
"Returns the hyperbolic cosine."
[x])
(defn tanh
"Returns the hyperbolic tangent."
[x])
(defn asinh
"Returns the inverse hyperbolic sine."
[x])
(defn acosh
"Returns the inverse hyperbolic cosine."
[x])
(defn atanh
"Returns the inverse hyperbolic tangent."
[x])
;; --- Rounding ---
(defn ceil
"Returns the smallest integer >= x (as float).
(math/ceil 3.2) ;=> 4.0"
[x])
(defn floor
"Returns the largest integer <= x (as float).
(math/floor 3.8) ;=> 3.0"
[x])
(defn fmod
"Returns the floating-point remainder of x/y.
(math/fmod 10.0 3.0) ;=> 1.0"
[x y])
(defn erf
"Returns the error function.
(math/erf 1) ;=> 0.8427007929497149"
[x])
(defn erfc
"Returns the complementary error function."
[x])
+72
View File
@@ -0,0 +1,72 @@
(ns os
"Erlang :os module — operating system interface.
In CljElixir: (os/cmd \"ls\"), (os/type), etc.")
(defn cmd
"Executes an OS command and returns the output as a charlist.
(os/cmd \"ls -la\") ;=> 'total 42\\n...'"
([command])
([command opts]))
(defn type
"Returns the OS type.
(os/type) ;=> {:unix :darwin} or {:win32 :nt}"
[])
(defn version
"Returns the OS version.
(os/version) ;=> {14 3 0}"
[])
(defn getenv
"Gets an environment variable. Returns charlist or false.
(os/getenv \"HOME\") ;=> '/Users/ajet'
(os/getenv \"MISSING\") ;=> false"
([name])
([name default]))
(defn putenv
"Sets an environment variable.
(os/putenv \"MY_VAR\" \"value\") ;=> true"
[name value])
(defn unsetenv
"Removes an environment variable.
(os/unsetenv \"MY_VAR\") ;=> true"
[name])
(defn getenv
"Gets all environment variables as list of \"KEY=VALUE\" strings."
[])
(defn timestamp
"Returns OS timestamp as {megasecs secs microsecs}.
(os/timestamp) ;=> {1709 123456 789012}"
[])
(defn system-time
"Returns OS system time.
(os/system-time :millisecond)"
([])
([unit]))
(defn perf-counter
"Returns a performance counter value.
(os/perf-counter :nanosecond)"
([])
([unit]))
(defn find-executable
"Finds an executable in PATH. Returns path or false.
(os/find-executable \"elixir\") ;=> '/usr/local/bin/elixir'"
[name])
(defn getpid
"Returns the OS process ID as a string.
(os/getpid) ;=> \"12345\""
[])
(defn set-signal
"Sets OS signal handler."
[signal action])
+73
View File
@@ -0,0 +1,73 @@
(ns timer
"Erlang :timer module — timer utilities.
In CljElixir: (timer/sleep 1000), (timer/send-after 5000 pid :msg), etc.
Note: for most uses, Process/send-after is preferred in Elixir.")
(defn sleep
"Suspends the calling process for `time` milliseconds.
(timer/sleep 1000) ;=> :ok (sleeps 1 second)
(timer/sleep :infinity) ;=> blocks forever"
[time])
(defn send-after
"Sends `message` to `pid` after `time` milliseconds. Returns {:ok tref}.
(timer/send-after 5000 (Process/self) :timeout)"
([time pid message])
([time message]))
(defn send-interval
"Sends `message` to `pid` every `time` milliseconds. Returns {:ok tref}.
(timer/send-interval 1000 (Process/self) :tick)"
([time pid message])
([time message]))
(defn apply-after
"Applies `module:function(args)` after `time` milliseconds.
(timer/apply-after 5000 IO :puts [\"delayed\"])"
[time module function args])
(defn apply-interval
"Applies `module:function(args)` every `time` milliseconds."
[time module function args])
(defn apply-repeatedly
"Applies `module:function(args)` repeatedly with `time` delay after each."
[time module function args])
(defn cancel
"Cancels a timer.
(timer/cancel tref) ;=> {:ok :cancel}"
[tref])
(defn tc
"Times the execution of a function. Returns {time result} in microseconds.
(timer/tc (fn [] (fib 30))) ;=> {12345 832040}
(timer/tc Enum :sort [[3 1 2]]) ;=> {microsecs [1 2 3]}"
([fun])
([fun args])
([module function args]))
(defn now-diff
"Returns time difference in microseconds between two erlang:now() tuples."
[t2 t1])
(defn seconds
"Converts seconds to milliseconds.
(timer/seconds 5) ;=> 5000"
[secs])
(defn minutes
"Converts minutes to milliseconds.
(timer/minutes 5) ;=> 300000"
[mins])
(defn hours
"Converts hours to milliseconds.
(timer/hours 1) ;=> 3600000"
[hours])
(defn hms
"Converts hours, minutes, seconds to milliseconds.
(timer/hms 1 30 0) ;=> 5400000"
[hours minutes seconds])
+86
View File
@@ -245,6 +245,92 @@ defmodule CljElixir.NReplTest do
[response] = Handler.handle(%{"op" => "bogus", "id" => "1"}, manager)
assert "unknown-op" in response["status"]
end
test "eval returns actual ns after ns declaration", %{manager: manager} do
[clone_resp] = Handler.handle(%{"op" => "clone", "id" => "1"}, manager)
session = clone_resp["new-session"]
# Default ns is "user"
responses =
Handler.handle(
%{"op" => "eval", "code" => "(+ 1 2)", "session" => session, "id" => "2"},
manager
)
values = Enum.filter(responses, &Map.has_key?(&1, "value"))
assert hd(values)["ns"] == "user"
# After ns declaration, ns should update
responses =
Handler.handle(
%{
"op" => "eval",
"code" => "(ns NReplNsTest)\n(defn hi [] :hey)",
"session" => session,
"id" => "3"
},
manager
)
values = Enum.filter(responses, &Map.has_key?(&1, "value"))
assert hd(values)["ns"] == "NReplNsTest"
end
test "incremental def in ns via nrepl", %{manager: manager} do
[clone_resp] = Handler.handle(%{"op" => "clone", "id" => "1"}, manager)
session = clone_resp["new-session"]
# Set up namespace
Handler.handle(
%{
"op" => "eval",
"code" => "(ns NReplIncrTest)\n(defn foo [] :original)",
"session" => session,
"id" => "2"
},
manager
)
# Add a new function without ns
Handler.handle(
%{
"op" => "eval",
"code" => "(defn bar [] :added)",
"session" => session,
"id" => "3"
},
manager
)
# Both should work
responses =
Handler.handle(
%{
"op" => "eval",
"code" => "(NReplIncrTest/bar)",
"session" => session,
"id" => "4"
},
manager
)
values = Enum.filter(responses, &Map.has_key?(&1, "value"))
assert hd(values)["value"] == ":added"
responses =
Handler.handle(
%{
"op" => "eval",
"code" => "(NReplIncrTest/foo)",
"session" => session,
"id" => "5"
},
manager
)
values = Enum.filter(responses, &Map.has_key?(&1, "value"))
assert hd(values)["value"] == ":original"
end
end
# --- TCP Integration Test ---
+133
View File
@@ -86,6 +86,139 @@ defmodule CljElixir.REPLTest do
end
end
describe "ns tracking" do
test "ns sets current namespace" do
state = REPL.new()
assert REPL.current_ns(state) == "user"
{:ok, _, state2} = REPL.eval("(ns ReplNsTest1)", state)
assert state2.current_ns == "ReplNsTest1"
assert REPL.current_ns(state2) == "ReplNsTest1"
end
test "ns with defn creates module" do
state = REPL.new()
{:ok, _, state2} =
REPL.eval("""
(ns ReplNsTest2)
(defn greet [name] (str "hello " name))
""", state)
assert state2.current_ns == "ReplNsTest2"
{:ok, result, _} = REPL.eval("(ReplNsTest2/greet \"world\")", state2)
assert result == "\"hello world\""
end
test "bare defn in active ns updates module" do
state = REPL.new()
# Set up initial module
{:ok, _, state2} =
REPL.eval("""
(ns ReplNsTest3)
(defn hello [] :hi)
""", state)
# Add a new function without ns
{:ok, result, state3} = REPL.eval("(defn world [] :earth)", state2)
assert result == "#'ReplNsTest3/world"
# Both functions should work
{:ok, r1, _} = REPL.eval("(ReplNsTest3/hello)", state3)
assert r1 == ":hi"
{:ok, r2, _} = REPL.eval("(ReplNsTest3/world)", state3)
assert r2 == ":earth"
end
test "redefine function in active ns" do
state = REPL.new()
{:ok, _, state2} =
REPL.eval("""
(ns ReplNsTest4)
(defn greet [] "v1")
""", state)
{:ok, r1, _} = REPL.eval("(ReplNsTest4/greet)", state2)
assert r1 == "\"v1\""
# Redefine greet
{:ok, _, state3} = REPL.eval("(defn greet [] \"v2\")", state2)
{:ok, r2, _} = REPL.eval("(ReplNsTest4/greet)", state3)
assert r2 == "\"v2\""
end
test "bare defn without ns evals plain" do
state = REPL.new()
# No namespace set — def outside module should fail
{:error, _, _} = REPL.eval("(defn orphan [] :lost)", state)
end
test "expressions eval normally in ns context" do
state = REPL.new()
{:ok, _, state2} =
REPL.eval("""
(ns ReplNsTest5)
(defn add [a b] (+ a b))
""", state)
# Expression (no def) goes through plain eval
{:ok, result, _} = REPL.eval("(ReplNsTest5/add 3 4)", state2)
assert result == "7"
end
test "mixed defs and exprs in ns context" do
state = REPL.new()
{:ok, _, state2} =
REPL.eval("""
(ns ReplNsTest6)
(defn double [x] (* x 2))
""", state)
# Eval def + expression together
{:ok, result, state3} =
REPL.eval("""
(defn triple [x] (* x 3))
(ReplNsTest6/triple 5)
""", state2)
# Result should be the expression value
assert result == "15"
# Both functions should work
{:ok, r1, _} = REPL.eval("(ReplNsTest6/double 4)", state3)
assert r1 == "8"
end
test "switching namespace" do
state = REPL.new()
{:ok, _, state2} =
REPL.eval("""
(ns ReplNsTestA)
(defn a-fn [] :from-a)
""", state)
{:ok, _, state3} =
REPL.eval("""
(ns ReplNsTestB)
(defn b-fn [] :from-b)
""", state2)
assert state3.current_ns == "ReplNsTestB"
# Both modules still work
{:ok, r1, _} = REPL.eval("(ReplNsTestA/a-fn)", state3)
assert r1 == ":from-a"
{:ok, r2, _} = REPL.eval("(ReplNsTestB/b-fn)", state3)
assert r2 == ":from-b"
end
end
describe "REPL.balanced?" do
test "balanced parens" do
assert REPL.balanced?("(+ 1 2)")