Skip to content

Commit

Permalink
Add tests and bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
TotalVerb committed May 20, 2016
1 parent cd923cb commit 377e152
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 99 deletions.
93 changes: 3 additions & 90 deletions src/PythonSyntax.jl
Expand Up @@ -9,99 +9,12 @@ include("symbol.jl")
include("ops.jl")
include("function.jl")
include("class.jl")
include("expr.jl")
include("control.jl")

export pyparse, @pysyntax_str, @pymodule_str


function transpileassign(t, aug=:(=))
targets = transpile.(t[:targets])
lhs = if length(targets) == 1
targets[1]
else
Expr(:tuple, targets...)
end

Expr(:(=), lhs, transpile(t[:value]))
end


function transpilefor(t)
if length(t[:orelse]) > 0
error("PythonSyntax cannot currently handle else for loops.")
end
quote
for $(transpile(t[:target])) in $(transpile(t[:iter]))
$(transpile(t[:body]))
end
end
end

function transpilewhile(t)
if length(t[:orelse]) > 0
error("PythonSyntax cannot currently handle else for loops.")
end
quote
while $(transpile(t[:test]))
$(transpile(t[:body]))
end
end
end

function transpilewith(t)
# FIXME: support
error("PythonSyntax does not yet support with.")
end

function transpileraise(t)
if t[:exc] == nothing
quote error() end
else
quote throw($(transpile(t[:exc]))) end
end
end

function transpiletry(t)
# FIXME: support
error("PythonSyntax does not yet support try.")
end

function transpilecmp(t)
args = Any[transpile(t[:left])]
for (op, val) in zip(t[:ops], t[:comparators])
push!(args, jlop(op), transpile(val))
end
Expr(:comparison, args...)
end

function transpileslice(t)
@match pytypeof(t) begin
ast.Slice => [Expr(:(:),
t[:lower] == nothing ? 1 : transpile(t[:lower]),
t[:step] == nothing ? 1 : transpile(t[:step]),
t[:upper] == nothing ? :end : transpile(t[:upper]))]
ast.ExtSlice => transpileslice.(t[:dims])
ast.Index => [transpile(t[:value])]
end
end

transpilerawcall(t) = Expr(:call, transpile(t[:func]), transpile.(t[:args])...)

"""
Transpiles a call. Traps some dunder names, like __jl_macro__.
"""
function transpilecall(t)
if pytypeof(t[:func]) == ast.Name
@match t[:func][:id] begin
"__jlmc__" => Expr(:macrocall,
Symbol('@', jlident(t[:args][1][:id])),
transpile.(t[:args][2:end])...)
_ => transpilerawcall(t)
end
else
transpilerawcall(t)
end
end

function transpile(s::Symbol, t::Vector)
Expr(s, transpile.(t)...)
end
Expand All @@ -117,7 +30,7 @@ function transpile(t::PyObject)
ast.Return => Expr(:return, transpile(t[:value]))
ast.Delete => error("Delete not yet supported.")
ast.Assign => transpileassign(t)
ast.AugAssign => transpileassign(t, jlaugop(t[:op]))
ast.AugAssign => transpileaugassign(t)
ast.For => transpilefor(t)
ast.AsyncFor => transpilefor(t) # FIXME: async
ast.While => transpilewhile(t)
Expand Down
6 changes: 2 additions & 4 deletions src/class.jl
Expand Up @@ -123,10 +123,8 @@ function transpilecls(t)
fields = sort(collect(guessfields(t)))

# Get the inner constructors and body methods
inner = map(x -> transpileinit(x, clsname),
filter(isconstructor, t[:body]))
body = map(x -> transpilemethod(x, clsname),
filter(ismethod, t[:body]))
inner = map(x -> transpileinit(x, clsname), filter(isconstructor, t[:body]))
body = map(x -> transpilemethod(x, clsname), filter(ismethod, t[:body]))

quote
type $clsname
Expand Down
39 changes: 39 additions & 0 deletions src/control.jl
@@ -0,0 +1,39 @@
function transpilefor(t)
if length(t[:orelse]) > 0
error("PythonSyntax cannot currently handle else for loops.")
end
quote
for $(transpile(t[:target])) in $(transpile(t[:iter]))
$(transpile(t[:body]))
end
end
end

function transpilewhile(t)
if length(t[:orelse]) > 0
error("PythonSyntax cannot currently handle else for loops.")
end
quote
while $(transpile(t[:test]))
$(transpile(t[:body]))
end
end
end

function transpilewith(t)
# FIXME: support
error("PythonSyntax does not yet support with.")
end

