diff --git a/core.nom b/core.nom index 1376990..ced3873 100644 --- a/core.nom +++ b/core.nom @@ -11,36 +11,32 @@ rule "macro %spec %body": rule "macro block %spec %body": lua block ".." - |do - | local spec, body = vars.spec, vars.body - | local wrapper = function(compiler, vars, kind) - | if kind == "Expression" then - | compiler:error("Macro: "..spec.." was defined to be a block, but is being used as an expression.") - | end - | return body(compiler, vars, kind), true + |local spec, body = vars.spec, vars.body + |local wrapper = function(compiler, vars, kind) + | if kind == "Expression" then + | compiler:error("Macro: "..spec.." was defined to be a block, but is being used as an expression.") | end - | compiler:defmacro(spec, wrapper) - | return nil + | return ("do\\n"..body(compiler, vars, kind).."\\nend"), true |end + |compiler:defmacro(spec, wrapper) + |return nil # Compiler tools rule ["eval %code", "run %code"]: lua expr "compiler:run(vars.code)" macro "source code %code": - lua expr "utils.repr(vars.code.src, true)" + lua expr "compiler.utils.repr(vars.code.src, true)" rule "run file %filename": lua block ".." - |do - | local file = io.open(vars.filename) - | return compiler:run(file:read("*a")) - |end + |local file = io.open(vars.filename) + |return compiler:run(file:read("*a")) # Macro helper functions rule "%tree as lua block": lua block ".." - |do return compiler:tree_to_lua(vars.tree, 'Statement'), true end + |return compiler:tree_to_lua(vars.tree, 'Statement'), true rule "%tree as lua expr": lua expr ".." @@ -49,48 +45,44 @@ rule "%tree as lua expr": # Moonscript! macro block "moonscript block %moonscript_code": lua block ".." - |do - | local parse, compile = require('moonscript.parse'), require('moonscript.compile') - | local moon_code = compiler:tree_to_value(vars.moonscript_code, vars) - | local tree, err = parse.string(moon_code) - | if not tree then - | compiler:error("Failed to parse moonscript: "..err) - | end - | local lua_code, err, pos = compile.tree(tree) - | if not lua_code then - | compiler:error(compile.format_error(err, pos, moon_code)) - | end - | return "do\\n"..lua_code.."\\nend" + |local parse, compile = require('moonscript.parse'), require('moonscript.compile') + |local moon_code = compiler:tree_to_value(vars.moonscript_code, vars) + |local tree, err = parse.string(moon_code) + |if not tree then + | compiler:error("Failed to parse moonscript: "..err) |end + |local lua_code, err, pos = compile.tree(tree) + |if not lua_code then + | compiler:error(compile.format_error(err, pos, moon_code)) + |end + |return "do\\n"..lua_code.."\\nend" macro "moonscript %moonscript_code": lua block ".." - |do - | local parse, compile = require('moonscript.parse'), require('moonscript.compile') - | local moon_code = compiler:tree_to_value(vars.moonscript_code, vars) - | local tree, err = parse.string(moon_code) - | if not tree then - | compiler:error("Failed to parse moonscript: "..err) - | end - | local lua_code, err, pos = compile.tree(tree) - | if not lua_code then - | compiler:error(compile.format_error(err, pos, moon_code)) - | end - | return "(function(compiler, vars)\\n"..lua_code.."\\nend)(compiler, vars)" + |local parse, compile = require('moonscript.parse'), require('moonscript.compile') + |local moon_code = compiler:tree_to_value(vars.moonscript_code, vars) + |local tree, err = parse.string(moon_code) + |if not tree then + | compiler:error("Failed to parse moonscript: "..err) |end + |local lua_code, err, pos = compile.tree(tree) + |if not lua_code then + | compiler:error(compile.format_error(err, pos, moon_code)) + |end + |return "(function(compiler, vars)\\n"..lua_code.."\\nend)(compiler, vars)" # String functions rule "join %strs": lua block ".." |local str_bits = {} - |for i,bit in ipairs(vars.strs) do str_bits[i] = utils.repr(bit) end - |do return table.concat(str_bits) end + |for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr(bit) end + |return table.concat(str_bits) rule "join %strs with glue %glue": lua block ".." |local str_bits = {} - |for i,bit in ipairs(vars.strs) do str_bits[i] = utils.repr(bit) end - |do return table.concat(str_bits, vars.glue) end + |for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr(bit) end + |return table.concat(str_bits, vars.glue) rule ["capitalize %str", "%str capitalized"]: lua expr ".."|vars.str:gsub("%l", string.upper, 1) @@ -142,22 +134,22 @@ macro "- %a": ".."|-(\%a as lua expr\) macro "not %a": ".."|not (\%a as lua expr\) rule "%a == %b": - lua expr "((vars.a == vars.b) or utils.equivalent(vars.a, vars.b))" + lua expr "((vars.a == vars.b) or compiler.utils.equivalent(vars.a, vars.b))" rule "%a != %b": - lua expr "((vars.a ~= vars.b) or not utils.equivalent(vars.a, vars.b))" + lua expr "((vars.a ~= vars.b) or not compiler.utils.equivalent(vars.a, vars.b))" macro "say %str": - ".."|compiler:writeln(utils.repr(\%str as lua expr\)) + ".."|compiler:writeln(compiler.utils.repr(\%str as lua expr\)) # Control flow rule "do %action": lua expr "vars.action(compiler, setmetatable({}, {__index=vars}))" macro block "return %return-value": - ".."|do return \%return-value as lua expr\ end + ".."|return \%return-value as lua expr\ macro block "return": - "do return nil end" + "return nil" # Conditionals macro block "if %condition %if_body": @@ -185,24 +177,20 @@ macro "%if_expr if %condition else %else_expr": # For loop macro block "for %varname in %iterable %body": let "varname" = (%varname as lua expr) - ".."|do - | local old_loopval = vars[\%varname\] - | for i,value in ipairs(\%iterable as lua expr\) do - | vars[\%varname\] = value - | \(lua expr "vars.body.value.value") as lua block\ - | end - | vars[\%varname\] = old_loopval + ".."|local old_loopval = vars[\%varname\] + |for i,value in ipairs(\%iterable as lua expr\) do + | vars[\%varname\] = value + | \(lua expr "vars.body.value.value") as lua block\ |end + |vars[\%varname\] = old_loopval macro block "for all %iterable %body": - ".."|do - | local old_loopval = vars.it - | for i,value in ipairs(\%iterable as lua expr\) do - | vars.it = value - | \(lua expr "vars.body.value.value") as lua block\ - | end - | vars.it = old_loopval + ".."|local old_loopval = vars.it + |for i,value in ipairs(\%iterable as lua expr\) do + | vars.it = value + | \(lua expr "vars.body.value.value") as lua block\ |end + |vars.it = old_loopval # List Comprehension # TODO: maybe make this lazy, or a lazy version? @@ -249,45 +237,45 @@ macro "%key = %value for all %iterable": # Number ranges rule "%start up to %stop": - lua expr "utils.range(vars.start,vars.stop-1)" + lua expr "compiler.utils.range(vars.start,vars.stop-1)" rule ["%start thru %stop", "%start through %stop"]: - lua expr "utils.range(vars.start,vars.stop)" + lua expr "compiler.utils.range(vars.start,vars.stop)" rule "%start down to %stop": - lua expr "utils.range(vars.start,vars.stop+1,-1)" + lua expr "compiler.utils.range(vars.start,vars.stop+1,-1)" rule ["%start down thru %stop", "%start down through %stop"]: - lua expr "utils.range(vars.start,vars.stop,-1)" + lua expr "compiler.utils.range(vars.start,vars.stop,-1)" rule "%start up to %stop via %step": - lua expr "utils.range(vars.start,vars.stop-1,vars.step)" + lua expr "compiler.utils.range(vars.start,vars.stop-1,vars.step)" rule ["%start thru %stop via %step", "%start through %stop via %step"]: - lua expr "utils.range(vars.start,vars.stop,vars.step)" + lua expr "compiler.utils.range(vars.start,vars.stop,vars.step)" rule "%start down to %stop via %step": - lua expr "utils.range(vars.start,vars.stop+1,-vars.step)" + lua expr "compiler.utils.range(vars.start,vars.stop+1,-vars.step)" rule ["%start down thru %stop via %step", "%start down through %stop via %step"]: - lua expr "utils.range(vars.start,vars.stop,-vars.step)" + lua expr "compiler.utils.range(vars.start,vars.stop,-vars.step)" # Common utility functions rule ["random number"]: lua expr "math.random()" -rule ["sum of %items"]: lua expr "utils.sum(vars.items)" -rule ["product of %items"]: lua expr "utils.product(vars.items)" -rule ["all of %items"]: lua expr "utils.all(vars.items)" -rule ["any of %items"]: lua expr "utils.any(vars.items)" -rule ["avg of %items", "average of %items"]: lua expr "(utils.sum(vars.items)/#vars.items)" +rule ["sum of %items"]: lua expr "compiler.utils.sum(vars.items)" +rule ["product of %items"]: lua expr "compiler.utils.product(vars.items)" +rule ["all of %items"]: lua expr "compiler.utils.all(vars.items)" +rule ["any of %items"]: lua expr "compiler.utils.any(vars.items)" +rule ["avg of %items", "average of %items"]: lua expr "(compiler.utils.sum(vars.items)/#vars.items)" rule ["min of %items", "smallest of %items", "lowest of %items"]: - lua expr "utils.min(vars.items)" + lua expr "compiler.utils.min(vars.items)" rule ["max of %items", "biggest of %items", "largest of %items", "highest of %items"]: - lua expr "utils.min(vars.items)" + lua expr "compiler.utils.min(vars.items)" rule ["min of %items with respect to %keys"]: - lua expr "utils.min(vars.items, vars.keys)" + lua expr "compiler.utils.min(vars.items, vars.keys)" rule ["max of %items with respect to %keys"]: - lua expr "utils.max(vars.items, vars.keys)" + lua expr "compiler.utils.max(vars.items, vars.keys)" # List/dict functions macro [..] @@ -395,7 +383,7 @@ rule "error %msg": |compiler:error(vars.msg) macro block "test %code yields %expected": - let "generated" = (lua expr "utils.repr(compiler:stringify_tree(vars.code.value.value), true)") + let "generated" = (lua expr "compiler.utils.repr(compiler:stringify_tree(vars.code.value.value), true)") let "expected" = (%expected as lua expr) if (%generated != %expected): say "Test failed!" diff --git a/nomsu.lua b/nomsu.lua index 4dc8b51..c8e12f7 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -259,6 +259,48 @@ do end return retval end, + serialize = function(self, obj) + local _exp_0 = type(obj) + if "function" == _exp_0 then + return "assert(load(" .. utils.repr(string.dump(obj), true) .. "))" + elseif "table" == _exp_0 then + if utils.is_list(obj) then + return "{" .. tostring(table.concat((function() + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #obj do + local i = obj[_index_0] + _accum_0[_len_0] = self:serialize(i) + _len_0 = _len_0 + 1 + end + return _accum_0 + end)(), ", ")) .. "}" + else + return "{" .. tostring(table.concat((function() + local _accum_0 = { } + local _len_0 = 1 + for k, v in pairs(obj) do + _accum_0[_len_0] = "[" .. tostring(self:serialize(k)) .. "]= " .. tostring(self:serialize(v)) + _len_0 = _len_0 + 1 + end + return _accum_0 + end)(), ", ")) .. "}" + end + elseif "number" == _exp_0 then + return utils.repr(obj, true) + elseif "string" == _exp_0 then + return utils.repr(obj, true) + else + return error("Serialization not implemented for: " .. tostring(type(obj))) + end + end, + deserialize = function(self, str) + local lua_thunk, err = load("return (function(compiler,vars)\n return " .. str .. "\n end)") + if not lua_thunk then + error("Failed to compile generated code:\n" .. tostring(str) .. "\n\n" .. tostring(err)) + end + return (lua_thunk())(self, { }) + end, parse = function(self, str) if self.debug then self:writeln("PARSING:\n" .. tostring(str)) @@ -331,7 +373,7 @@ do return tree end, tree_to_value = function(self, tree, vars) - local code = "\n local utils = require('utils')\n return (function(compiler, vars)\nreturn " .. tostring(self:tree_to_lua(tree)) .. "\nend)" + local code = "\n return (function(compiler, vars)\nreturn " .. tostring(self:tree_to_lua(tree)) .. "\nend)" local lua_thunk, err = load(code) if not lua_thunk then error("Failed to compile generated code:\n" .. tostring(code) .. "\n\n" .. tostring(err)) @@ -364,7 +406,7 @@ do for _index_0 = 1, #_list_0 do local statement = _list_0[_index_0] local code = to_lua(statement, "Statement") - local lua_code = "\n local utils = require('utils')\n return (function(compiler, vars)\n" .. tostring(code) .. "\nend)" + local lua_code = "\n return (function(compiler, vars)\n" .. tostring(code) .. "\nend)" local lua_thunk, err = load(lua_code) if not lua_thunk then error("Failed to compile generated code:\n" .. tostring(code) .. "\n\n" .. tostring(err) .. "\n\nProduced by statement:\n" .. tostring(utils.repr(statement))) @@ -385,9 +427,8 @@ do elseif "Thunk" == _exp_0 then assert(tree.value.type == "Block", "Non-block value in Thunk") add([[ (function(compiler, vars) - local ret]]) - add(to_lua(tree.value)) - add([[ return ret + local ret + ]] .. to_lua(tree.value) .. "\n" .. [[ return ret end) ]]) elseif "Statement" == _exp_0 then @@ -455,7 +496,7 @@ do table.insert(concat_parts, utils.repr(string_buffer, true)) string_buffer = "" end - table.insert(concat_parts, "utils.repr(" .. tostring(to_lua(bit)) .. ")") + table.insert(concat_parts, "compiler.utils.repr(" .. tostring(to_lua(bit)) .. ")") end end end @@ -716,36 +757,12 @@ do if kind == "Expression" then error("Expected to be in statement.") end - return self:tree_to_value(vars.lua_code, vars), true + return "do\n" .. self:tree_to_value(vars.lua_code, vars) .. "\nend", true end) self:defmacro([[lua expr %lua_code]], function(self, vars, kind) local lua_code = vars.lua_code.value return self:tree_to_value(vars.lua_code, vars) end) - self:def("rule %spec %body", function(self, vars) - return self:def(vars.spec, vars.body) - end) - self:defmacro([[macro %spec %body]], function(self, vars, kind) - if kind == "Expression" then - error("Macro definitions cannot be used as expressions.") - end - self:defmacro(self:tree_to_value(vars.spec, vars), self:tree_to_value(vars.body, vars)) - return "", true - end) - self:defmacro([[macro block %spec %body]], function(self, vars, kind) - if kind == "Expression" then - error("Macro definitions cannot be used as expressions.") - end - local invocation = self:tree_to_value(vars.spec, vars) - local fn = self:tree_to_value(vars.body, vars) - self:defmacro(invocation, (function(self, vars, kind) - if kind == "Expression" then - error("Macro: " .. tostring(invocation) .. " was defined to be a block, not an expression.") - end - return fn(self, vars, kind), true - end)) - return "", true - end) return self:def("run file %filename", function(self, vars) local file = io.open(vars.filename) return self:run(file:read('*a')) @@ -764,6 +781,7 @@ do self.write = function(self, ...) return io.write(...) end + self.utils = utils end, __base = _base_0, __name = "NomsuCompiler" @@ -816,8 +834,7 @@ if arg and arg[1] then else output = io.open(arg[2], 'w') end - output:write([[ local utils = require('utils') - local load = function() + output:write([[ local load = function() ]]) output:write(code) output:write([[ diff --git a/nomsu.moon b/nomsu.moon index 513f8e8..64d16b0 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -97,6 +97,7 @@ class NomsuCompiler @debug = false @initialize_core! @write = (...)=> io.write(...) + @utils = utils writeln:(...)=> @write(...) @@ -167,6 +168,30 @@ class NomsuCompiler if @debug @writeln "\nGENERATED LUA CODE:\n#{code}" return retval + + serialize: (obj)=> + switch type(obj) + when "function" + "assert(load("..utils.repr(string.dump(obj), true).."))" + when "table" + if utils.is_list obj + "{#{table.concat([@serialize(i) for i in *obj], ", ")}}" + else + "{#{table.concat(["[#{@serialize(k)}]= #{@serialize(v)}" for k,v in pairs(obj)], ", ")}}" + when "number" + utils.repr(obj, true) + when "string" + utils.repr(obj, true) + else + error "Serialization not implemented for: #{type(obj)}" + + deserialize: (str)=> + lua_thunk, err = load("return (function(compiler,vars) + return "..str.." + end)") + if not lua_thunk + error("Failed to compile generated code:\n#{str}\n\n#{err}") + return (lua_thunk!)(self, {}) parse: (str)=> if @debug @@ -240,9 +265,7 @@ class NomsuCompiler return tree tree_to_value: (tree, vars)=> - -- TODO: clean up require utils code = " - local utils = require('utils') return (function(compiler, vars)\nreturn #{@tree_to_lua(tree)}\nend)" lua_thunk, err = load(code) if not lua_thunk @@ -269,9 +292,7 @@ class NomsuCompiler for statement in *tree.value.body.value code = to_lua(statement, "Statement") -- Run the fuckers as we go - -- TODO: clean up repeated loading of utils? lua_code = " - local utils = require('utils') return (function(compiler, vars)\n#{code}\nend)" lua_thunk, err = load(lua_code) if not lua_thunk @@ -292,9 +313,8 @@ class NomsuCompiler assert tree.value.type == "Block", "Non-block value in Thunk" add [[ (function(compiler, vars) - local ret]] - add to_lua(tree.value) - add [[ + local ret + ]]..to_lua(tree.value).."\n"..[[ return ret end) ]] @@ -339,7 +359,7 @@ class NomsuCompiler if string_buffer ~= "" table.insert concat_parts, utils.repr(string_buffer, true) string_buffer = "" - table.insert concat_parts, "utils.repr(#{to_lua(bit)})" + table.insert concat_parts, "compiler.utils.repr(#{to_lua(bit)})" if string_buffer ~= "" table.insert concat_parts, utils.repr(string_buffer, true) @@ -535,32 +555,12 @@ class NomsuCompiler @defmacro [[lua block %lua_code]], (vars, kind)=> if kind == "Expression" then error("Expected to be in statement.") - return @tree_to_value(vars.lua_code, vars), true + return "do\n"..@tree_to_value(vars.lua_code, vars).."\nend", true @defmacro [[lua expr %lua_code]], (vars, kind)=> lua_code = vars.lua_code.value return @tree_to_value(vars.lua_code, vars) - @def "rule %spec %body", (vars)=> - @def vars.spec, vars.body - - @defmacro [[macro %spec %body]], (vars, kind)=> - if kind == "Expression" - error("Macro definitions cannot be used as expressions.") - @defmacro @tree_to_value(vars.spec, vars), @tree_to_value(vars.body, vars) - return "", true - - @defmacro [[macro block %spec %body]], (vars, kind)=> - if kind == "Expression" - error("Macro definitions cannot be used as expressions.") - invocation = @tree_to_value(vars.spec, vars) - fn = @tree_to_value(vars.body, vars) - @defmacro invocation, ((vars,kind)=> - if kind == "Expression" - error("Macro: #{invocation} was defined to be a block, not an expression.") - return fn(@,vars,kind), true) - return "", true - @def "run file %filename", (vars)=> file = io.open(vars.filename) return @run(file\read('*a')) @@ -585,7 +585,6 @@ if arg and arg[1] else io.open(arg[2], 'w') output\write [[ - local utils = require('utils') local load = function() ]] output\write(code)