Created
November 20, 2012 21:06
-
-
Save toivoh/4121122 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
Simple lispy AST printer and reader.
Examples:
Thanks to the mysterious ways of AST interpolation, interpolation seems to work in the
@sexpr
macro too:The macro is somewhat forgiving, adding quotations to unquoted elements.
So you can omit the quotes on symbols when it doesn't confuse the julia parser, e.g.
works just as well. This also allows to use expressions instead of symbols as elements in the S-expressions:
This could be useful if there's a tricky bit that you want to do with S-expressions, but maybe not the whole AST.