function transpileraise(t)
if t[:exc] == nothing
quote error() end
else
quote throw($(transpile(t[:exc]))) end
end
end

function transpiletry(t)
# FIXME: support
error("PythonSyntax does not yet support try.")
end
55 changes: 55 additions & 0 deletions src/expr.jl
@@ -0,0 +1,55 @@
function transpileassign(t)
targets = transpile.(t[:targets])
lhs = if length(targets) == 1
targets[1]
else
Expr(:tuple, targets...)
end

Expr(:(=), lhs, transpile(t[:value]))
end

function transpileaugassign(t)
lhs = transpile(t[:target])
op = jlaugop(t[:op])
rhs = transpile(t[:value])
Expr(op, lhs, rhs)
end


function transpilecmp(t)
args = Any[transpile(t[:left])]
for (op, val) in zip(t[:ops], t[:comparators])
push!(args, jlop(op), transpile(val))
end
Expr(:comparison, args...)
end

function transpileslice(t)
@match pytypeof(t) begin
ast.Slice => [Expr(:(:),
t[:lower] == nothing ? 1 : transpile(t[:lower]),
t[:step] == nothing ? 1 : transpile(t[:step]),
t[:upper] == nothing ? :end : transpile(t[:upper]))]
ast.ExtSlice => transpileslice.(t[:dims])
ast.Index => [transpile(t[:value])]
end
end

transpilerawcall(t) = Expr(:call, transpile(t[:func]), transpile.(t[:args])...)

"""
Transpiles a call. Traps some dunder names, like __jl_macro__.
"""
function transpilecall(t)
if pytypeof(t[:func]) == ast.Name
@match t[:func][:id] begin
"__jlmc__" => Expr(:macrocall,
Symbol('@', jlident(t[:args][1][:id])),
transpile.(t[:args][2:end])...)
_ => transpilerawcall(t)
end
else
transpilerawcall(t)
end
end
6 changes: 2 additions & 4 deletions src/function.jl
@@ -1,6 +1,4 @@
function kw(args)
Expr(:parameters, args...)
end
kw(args) = Expr(:parameters, args...)

var(args, ::Void) = args
var(args, vararg) = [args; [Expr(:(...), vararg)]]
Expand Down Expand Up @@ -73,7 +71,7 @@ end
function transpilefn(t)
fnbody = transpile(t[:body])

kw, a = transpileargs(t[:args])
a, kw = transpileargs(t[:args])
fnheader = header(a, kw)
Expr(:function, fnheader, fnbody)
end
2 changes: 1 addition & 1 deletion src/ops.jl
Expand Up @@ -35,7 +35,7 @@ end

"""Return Julia augmented version of Python operator."""
function jlaugop(t)
jlident(jlop(t), :(=))
Symbol(jlop(t), :(=))
end

"""Return symbol for Julia boolean operator."""
Expand Down
5 changes: 5 additions & 0 deletions test/arithmetic.jl
@@ -1,6 +1,11 @@
@testset "Arithmetic" begin

@test pysyntax"10" == 10
@test pysyntax"+10" == 10
@test pysyntax"-10" == -10

@test pysyntax"1 + 2" == 3
@test pysyntax"1 - 2" == -1
@test pysyntax"8 * 5" == 40
@test pysyntax"2 ** 5" == 32
@test pysyntax"10 // 3" == 3
Expand Down
17 changes: 17 additions & 0 deletions test/control.jl
@@ -0,0 +1,17 @@
@testset "Control" begin

@test pysyntax"1 if True else 0" == 1
@test pysyntax"1 if False else 0" == 0

@test pysyntax"
x = 1
if 1 + 1 == 2:
x *= 3
x" == 3

@test pysyntax"
x = 1
if 1 + 1 == 2:
x *= 3
x" == 3
end # testset
26 changes: 26 additions & 0 deletions test/io.jl
Expand Up @@ -21,4 +21,30 @@ show(s, [1, 2, 3])
takebuf_string(s)
""" == "[1,2,3]"

# FizzBuzz!
@test pysyntax"""
s = IOBuffer()
for i in range(1, 10):
if i % 15 == 0:
println(s, "FizzBuzz")
elif i % 3 == 0:
println(s, "Fizz")
elif i % 5 == 0:
println(s, "Buzz")
else:
println(s, i)
takebuf_string(s)
""" == """
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
"""

end # testset
1 change: 1 addition & 0 deletions test/runtests.jl
Expand Up @@ -2,6 +2,7 @@ using PythonSyntax
using Base.Test

include("arithmetic.jl")
include("control.jl")
include("io.jl")
include("functions.jl")
include("classes.jl")
Expand Down

0 comments on commit 377e152

Please sign in to comment.