-
-
Save stefco/a772f8186cd992d74f064044c17667cc to your computer and use it in GitHub Desktop.
lispy AST printer and reader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ---- @sexpr: S-expression to AST conversion ---- | |
is_expr(ex, head::Symbol) = (isa(ex, Expr) && (ex.head == head)) | |
is_expr(ex, head::Symbol, n::Int) = is_expr(ex, head) && length(ex.args) == n | |
macro sexpr(ex) | |
esc(sexpr_to_expr(ex)) | |
end | |
sexpr_to_expr(ex) = expr(:quote, ex) | |
sexpr_to_expr(ex::QuoteNode) = ex | |
function sexpr_to_expr(ex::Expr) | |
head, args = ex.head, ex.args | |
if head === :tuple | |
h = sexpr_to_head(args[1]) | |
expr(:call, :expr, h, {sexpr_to_expr(arg) for arg in args[2:end]}...) | |
elseif head === :quote; ex | |
else expr(:quote, ex) | |
end | |
end | |
sexpr_to_head(ex::Symbol) = expr(:quote, ex) | |
sexpr_to_head(ex::QuoteNode) = ex | |
function sexpr_to_head(ex) | |
if is_expr(ex, :quote, 1); ex | |
else error("@sexpr: Cannot interpret $ex as a head") | |
end | |
end | |
# ---- show_sexpr: print an AST as an S-expression ---- | |
show_sexpr(ex) = show_sexpr(OUTPUT_STREAM, ex) | |
show_sexpr(io::IO, ex) = show_sexpr(io, ex, 0) | |
show_sexpr(io::IO, ex, indent::Int) = show(io, ex) | |
const paren_quoted_syms = Set{Symbol}(:(:),:(::),:(:=),:(=),:(==),:(===),:(=>)) | |
function show_sexpr(io::IO, sym::Symbol, indent::Int) | |
if has(paren_quoted_syms, sym); print(io, ":($sym)") | |
else print(io, ":$sym") | |
end | |
end | |
const sexpr_indent_width = 2 | |
function show_sexpr(io::IO, ex::Expr, indent::Int) | |
inner = indent + sexpr_indent_width | |
if (ex.head === :block) inter, post = (",\n"*" "^inner, "\n"*" "^indent) | |
else inter, post = (", ", "") | |
end | |
print(io, '(') | |
show_sexpr(io, ex.head, inner) | |
for arg in ex.args | |
print(io, inter) | |
show_sexpr(io, arg, inner) | |
end | |
if length(ex.args) == 0; print(io, ",)") | |
else print(io, post, ')') | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
load("sexpr.jl") | |
ex = quote | |
function show_sexpr(io::IO, ex::Expr, indent::Int) | |
inner = indent + sexpr_indent_width | |
if (ex.head === :block) inter, post = (",\n"*" "^inner,"\n"*" "^indent) | |
else inter, post = (", ", "") | |
end | |
print(io, '(') | |
show_sexpr(io, ex.head, inner) | |
for arg in ex.args | |
print(io, inter) | |
show_sexpr(io, arg, inner) | |
end | |
if length(ex.args) == 0; print(io, ",)") | |
else print(io, post, ')') | |
end | |
end | |
end | |
show_sexpr(ex) | |
ex2 = @sexpr (:block, | |
(:line, 4), | |
(:function, (:call, :show_sexpr, (:(::), :io, :IO), (:(::), :ex, :Expr), (:(::), :indent, :Int)), (:block, | |
(:line, 5, $(symbol("test.jl"))), | |
(:(=), :inner, (:call, :+, :indent, :sexpr_indent_width)), | |
(:line, 6), | |
(:if, (:comparison, (:., :ex, (:quote, :head)), :(===), (:quote, :block)), (:block, | |
(:line, 6), | |
(:(=), (:tuple, :inter, :post), (:tuple, (:call, :*, ",\n", (:call, :^, " ", :inner)), (:call, :*, "\n", (:call, :^, " ", :indent)))) | |
), (:block, | |
(:line, 7), | |
(:(=), (:tuple, :inter, :post), (:tuple, ", ", "")) | |
)), | |
(:line, 10), | |
(:call, :print, :io, '('), | |
(:line, 11), | |
(:call, :show_sexpr, :io, (:., :ex, (:quote, :head)), :inner), | |
(:line, 12), | |
(:for, (:(=), :arg, (:., :ex, (:quote, :args))), (:block, | |
(:line, 13), | |
(:call, :print, :io, :inter), | |
(:line, 14), | |
(:call, :show_sexpr, :io, :arg, :inner) | |
)), | |
(:line, 16), | |
(:if, (:comparison, (:call, :length, (:., :ex, (:quote, :args))), :(==), 0), (:block, | |
(:line, 16), | |
(:block, | |
(:call, :print, :io, ",)") | |
) | |
), (:block, | |
(:line, 17), | |
(:call, :print, :io, :post, ')') | |
)) | |
)) | |
) | |
# Works if you're lucky. Seems to work with include("test.jl"), but not with load("test.jl"): | |
# the latter puts a longer path in the filename of the (:line, 5) expr. | |
@assert ex == ex2 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment