diff --git a/src/LispSyntax.jl b/src/LispSyntax.jl index 780ab7f..584dcd6 100644 --- a/src/LispSyntax.jl +++ b/src/LispSyntax.jl @@ -1,7 +1,7 @@ module LispSyntax include("parser.jl") -export sx, desx, codegen, @lisp_str, assign_reader_dispatch +export sx, desx, codegen, @lisp_str, assign_reader_dispatch, include_lisp # Internal types mutable struct s_expr @@ -158,4 +158,27 @@ macro lisp_str(str) return esc(lisp_eval_helper(str)) end + +""" + include_lisp(mod, source) + +Parse expressions from `source` (which may be an `IO` or file name) and +evaluate them sequentially as top level code in module `mod`. +""" +function include_lisp(mod::Module, filename::AbstractString) + open(filename) do io + include_lisp(mod, io) + end +end + +function include_lisp(mod::Module, io::IO) + content = Base.read(io, String) + res = nothing + for sxpr in parse_one(content, top_level) + ex = codegen(desx(sxpr)) + res = Base.eval(mod, ex) + end + res +end + end # module diff --git a/src/parser.jl b/src/parser.jl index ac1a8b4..3505b11 100644 --- a/src/parser.jl +++ b/src/parser.jl @@ -37,6 +37,8 @@ expr.matcher = doubley | floaty | inty | uchary | achary | chary | stringy | boo macrosymy | dispatchy | sexpr | hashy | curly | bracket | quot | quasi | tildeseq | tilde +top_level = Repeat(~opt_ws + expr) + ~opt_ws + Eos() + function read(str) x = parse_one(str, expr) x[1] diff --git a/test/lisp.clj b/test/lisp.clj new file mode 100644 index 0000000..74eeed9 --- /dev/null +++ b/test/lisp.clj @@ -0,0 +1,9 @@ +; File containing top level Clojure-syntax code + +(defn func_in_clj_file [x y] (string "x = " x "; y = " y)) + +(def some_global 1.23f) + +; Following should be the return value of include_lisp +(let [not_a_global 10] + (* not_a_global not_a_global)) diff --git a/test/runtests.jl b/test/runtests.jl index 284eecd..9074739 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -252,23 +252,32 @@ end #------------------------------------------------------------------------------- lisp"(import ParserCombinator)" -@testset "Import" begin - @test lisp"(@E_str \"S\")" == E"S" -end +@test lisp"(@E_str \"S\")" == E"S" #------------------------------------------------------------------------------- -@testset "Bug reports" begin - @test lisp"""(def game_map (Dict - (=> 'living_room - '((you are in the living room - of a wizards house - there is a wizard - snoring loudly on the couch -) - (west door garden) - (upstairs stairway attic)))))""" == - Dict(:living_room => - Any[ Any[ :you, :are, :in, :the, :living, :room, :of, :a, :wizards, :house, :-, - :there, :is, :a, :wizard, :snoring, :loudly, :on, :the, :couch, :- ], - Any[ :west, :door, :garden ], - Any[ :upstairs, :stairway, :attic ] ]) +@testset "Include from file" begin + # Return value is value of last expression + @test include_lisp(@__MODULE__, "lisp.clj") == 100 + # Test objects defined in lisp.clj + @test func_in_clj_file(1, 2) == "x = 1; y = 2" + @test func_in_clj_file(10, 20) == "x = 10; y = 20" + @test some_global === 1.23f0 + @test !isdefined(@__MODULE__, :not_a_global) end +# ---------------------------------------------------------------------------------------------------------------------- +# Bug reports +# ---------------------------------------------------------------------------------------------------------------------- +@test lisp"""(def game_map (Dict + (=> 'living_room + '((you are in the living room + of a wizards house - there is a wizard + snoring loudly on the couch -) + (west door garden) + (upstairs stairway attic)))))""" == Dict(:living_room => + Any[ Any[ :you, :are, :in, :the, :living, :room, :of, :a, :wizards, :house, :-, + :there, :is, :a, :wizard, :snoring, :loudly, :on, :the, :couch, :- ], + Any[ :west, :door, :garden ], + Any[ :upstairs, :stairway, :attic ] ]) + +