From 82cfd3e54b5910843c091a9fb6ef3ad6b64ba757 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Thu, 14 Jun 2018 21:59:25 -0700 Subject: [PATCH] More streamlining and cleanup. Especially for core/metaprogramming.nom --- code_obj.lua | 23 ++++ code_obj.moon | 16 +++ core/collections.nom | 12 +- core/control_flow.nom | 2 +- core/math.nom | 27 ++--- core/metaprogramming.nom | 247 ++++++++++++++++----------------------- core/operators.nom | 15 +-- core/text.nom | 20 +++- nomsu.lua | 231 +++++++++++++++--------------------- nomsu.moon | 172 ++++++++++++--------------- nomsu_tree.lua | 12 ++ nomsu_tree.moon | 3 + tests/collections.nom | 4 +- tests/control_flow.nom | 2 +- tests/math.nom | 2 +- tests/object.nom | 2 +- tests/operators.nom | 4 - 17 files changed, 360 insertions(+), 434 deletions(-) diff --git a/code_obj.lua b/code_obj.lua index 57abef9..426a973 100644 --- a/code_obj.lua +++ b/code_obj.lua @@ -98,6 +98,29 @@ do end self.__str = nil end, + concat_append = function(self, values, joiner) + local bits, indents = self.bits, self.indents + local match = string.match + for i = 1, #values do + local b = values[i] + assert(b) + if i > 1 then + bits[#bits + 1] = joiner + end + bits[#bits + 1] = b + if type(b) == 'string' then + do + local spaces = match(b, "\n([ ]*)[^\n]*$") + if spaces then + self.current_indent = #spaces + end + end + elseif self.current_indent ~= 0 then + indents[#bits] = self.current_indent + end + end + self.__str = nil + end, prepend = function(self, ...) local n = select("#", ...) local bits, indents = self.bits, self.indents diff --git a/code_obj.moon b/code_obj.moon index ec644a5..d3de65c 100644 --- a/code_obj.moon +++ b/code_obj.moon @@ -65,6 +65,22 @@ class Code elseif @current_indent != 0 indents[#bits] = @current_indent @__str = nil + + concat_append: (values, joiner)=> + bits, indents = @bits, @indents + match = string.match + for i=1,#values + b = values[i] + assert(b) + if i > 1 + bits[#bits+1] = joiner + bits[#bits+1] = b + if type(b) == 'string' + if spaces = match(b, "\n([ ]*)[^\n]*$") + @current_indent = #spaces + elseif @current_indent != 0 + indents[#bits] = @current_indent + @__str = nil prepend: (...)=> n = select("#",...) diff --git a/core/collections.nom b/core/collections.nom index a8a28b2..8a59852 100644 --- a/core/collections.nom +++ b/core/collections.nom @@ -16,8 +16,8 @@ immediately ..to: Lua value "utils.nth_to_last(\(%list as lua expr), \(%index as lua expr))" immediately - parse [first in %list, first %list] as: 1 st in %list - parse [last in %list, last %list] as: 1 st to last in %list + parse [last in %list] as: 1st to last in %list + parse [first in %list] as: %list.1 # Membership testing immediately @@ -62,8 +62,8 @@ immediately parse [%expression for %item in %iterable] as result of %comprehension <- [] - for %i = %item in %iterable - %comprehension.%i <- %expression + for %item in %iterable + add %expression to %comprehension return %comprehension parse [..] @@ -163,8 +163,8 @@ immediately %unique <- [] %seen <- {} for % in %items - unless: % in %seen + unless: %seen.% add % to %unique - (% in %seen) <- (yes) + %seen.% <- (yes) return %unique diff --git a/core/control_flow.nom b/core/control_flow.nom index f0238a4..2c85221 100644 --- a/core/control_flow.nom +++ b/core/control_flow.nom @@ -39,7 +39,7 @@ immediately ..to # If %when_true_expr is guaranteed to be truthy, we can use Lua's idiomatic equivalent of a conditional expression: (cond and if_true or if_false) - if: %when_true_expr.type in {Text:yes, List:yes, Dict:yes, Number:yes} + if: {Text:yes, List:yes, Dict:yes, Number:yes}.(%when_true_expr.type) return Lua value ".." (\(%condition as lua expr) and \(%when_true_expr as lua expr) or \(%when_false_expr as lua expr)) diff --git a/core/math.nom b/core/math.nom index 1e733e7..57088eb 100644 --- a/core/math.nom +++ b/core/math.nom @@ -5,6 +5,7 @@ use "core/metaprogramming.nom" use "core/text.nom" use "core/operators.nom" use "core/control_flow.nom" +use "core/collections.nom" # Literals: compile [infinity, inf] to: Lua value "math.huge" @@ -30,7 +31,7 @@ compile [hyperbolic cosine %, cosh %] to: Lua value "math.cosh(\(% as lua expr)) compile [hyperbolic tangent %, tanh %] to: Lua value "math.tanh(\(% as lua expr))" compile [e^%, exp %] to: Lua value "math.exp(\(% as lua expr))" compile [natural log %, ln %, log %] to: Lua value "math.log(\(% as lua expr))" -compile [log % base %base, log_%base %, log base %base %] to: Lua value "math.log(\(% as lua expr), \(%base as lua expr))" +compile [log % base %base, log base %base of %] to: Lua value "math.log(\(% as lua expr), \(%base as lua expr))" compile [floor %] to: Lua value "math.floor(\(% as lua expr))" compile [ceiling %, ceil %] to: Lua value "math.ceil(\(% as lua expr))" compile [round %, % rounded] to: Lua value "math.floor(\(% as lua expr) + .5)" @@ -39,34 +40,26 @@ action [%n to the nearest %rounder] # Any/all/none compile [all of %items, all %items] to - unless: (%items' "type") is "List" + unless: %items.type is "List" return: Lua value "utils.all(\(%items as lua expr))" - %clauses <- [] - for % in %items - lua> "table.insert(\%clauses, \(% as lua expr));" + %clauses <- ((% as lua expr) for % in %items) return: Lua value "(\(%clauses joined with " and "))" parse [not all of %items, not all %items] as: not (all of %items) compile [any of %items, any %items] to - unless: (%items' "type") is "List" + unless: %items.type is "List" return: Lua value "utils.any(\(%items as lua expr))" - %clauses <- [] - for % in %items - lua> "table.insert(\%clauses, \(% as lua expr));" + %clauses <- ((% as lua expr) for % in %items) return: Lua value "(\(%clauses joined with " or "))" parse [none of %items, none %items] as: not (any of %items) compile [sum of %items, sum %items] to - unless: (%items' "type") is "List" + unless: %items.type is "List" return: Lua value "utils.sum(\(%items as lua expr))" - %clauses <- [] - for % in %items - lua> "table.insert(\%clauses, \(% as lua expr));" + %clauses <- ((% as lua expr) for % in %items) return: Lua value "(\(%clauses joined with " + "))" compile [product of %items, product %items] to - unless: (%items' "type") is "List" + unless: %items.type is "List" return: Lua value "utils.product(\(%items as lua expr))" - %clauses <- [] - for % in %items - lua> "table.insert(\%clauses, \(% as lua expr));" + %clauses <- ((% as lua expr) for % in %items) return: Lua value "(\(%clauses joined with " * "))" action [avg of %items, average of %items] =lua "(utils.sum(\%items)/#\%items)" diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom index 83179f5..15c5817 100644 --- a/core/metaprogramming.nom +++ b/core/metaprogramming.nom @@ -5,161 +5,109 @@ # Compile-time action to make compile-time actions: immediately lua> ".." - _ENV['ACTION'..string.as_lua_id("compile % to %")] = compile_time(function(tree, \%actions, \%lua) - local lua = Lua(tree.source) - local canonical = \%actions[1] - lua:append("ACTION", string.as_lua_id(canonical.stub), ' = compile_time(function(tree') - local args = {} - for i,tok in ipairs(canonical) do - if tok.type == "Var" then args[#args+1] = tok end + A_give_1_nickname_2 = compile_time(function(tree, \%action, \%nickname, is_compile_time) + local function arg_to_string(a) return tostring(nomsu:tree_to_lua(a)) end + local action_args = table.map(\%action:get_args(), arg_to_string) + local nickname_args = table.map(\%nickname:get_args(), arg_to_string) + if utils.equivalent(action_args, nickname_args) then + return Lua(tree.source, "A", string.as_lua_id(\%nickname.stub), " = A", string.as_lua_id(\%action.stub)) end - local canonical_arg_positions = {} - for i, arg in ipairs(args) do - canonical_arg_positions[arg[1]] = i - lua:append(", ", nomsu:tree_to_lua(arg)) + local lua = Lua(tree.source, "A", string.as_lua_id(\%nickname.stub), " = ") + if is_compile_time or COMPILE_TIME[_ENV["A"..string.as_lua_id(\%action.stub)]] then + lua:append("compile_time") + table.insert(action_args, 1, "tree") + table.insert(nickname_args, 1, "tree") end - local body_lua = nomsu:tree_to_lua(\%lua):as_statements("return ") - body_lua:remove_free_vars(args) - body_lua:declare_locals() - lua:append(")\n ", body_lua, "\nend)") - - for i=2,#\%actions do - local action = \%actions[i] - lua:append("\n", "ACTION", string.as_lua_id(action.stub), " = ACTION", string.as_lua_id(canonical.stub)) - - local arg_positions = {} - for _,tok in ipairs(action) do - if tok.type == 'Var' then - arg_positions[#arg_positions+1] = canonical_arg_positions[tok[1]] - end - end - lua:append("\n", "ARG_ORDERS[", repr(action.stub), "] = ", repr(arg_positions)) - end - lua:append("\nALIASES[ACTION", string.as_lua_id(canonical.stub), "] = {") - for i,action in ipairs(\%actions) do - if i > 1 then lua:append(", ") end - lua:append(repr(action.stub)) - end - lua:append("}") + lua:append("(function(") + lua:concat_append(nickname_args, ", ") + lua:append(")\n return A", string.as_lua_id(\%action.stub), "(") + lua:concat_append(action_args, ", ") + lua:append(")\nend)") return lua end) -# Compile-time action to make actions -immediately - compile [action %actions %body] to - lua> ".." - local lua = Lua(tree.source) - local canonical = \%actions[1] - lua:append("ACTION", string.as_lua_id(canonical.stub), ' = function(') - local args = {} - for i,tok in ipairs(canonical) do - if tok.type == "Var" then args[#args+1] = tok end + __MANGLE_INDEX = 0 + A_parse_1_as_2 = compile_time(function(tree, \%actions, \%body) + local replacements = {} + for i,arg in ipairs(\%actions[1]:get_args()) do + replacements[arg[1]] = tostring(nomsu:tree_to_lua(arg)) end - local canonical_arg_positions = {} - for i, arg in ipairs(args) do - canonical_arg_positions[arg[1]] = i - lua:append(nomsu:tree_to_lua(arg)) - if i < #args then lua:append(", ") end + local function make_tree(t) + if not AST.is_syntax_tree(t) then + return repr(t) + elseif t.type ~= 'Var' then + local args = table.map(t, make_tree) + table.insert(args, 1, repr(tostring(t.source))) + return t.type.."("..table.concat(args, ", ")..")" + elseif replacements[t[1]] then + return replacements[t[1]] + else + return t.type.."("..repr(tostring(t.source))..", "..repr(t[1].." \\0").."..('%X'):format(__MANGLE_INDEX))" + end end + local lua = Lua(tree.source, "A", string.as_lua_id(\%actions[1].stub), " = compile_time(function(tree") + for _,arg in ipairs(\%actions[1]:get_args()) do + lua:append(", ", nomsu:tree_to_lua(arg)) + end + lua:append(")\n __MANGLE_INDEX = __MANGLE_INDEX + 1", + "\n local tree = ", make_tree(\%body), + "\n local lua = nomsu:tree_to_lua(tree)", + "\n lua:remove_free_vars({") + local vars = table.map(\%actions[1]:get_args(), function(a) + return "Var("..repr(tostring(a.source))..", "..repr(a[1])..")" + end) + lua:concat_append(vars, ", ") + lua:append("})\n return lua\nend)") + + for i=2,#\%actions do + lua:append("\n", A_give_1_nickname_2(\%actions[i], \%actions[1], \%actions[i], true)) + end + return lua + end) + + A_action_1_2 = compile_time(function(tree, \%actions, \%body, is_compile_time) + local lua = Lua(tree.source, "A", string.as_lua_id(\%actions[1].stub), " = ") + if is_compile_time then lua:append("compile_time") end + lua:append("(function(") + local args = \%actions[1]:get_args() + local lua_args = table.map(args, function(a) return nomsu:tree_to_lua(a) end) + if is_compile_time then table.insert(lua_args, 1, "tree") end + lua:concat_append(lua_args, ", ") local body_lua = nomsu:tree_to_lua(\%body):as_statements("return ") body_lua:remove_free_vars(args) body_lua:declare_locals() - lua:append(")\n ", body_lua, "\nend") - + lua:append(")\n ", body_lua, "\nend)") for i=2,#\%actions do - local action = \%actions[i] - lua:append("\n", "ACTION", string.as_lua_id(action.stub), " = ACTION", string.as_lua_id(canonical.stub)) - - local arg_positions = {} - for _,tok in ipairs(action) do - if tok.type == 'Var' then - arg_positions[#arg_positions+1] = canonical_arg_positions[tok[1]] - end - end - lua:append("\n", "ARG_ORDERS[", repr(action.stub), "] = ", repr(arg_positions)) + lua:append("\n", A_give_1_nickname_2(\%actions[i], \%actions[1], \%actions[i], is_compile_time)) end - lua:append("\nALIASES[ACTION", string.as_lua_id(canonical.stub), "] = {") - for i,action in ipairs(\%actions) do - if i > 1 then lua:append(", ") end - lua:append(repr(action.stub)) - end - lua:append("}") return lua + end) -# Macro to make nomsu macros -immediately - compile [parse %shorthand as %longhand] to - lua> ".." - local lua = Lua(tree.source) - local canonical = \%shorthand[1] - lua:append("ACTION", string.as_lua_id(canonical.stub), ' = compile_time(function(tree') - local args = {} - for i,tok in ipairs(canonical) do - if tok.type == "Var" then args[#args+1] = tok end - end - local canonical_arg_positions = {} - for i, arg in ipairs(args) do - canonical_arg_positions[arg[1]] = i - lua:append(", ", nomsu:tree_to_lua(arg)) - end - - local replacements = {} - for i,tok in ipairs(canonical) do - if tok.type == "Var" then - local lua_var = tostring(nomsu:tree_to_lua(tok)) - replacements[tok[1]] = lua_var - end - end - MANGLE_INDEX = (MANGLE_INDEX or 0) + 1 - local function make_tree(t) - if type(t) ~= 'table' and type(t) ~= 'userdata' then - return repr(t) - elseif t.type == 'Var' and replacements[t[1]] then - return replacements[t[1]] - elseif t.type == 'Var' then - return t.type.."("..repr(tostring(t.source))..", "..repr(t[1].."#"..tostring(MANGLE_INDEX))..")" - else - local bits = {repr(tostring(t.source))} - for i, entry in ipairs(t) do - bits[#bits+1] = make_tree(entry) - end - return t.type.."("..table.concat(bits, ", ")..")" - end - end - lua:append(")\n local tree = ", make_tree(\%longhand), "\n return nomsu:tree_to_lua(tree)\nend)") - - for i=2,#\%shorthand do - local action = \%shorthand[i] - lua:append("\n", "ACTION", string.as_lua_id(action.stub), " = ACTION", string.as_lua_id(canonical.stub)) - - local arg_positions = {} - for _,tok in ipairs(action) do - if tok.type == 'Var' then - arg_positions[#arg_positions+1] = canonical_arg_positions[tok[1]] - end - end - lua:append("\n", "ARG_ORDERS[", repr(action.stub), "] = ", repr(arg_positions)) - end - lua:append("\nALIASES[ACTION", string.as_lua_id(canonical.stub), "] = {") - for i,action in ipairs(\%shorthand) do - if i > 1 then lua:append(", ") end - lua:append(repr(action.stub)) - end - lua:append("}") - return lua + A_compile_1_to_2 = compile_time(function(tree, \%actions, \%body) + return A_action_1_2(tree, \%actions, \%body, true) + end) compile [remove action %action] to Lua ".." - do - local fn = ACTION\(=lua "string.as_lua_id(\(%action.stub))") - for stub in pairs(ALIASES[fn]) do - _ENV['ACTION'..string.as_lua_id(stub)] = nil - end - ARG_ORDERS[fn] = nil - COMPILE_TIME[fn] = nil - end + A\(=lua "string.as_lua_id(\(%action.stub))") = nil + ARG_ORDERS[fn] = nil + COMPILE_TIME[fn] = nil immediately + action [read file %filename] + lua> ".." + local file = io.open(\%filename) + local contents = file:read("*a") + file:close() + return contents + + action [sh> %cmd] + lua> ".." + local result = io.popen(\%cmd) + local contents = result:read("*a") + result:close() + return contents + action [%tree as nomsu] =lua "nomsu:tree_to_nomsu(\%tree)" @@ -182,11 +130,16 @@ immediately action [%tree as lua return] =lua "nomsu:tree_to_lua(\%tree):as_statements('return ')" + + action [%var as lua identifier] + =lua "type(\%var) == 'string' and string.as_lua_id(\%var) or nomsu:tree_to_lua(\%var)" + +immediately + compile [parse %text] to + Lua value ".." + nomsu:parse(Nomsu("\("\(%text.source)")", \(%text as lua expr))) immediately - compile [%tree with vars %vars] to - barf "Deprecated" - compile [%tree with %t -> %replacement] to Lua value ".." \(%tree as lua expr):map(function(\(%t as lua expr)) @@ -211,8 +164,7 @@ immediately return nomsu:run_lua(lua) immediately - parse [%var <-write %code] as: lua> "\%var:append(\%code);" - parse [to %var write %code] as: lua> "\%var:append(\%code);" + parse [%lua <-write %code, to %lua write %code] as: lua> "\%lua:append(\%code);" immediately compile [quote %s] to @@ -222,7 +174,6 @@ immediately immediately compile [nomsu] to: Lua value "nomsu" - compile [%var as lua identifier] to: Lua value "nomsu:tree_to_lua(\(%var as lua expr))" # Compiler tools immediately @@ -239,9 +190,17 @@ immediately compile [say %message] to lua> ".." if \%message.type == "Text" then - return Lua(tree.source, "print(", \(%message as lua expr), ");"); + return Lua(tree.source, "io.write(", \(%message as lua expr), ", '\\\\n');"); else - return Lua(tree.source, "print(tostring(", \(%message as lua expr), "));"); + return Lua(tree.source, "io.write(tostring(", \(%message as lua expr), "), '\\\\n');"); + end + + compile [ask %prompt] to + lua> ".." + if \%prompt.type == "Text" then + return Lua.Value(tree.source, "(io.write(", \(%prompt as lua expr), ") and io.read())"); + else + return Lua.Value(tree.source, "(io.write(tostring(", \(%prompt as lua expr), ")) and io.read())"); end # Return diff --git a/core/operators.nom b/core/operators.nom index 555a636..3ea0fe1 100644 --- a/core/operators.nom +++ b/core/operators.nom @@ -3,19 +3,6 @@ use "core/metaprogramming.nom" -# Indexing -immediately - # NOTE!!! It's critical that there are spaces around %key if it's a string, - otherwise, Lua will get confused and interpret %obj[[[foo]]] as %obj("[foo]") - instead of %obj[ "foo" ]. - It's also critical to have parens around %obj, otherwise Lua is too dumb to - realize that {x=1}["x"] is the same as ({x=1})["x"] or that - {x=1}.x is the same as ({x=1}).x - parse [..] - %obj' %key, %obj's %key, %key in %obj, %key'th in %obj, %key of %obj, - %key st in %obj, %key nd in %obj, %key rd in %obj, %key th in %obj, - ..as: %obj.%key - # Comparison Operators immediately compile [%x < %y] to: Lua value "(\(%x as lua expr) < \(%y as lua expr))" @@ -53,7 +40,7 @@ immediately immediately # Simultaneous mutli-assignments like: x,y,z = 1,x,3; compile [<- %assignments] to - assume ((%assignments' "type") is "Dict") or barf ".." + assume (%assignments.type is "Dict") or barf ".." Expected a Dict for the assignments part of '<- %' statement, not \%assignments lua> ".." local lhs, rhs = Lua(tree.source), Lua(tree.source) diff --git a/core/text.nom b/core/text.nom index 8b75ca4..e6065c1 100644 --- a/core/text.nom +++ b/core/text.nom @@ -18,6 +18,14 @@ compile [capitalized %text, %text capitalized] to compile [%text with %sub instead of %patt, %text s/%patt/%sub] to Lua value "((\(%text as lua expr)):gsub(\(%patt as lua expr), \(%sub as lua expr)))" +action [lines in %text, lines of %text] + lua> ".." + local result = {} + for line in (\%text):gmatch('[^\n]+') do + result[#result+1] = line + end + return result + # Text literals lua> ".." do @@ -26,8 +34,8 @@ lua> ".." backspace="\\\\b", ["form feed"]="\\\\f", formfeed="\\\\f", ["vertical tab"]="\\\\v", }; for name, e in pairs(escapes) do - local lua = "'"..e.."'"; - nomsu:define_compile_action(name, function(tree) return Lua.Value(tree.source, lua); end); + local lua = "'"..e.."'" + _ENV["A"..name:as_lua_id()] = compile_time(function(tree) return Lua.Value(tree.source, lua) end) end local colors = { ["reset color"]="\\\\27[0m", bright="\\\\27[1m", dim="\\\\27[2m", underscore="\\\\27[4m", @@ -42,10 +50,10 @@ lua> ".." for name, c in pairs(colors) do local color = "'"..c.."'"; local reset = "'"..colors["reset color"].."'"; - nomsu:define_compile_action(name, function(tree) return Lua.Value(tree.source, color); end); - nomsu:define_compile_action(name.." %", function(\%) - return Lua.Value(tree.source, color, "..", \(% as lua), "..", reset); - end); + _ENV["A"..name:as_lua_id()] = compile_time(function(tree) return Lua.Value(tree.source, color) end) + _ENV["A"..name:as_lua_id().."_1"] = compile_time(function(tree, text) + return Lua.Value(tree.source, color, "..", nomsu:tree_to_lua(text), "..", reset); + end) end end diff --git a/nomsu.lua b/nomsu.lua index 540fa8e..4d06969 100644 --- a/nomsu.lua +++ b/nomsu.lua @@ -44,6 +44,7 @@ end local STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2" string.as_lua_id = function(str) local argnum = 0 + str = gsub(str, "x([0-9A-F][0-9A-F])", "x\0%1") str = gsub(str, "%W", function(c) if c == ' ' then return '_' @@ -51,11 +52,20 @@ string.as_lua_id = function(str) argnum = argnum + 1 return tostring(argnum) else - return format("x%X", byte(c)) + return format("x%02X", byte(c)) end end) return '_' .. str end +table.map = function(self, fn) + local _accum_0 = { } + local _len_0 = 1 + for _, v in ipairs(self) do + _accum_0[_len_0] = fn(v) + _len_0 = _len_0 + 1 + end + return _accum_0 +end FILE_CACHE = setmetatable({ }, { __index = function(self, filename) local file = io.open(filename) @@ -273,52 +283,8 @@ end local NomsuCompiler do local _class_0 - local compile_error, stub_pattern, var_pattern, _running_files, MAX_LINE, math_expression + local compile_error, _running_files, MAX_LINE, math_expression local _base_0 = { - define_action = function(self, signature, fn) - if type(fn) ~= 'function' then - error("Not a function: " .. tostring(repr(fn))) - end - if type(signature) == 'string' then - signature = { - signature - } - elseif type(signature) ~= 'table' then - error("Invalid signature, expected list of strings, but got: " .. tostring(repr(signature)), 0) - end - local fn_info = debug_getinfo(fn, "u") - assert(not fn_info.isvararg, "Vararg functions aren't supported. Sorry, use a list instead.") - local fn_arg_positions - do - local _tbl_0 = { } - for i = 1, fn_info.nparams do - _tbl_0[debug.getlocal(fn, i)] = i - end - fn_arg_positions = _tbl_0 - end - local arg_orders = { } - for _index_0 = 1, #signature do - local alias = signature[_index_0] - local stub = concat(assert(stub_pattern:match(alias)), ' ') - local stub_args = assert(var_pattern:match(alias)) - self.environment['ACTION' .. string.as_lua_id(stub)] = fn - do - local _accum_0 = { } - local _len_0 = 1 - for _index_1 = 1, #stub_args do - local a = stub_args[_index_1] - _accum_0[_len_0] = fn_arg_positions[string.as_lua_id(a)] - _len_0 = _len_0 + 1 - end - arg_orders[stub] = _accum_0 - end - end - self.environment.ARG_ORDERS[fn] = arg_orders - end, - define_compile_action = function(self, signature, fn) - self:define_action(signature, fn) - self.environment.COMPILE_TIME[fn] = true - end, parse = function(self, nomsu_code) assert(type(nomsu_code) ~= 'string') local userdata = { @@ -485,7 +451,7 @@ do local _exp_0 = tree.type if "Action" == _exp_0 then local stub = tree.stub - local action = self.environment['ACTION' .. string.as_lua_id(stub)] + local action = self.environment['A' .. string.as_lua_id(stub)] if action and self.environment.COMPILE_TIME[action] then local args do @@ -578,7 +544,7 @@ do end end end - lua:append("ACTION", string.as_lua_id(stub), "(") + lua:append("A", string.as_lua_id(stub), "(") for i, arg in ipairs(args) do lua:append(arg) if i < #args then @@ -1127,89 +1093,93 @@ do end, initialize_core = function(self) local nomsu = self - self:define_compile_action("immediately %block", function(self, _block) - local lua = nomsu:tree_to_lua(_block):as_statements() - lua:declare_locals() - nomsu:run_lua(lua) - return Lua(_block.source, "if IMMEDIATE then\n ", lua, "\nend") - end) - local add_lua_string_bits - add_lua_string_bits = function(lua, code) - local line_len = 0 - if code.type ~= "Text" then - lua:append(", ", nomsu:tree_to_lua(code)) - return - end - for _index_0 = 1, #code do - local bit = code[_index_0] - local bit_lua - if type(bit) == "string" then - bit_lua = repr(bit) - else - bit_lua = nomsu:tree_to_lua(bit) - if not (bit_lua.is_value) then - compile_error(bit, "Cannot use:\n%s\nas a string interpolation value, since it's not an expression.") + do + local _with_0 = nomsu.environment + _with_0.A_immediately_1 = _with_0.compile_time(function(self, _block) + local lua = nomsu:tree_to_lua(_block):as_statements() + lua:declare_locals() + nomsu:run_lua(lua) + return Lua(_block.source, "if IMMEDIATE then\n ", lua, "\nend") + end) + local add_lua_string_bits + add_lua_string_bits = function(lua, code) + local line_len = 0 + if code.type ~= "Text" then + lua:append(", ", nomsu:tree_to_lua(code)) + return + end + for _index_0 = 1, #code do + local bit = code[_index_0] + local bit_lua + if type(bit) == "string" then + bit_lua = repr(bit) + else + bit_lua = nomsu:tree_to_lua(bit) + if not (bit_lua.is_value) then + compile_error(bit, "Cannot use:\n%s\nas a string interpolation value, since it's not an expression.") + end + bit_lua = bit_lua end - bit_lua = bit_lua - end - line_len = line_len + #tostring(bit_lua) - if line_len > MAX_LINE then - lua:append(",\n ") - line_len = 4 - else - lua:append(", ") - end - lua:append(bit_lua) - end - end - self:define_compile_action("Lua %code", function(self, _code) - local lua = Lua.Value(_code.source, "Lua(", repr(tostring(_code.source))) - add_lua_string_bits(lua, _code) - lua:append(")") - return lua - end) - self:define_compile_action("Lua value %code", function(self, _code) - local lua = Lua.Value(_code.source, "Lua.Value(", repr(tostring(_code.source))) - add_lua_string_bits(lua, _code) - lua:append(")") - return lua - end) - local add_lua_bits - add_lua_bits = function(lua, code) - for _index_0 = 1, #code do - local bit = code[_index_0] - if type(bit) == "string" then - lua:append(bit) - else - local bit_lua = nomsu:tree_to_lua(bit) - if not (bit_lua.is_value) then - compile_error(bit, "Cannot use:\n%s\nas a string interpolation value, since it's not an expression.") + line_len = line_len + #tostring(bit_lua) + if line_len > MAX_LINE then + lua:append(",\n ") + line_len = 4 + else + lua:append(", ") end lua:append(bit_lua) end end - return lua + _with_0.A_Lua_1 = _with_0.compile_time(function(self, _code) + local lua = Lua.Value(_code.source, "Lua(", repr(tostring(_code.source))) + add_lua_string_bits(lua, _code) + lua:append(")") + return lua + end) + _with_0.A_Lua_value_1 = _with_0.compile_time(function(self, _code) + local lua = Lua.Value(_code.source, "Lua.Value(", repr(tostring(_code.source))) + add_lua_string_bits(lua, _code) + lua:append(")") + return lua + end) + local add_lua_bits + add_lua_bits = function(lua, code) + for _index_0 = 1, #code do + local bit = code[_index_0] + if type(bit) == "string" then + lua:append(bit) + else + local bit_lua = nomsu:tree_to_lua(bit) + if not (bit_lua.is_value) then + compile_error(bit, "Cannot use:\n%s\nas a string interpolation value, since it's not an expression.") + end + lua:append(bit_lua) + end + end + return lua + end + nomsu.environment["A" .. string.as_lua_id("lua > 1")] = _with_0.compile_time(function(self, _code) + if _code.type ~= "Text" then + return Lua(self.source, "nomsu:run_lua(", nomsu:tree_to_lua(_code), ");") + end + return add_lua_bits(Lua(self.source), _code) + end) + nomsu.environment["A" .. string.as_lua_id("= lua 1")] = _with_0.compile_time(function(self, _code) + if _code.type ~= "Text" then + return Lua.Value(self.source, "nomsu:run_lua(", nomsu:tree_to_lua(_code), ":as_statements('return '))") + end + return add_lua_bits(Lua.Value(self.source), _code) + end) + _with_0.A_use_1 = _with_0.compile_time(function(self, _path) + if not (_path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string') then + return Lua(_path.source, "nomsu:run_file(" .. tostring(nomsu:tree_to_lua(_path)) .. ");") + end + local path = _path[1] + nomsu:run_file(path) + return Lua(_path.source, "nomsu:run_file(" .. tostring(repr(path)) .. ");") + end) + return _with_0 end - self:define_compile_action("lua> %code", function(self, _code) - if _code.type ~= "Text" then - return Lua(self.source, "nomsu:run_lua(", nomsu:tree_to_lua(_code), ");") - end - return add_lua_bits(Lua(self.source), _code) - end) - self:define_compile_action("=lua %code", function(self, _code) - if _code.type ~= "Text" then - return Lua.Value(self.source, "nomsu:run_lua(", nomsu:tree_to_lua(_code), ":as_statements('return '))") - end - return add_lua_bits(Lua.Value(self.source), _code) - end) - return self:define_compile_action("use %path", function(self, _path) - if not (_path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string') then - return Lua(_path.source, "nomsu:run_file(" .. tostring(nomsu:tree_to_lua(_path)) .. ");") - end - local path = _path[1] - nomsu:run_file(path) - return Lua(_path.source, "nomsu:run_file(" .. tostring(repr(path)) .. ");") - end) end } _base_0.__index = _base_0 @@ -1361,17 +1331,6 @@ do local err_msg = err_format_string:format(src, ...) return error(tostring(tok.source.filename) .. ":" .. tostring(line_no) .. ": " .. err_msg, 0) end - local stub_defs - do - stub_defs = { - word = (-R("09") * NOMSU_DEFS.ident_char ^ 1) + NOMSU_DEFS.operator_char ^ 1, - varname = (NOMSU_DEFS.ident_char ^ 1 * ((-P("'") * NOMSU_DEFS.operator_char ^ 1) + NOMSU_DEFS.ident_char ^ 1) ^ 0) ^ -1 - } - end - stub_pattern = re.compile([=[ stub <- {| tok (([ ])* tok)* |} !. - tok <- ({'%'} %varname) / {%word} - ]=], stub_defs) - var_pattern = re.compile("{| ((('%' {%varname}) / %word) ([ ])*)+ !. |}", stub_defs) _running_files = { } MAX_LINE = 80 math_expression = re.compile([[ ([+-] " ")* "%" (" " [*/^+-] (" " [+-])* " %")+ !. ]]) diff --git a/nomsu.moon b/nomsu.moon index 1181ae4..bf26844 100755 --- a/nomsu.moon +++ b/nomsu.moon @@ -37,14 +37,19 @@ STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2" string.as_lua_id = (str)-> argnum = 0 + -- Cut up escape-sequence-like chunks + str = gsub str, "x([0-9A-F][0-9A-F])", "x\0%1" + -- Alphanumeric unchanged, spaces to underscores, and everything else to hex escape sequences str = gsub str, "%W", (c)-> if c == ' ' then '_' elseif c == '%' then argnum += 1 tostring(argnum) - else format("x%X", byte(c)) + else format("x%02X", byte(c)) return '_'..str +table.map = (fn)=> [fn(v) for _,v in ipairs(@)] + -- TODO: -- consider non-linear codegen, rather than doing thunks for things like comprehensions -- type checking? @@ -296,40 +301,6 @@ class NomsuCompiler @environment._ENV = @environment @initialize_core! - local stub_defs - with NOMSU_DEFS - stub_defs = { - word: (-R("09") * .ident_char^1) + .operator_char^1 - varname: (.ident_char^1 * ((-P("'") * .operator_char^1) + .ident_char^1)^0)^-1 - } - stub_pattern = re.compile [=[ - stub <- {| tok (([ ])* tok)* |} !. - tok <- ({'%'} %varname) / {%word} - ]=], stub_defs - var_pattern = re.compile "{| ((('%' {%varname}) / %word) ([ ])*)+ !. |}", stub_defs - define_action: (signature, fn)=> - if type(fn) != 'function' - error("Not a function: #{repr fn}") - if type(signature) == 'string' - signature = {signature} - elseif type(signature) != 'table' - error("Invalid signature, expected list of strings, but got: #{repr signature}", 0) - - fn_info = debug_getinfo(fn, "u") - assert(not fn_info.isvararg, "Vararg functions aren't supported. Sorry, use a list instead.") - fn_arg_positions = {debug.getlocal(fn, i), i for i=1,fn_info.nparams} - arg_orders = {} - for alias in *signature - stub = concat(assert(stub_pattern\match(alias)), ' ') - stub_args = assert(var_pattern\match(alias)) - @environment['ACTION'..string.as_lua_id(stub)] = fn - arg_orders[stub] = [fn_arg_positions[string.as_lua_id a] for a in *stub_args] - @environment.ARG_ORDERS[fn] = arg_orders - - define_compile_action: (signature, fn)=> - @define_action(signature, fn) - @environment.COMPILE_TIME[fn] = true - parse: (nomsu_code)=> assert(type(nomsu_code) != 'string') userdata = { @@ -444,7 +415,7 @@ class NomsuCompiler switch tree.type when "Action" stub = tree.stub - action = @environment['ACTION'..string.as_lua_id(stub)] + action = @environment['A'..string.as_lua_id(stub)] if action and @environment.COMPILE_TIME[action] args = [arg for arg in *tree when type(arg) != "string"] -- Force all compile-time actions to take a tree location @@ -491,7 +462,7 @@ class NomsuCompiler if arg_orders = @environment.ARG_ORDERS[stub] args = [args[p] for p in *arg_orders] - lua\append "ACTION",string.as_lua_id(stub),"(" + lua\append "A",string.as_lua_id(stub),"(" for i, arg in ipairs args lua\append arg if i < #args then lua\append ", " @@ -909,74 +880,75 @@ class NomsuCompiler initialize_core: => -- Sets up some core functionality nomsu = self - @define_compile_action "immediately %block", (_block)=> - lua = nomsu\tree_to_lua(_block)\as_statements! - lua\declare_locals! - nomsu\run_lua(lua) - return Lua(_block.source, "if IMMEDIATE then\n ", lua, "\nend") + with nomsu.environment + .A_immediately_1 = .compile_time (_block)=> + lua = nomsu\tree_to_lua(_block)\as_statements! + lua\declare_locals! + nomsu\run_lua(lua) + return Lua(_block.source, "if IMMEDIATE then\n ", lua, "\nend") - add_lua_string_bits = (lua, code)-> - line_len = 0 - if code.type != "Text" - lua\append ", ", nomsu\tree_to_lua(code) - return - for bit in *code - bit_lua = if type(bit) == "string" - repr(bit) - else - bit_lua = nomsu\tree_to_lua(bit) - unless bit_lua.is_value - compile_error bit, - "Cannot use:\n%s\nas a string interpolation value, since it's not an expression." - bit_lua - line_len += #tostring(bit_lua) - if line_len > MAX_LINE - lua\append ",\n " - line_len = 4 - else - lua\append ", " - lua\append bit_lua - - @define_compile_action "Lua %code", (_code)=> - lua = Lua.Value(_code.source, "Lua(", repr(tostring _code.source)) - add_lua_string_bits(lua, _code) - lua\append ")" - return lua - - @define_compile_action "Lua value %code", (_code)=> - lua = Lua.Value(_code.source, "Lua.Value(", repr(tostring _code.source)) - add_lua_string_bits(lua, _code) - lua\append ")" - return lua - - add_lua_bits = (lua, code)-> - for bit in *code - if type(bit) == "string" - lua\append bit - else - bit_lua = nomsu\tree_to_lua(bit) - unless bit_lua.is_value - compile_error bit, - "Cannot use:\n%s\nas a string interpolation value, since it's not an expression." + add_lua_string_bits = (lua, code)-> + line_len = 0 + if code.type != "Text" + lua\append ", ", nomsu\tree_to_lua(code) + return + for bit in *code + bit_lua = if type(bit) == "string" + repr(bit) + else + bit_lua = nomsu\tree_to_lua(bit) + unless bit_lua.is_value + compile_error bit, + "Cannot use:\n%s\nas a string interpolation value, since it's not an expression." + bit_lua + line_len += #tostring(bit_lua) + if line_len > MAX_LINE + lua\append ",\n " + line_len = 4 + else + lua\append ", " lua\append bit_lua - return lua - @define_compile_action "lua> %code", (_code)=> - if _code.type != "Text" - return Lua @source, "nomsu:run_lua(", nomsu\tree_to_lua(_code), ");" - return add_lua_bits(Lua(@source), _code) + .A_Lua_1 = .compile_time (_code)=> + lua = Lua.Value(_code.source, "Lua(", repr(tostring _code.source)) + add_lua_string_bits(lua, _code) + lua\append ")" + return lua + + .A_Lua_value_1 = .compile_time (_code)=> + lua = Lua.Value(_code.source, "Lua.Value(", repr(tostring _code.source)) + add_lua_string_bits(lua, _code) + lua\append ")" + return lua - @define_compile_action "=lua %code", (_code)=> - if _code.type != "Text" - return Lua.Value @source, "nomsu:run_lua(", nomsu\tree_to_lua(_code), ":as_statements('return '))" - return add_lua_bits(Lua.Value(@source), _code) + add_lua_bits = (lua, code)-> + for bit in *code + if type(bit) == "string" + lua\append bit + else + bit_lua = nomsu\tree_to_lua(bit) + unless bit_lua.is_value + compile_error bit, + "Cannot use:\n%s\nas a string interpolation value, since it's not an expression." + lua\append bit_lua + return lua - @define_compile_action "use %path", (_path)=> - unless _path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string' - return Lua(_path.source, "nomsu:run_file(#{nomsu\tree_to_lua(_path)});") - path = _path[1] - nomsu\run_file(path) - return Lua(_path.source, "nomsu:run_file(#{repr path});") + nomsu.environment["A"..string.as_lua_id("lua > 1")] = .compile_time (_code)=> + if _code.type != "Text" + return Lua @source, "nomsu:run_lua(", nomsu\tree_to_lua(_code), ");" + return add_lua_bits(Lua(@source), _code) + + nomsu.environment["A"..string.as_lua_id("= lua 1")] = .compile_time (_code)=> + if _code.type != "Text" + return Lua.Value @source, "nomsu:run_lua(", nomsu\tree_to_lua(_code), ":as_statements('return '))" + return add_lua_bits(Lua.Value(@source), _code) + + .A_use_1 = .compile_time (_path)=> + unless _path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string' + return Lua(_path.source, "nomsu:run_file(#{nomsu\tree_to_lua(_path)});") + path = _path[1] + nomsu\run_file(path) + return Lua(_path.source, "nomsu:run_file(#{repr path});") -- Only run this code if this file was run directly with command line arguments, and not require()'d: if arg and debug_getinfo(2).func != require diff --git a/nomsu_tree.lua b/nomsu_tree.lua index baec3ef..7288613 100644 --- a/nomsu_tree.lua +++ b/nomsu_tree.lua @@ -116,4 +116,16 @@ AST.Action.__init = function(self) end self.stub = concat(stub_bits, " ") end +AST.Action.get_args = function(self) + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #self do + local tok = self[_index_0] + if type(tok) ~= 'string' then + _accum_0[_len_0] = tok + _len_0 = _len_0 + 1 + end + end + return _accum_0 +end return AST diff --git a/nomsu_tree.moon b/nomsu_tree.moon index b9bcdbd..0c6e925 100644 --- a/nomsu_tree.moon +++ b/nomsu_tree.moon @@ -41,4 +41,7 @@ AST.Action.__init = => stub_bits = [type(a) == 'string' and a or '%' for a in *@] @stub = concat stub_bits, " " +AST.Action.get_args = => + [tok for tok in *@ when type(tok) != 'string'] + return AST diff --git a/tests/collections.nom b/tests/collections.nom index 2cc6adf..4170d17 100644 --- a/tests/collections.nom +++ b/tests/collections.nom @@ -4,8 +4,6 @@ use "core" assume ((2nd to last in [1,2,3,4,5]) = 4) -assume ((first in [1,2]) = 1) -assume ((last in [1,2]) = 2) assume (3 is in [1,2,3,4,5]) assume (99 isn't in [1,2,3]) assume ({x:no} has key "x") @@ -27,7 +25,7 @@ assume (([[1,2],[3,4]] flattened) = [1,2,3,4]) assume ((entries in {x:1}) = [{key:"x",value:1}]) assume ((keys in {x:1}) = ["x"]) assume ((values in {x:1}) = [1]) -#assume ((sorted [3,1,2]) = [1,2,3]) +assume ((sorted [3,1,2]) = [1,2,3]) %x <- [3,1,2] sort %x assume (%x = [1,2,3]) diff --git a/tests/control_flow.nom b/tests/control_flow.nom index 5553811..20ccabe 100644 --- a/tests/control_flow.nom +++ b/tests/control_flow.nom @@ -104,7 +104,7 @@ assume (%x = 6) or barf "backwards numeric for range failed" %result <- {} for %key = %value in {x:1,y:2} - (%result's ("\%key\%key")) <- (%value * 11) + %result.("\%key\%key") <- (%value * 11) assume (%result = {xx:11,yy:22}) or barf "key/value iteration failed" for %key = %value in {x:1,y:2} diff --git a/tests/math.nom b/tests/math.nom index 8cfd0f4..115354c 100644 --- a/tests/math.nom +++ b/tests/math.nom @@ -11,7 +11,7 @@ assume all of [..] abs 5, |5|, sqrt 5, √(5), sine 5, cosine 5, tangent 5, arc sine 5, arc cosine 5, arc tangent 5, arc tangent 5/10, hyperbolic sine 5, hyperbolic cosine 5, - hyperbolic tangent 5, e^5, ln 5, log_(2) 5, floor 5, ceiling 5, round 5, + hyperbolic tangent 5, e^5, ln 5, log base 2 of 5, floor 5, ceiling 5, round 5, ..or barf "math functions failed" assume ((463 to the nearest 100) = 500) or barf "rounding failed" assume ((2.6 to the nearest 0.25) = 2.5) or barf "rounding failed" diff --git a/tests/object.nom b/tests/object.nom index 041b784..8ad5b82 100644 --- a/tests/object.nom +++ b/tests/object.nom @@ -18,7 +18,7 @@ as %d assume: ((me).barks) = 3 assume: (bark) = "Bark! Bark! Bark!" assume: "\(%d.class)" = "Dog" -assume: (%d's "barks") = 3 +assume: %d.barks = 3 as: new "Dog" {barks:1} assume: (bark) = "Bark!" diff --git a/tests/operators.nom b/tests/operators.nom index b3853e9..2452ba5 100644 --- a/tests/operators.nom +++ b/tests/operators.nom @@ -3,10 +3,6 @@ use "core" -assume (({x:5}'s "x") = 5) or barf "indexing doesn't work." -try: % <- ({}'s "[[[\n]]]") -..and if it barfs: barf "failed to index a table literal with a string containing brackets n stuff" - <-{%x:10,%y:20} assume ((%x = 10) and (%y = 20)) or barf "mutli-assignment failed." <-{%x:%y, %y:%x}