diff options
| author | Bruce Hill <bitbucket@bruce-hill.com> | 2017-09-21 04:51:02 -0700 |
|---|---|---|
| committer | Bruce Hill <bitbucket@bruce-hill.com> | 2017-09-21 04:51:02 -0700 |
| commit | c82e4f34097f7a27d9cb5d2d254ccb6916d63694 (patch) | |
| tree | 6b60dae84c8064a5a19d7d2c5963b4f056a06010 | |
| parent | 34f50056ad3db1b503fdb3f125f44a8e73c5eb0d (diff) | |
Cleaning up and simplifying code.
| -rw-r--r-- | nomsu.lua | 170 | ||||
| -rwxr-xr-x | nomsu.moon | 134 |
2 files changed, 114 insertions, 190 deletions
@@ -1,10 +1,20 @@ local re = require('re') local lpeg = require('lpeg') local utils = require('utils') +local insert = table.insert local INDENT = " " lpeg.setmaxstack(10000) local P, V, S, Cg, C, Cp, B, Cmt P, V, S, Cg, C, Cp, B, Cmt = lpeg.P, lpeg.V, lpeg.S, lpeg.Cg, lpeg.C, lpeg.Cp, lpeg.B, lpeg.Cmt +local STRING_ESCAPES = { + n = "\n", + t = "\t", + b = "\b", + a = "\a", + v = "\v", + f = "\f", + r = "\r" +} local NomsuCompiler do local _class_0 @@ -24,7 +34,7 @@ do if not (self:check_permission(fn_name)) then self:error("You do not have the authority to call: " .. tostring(fn_name)) end - table.insert(self.callstack, fn_name) + insert(self.callstack, fn_name) local fn, arg_names fn, arg_names = fn_info.fn, fn_info.arg_names local args @@ -38,11 +48,8 @@ do if self.debug then self:writeln("Calling " .. tostring(fn_name) .. " with args: " .. tostring(utils.repr(args))) end - local ok, ret = pcall(fn, self, args) + local ret = fn(self, args) table.remove(self.callstack) - if not ok then - error(ret) - end return ret end, check_permission = function(self, fn_name) @@ -93,7 +100,7 @@ do repeat local item = _list_0[_index_0] if item.type == "String" then - table.insert(invocations, self:tree_to_value(item, vars)) + insert(invocations, self:tree_to_value(item, vars)) _continue_0 = true break end @@ -105,14 +112,14 @@ do for _index_1 = 1, #_list_1 do local token = _list_1[_index_1] if token.type == "Word" then - table.insert(name_bits, token.value) + insert(name_bits, token.value) elseif token.type == "Var" then - table.insert(name_bits, token.src) + insert(name_bits, token.src) else self:error("Unexpected token type in definition: " .. tostring(token.type) .. " (expected Word or Var)") end end - table.insert(invocations, table.concat(name_bits, " ")) + insert(invocations, table.concat(name_bits, " ")) _continue_0 = true until true if not _continue_0 then @@ -149,7 +156,7 @@ do end _arg_names = _accum_0 end - table.insert(invocations, invocation) + insert(invocations, invocation) if prev_arg_names then if not utils.equivalent(utils.set(prev_arg_names), utils.set(_arg_names)) then self:error("Conflicting argument names " .. tostring(utils.repr(prev_arg_names)) .. " and " .. tostring(utils.repr(_arg_names)) .. " for " .. tostring(utils.repr(text))) @@ -178,17 +185,6 @@ do self.defs[invocation] = fn_info end end, - run = function(self, text, filename) - if self.debug then - self:writeln("RUNNING TEXT:\n" .. tostring(text)) - end - local code, retval = self:compile(text, filename) - if self.debug then - self:writeln("\nGENERATED LUA CODE:\n" .. tostring(code)) - self:writeln("\nPRODUCED RETURN VALUE:\n" .. tostring(retval)) - end - return retval - end, serialize = function(self, obj) local _exp_0 = type(obj) if "function" == _exp_0 then @@ -236,44 +232,26 @@ do if self.debug then self:writeln("PARSING:\n" .. tostring(str)) end - local get_line_indentation - get_line_indentation = function(line) - local indent_amounts = { - [" "] = 1, - ["\t"] = 4 - } - do - local sum = 0 - local leading_space = line:match("[\t ]*") - for c in leading_space:gmatch("[\t ]") do - sum = sum + indent_amounts[c] - end - return sum - end - end local indent_stack = { 0 } local check_indent check_indent = function(subject, end_pos, spaces) - local num_spaces = get_line_indentation(spaces) - if num_spaces > indent_stack[#indent_stack] then - table.insert(indent_stack, num_spaces) + if #spaces > indent_stack[#indent_stack] then + insert(indent_stack, #spaces) return end_pos end end local check_dedent check_dedent = function(subject, end_pos, spaces) - local num_spaces = get_line_indentation(spaces) - if num_spaces < indent_stack[#indent_stack] then + if #spaces < indent_stack[#indent_stack] then table.remove(indent_stack) return end_pos end end local check_nodent check_nodent = function(subject, end_pos, spaces) - local num_spaces = get_line_indentation(spaces) - if num_spaces == indent_stack[#indent_stack] then + if #spaces == indent_stack[#indent_stack] then return end_pos end end @@ -420,27 +398,17 @@ do if not tree.type then self:error("Invalid tree: " .. tostring(utils.repr(tree))) end - local indent = "" - local buffer = { } - local return_value = nil - local to_lua - to_lua = function(t) - local ret = self:tree_to_lua(t) - return ret - end - local add - add = function(code) - return table.insert(buffer, code) - end local _exp_0 = tree.type if "File" == _exp_0 then - add([[return (function(compiler, vars) - local ret]]) + local buffer = { + [[return (function(compiler, vars) + local ret]] + } local vars = { } local _list_0 = tree.value.body.value for _index_0 = 1, #_list_0 do local statement = _list_0[_index_0] - local ok, code = pcall(to_lua, statement, "Statement") + local ok, code = pcall(self.tree_to_lua, self, statement) if not ok then self:writeln("Error occurred in statement:\n" .. tostring(statement.src)) error(code) @@ -451,44 +419,45 @@ do error("Failed to compile generated code:\n" .. tostring(code) .. "\n\n" .. tostring(err) .. "\n\nProduced by statement:\n" .. tostring(utils.repr(statement))) end local value = lua_thunk() + local return_value ok, return_value = pcall(value, self, vars) if not ok then self:writeln("Error occurred in statement:\n" .. tostring(statement.src)) error(return_value) end - add(code) + insert(buffer, code) end - add([[ return ret + insert(buffer, [[ return ret end) ]]) + return table.concat(buffer, "\n"), return_value elseif "Block" == _exp_0 then + local buffer = { } local _list_0 = tree.value for _index_0 = 1, #_list_0 do local statement = _list_0[_index_0] - add(to_lua(statement)) + insert(buffer, self:tree_to_lua(statement)) end + return table.concat(buffer, "\n") elseif "Thunk" == _exp_0 then assert(tree.value.type == "Block", "Non-block value in Thunk") - add([[ (function(compiler, vars) + return [[ (function(compiler, vars) local ret - ]] .. to_lua(tree.value) .. "\n" .. [[ return ret + ]] .. self:tree_to_lua(tree.value) .. "\n" .. [[ return ret end) - ]]) + ]] elseif "Statement" == _exp_0 then if tree.value.type == "FunctionCall" then local name = self:fn_name_from_tree(tree.value) if self.defs[name] and self.defs[name].is_macro then - add(self:run_macro(tree.value, "Statement")) - else - add("ret = " .. (to_lua(tree.value):match("%s*(.*)"))) + return self:run_macro(tree.value, "Statement") end - else - add("ret = " .. (to_lua(tree.value):match("%s*(.*)"))) end + return "ret = " .. (self:tree_to_lua(tree.value)) elseif "FunctionCall" == _exp_0 then local name = self:fn_name_from_tree(tree) if self.defs[name] and self.defs[name].is_macro then - add(self:run_macro(tree, "Expression")) + return self:run_macro(tree, "Expression") else local args do @@ -498,29 +467,20 @@ do for _index_0 = 1, #_list_0 do local a = _list_0[_index_0] if a.type ~= "Word" then - _accum_0[_len_0] = to_lua(a) + _accum_0[_len_0] = self:tree_to_lua(a) _len_0 = _len_0 + 1 end end args = _accum_0 end - table.insert(args, 1, utils.repr(name)) - add(self.__class:comma_separated_items("compiler:call(", args, ")")) + insert(args, 1, utils.repr(name)) + return self.__class:comma_separated_items("compiler:call(", args, ")") end elseif "String" == _exp_0 then - local escapes = { - n = "\n", - t = "\t", - b = "\b", - a = "\a", - v = "\v", - f = "\f", - r = "\r" - } local unescaped = tree.value:gsub("\\(.)", (function(c) - return escapes[c] or c + return STRING_ESCAPES[c] or c end)) - add(utils.repr(unescaped)) + return utils.repr(unescaped) elseif "Longstring" == _exp_0 then local concat_parts = { } local string_buffer = "" @@ -534,50 +494,48 @@ do string_buffer = string_buffer .. bit:gsub("\\\\", "\\") else if string_buffer ~= "" then - table.insert(concat_parts, utils.repr(string_buffer)) + insert(concat_parts, utils.repr(string_buffer)) string_buffer = "" end - table.insert(concat_parts, "compiler.utils.repr_if_not_string(" .. tostring(to_lua(bit)) .. ")") + insert(concat_parts, "compiler.utils.repr_if_not_string(" .. tostring(self:tree_to_lua(bit)) .. ")") end end end if string_buffer ~= "" then - table.insert(concat_parts, utils.repr(string_buffer)) + insert(concat_parts, utils.repr(string_buffer)) end if #concat_parts == 0 then - add("''") + return "''" elseif #concat_parts == 1 then - add(concat_parts[1]) + return concat_parts[1] else - add("(" .. tostring(table.concat(concat_parts, "..")) .. ")") + return "(" .. tostring(table.concat(concat_parts, "..")) .. ")" end elseif "Number" == _exp_0 then - add(tree.value) + return tree.value elseif "List" == _exp_0 then if #tree.value == 0 then - add("{}") + return "{}" elseif #tree.value == 1 then - add("{" .. tostring(to_lua(tree.value[1])) .. "}") + return "{" .. tostring(self:tree_to_lua(tree.value[1])) .. "}" else - add(self.__class:comma_separated_items("{", (function() + return self.__class:comma_separated_items("{", (function() local _accum_0 = { } local _len_0 = 1 local _list_0 = tree.value for _index_0 = 1, #_list_0 do local item = _list_0[_index_0] - _accum_0[_len_0] = to_lua(item) + _accum_0[_len_0] = self:tree_to_lua(item) _len_0 = _len_0 + 1 end return _accum_0 - end)(), "}")) + end)(), "}") end elseif "Var" == _exp_0 then - add("vars[" .. tostring(utils.repr(tree.value)) .. "]") + return "vars[" .. tostring(utils.repr(tree.value)) .. "]" else - self:error("Unknown/unimplemented thingy: " .. tostring(tree.type)) + return self:error("Unknown/unimplemented thingy: " .. tostring(tree.type)) end - buffer = table.concat(buffer, "\n") - return buffer, return_value end, fn_name_from_tree = function(self, tree) assert(tree.type == "FunctionCall", "Attempt to get fn name from non-functioncall tree: " .. tostring(tree.type)) @@ -585,7 +543,7 @@ do local _list_0 = tree.value for _index_0 = 1, #_list_0 do local token = _list_0[_index_0] - table.insert(name_bits, (function() + insert(name_bits, (function() if token.type == "Word" then return token.value else @@ -644,7 +602,7 @@ do end args = _tbl_0 end - table.insert(self.callstack, name) + insert(self.callstack, name) local ret, manual_mode = fn(self, args, kind) table.remove(self.callstack) if not ret then @@ -741,11 +699,11 @@ do for line in coroutine.wrap(function() return self:_yield_tree(tree) end) do - table.insert(result, line) + insert(result, line) end return table.concat(result, "\n") end, - compile = function(self, src, filename, output_file) + run = function(self, src, filename, output_file) if output_file == nil then output_file = nil end @@ -759,7 +717,7 @@ do local output = io.open(output_file, "w") output:write(code) end - return code, retval + return retval, code end, error = function(self, ...) self:writeln("ERROR!") @@ -900,7 +858,7 @@ if arg and arg[1] then if arg[2] == "-" then c.write = function() end end - local code, retval = c:compile(input, arg[1]) + local retval, code = c:run(input, arg[1]) c.write = _write if arg[2] then local output @@ -2,6 +2,7 @@ re = require 're' lpeg = require 'lpeg' utils = require 'utils' +insert = table.insert -- TODO: -- improve indentation of generated lua code @@ -16,6 +17,7 @@ utils = require 'utils' INDENT = " " lpeg.setmaxstack 10000 -- whoa {:P,:V,:S,:Cg,:C,:Cp,:B,:Cmt} = lpeg +STRING_ESCAPES = n:"\n", t:"\t", b:"\b", a:"\a", v:"\v", f:"\f", r:"\r" class NomsuCompiler @@ -40,15 +42,13 @@ class NomsuCompiler @error "Attempt to call macro at runtime: #{fn_name}\nThis can be caused by using a macro in a function that is defined before the macro." unless @check_permission(fn_name) @error "You do not have the authority to call: #{fn_name}" - table.insert @callstack, fn_name + insert @callstack, fn_name {:fn, :arg_names} = fn_info args = {name, select(i,...) for i,name in ipairs(arg_names[fn_name])} if @debug @writeln "Calling #{fn_name} with args: #{utils.repr(args)}" - ok,ret = pcall(fn, self, args) + ret = fn(self, args) table.remove @callstack - if not ok - error(ret) return ret check_permission: (fn_name)=> @@ -79,19 +79,19 @@ class NomsuCompiler invocations = {} for item in *def.value if item.type == "String" - table.insert invocations, @tree_to_value(item, vars) + insert invocations, @tree_to_value(item, vars) continue if item.type != "FunctionCall" @error "Invalid list item: #{item.type}, expected FunctionCall or String" name_bits = {} for token in *item.value if token.type == "Word" - table.insert name_bits, token.value + insert name_bits, token.value elseif token.type == "Var" - table.insert name_bits, token.src + insert name_bits, token.src else @error "Unexpected token type in definition: #{token.type} (expected Word or Var)" - table.insert invocations, table.concat(name_bits, " ") + insert invocations, table.concat(name_bits, " ") return invocations get_invocations:(text)=> @@ -106,7 +106,7 @@ class NomsuCompiler for _text in *text invocation = _text\gsub("'"," '")\gsub("%%%S+","%%")\gsub("%s+"," ") _arg_names = [arg for arg in _text\gmatch("%%(%S[^%s']*)")] - table.insert(invocations, invocation) + insert(invocations, invocation) if prev_arg_names if not utils.equivalent(utils.set(prev_arg_names), utils.set(_arg_names)) @error("Conflicting argument names #{utils.repr(prev_arg_names)} and #{utils.repr(_arg_names)} for #{utils.repr(text)}") @@ -121,16 +121,6 @@ class NomsuCompiler fn_info = {fn:lua_gen_fn, :arg_names, :invocations, :src, is_macro:true} for invocation in *invocations @defs[invocation] = fn_info - - run: (text, filename)=> - if @debug - @writeln "RUNNING TEXT:\n#{text}" - -- This will execute each chunk as it goes along - code, retval = @compile(text, filename) - if @debug - @writeln "\nGENERATED LUA CODE:\n#{code}" - @writeln "\nPRODUCED RETURN VALUE:\n#{retval}" - return retval serialize: (obj)=> switch type(obj) @@ -161,27 +151,17 @@ class NomsuCompiler if @debug @writeln("PARSING:\n#{str}") - get_line_indentation = (line)-> - indent_amounts = {[" "]:1, ["\t"]:4} - with sum = 0 - leading_space = line\match("[\t ]*") - for c in leading_space\gmatch "[\t ]" - sum += indent_amounts[c] - indent_stack = {0} check_indent = (subject,end_pos,spaces)-> - num_spaces = get_line_indentation(spaces) - if num_spaces > indent_stack[#indent_stack] - table.insert(indent_stack, num_spaces) + if #spaces > indent_stack[#indent_stack] + insert(indent_stack, #spaces) return end_pos check_dedent = (subject,end_pos,spaces)-> - num_spaces = get_line_indentation(spaces) - if num_spaces < indent_stack[#indent_stack] + if #spaces < indent_stack[#indent_stack] table.remove(indent_stack) return end_pos check_nodent = (subject,end_pos,spaces)-> - num_spaces = get_line_indentation(spaces) - if num_spaces == indent_stack[#indent_stack] + if #spaces == indent_stack[#indent_stack] return end_pos lingo = [=[ @@ -315,23 +295,13 @@ class NomsuCompiler assert tree, "No tree provided." if not tree.type @error "Invalid tree: #{utils.repr(tree)}" - indent = "" - buffer = {} - return_value = nil - - to_lua = (t)-> - ret = @tree_to_lua(t) - return ret - - add = (code)-> table.insert(buffer, code) - switch tree.type when "File" - add [[return (function(compiler, vars) - local ret]] + buffer = {[[return (function(compiler, vars) + local ret]]} vars = {} for statement in *tree.value.body.value - ok,code = pcall(to_lua, statement, "Statement") + ok,code = pcall(@tree_to_lua, self, statement) if not ok @writeln "Error occurred in statement:\n#{statement.src}" error(code) @@ -346,22 +316,25 @@ class NomsuCompiler if not ok @writeln "Error occurred in statement:\n#{statement.src}" error(return_value) - add code - add [[ + insert buffer, code + insert buffer, [[ return ret end) ]] + return table.concat(buffer, "\n"), return_value when "Block" + buffer = {} for statement in *tree.value - add to_lua(statement) + insert buffer, @tree_to_lua(statement) + return table.concat(buffer, "\n") when "Thunk" assert tree.value.type == "Block", "Non-block value in Thunk" - add [[ + return [[ (function(compiler, vars) local ret - ]]..to_lua(tree.value).."\n"..[[ + ]]..@tree_to_lua(tree.value).."\n"..[[ return ret end) ]] @@ -371,25 +344,21 @@ class NomsuCompiler if tree.value.type == "FunctionCall" name = @fn_name_from_tree(tree.value) if @defs[name] and @defs[name].is_macro - add @run_macro(tree.value, "Statement") - else - add "ret = "..(to_lua(tree.value)\match("%s*(.*)")) - else - add "ret = "..(to_lua(tree.value)\match("%s*(.*)")) + return @run_macro(tree.value, "Statement") + return "ret = "..(@tree_to_lua(tree.value)) when "FunctionCall" name = @fn_name_from_tree(tree) if @defs[name] and @defs[name].is_macro - add @run_macro(tree, "Expression") + return @run_macro(tree, "Expression") else - args = [to_lua(a) for a in *tree.value when a.type != "Word"] - table.insert args, 1, utils.repr(name) - add @@comma_separated_items("compiler:call(", args, ")") + args = [@tree_to_lua(a) for a in *tree.value when a.type != "Word"] + insert args, 1, utils.repr(name) + return @@comma_separated_items("compiler:call(", args, ")") when "String" - escapes = n:"\n", t:"\t", b:"\b", a:"\a", v:"\v", f:"\f", r:"\r" - unescaped = tree.value\gsub("\\(.)", ((c)-> escapes[c] or c)) - add utils.repr(unescaped) + unescaped = tree.value\gsub("\\(.)", ((c)-> STRING_ESCAPES[c] or c)) + return utils.repr(unescaped) when "Longstring" concat_parts = {} @@ -401,41 +370,37 @@ class NomsuCompiler string_buffer ..= bit\gsub("\\\\","\\") else if string_buffer ~= "" - table.insert concat_parts, utils.repr(string_buffer) + insert concat_parts, utils.repr(string_buffer) string_buffer = "" - table.insert concat_parts, "compiler.utils.repr_if_not_string(#{to_lua(bit)})" + insert concat_parts, "compiler.utils.repr_if_not_string(#{@tree_to_lua(bit)})" if string_buffer ~= "" - table.insert concat_parts, utils.repr(string_buffer) + insert concat_parts, utils.repr(string_buffer) if #concat_parts == 0 - add "''" + return "''" elseif #concat_parts == 1 - add concat_parts[1] + return concat_parts[1] else - add "(#{table.concat(concat_parts, "..")})" + return "(#{table.concat(concat_parts, "..")})" when "Number" - add tree.value + return tree.value when "List" if #tree.value == 0 - add "{}" + return "{}" elseif #tree.value == 1 - add "{#{to_lua(tree.value[1])}}" + return "{#{@tree_to_lua(tree.value[1])}}" else - add @@comma_separated_items("{", [to_lua(item) for item in *tree.value], "}") + return @@comma_separated_items("{", [@tree_to_lua(item) for item in *tree.value], "}") when "Var" - add "vars[#{utils.repr(tree.value)}]" + return "vars[#{utils.repr(tree.value)}]" else @error("Unknown/unimplemented thingy: #{tree.type}") - -- TODO: make indentation clean - buffer = table.concat(buffer, "\n") - return buffer, return_value - @comma_separated_items: (open, items, close)=> utils.accumulate "\n", -> buffer = open @@ -456,7 +421,7 @@ class NomsuCompiler assert(tree.type == "FunctionCall", "Attempt to get fn name from non-functioncall tree: #{tree.type}") name_bits = {} for token in *tree.value - table.insert name_bits, if token.type == "Word" then token.value else "%" + insert name_bits, if token.type == "Word" then token.value else "%" table.concat(name_bits, " ") var_to_lua_identifier: (var)=> @@ -474,7 +439,7 @@ class NomsuCompiler {:fn, :arg_names} = @defs[name] args = [a for a in *tree.value when a.type != "Word"] args = {name,args[i] for i,name in ipairs(arg_names[name])} - table.insert @callstack, name + insert @callstack, name ret, manual_mode = fn(self, args, kind) table.remove @callstack if not ret @@ -547,10 +512,10 @@ class NomsuCompiler stringify_tree:(tree)=> result = {} for line in coroutine.wrap(-> @_yield_tree(tree)) - table.insert(result, line) + insert(result, line) return table.concat result, "\n" - compile: (src, filename, output_file=nil)=> + run: (src, filename, output_file=nil)=> if @debug @writeln "COMPILING:\n#{src}" tree = @parse(src, filename) @@ -559,7 +524,7 @@ class NomsuCompiler if output_file output = io.open(output_file, "w") output\write(code) - return code, retval + return retval, code error: (...)=> @writeln "ERROR!" @@ -635,7 +600,7 @@ if arg and arg[1] _write = c.write if arg[2] == "-" c.write = -> - code, retval = c\compile(input, arg[1]) + retval, code = c\run(input, arg[1]) c.write = _write -- put it back if arg[2] output = if arg[2] == "-" @@ -653,6 +618,7 @@ if arg and arg[1] local c = NomsuCompiler() return load()(c, {}) ]] + elseif arg -- REPL: c = NomsuCompiler() |
