Skip to content

Instantly share code, notes, and snippets.

@brenhinkeller
Last active July 6, 2024 10:59
Show Gist options
  • Save brenhinkeller/44051118c2f9d18b26dc765466f749ba to your computer and use it in GitHub Desktop.
Save brenhinkeller/44051118c2f9d18b26dc765466f749ba to your computer and use it in GitHub Desktop.
Bare-bones REPL for Julia's secret built-in s-expression syntax, in under 30 lines
to_expr(x) = x
to_expr(t::Tuple) = Expr(to_expr.(t)...) # Recursive to_expr implementation courtesy of Mason Protter
lisparse(x) = to_expr(eval(Meta.parse(x))) # Note that the `eval` in here means that any normal (non-s-expression) Julia syntax gets treated a bit like a preprocessor macro: evaluated _before_ the s-expression syntax is compiled and evaluated
function lispmode()
# READ
printstyled("\nlisp> ", color=:magenta, bold=true)
l = readline()
while l !== "(:exit)"
try # So we don't get thrown out of the mode
# EVAL
result = eval(lisparse(l))
# PRINT, making sure that printed results are also in s-expression syntax where applicable
if isa(result, Expr)
Meta.show_sexpr(result)
println()
elseif isa(result, Tuple)
Meta.show_sexpr(:($(result...),))
println()
else
display(result)
end
catch e
display(e)
end
# LOOP
printstyled("\nlisp> ", color=:magenta, bold=true)
l = readline()
end
end
# Enter the Julia-lisp repl by calling the `lispmode()` function, exit with `(:exit)`
# Some examples:
# 1+1
# (:call, :+, 1, 1)
#
# Define a function and call it
#(:(=), (:call, :square, :x), (:call, :^, :x, 2))
#(:call, :square, 5)
#
# time to get meta -- quote and eval
#(:(=), :foo, (:quote, (:call, :+, 1, 1)))
#(:call, :eval, :foo)
## - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# N.B. you can make a much more functional Julia-lisp REPL (with proper readline wrapping, etc.)
# using with ReplMaker.jl if you're willing to add a package dependency or two!
# All you need in that case is:
# Core parsing functions for s-exprs
to_expr(x) = x # Thanks to Mason Protter for the recursive to_expr implementation!
to_expr(t::Tuple) = isa(t[1], Symbol) ? Expr(to_expr.(t)...) : Expr(:tuple, to_expr.(t)...) # Tweak parsing of tuples for convenience
lisparse(x) = to_expr(eval(Meta.parse(x)))
# Provide a completion checker
using REPL: LineEdit
iscomplete(x) = true
iscomplete(ex::Expr) = (ex.head == :incomplete) ? false : true
valid_julia(s) = iscomplete(Meta.parse(String(take!(copy(LineEdit.buffer(s))))))
# Custom replshow functions for this REPL mode
lispshow(io, M, x) = (show(io, M, x); println(io))
lispshow(io, M::MIME"text/plain", x::Expr) = (Meta.show_sexpr(io, x); println(io))
lispshow(io, M::MIME"text/plain", x::Tuple) = (Meta.show_sexpr(io, :($(x...),)); println(io))
# Define the custom repl
using ReplMaker
initrepl(lisparse,
prompt_text="lisp> ",
prompt_color=:magenta,
start_key=')',
show_function=lispshow,
valid_input_checker=valid_julia,
mode_name="lisp_mode"
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment