aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/control_flow.nom40
-rw-r--r--lib/metaprogramming.nom196
-rw-r--r--lib/operators.nom4
-rw-r--r--nomsu.lua371
-rwxr-xr-xnomsu.moon276
-rw-r--r--nomsu.peg18
6 files changed, 469 insertions, 436 deletions
diff --git a/lib/control_flow.nom b/lib/control_flow.nom
index 33f72ab..5169ac2 100644
--- a/lib/control_flow.nom
+++ b/lib/control_flow.nom
@@ -43,7 +43,7 @@ rule [tree %tree has function call %call] =:
return true;
end
end
- do return false; end
+ return false;
# While loops
compile [do next repeat-loop] to code: "goto continue_repeat;"
@@ -100,12 +100,14 @@ compile [..]
%stop_labels join= "\n::stop_for::;"
if (tree %body has function call (tree \(stop %) with {""=%var})):
%stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%var])::;"
- if (%stop_labels != ""): ".."
- do --for-loop label scope
- \%code\
- ..\%stop_labels
- end --for-loop label scope
- ..else: %code
+ return (..)
+ ".."
+ do --for-loop label scope
+ \%code\
+ ..\%stop_labels
+ end --for-loop label scope
+ ..if %stop_labels != "" else %code
+
parse [for %var from %start to %stop %body] as: for %var from %start to %stop via 1 %body
parse [..]
for all %start to %stop by %step %body
@@ -131,11 +133,12 @@ compile [for %var in %iterable %body] to code:
%stop_labels join= "\n::stop_for::;"
if (tree %body has function call (tree \(stop %) with {""=%var})):
%stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%var])::;"
- if (%stop_labels != ""): ".."
- do --for-loop label scope
- \%code\%stop_labels
- end --for-loop label scope
- ..else: %code
+ return (..)
+ ".."
+ do --for-loop label scope
+ \%code\%stop_labels
+ end --for-loop label scope
+ ..if %stop_labels != "" else %code
parse [for all %iterable %body] as: for % in %iterable %body
# Dict iteration (lua's "pairs()")
@@ -161,12 +164,13 @@ compile [for %key = %value in %iterable %body] to code:
%stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%key])::;"
if (tree %body has function call (tree \(stop %) with {""=%value})):
%stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%value])::;"
- if (%stop_labels != ""): ".."
- do --for-loop label scope
- \%code\
- ..\%stop_labels
- end --for-loop label scope
- ..else: %code
+ return (..)
+ ".."
+ do --for-loop label scope
+ \%code\
+ ..\%stop_labels
+ end --for-loop label scope
+ ..if %stop_labels != "" else %code
# Switch statement/multi-branch if
compile [when %body] to code:
diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom
index 325f102..f59ce04 100644
--- a/lib/metaprogramming.nom
+++ b/lib/metaprogramming.nom
@@ -3,74 +3,93 @@
that easier.
# Rule to make rules:
-lua> ".."
- nomsu:defmacro("rule %signature = %body", (function(nomsu, vars)
- nomsu:assert(\%signature.type == "List",
- "Invalid type for rule definition signature. Expected List, but got: "..tostring(\%signature.type));
- nomsu:assert(\%body.type == "Thunk",
- "Invalid type for rule definition body. Expected Thunk, but got: "..tostring(\%body.type));
- local signature = {};
- for i, alias in ipairs(\%signature.value) do
- signature[i] = alias.src;
- end
- local src = nomsu:source_code(0);
- return nil, ([[
- nomsu:def(%s, %s, %s)
- ]]):format(nomsu:repr(signature), nomsu:tree_to_lua(\%body), nomsu:repr(nomsu:dedent(src)));
- end), \(__src__ 1));
-
-# Rule to make lua macros:
-rule [compile \%macro_def to \%body] =:
+immediately:
lua> ".."
- nomsu:assert(\%macro_def.type == "List",
- "Invalid type for compile definition signature. Expected List, but got: "..tostring(\%macro_def.type));
- nomsu:assert(\%body.type == "Thunk",
- "Invalid type for compile definition body. Expected Thunk, but got: "..tostring(\%body.type));
- local signature = {};
- for i, alias in ipairs(\%macro_def.value) do
- signature[i] = alias.src;
- end
- local thunk = nomsu:tree_to_value(\%body);
- nomsu:defmacro(signature, thunk, ("compile %s\\n..to %s"):format(\%macro_def.src, \%body.src));
+ nomsu:defmacro("rule %signature = %body", (function(nomsu, vars)
+ nomsu:assert(\%signature.type == "List",
+ "Invalid type for rule definition signature. Expected List, but got: "..tostring(\%signature.type));
+ nomsu:assert(\%body.type == "Block",
+ "Invalid type for rule definition body. Expected Block, but got: "..tostring(\%body.type));
+ local signature = {};
+ for i, alias in ipairs(\%signature.value) do
+ signature[i] = alias.src;
+ end
+ local src = nomsu:source_code(0);
+ local body_lua = nomsu:tree_to_lua(\%body);
+ local fn_src = ([[
+ function(nomsu, vars)
+ %s
+ end]]):format(body_lua.statements or ("return "..body_lua.expr..";"));
+ return {statements=([[
+ nomsu:def(%s, %s, %s)
+ ]]):format(nomsu:repr(signature), fn_src, nomsu:repr(nomsu:dedent(src)))};
+ end), \(__src__ 1));
-rule [compile \%macro_def to code \%body] =:
- lua> ".."
- nomsu:assert(\%macro_def.type == "List",
- "Invalid type for compile definition signature. Expected List, but got: "..tostring(\%macro_def.type));
- nomsu:assert(\%body.type == "Thunk",
- "Invalid type for compile definition body. Expected Thunk, but got: "..tostring(\%body.type));
- local signature = {};
- for i, alias in ipairs(\%macro_def.value) do
- signature[i] = alias.src;
- end
- local thunk = nomsu:tree_to_value(\%body);
- local thunk_wrapper = function(...) return nil, thunk(...); end
- nomsu:defmacro(signature, thunk_wrapper, ("compile %s\\n..to code %s"):format(\%macro_def.src, \%body.src));
+# Rules to make lua macros:
+immediately:
+ rule [compile \%macro_def to \%body] =:
+ lua> ".."
+ nomsu:assert(\%macro_def.type == "List",
+ "Invalid type for compile definition signature. Expected List, but got: "..tostring(\%macro_def.type));
+ nomsu:assert(\%body.type == "Block",
+ "Invalid type for compile definition body. Expected Block, but got: "..tostring(\%body.type));
+ local signature = {};
+ for i, alias in ipairs(\%macro_def.value) do
+ signature[i] = alias.src;
+ end
+ local body_lua = nomsu:tree_to_lua(\%body);
+ local fn_src = ([[
+ function(nomsu, vars)
+ %s
+ end]]):format(body_lua.statements or ("return "..body_lua.expr..";"));
+ local fn = nomsu:run_lua("return "..fn_src..";");
+ local fn_wrapper = function(...) return {expr=fn(...)}; end;
+ nomsu:defmacro(signature, fn_wrapper, ("compile %s\\n..to %s"):format(\%macro_def.src, \%body.src));
+
+ rule [compile \%macro_def to code \%body] =:
+ lua> ".."
+ nomsu:assert(\%macro_def.type == "List",
+ "Invalid type for compile definition signature. Expected List, but got: "..tostring(\%macro_def.type));
+ nomsu:assert(\%body.type == "Block",
+ "Invalid type for compile definition body. Expected Block, but got: "..tostring(\%body.type));
+ local signature = {};
+ for i, alias in ipairs(\%macro_def.value) do
+ signature[i] = alias.src;
+ end
+ local body_lua = nomsu:tree_to_lua(\%body);
+ local fn_src = ([[
+ function(nomsu, vars)
+ %s
+ end]]):format(body_lua.statements or ("return "..body_lua.expr..";"));
+ local fn = nomsu:run_lua("return "..fn_src..";");
+ local fn_wrapper = function(...) return {statements=fn(...)}; end;
+ nomsu:defmacro(signature, fn_wrapper, ("compile %s\\n..to code %s"):format(\%macro_def.src, \%body.src));
# Rule to make nomsu macros:
-lua> ".."
- nomsu:defmacro("parse %shorthand as %longhand", (function(nomsu, vars)
- nomsu:assert(\%shorthand.type == "List",
- "Invalid type for parse definition signature. Expected List, but got: "..tostring(\%shorthand.type));
- nomsu:assert(\%longhand.type == "Thunk",
- "Invalid type for parse definition body. Expected Thunk, but got: "..tostring(\%longhand.type));
- local signature = {};
- for i, alias in ipairs(\%shorthand.value) do
- signature[i] = alias.src;
- end
- local template = {};
- for i, line in ipairs(\%longhand.value) do
- template[i] = nomsu:dedent(line.src);
- end
- signature, template = nomsu:repr(signature), nomsu:repr(table.concat(template, "\\n"));
- return nil, ([[
- nomsu:defmacro(%s, (function(nomsu, vars)
- local template = nomsu:parse(%s, %s);
- if #template.value == 1 then template = template.value[1]; end
- local replacement = nomsu:replaced_vars(template, vars);
- return nomsu:tree_to_lua(replacement);
- end), %s)]]):format(signature, template, nomsu:repr(\%shorthand.line_no), nomsu:repr(nomsu:source_code(0)));
- end), \(__src__ 1));
+immediately:
+ lua> ".."
+ nomsu:defmacro("parse %shorthand as %longhand", (function(nomsu, vars)
+ nomsu:assert(\%shorthand.type == "List",
+ "Invalid type for parse definition signature. Expected List, but got: "..tostring(\%shorthand.type));
+ nomsu:assert(\%longhand.type == "Block",
+ "Invalid type for parse definition body. Expected Block, but got: "..tostring(\%longhand.type));
+ local signature = {};
+ for i, alias in ipairs(\%shorthand.value) do
+ signature[i] = alias.src;
+ end
+ local template = {};
+ for i, line in ipairs(\%longhand.value) do
+ template[i] = nomsu:dedent(line.src);
+ end
+ signature, template = nomsu:repr(signature), nomsu:repr(table.concat(template, "\\n"));
+ return {expr=([[
+ nomsu:defmacro(%s, (function(nomsu, vars)
+ local template = nomsu:parse(%s, %s);
+ if #template.value == 1 then template = template.value[1]; end
+ local replacement = nomsu:replaced_vars(template, vars);
+ return nomsu:tree_to_lua(replacement);
+ end), %s)]]):format(signature, template, nomsu:repr(\%shorthand.line_no), nomsu:repr(nomsu:source_code(0)))};
+ end), \(__src__ 1));
rule [remove rule %stub] =:
lua> ".."
@@ -79,40 +98,29 @@ rule [remove rule %stub] =:
nomsu.defs[alias] = false;
end
-rule [%tree as lua] =:
- =lua "nomsu:tree_to_lua(\%tree)"
-rule [%tree as value] =:
- =lua "nomsu:tree_to_value(\%tree, vars)"
-compile [repr %obj] to:
- "nomsu:repr(\(%obj as lua))"
-compile [indented %obj] to:
- "nomsu:indent(\(%obj as lua))"
-compile [dedented %obj] to:
- "nomsu:dedent(\(%obj as lua))"
-compile [type %obj, type of %obj] to:
- "type(\(%obj as lua))"
+immediately:
+ rule [%tree as lua] =:
+ =lua "nomsu:tree_to_lua(\%tree).expr"
+ rule [%tree as lua statements] =:
+ lua> ".."
+ local lua = nomsu:tree_to_lua(\%tree);
+ return lua.statements or (lua.expr..";");
+ rule [%tree as value] =:
+ =lua "nomsu:tree_to_value(\%tree, vars)"
+ compile [repr %obj] to:
+ "nomsu:repr(\(%obj as lua))"
+ compile [indented %obj] to:
+ "nomsu:indent(\(%obj as lua))"
+ compile [dedented %obj] to:
+ "nomsu:dedent(\(%obj as lua))"
+ compile [type %obj, type of %obj] to:
+ "type(\(%obj as lua))"
parse [lua do> %block] as:
lua> "do"
lua> %block
lua> "end"
-rule [%tree as lua statement] =:
- lua do> ".."
- local _,statement = nomsu:tree_to_lua(\%tree);
- return statement;
-rule [%tree as lua statements] =:
- lua do> ".."
- local lua_bits = {};
- nomsu:assert(type(\%tree) == 'table' and \%tree.type == "Thunk",
- "Invalid type. Expected Thunk.");
- for _,bit in ipairs(\%tree.value) do
- local expr, statement = nomsu:tree_to_lua(bit);
- if statement then table.insert(lua_bits, statement); end
- if expr then table.insert(lua_bits, "ret = "..expr..";"); end
- end
- return table.concat(lua_bits, "\\n");
-
compile [nomsu] to: "nomsu"
compile [nomsu's %key] to: "nomsu[\(%key as lua)]"
compile [nomsu %method %args] to: "nomsu[\(%method as lua)](nomsu, unpack(\(%args as lua)))"
@@ -124,7 +132,7 @@ parse [rule %signature] as:
# Get the source code for a function
rule [help %rule] =:
- lua do> ".."
+ lua> ".."
local fn_def = nomsu.defs[nomsu:get_stub(\%rule)]
if not fn_def then
nomsu:writeln("Rule not found: "..nomsu:repr(\%rule));
@@ -135,7 +143,7 @@ rule [help %rule] =:
# Compiler tools
parse [eval %code, run %code] as: nomsu "run" [%code]
rule [source code from tree %tree] =:
- lua do> ".."
+ lua> ".."
local _,_,leading_space = \%tree.src:find("\\n(%s*)%S");
if leading_space then
local chunk1, chunk2 = \%tree.src:match(":%s*([^\\n]*)(\\n.*)");
diff --git a/lib/operators.nom b/lib/operators.nom
index 59a9ad2..2f08089 100644
--- a/lib/operators.nom
+++ b/lib/operators.nom
@@ -51,9 +51,9 @@ compile [%var = %val] to code:
lua> ".."
if \%var.type == 'List' and \%val.type == 'List' then
local lhs = {};
- for i,x in ipairs(\%var.value) do lhs[i] = nomsu:tree_to_lua(x); end
+ for i,x in ipairs(\%var.value) do lhs[i] = nomsu:tree_to_lua(x).expr; end
local rhs = {};
- for i,x in ipairs(\%val.value) do rhs[i] = nomsu:tree_to_lua(x); end
+ for i,x in ipairs(\%val.value) do rhs[i] = nomsu:tree_to_lua(x).expr; end
return table.concat(lhs, ", ").." = "..table.concat(rhs, ", ")..";";
else
return \(%var as lua).." = "..\(%val as lua)..";";
diff --git a/nomsu.lua b/nomsu.lua
index 730ab13..39e83fc 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -194,7 +194,7 @@ do
self:write_err(...)
return self:write_err("\n")
end,
- def = function(self, signature, thunk, src, is_macro)
+ def = function(self, signature, fn, src, is_macro)
if is_macro == nil then
is_macro = false
end
@@ -205,13 +205,13 @@ do
elseif type(signature) == 'table' and type(signature[1]) == 'string' then
signature = self:get_stubs(signature)
end
- self:assert(type(thunk) == 'function', "Bad thunk: " .. tostring(repr(thunk)))
+ self:assert(type(fn) == 'function', "Bad fn: " .. tostring(repr(fn)))
local canonical_args = nil
local canonical_escaped_args = nil
local aliases = { }
self.__class.def_number = self.__class.def_number + 1
local def = {
- thunk = thunk,
+ fn = fn,
src = src,
is_macro = is_macro,
aliases = { },
@@ -256,8 +256,8 @@ do
rawset(where_defs_go, stub, stub_def)
end
end,
- defmacro = function(self, signature, thunk, src)
- return self:def(signature, thunk, src, true)
+ defmacro = function(self, signature, fn, src)
+ return self:def(signature, fn, src, true)
end,
scoped = function(self, thunk)
local old_defs = self.defs
@@ -363,8 +363,8 @@ do
if not (def.is_macro) then
self:assert_permission(stub)
end
- local thunk, arg_names
- thunk, arg_names = def.thunk, def.arg_names
+ local fn, arg_names
+ fn, arg_names = def.fn, def.arg_names
local args
do
local _tbl_0 = { }
@@ -375,12 +375,15 @@ do
end
if self.debug then
self:write(tostring(colored.bright("CALLING")) .. " " .. tostring(colored.magenta(colored.underscore(stub))) .. " ")
- self:writeln(tostring(colored.bright("WITH ARGS:")) .. " " .. tostring(colored.dim(repr(args))))
+ self:writeln(tostring(colored.bright("WITH ARGS:")))
+ for name, value in pairs(args) do
+ self:writeln(" " .. tostring(colored.bright("* " .. tostring(name))) .. " = " .. tostring(colored.dim(repr(value))))
+ end
end
local old_defs
old_defs, self.defs = self.defs, def.defs
local rets = {
- thunk(self, args)
+ fn(self, args)
}
self.defs = old_defs
remove(self.callstack)
@@ -406,9 +409,9 @@ do
self:writeln(tostring(colored.bright("WITH ARGS:")) .. " " .. tostring(colored.dim(repr(args))))
end
insert(self.callstack, "#macro")
- local expr, statement = self:call(tree.stub, tree.line_no, unpack(args))
+ local ret = self:call(tree.stub, tree.line_no, unpack(args))
remove(self.callstack)
- return expr, statement
+ return ret
end,
dedent = function(self, code)
if not (code:find("\n")) then
@@ -448,8 +451,11 @@ do
return (code:gsub("\n" .. (" "):rep(indent_spaces), "\n "))
end
end,
- indent = function(self, code)
- return (code:gsub("\n", "\n "))
+ indent = function(self, code, levels)
+ if levels == nil then
+ levels = 1
+ end
+ return code:gsub("\n", "\n" .. (" "):rep(levels))
end,
assert_permission = function(self, stub)
local fn_def = self.defs[stub]
@@ -525,72 +531,46 @@ do
debug.sethook(timeout, "", max_operations)
end
local tree = self:parse(src, filename)
- self:assert(tree, "Tree failed to compile: " .. tostring(src))
+ self:assert(tree, "Failed to parse: " .. tostring(src))
self:assert(tree.type == "File", "Attempt to run non-file: " .. tostring(tree.type))
- local buffer = { }
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local statement = _list_0[_index_0]
- if self.debug then
- self:writeln(tostring(colored.bright("RUNNING NOMSU:")) .. "\n" .. tostring(colored.bright(colored.yellow(statement.src))))
- self:writeln(colored.bright("PARSED TO TREE:"))
- self:print_tree(statement)
- end
- local ok, expr, statements = pcall(self.tree_to_lua, self, statement, filename)
- if not ok then
- self:errorln(tostring(colored.red("Error occurred in statement:")) .. "\n" .. tostring(colored.bright(colored.yellow(statement.src))))
- error(expr)
- end
- local code_for_statement = ([[return (function(nomsu, vars)
-%s
-end);]]):format(statements or ("return " .. expr .. ";"))
- if output_file then
- if statements and #statements > 0 then
- output_file:write("lua> \"..\"\n " .. tostring(self:indent(statements:gsub("\\", "\\\\"))) .. "\n")
- end
- if expr and #expr > 0 then
- output_file:write("=lua \"..\"\n " .. tostring(self:indent(expr:gsub("\\", "\\\\"))) .. "\n")
- end
- end
- if self.debug then
- self:writeln(tostring(colored.bright("RUNNING LUA:")) .. "\n" .. tostring(colored.blue(colored.bright(code_for_statement))))
- end
- local lua_thunk, err = load(code_for_statement)
- if not lua_thunk then
- local n = 1
- local fn
- fn = function()
- n = n + 1
- return ("\n%-3d|"):format(n)
- end
- local code = "1 |" .. code_for_statement:gsub("\n", fn)
- error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(code)))) .. "\n\n" .. tostring(err) .. "\n\nProduced by statement:\n" .. tostring(colored.bright(colored.yellow(statement.src))))
- end
- local run_statement = lua_thunk()
- local ret
- ok, ret = pcall(run_statement, self, vars)
- if not ok then
- self:errorln(tostring(colored.red("Error occurred in statement:")) .. "\n" .. tostring(colored.yellow(statement.src)))
- self:errorln(debug.traceback())
- error(ret)
- end
- if statements then
- insert(buffer, statements)
- end
- if expr then
- insert(buffer, tostring(expr) .. ";")
- end
- end
+ local lua = self:tree_to_lua(tree)
+ local lua_code = lua.statements or (lua.expr .. ";")
+ local ret = self:run_lua(lua_code, vars)
if max_operations then
debug.sethook()
end
- local lua_code = ([[return (function(nomsu, vars)
-%s
-end);]]):format(concat(buffer, "\n"))
- return nil, lua_code, vars
+ if output_file then
+ output_file:write(lua_code)
+ end
+ return ret, lua_code, vars
+ end,
+ run_lua = function(self, lua_code, vars)
+ if vars == nil then
+ vars = { }
+ end
+ local load_lua_fn, err = load(([[return function(nomsu, vars)
+ %s
+end]]):format(lua_code))
+ if not load_lua_fn then
+ local n = 1
+ local fn
+ fn = function()
+ n = n + 1
+ return ("\n%-3d|"):format(n)
+ end
+ local code = "1 |" .. lua_code:gsub("\n", fn)
+ self:error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(code)))) .. "\n\n" .. tostring(err))
+ end
+ local run_lua_fn = load_lua_fn()
+ local ok, ret = pcall(run_lua_fn, self, vars)
+ if not ok then
+ self:errorln(debug.traceback())
+ self:error(ret)
+ end
+ return ret
end,
tree_to_value = function(self, tree, vars, filename)
- local code = "return (function(nomsu, vars)\nreturn " .. tostring(self:tree_to_lua(tree, filename)) .. ";\nend);"
+ local code = "return (function(nomsu, vars)\nreturn " .. tostring(self:tree_to_lua(tree, filename).expr) .. ";\nend);"
if self.debug then
self:writeln(tostring(colored.bright("RUNNING LUA TO GET VALUE:")) .. "\n" .. tostring(colored.blue(colored.bright(code))))
end
@@ -625,9 +605,9 @@ end);]]):format(concat(buffer, "\n"))
elseif "Nomsu" == _exp_0 then
local inside, inline = self:tree_to_nomsu(tree.value, force_inline)
return "\\" .. tostring(inside), inline
- elseif "Thunk" == _exp_0 then
+ elseif "Block" == _exp_0 then
if force_inline then
- return "{" .. tostring(concat((function()
+ return "(:" .. tostring(concat((function()
local _accum_0 = { }
local _len_0 = 1
local _list_0 = tree.value
@@ -637,7 +617,7 @@ end);]]):format(concat(buffer, "\n"))
_len_0 = _len_0 + 1
end
return _accum_0
- end)(), "; ")), true
+ end)(), "; ")) .. ")", true
else
return ":" .. self:indent("\n" .. concat((function()
local _accum_0 = { }
@@ -739,7 +719,7 @@ end);]]):format(concat(buffer, "\n"))
return longbuff, false
end
elseif "Dict" == _exp_0 then
- return error("Sorry, not yet implemented.")
+ return self:error("Sorry, not yet implemented.")
elseif "Number" == _exp_0 then
return repr(tree.value), true
elseif "Var" == _exp_0 then
@@ -771,7 +751,7 @@ end);]]):format(concat(buffer, "\n"))
return _accum_0
end)(), ", ")) .. "]"
else
- return "(d{" .. tostring(concat((function()
+ return "{" .. tostring(concat((function()
local _accum_0 = { }
local _len_0 = 1
for k, v in pairs(value) do
@@ -779,7 +759,7 @@ end);]]):format(concat(buffer, "\n"))
_len_0 = _len_0 + 1
end
return _accum_0
- end)(), "; ")) .. "})"
+ end)(), ", ")) .. "}"
end
elseif "string" == _exp_0 then
if value == "\n" then
@@ -801,49 +781,59 @@ end);]]):format(concat(buffer, "\n"))
end
local _exp_0 = tree.type
if "File" == _exp_0 then
+ if #tree.value == 1 then
+ return self:tree_to_lua(tree.value[1], filename)
+ end
local lua_bits = { }
local _list_0 = tree.value
for _index_0 = 1, #_list_0 do
local line = _list_0[_index_0]
- local expr, statement = self:tree_to_lua(line, filename)
- if statement then
- insert(lua_bits, statement)
+ local lua = self:tree_to_lua(line, filename)
+ if not lua then
+ self:error("No lua produced by " .. tostring(repr(line)))
+ end
+ if lua.statements then
+ insert(lua_bits, lua.statements)
end
- if expr then
- insert(lua_bits, tostring(expr) .. ";")
+ if lua.expr then
+ insert(lua_bits, tostring(lua.expr) .. ";")
end
end
- return nil, concat(lua_bits, "\n")
+ return {
+ statements = concat(lua_bits, "\n")
+ }
elseif "Nomsu" == _exp_0 then
- return "nomsu:parse(" .. tostring(repr(tree.value.src)) .. ", " .. tostring(repr(tree.line_no)) .. ").value[1]", nil
- elseif "Thunk" == _exp_0 then
+ return {
+ expr = "nomsu:parse(" .. tostring(repr(tree.value.src)) .. ", " .. tostring(repr(tree.line_no)) .. ").value[1]"
+ }
+ elseif "Block" == _exp_0 then
local lua_bits = { }
local _list_0 = tree.value
for _index_0 = 1, #_list_0 do
local arg = _list_0[_index_0]
- local expr, statement = self:tree_to_lua(arg, filename)
- if #tree.value == 1 and expr and not statement then
- return ([[(function(nomsu, vars)
- return %s;
-end)]]):format(expr)
+ local lua = self:tree_to_lua(arg, filename)
+ if #tree.value == 1 and lua.expr and not lua.statements then
+ return {
+ expr = lua.expr
+ }
end
- if statement then
- insert(lua_bits, statement)
+ if lua.statements then
+ insert(lua_bits, lua.statements)
end
- if expr then
- insert(lua_bits, tostring(expr) .. ";")
+ if lua.expr then
+ insert(lua_bits, tostring(lua.expr) .. ";")
end
end
- return ([[(function(nomsu, vars)
-%s
-end)]]):format(concat(lua_bits, "\n"))
+ return {
+ statements = concat(lua_bits, "\n")
+ }
elseif "FunctionCall" == _exp_0 then
insert(self.compilestack, tree)
local def = self.defs[tree.stub]
if def and def.is_macro then
- local expr, statement = self:run_macro(tree)
+ local lua = self:run_macro(tree)
remove(self.compilestack)
- return expr, statement
+ return lua
elseif not def and self.__class.math_patt:match(tree.stub) then
local bits = { }
local _list_0 = tree.value
@@ -852,12 +842,15 @@ end)]]):format(concat(lua_bits, "\n"))
if tok.type == "Word" then
insert(bits, tok.value)
else
- local expr, statement = self:tree_to_lua(tok, filename)
- self:assert(statement == nil, "non-expression value inside math expression")
- insert(bits, expr)
+ local lua = self:tree_to_lua(tok, filename)
+ self:assert(lua.statements == nil, "non-expression value inside math expression")
+ insert(bits, lua.expr)
end
end
- return "(" .. tostring(concat(bits, " ")) .. ")"
+ remove(self.compilestack)
+ return {
+ expr = "(" .. tostring(concat(bits, " ")) .. ")"
+ }
end
local args = {
repr(tree.stub),
@@ -894,11 +887,11 @@ end)]]):format(concat(lua_bits, "\n"))
if escaped_args[arg_names[arg_num]] then
insert(args, "nomsu:parse(" .. tostring(repr(arg.src)) .. ", " .. tostring(repr(tree.line_no)) .. ").value[1]")
else
- local expr, statement = self:tree_to_lua(arg, filename)
- if statement then
+ local lua = self:tree_to_lua(arg, filename)
+ if lua.statements then
self:error("Cannot use [[" .. tostring(arg.src) .. "]] as a function argument, since it's not an expression.")
end
- insert(args, expr)
+ insert(args, lua.expr)
end
arg_num = arg_num + 1
_continue_0 = true
@@ -908,7 +901,9 @@ end)]]):format(concat(lua_bits, "\n"))
end
end
remove(self.compilestack)
- return self.__class:comma_separated_items("nomsu:call(", args, ")"), nil
+ return {
+ expr = self.__class:comma_separated_items("nomsu:call(", args, ")")
+ }
elseif "String" == _exp_0 then
local concat_parts = { }
local string_buffer = ""
@@ -926,16 +921,16 @@ end)]]):format(concat(lua_bits, "\n"))
insert(concat_parts, repr(string_buffer))
string_buffer = ""
end
- local expr, statement = self:tree_to_lua(bit, filename)
+ local lua = self:tree_to_lua(bit, filename)
if self.debug then
self:writeln((colored.bright("INTERP:")))
self:print_tree(bit)
- self:writeln(tostring(colored.bright("EXPR:")) .. " " .. tostring(expr) .. ", " .. tostring(colored.bright("STATEMENT:")) .. " " .. tostring(statement))
+ self:writeln(tostring(colored.bright("EXPR:")) .. " " .. tostring(lua.expr) .. ", " .. tostring(colored.bright("STATEMENT:")) .. " " .. tostring(lua.statements))
end
- if statement then
+ if lua.statements then
self:error("Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.")
end
- insert(concat_parts, "nomsu:stringify(" .. tostring(expr) .. ")")
+ insert(concat_parts, "nomsu:stringify(" .. tostring(lua.expr) .. ")")
_continue_0 = true
until true
if not _continue_0 then
@@ -946,57 +941,75 @@ end)]]):format(concat(lua_bits, "\n"))
insert(concat_parts, repr(string_buffer))
end
if #concat_parts == 0 then
- return "''", nil
+ return {
+ expr = "''"
+ }
elseif #concat_parts == 1 then
- return concat_parts[1], nil
+ return {
+ expr = concat_parts[1]
+ }
else
- return "(" .. tostring(concat(concat_parts, "..")) .. ")", nil
+ return {
+ expr = "(" .. tostring(concat(concat_parts, "..")) .. ")"
+ }
end
elseif "List" == _exp_0 then
local items = { }
local _list_0 = tree.value
for _index_0 = 1, #_list_0 do
local item = _list_0[_index_0]
- local expr, statement = self:tree_to_lua(item, filename)
- if statement then
+ local lua = self:tree_to_lua(item, filename)
+ if lua.statements then
self:error("Cannot use [[" .. tostring(item.src) .. "]] as a list item, since it's not an expression.")
end
- insert(items, expr)
+ insert(items, lua.expr)
end
- return self.__class:comma_separated_items("{", items, "}"), nil
+ return {
+ expr = self.__class:comma_separated_items("{", items, "}")
+ }
elseif "Dict" == _exp_0 then
local items = { }
local _list_0 = tree.value
for _index_0 = 1, #_list_0 do
local entry = _list_0[_index_0]
- local key_expr, key_statement
+ local key_lua
if entry.dict_key.type == "Word" then
- key_expr, key_statement = repr(entry.dict_key.value), nil
+ key_lua = {
+ expr = repr(entry.dict_key.value)
+ }
else
- key_expr, key_statement = self:tree_to_lua(entry.dict_key, filename)
+ key_lua = self:tree_to_lua(entry.dict_key, filename)
end
- if key_statement then
+ if key_lua.statements then
self:error("Cannot use [[" .. tostring(entry.dict_key.src) .. "]] as a dict key, since it's not an expression.")
end
- local value_expr, value_statement = self:tree_to_lua(entry.dict_value, filename)
- if value_statement then
+ local value_lua = self:tree_to_lua(entry.dict_value, filename)
+ if value_lua.statements then
self:error("Cannot use [[" .. tostring(entry.dict_value.src) .. "]] as a dict value, since it's not an expression.")
end
- local key_str = key_expr:match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
+ local key_str = key_lua.expr:match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
if key_str then
- insert(items, tostring(key_str) .. "=" .. tostring(value_expr))
+ insert(items, tostring(key_str) .. "=" .. tostring(value_lua.expr))
else
- insert(items, "[" .. tostring(key_expr) .. "]=" .. tostring(value_expr))
+ insert(items, "[" .. tostring(key_lua.expr) .. "]=" .. tostring(value_lua.expr))
end
end
- return self.__class:comma_separated_items("{", items, "}"), nil
+ return {
+ expr = self.__class:comma_separated_items("{", items, "}")
+ }
elseif "Number" == _exp_0 then
- return repr(tree.value), nil
+ return {
+ expr = repr(tree.value)
+ }
elseif "Var" == _exp_0 then
if tree.value:match("^[a-zA-Z_][a-zA-Z0-9_]*$") then
- return "vars." .. tostring(tree.value), nil
+ return {
+ expr = "vars." .. tostring(tree.value)
+ }
else
- return "vars[" .. tostring(repr(tree.value)) .. "]", nil
+ return {
+ expr = "vars[" .. tostring(repr(tree.value)) .. "]"
+ }
end
else
return self:error("Unknown/unimplemented thingy: " .. tostring(tree.type))
@@ -1011,7 +1024,7 @@ end)]]):format(concat(lua_bits, "\n"))
return
end
local _exp_0 = tree.type
- if "List" == _exp_0 or "File" == _exp_0 or "Thunk" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then
+ if "List" == _exp_0 or "File" == _exp_0 or "Block" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then
local _list_0 = tree.value
for _index_0 = 1, #_list_0 do
local v = _list_0[_index_0]
@@ -1064,7 +1077,7 @@ end)]]):format(concat(lua_bits, "\n"))
if vars[tree.value] ~= nil then
tree = vars[tree.value]
end
- elseif "File" == _exp_0 or "Nomsu" == _exp_0 or "Thunk" == _exp_0 or "List" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then
+ elseif "File" == _exp_0 or "Nomsu" == _exp_0 or "Block" == _exp_0 or "List" == _exp_0 or "FunctionCall" == _exp_0 or "String" == _exp_0 then
local new_value = self:replaced_vars(tree.value, vars)
if new_value ~= tree.value then
do
@@ -1281,43 +1294,52 @@ end)]]):format(concat(lua_bits, "\n"))
if type(bit) == "string" then
insert(concat_parts, bit)
else
- local expr, statement = self:tree_to_lua(bit, filename)
- if statement then
+ local lua = self:tree_to_lua(bit, filename)
+ if lua.statements then
self:error("Cannot use [[" .. tostring(bit.src) .. "]] as a string interpolation value, since it's not an expression.")
end
- insert(concat_parts, expr)
+ insert(concat_parts, lua.expr)
end
end
return concat(concat_parts)
end
- local lua_code
- lua_code = function(self, vars)
+ self:defmacro("do %block", function(self, vars)
+ local make_line
+ make_line = function(lua)
+ return lua.expr and (lua.expr .. ";") or lua.statements
+ end
+ if vars.block.type == "Block" then
+ return self:tree_to_lua(vars.block)
+ else
+ return {
+ expr = tostring(self:tree_to_lua(vars.block)) .. "(nomsu, vars)"
+ }
+ end
+ end)
+ self:defmacro("immediately %block", function(self, vars)
+ local lua = self:tree_to_lua(vars.block)
+ local lua_code = lua.statements or (lua.expr .. ";")
+ self:run_lua(lua_code, vars)
+ return {
+ statements = lua_code
+ }
+ end)
+ self:defmacro("lua> %code", function(self, vars)
local lua = nomsu_string_as_lua(self, vars.code)
- return nil, lua
- end
- self:defmacro("lua> %code", lua_code)
- local lua_value
- lua_value = function(self, vars)
+ return {
+ statements = lua
+ }
+ end)
+ self:defmacro("=lua %code", function(self, vars)
local lua = nomsu_string_as_lua(self, vars.code)
- return lua, nil
- end
- self:defmacro("=lua %code", lua_value)
- self:defmacro("__src__ %level", function(self, vars)
- return self:repr(self:source_code(self:tree_to_value(vars.level)))
+ return {
+ expr = lua
+ }
end)
- self:def("derp \\%foo derp \\%bar", function(self, vars)
- local lua = "local x = " .. repr((function()
- local _accum_0 = { }
- local _len_0 = 1
- local _list_0 = vars.foo.value
- for _index_0 = 1, #_list_0 do
- local t = _list_0[_index_0]
- _accum_0[_len_0] = t.stub
- _len_0 = _len_0 + 1
- end
- return _accum_0
- end)()) .. ";\nlocal y = " .. self:tree_to_lua(vars.bar)
- return print(colored.green(lua))
+ self:defmacro("__src__ %level", function(self, vars)
+ return {
+ expr = repr(self:source_code(self:tree_to_value(vars.level)))
+ }
end)
local run_file
run_file = function(self, vars)
@@ -1340,17 +1362,19 @@ end)]]):format(concat(lua_bits, "\n"))
end
end
self:def("run file %filename", run_file)
- local _require
- _require = function(self, vars)
+ return self:defmacro("require %filename", function(self, vars)
+ local filename = self:tree_to_value(vars.filename)
local loaded = self.defs["#loaded_files"]
- if not loaded[vars.filename] then
- loaded[vars.filename] = run_file(self, {
- filename = vars.filename
+ if not loaded[filename] then
+ loaded[filename] = run_file(self, {
+ filename = filename
}) or true
end
- return loaded[vars.filename]
- end
- return self:def("require %filename", _require)
+ local _ = loaded[filename]
+ return {
+ statements = ""
+ }
+ end)
end
}
_base_0.__index = _base_0
@@ -1476,7 +1500,10 @@ if arg then
input = io.open(args.input):read("*a")
end
local vars = { }
- local retval, code = c:run(input, args.input, vars, nil, compiled_output)
+ local retval, code = c:run(input, args.input, vars)
+ if args.output then
+ compiled_output:write("lua> \"..\"\n " .. c:indent(code:gsub("\\", "\\\\"), 1))
+ end
end
if args.flags["-p"] then
c.write = _write
diff --git a/nomsu.moon b/nomsu.moon
index 8f77c87..03005bb 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -36,6 +36,7 @@ if _VERSION == "Lua 5.1"
-- type checking?
-- Fix compiler bug that breaks when file ends with a block comment
-- Add compiler options for optimization level (compile-fast vs. run-fast, etc.)
+-- Do a pass on all rules to enforce parameters-are-nouns heuristic
lpeg.setmaxstack 10000 -- whoa
{:P,:R,:V,:S,:Cg,:C,:Cp,:B,:Cmt} = lpeg
@@ -164,17 +165,17 @@ class NomsuCompiler
@write_err(...)
@write_err("\n")
- def: (signature, thunk, src, is_macro=false)=>
+ def: (signature, fn, src, is_macro=false)=>
if type(signature) == 'string'
signature = @get_stubs {signature}
elseif type(signature) == 'table' and type(signature[1]) == 'string'
signature = @get_stubs signature
- @assert type(thunk) == 'function', "Bad thunk: #{repr thunk}"
+ @assert type(fn) == 'function', "Bad fn: #{repr fn}"
canonical_args = nil
canonical_escaped_args = nil
aliases = {}
@@def_number += 1
- def = {:thunk, :src, :is_macro, aliases:{}, def_number:@@def_number, defs:@defs}
+ def = {:fn, :src, :is_macro, aliases:{}, def_number:@@def_number, defs:@defs}
where_defs_go = (getmetatable(@defs) or {}).__newindex or @defs
for {stub, arg_names, escaped_args} in *signature
@assert stub, "NO STUB FOUND: #{repr signature}"
@@ -193,8 +194,8 @@ class NomsuCompiler
stub_def = setmetatable({:stub, :arg_names, :escaped_args}, {__index:def})
rawset(where_defs_go, stub, stub_def)
- defmacro: (signature, thunk, src)=>
- @def(signature, thunk, src, true)
+ defmacro: (signature, fn, src)=>
+ @def(signature, fn, src, true)
scoped: (thunk)=>
old_defs = @defs
@@ -253,13 +254,15 @@ class NomsuCompiler
@error "Attempt to call undefined function: #{stub}"
unless def.is_macro
@assert_permission(stub)
- {:thunk, :arg_names} = def
+ {:fn, :arg_names} = def
args = {name, select(i,...) for i,name in ipairs(arg_names)}
if @debug
@write "#{colored.bright "CALLING"} #{colored.magenta(colored.underscore stub)} "
- @writeln "#{colored.bright "WITH ARGS:"} #{colored.dim repr(args)}"
+ @writeln "#{colored.bright "WITH ARGS:"}"
+ for name, value in pairs(args)
+ @writeln " #{colored.bright "* #{name}"} = #{colored.dim repr(value)}"
old_defs, @defs = @defs, def.defs
- rets = {thunk(self,args)}
+ rets = {fn(self,args)}
@defs = old_defs
remove @callstack
return unpack(rets)
@@ -270,9 +273,9 @@ class NomsuCompiler
@write "#{colored.bright "RUNNING MACRO"} #{colored.underscore colored.magenta(tree.stub)} "
@writeln "#{colored.bright "WITH ARGS:"} #{colored.dim repr args}"
insert @callstack, "#macro"
- expr, statement = @call(tree.stub, tree.line_no, unpack(args))
+ ret = @call(tree.stub, tree.line_no, unpack(args))
remove @callstack
- return expr, statement
+ return ret
dedent: (code)=>
unless code\find("\n")
@@ -290,8 +293,8 @@ class NomsuCompiler
else
return (code\gsub("\n"..(" ")\rep(indent_spaces), "\n "))
- indent: (code)=>
- (code\gsub("\n","\n "))
+ indent: (code, levels=1)=>
+ return code\gsub("\n","\n"..(" ")\rep(levels))
assert_permission: (stub)=>
fn_def = @defs[stub]
@@ -338,60 +341,40 @@ class NomsuCompiler
@error "Execution quota exceeded. Your code took too long."
debug.sethook timeout, "", max_operations
tree = @parse(src, filename)
- @assert tree, "Tree failed to compile: #{src}"
+ @assert tree, "Failed to parse: #{src}"
@assert tree.type == "File", "Attempt to run non-file: #{tree.type}"
- buffer = {}
- -- TODO: handle return statements in a file
- for statement in *tree.value
- if @debug
- @writeln "#{colored.bright "RUNNING NOMSU:"}\n#{colored.bright colored.yellow statement.src}"
- @writeln colored.bright("PARSED TO TREE:")
- @print_tree statement
- ok,expr,statements = pcall(@tree_to_lua, self, statement, filename)
- if not ok
- @errorln "#{colored.red "Error occurred in statement:"}\n#{colored.bright colored.yellow statement.src}"
- error(expr)
- code_for_statement = ([[
-return (function(nomsu, vars)
-%s
-end);]])\format(statements or ("return "..expr..";"))
- if output_file
- if statements and #statements > 0
- output_file\write "lua> \"..\"\n #{@indent statements\gsub("\\","\\\\")}\n"
- if expr and #expr > 0
- output_file\write "=lua \"..\"\n #{@indent expr\gsub("\\","\\\\")}\n"
- if @debug
- @writeln "#{colored.bright "RUNNING LUA:"}\n#{colored.blue colored.bright(code_for_statement)}"
- lua_thunk, err = load(code_for_statement)
- if not lua_thunk
- n = 1
- fn = ->
- n = n + 1
- ("\n%-3d|")\format(n)
- code = "1 |"..code_for_statement\gsub("\n", fn)
- error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack code}\n\n#{err}\n\nProduced by statement:\n#{colored.bright colored.yellow statement.src}")
- run_statement = lua_thunk!
- ok,ret = pcall(run_statement, self, vars)
- if not ok
- @errorln "#{colored.red "Error occurred in statement:"}\n#{colored.yellow statement.src}"
- @errorln debug.traceback!
- error(ret)
- if statements
- insert buffer, statements
- if expr
- insert buffer, "#{expr};"
-
+ lua = @tree_to_lua(tree)
+ lua_code = lua.statements or (lua.expr..";")
+ ret = @run_lua(lua_code, vars)
if max_operations
debug.sethook!
- lua_code = ([[
-return (function(nomsu, vars)
-%s
-end);]])\format(concat(buffer, "\n"))
- return nil, lua_code, vars
+ if output_file
+ output_file\write(lua_code)
+ return ret, lua_code, vars
+
+ run_lua: (lua_code, vars={})=>
+ load_lua_fn, err = load([[
+return function(nomsu, vars)
+ %s
+end]]\format(lua_code))
+ if not load_lua_fn
+ n = 1
+ fn = ->
+ n = n + 1
+ ("\n%-3d|")\format(n)
+ code = "1 |"..lua_code\gsub("\n", fn)
+ @error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack code}\n\n#{err}")
+ run_lua_fn = load_lua_fn!
+ ok,ret = pcall(run_lua_fn, self, vars)
+ if not ok
+ --@errorln "#{colored.red "Error occurred in statement:"}\n#{colored.yellow tree.src}"
+ @errorln debug.traceback!
+ @error(ret)
+ return ret
tree_to_value: (tree, vars, filename)=>
- code = "return (function(nomsu, vars)\nreturn #{@tree_to_lua(tree, filename)};\nend);"
+ code = "return (function(nomsu, vars)\nreturn #{@tree_to_lua(tree, filename).expr};\nend);"
if @debug
@writeln "#{colored.bright "RUNNING LUA TO GET VALUE:"}\n#{colored.blue colored.bright(code)}"
lua_thunk, err = load(code)
@@ -413,9 +396,9 @@ end);]])\format(concat(buffer, "\n"))
inside, inline = @tree_to_nomsu(tree.value, force_inline)
return "\\#{inside}", inline
- when "Thunk"
+ when "Block"
if force_inline
- return "{#{concat([@tree_to_nomsu(v, true) for v in *tree.value], "; ")}", true
+ return "(:#{concat([@tree_to_nomsu(v, true) for v in *tree.value], "; ")})", true
else
return ":"..@indent("\n"..concat([@tree_to_nomsu v for v in *tree.value], "\n")), false
@@ -491,7 +474,8 @@ end);]])\format(concat(buffer, "\n"))
return longbuff, false
when "Dict"
- error("Sorry, not yet implemented.")
+ -- TODO: Implement
+ @error("Sorry, not yet implemented.")
when "Number"
return repr(tree.value), true
@@ -517,7 +501,7 @@ end);]])\format(concat(buffer, "\n"))
if is_list(value)
return "[#{concat [@value_to_nomsu(v) for v in *value], ", "}]"
else
- return "(d{#{concat ["#{@value_to_nomsu(k)}=#{@value_to_nomsu(v)}" for k,v in pairs(value)], "; "}})"
+ return "{#{concat ["#{@value_to_nomsu(k)}=#{@value_to_nomsu(v)}" for k,v in pairs(value)], ", "}}"
when "string"
if value == "\n"
return "'\\n'"
@@ -538,50 +522,52 @@ end);]])\format(concat(buffer, "\n"))
@error "Invalid tree: #{repr(tree)}"
switch tree.type
when "File"
+ if #tree.value == 1
+ return @tree_to_lua(tree.value[1], filename)
lua_bits = {}
for line in *tree.value
- expr,statement = @tree_to_lua line, filename
- if statement then insert lua_bits, statement
- if expr then insert lua_bits, "#{expr};"
- return nil, concat(lua_bits, "\n")
+ lua = @tree_to_lua line, filename
+ if not lua
+ @error "No lua produced by #{repr line}"
+ if lua.statements then insert lua_bits, lua.statements
+ if lua.expr then insert lua_bits, "#{lua.expr};"
+ return statements:concat(lua_bits, "\n")
when "Nomsu"
- return "nomsu:parse(#{repr tree.value.src}, #{repr tree.line_no}).value[1]", nil
+ return expr:"nomsu:parse(#{repr tree.value.src}, #{repr tree.line_no}).value[1]"
- when "Thunk"
+ when "Block"
lua_bits = {}
for arg in *tree.value
- expr,statement = @tree_to_lua arg, filename
- if #tree.value == 1 and expr and not statement
- return ([[
-(function(nomsu, vars)
- return %s;
-end)]])\format(expr)
- if statement then insert lua_bits, statement
- if expr then insert lua_bits, "#{expr};"
- return ([[
-(function(nomsu, vars)
-%s
-end)]])\format(concat(lua_bits, "\n"))
+ lua = @tree_to_lua arg, filename
+ if #tree.value == 1 and lua.expr and not lua.statements
+ return expr:lua.expr
+ if lua.statements then insert lua_bits, lua.statements
+ if lua.expr then insert lua_bits, "#{lua.expr};"
+ return statements:concat(lua_bits, "\n")
when "FunctionCall"
insert @compilestack, tree
def = @defs[tree.stub]
if def and def.is_macro
- expr, statement = @run_macro(tree)
+ lua = @run_macro(tree)
remove @compilestack
- return expr, statement
+ return lua
elseif not def and @@math_patt\match(tree.stub)
+ -- This is a bit of a hack, but this code handles arbitrarily complex
+ -- math expressions like 2*x + 3^2 without having to define a single
+ -- rule for every possibility.
bits = {}
for tok in *tree.value
if tok.type == "Word"
insert bits, tok.value
else
- expr, statement = @tree_to_lua(tok, filename)
- @assert(statement == nil, "non-expression value inside math expression")
- insert bits, expr
- return "(#{concat bits, " "})"
+ lua = @tree_to_lua(tok, filename)
+ @assert(lua.statements == nil, "non-expression value inside math expression")
+ insert bits, lua.expr
+ remove @compilestack
+ return expr:"(#{concat bits, " "})"
args = {repr(tree.stub), repr(tree.line_no)}
local arg_names, escaped_args
@@ -595,14 +581,14 @@ end)]])\format(concat(lua_bits, "\n"))
if escaped_args[arg_names[arg_num]]
insert args, "nomsu:parse(#{repr arg.src}, #{repr tree.line_no}).value[1]"
else
- expr,statement = @tree_to_lua arg, filename
- if statement
+ lua = @tree_to_lua arg, filename
+ if lua.statements
@error "Cannot use [[#{arg.src}]] as a function argument, since it's not an expression."
- insert args, expr
+ insert args, lua.expr
arg_num += 1
remove @compilestack
- return @@comma_separated_items("nomsu:call(", args, ")"), nil
+ return expr:@@comma_separated_items("nomsu:call(", args, ")")
when "String"
concat_parts = {}
@@ -614,61 +600,60 @@ end)]])\format(concat(lua_bits, "\n"))
if string_buffer ~= ""
insert concat_parts, repr(string_buffer)
string_buffer = ""
- expr, statement = @tree_to_lua bit, filename
+ lua = @tree_to_lua bit, filename
if @debug
@writeln (colored.bright "INTERP:")
@print_tree bit
- @writeln "#{colored.bright "EXPR:"} #{expr}, #{colored.bright "STATEMENT:"} #{statement}"
- if statement
+ @writeln "#{colored.bright "EXPR:"} #{lua.expr}, #{colored.bright "STATEMENT:"} #{lua.statements}"
+ if lua.statements
@error "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression."
- insert concat_parts, "nomsu:stringify(#{expr})"
+ insert concat_parts, "nomsu:stringify(#{lua.expr})"
if string_buffer ~= ""
insert concat_parts, repr(string_buffer)
if #concat_parts == 0
- return "''", nil
+ return expr:"''"
elseif #concat_parts == 1
- return concat_parts[1], nil
- else return "(#{concat(concat_parts, "..")})", nil
+ return expr:concat_parts[1]
+ else return expr:"(#{concat(concat_parts, "..")})"
when "List"
items = {}
for item in *tree.value
- expr,statement = @tree_to_lua item, filename
- if statement
+ lua = @tree_to_lua item, filename
+ if lua.statements
@error "Cannot use [[#{item.src}]] as a list item, since it's not an expression."
- insert items, expr
- return @@comma_separated_items("{", items, "}"), nil
+ insert items, lua.expr
+ return expr:@@comma_separated_items("{", items, "}")
when "Dict"
items = {}
for entry in *tree.value
- local key_expr,key_statement
- if entry.dict_key.type == "Word"
- key_expr,key_statement = repr(entry.dict_key.value),nil
+ key_lua = if entry.dict_key.type == "Word"
+ {expr:repr(entry.dict_key.value)}
else
- key_expr,key_statement = @tree_to_lua entry.dict_key, filename
- if key_statement
+ @tree_to_lua entry.dict_key, filename
+ if key_lua.statements
@error "Cannot use [[#{entry.dict_key.src}]] as a dict key, since it's not an expression."
- value_expr,value_statement = @tree_to_lua entry.dict_value, filename
- if value_statement
+ value_lua = @tree_to_lua entry.dict_value, filename
+ if value_lua.statements
@error "Cannot use [[#{entry.dict_value.src}]] as a dict value, since it's not an expression."
- key_str = key_expr\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
+ key_str = key_lua.expr\match([=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
if key_str
- insert items, "#{key_str}=#{value_expr}"
+ insert items, "#{key_str}=#{value_lua.expr}"
else
- insert items, "[#{key_expr}]=#{value_expr}"
- return @@comma_separated_items("{", items, "}"), nil
+ insert items, "[#{key_lua.expr}]=#{value_lua.expr}"
+ return expr:@@comma_separated_items("{", items, "}")
when "Number"
- return repr(tree.value), nil
+ return expr:repr(tree.value)
when "Var"
if tree.value\match("^[a-zA-Z_][a-zA-Z0-9_]*$")
- return "vars.#{tree.value}", nil
+ return expr:"vars.#{tree.value}"
else
- return "vars[#{repr tree.value}]", nil
+ return expr:"vars[#{repr tree.value}]"
else
@error("Unknown/unimplemented thingy: #{tree.type}")
@@ -678,7 +663,7 @@ end)]])\format(concat(lua_bits, "\n"))
if type(tree) != 'table' or not tree.type
return
switch tree.type
- when "List", "File", "Thunk", "FunctionCall", "String"
+ when "List", "File", "Block", "FunctionCall", "String"
for v in *tree.value
@walk_tree(v, depth+1)
when "Dict"
@@ -728,7 +713,7 @@ end)]])\format(concat(lua_bits, "\n"))
when "Var"
if vars[tree.value] ~= nil
tree = vars[tree.value]
- when "File", "Nomsu", "Thunk", "List", "FunctionCall", "String"
+ when "File", "Nomsu", "Block", "List", "FunctionCall", "String"
new_value = @replaced_vars tree.value, vars
if new_value != tree.value
tree = {k,v for k,v in pairs(tree)}
@@ -834,29 +819,35 @@ end)]])\format(concat(lua_bits, "\n"))
if type(bit) == "string"
insert concat_parts, bit
else
- expr, statement = @tree_to_lua bit, filename
- if statement
+ lua = @tree_to_lua bit, filename
+ if lua.statements
@error "Cannot use [[#{bit.src}]] as a string interpolation value, since it's not an expression."
- insert concat_parts, expr
+ insert concat_parts, lua.expr
return concat(concat_parts)
+
+ @defmacro "do %block", (vars)=>
+ make_line = (lua)-> lua.expr and (lua.expr..";") or lua.statements
+ if vars.block.type == "Block"
+ return @tree_to_lua(vars.block)
+ else
+ return expr:"#{@tree_to_lua vars.block}(nomsu, vars)"
- -- Uses named local functions to help out callstack readability
- lua_code = (vars)=>
+ @defmacro "immediately %block", (vars)=>
+ lua = @tree_to_lua(vars.block)
+ lua_code = lua.statements or (lua.expr..";")
+ @run_lua(lua_code, vars)
+ return statements:lua_code
+
+ @defmacro "lua> %code", (vars)=>
lua = nomsu_string_as_lua(@, vars.code)
- return nil, lua
- @defmacro "lua> %code", lua_code
+ return statements:lua
- lua_value = (vars)=>
+ @defmacro "=lua %code", (vars)=>
lua = nomsu_string_as_lua(@, vars.code)
- return lua, nil
- @defmacro "=lua %code", lua_value
+ return expr:lua
@defmacro "__src__ %level", (vars)=>
- @repr @source_code @tree_to_value vars.level
-
- @def "derp \\%foo derp \\%bar", (vars)=>
- lua = "local x = "..repr([t.stub for t in *vars.foo.value])..";\nlocal y = "..@tree_to_lua(vars.bar)
- print(colored.green lua)
+ expr: repr(@source_code(@tree_to_value(vars.level)))
run_file = (vars)=>
if vars.filename\match(".*%.lua")
@@ -873,13 +864,13 @@ end)]])\format(concat(lua_bits, "\n"))
else
@error "Invalid filetype for #{vars.filename}"
@def "run file %filename", run_file
-
- _require = (vars)=>
+ @defmacro "require %filename", (vars)=>
+ filename = @tree_to_value(vars.filename)
loaded = @defs["#loaded_files"]
- if not loaded[vars.filename]
- loaded[vars.filename] = run_file(self, {filename:vars.filename}) or true
- return loaded[vars.filename]
- @def "require %filename", _require
+ if not loaded[filename]
+ loaded[filename] = run_file(self, {:filename}) or true
+ loaded[filename]
+ return statements:""
if arg
@@ -923,7 +914,10 @@ if arg
io.read('*a')
else io.open(args.input)\read("*a")
vars = {}
- retval, code = c\run(input, args.input, vars, nil, compiled_output)
+ retval, code = c\run(input, args.input, vars)
+ if args.output
+ compiled_output\write("lua> \"..\"\n "..c\indent(code\gsub("\\","\\\\"), 1))
+
if args.flags["-p"]
c.write = _write
diff --git a/nomsu.peg b/nomsu.peg
index 2946891..0bd51df 100644
--- a/nomsu.peg
+++ b/nomsu.peg
@@ -11,12 +11,12 @@ statement: functioncall / expression
noeol_statement: noeol_functioncall / noeol_expression
inline_statement: inline_functioncall / inline_expression
-inline_thunk (Thunk): {| ":" %ws* inline_statement |}
-eol_thunk (Thunk): {| ":" %ws* noeol_statement eol |}
-indented_thunk (Thunk):
+inline_block (Block): {| ":" %ws* inline_statement |}
+eol_block (Block): {| ":" %ws* noeol_statement eol |}
+indented_block (Block):
{| ":" indent
statement (nodent statement)*
- (dedent / (("" -> "Error while parsing thunk") => error))
+ (dedent / (("" -> "Error while parsing block") => error))
|}
inline_nomsu (Nomsu): "\" inline_expression
@@ -25,18 +25,18 @@ indented_nomsu (Nomsu): "\" expression
inline_expression:
number / variable / inline_string / inline_list / inline_dict / inline_nomsu
- / ("(" %ws* (inline_thunk / inline_statement) %ws* ")")
+ / ("(" %ws* (inline_block / inline_statement) %ws* ")")
noeol_expression:
- indented_string / indented_nomsu / indented_list / indented_dict / indented_thunk
+ indented_string / indented_nomsu / indented_list / indented_dict / indented_block
/ ("(..)" indent
statement
(dedent / (("" -> "Error while parsing indented expression") => error))
) / inline_expression
-expression: eol_thunk / eol_nomsu / noeol_expression
+expression: eol_block / eol_nomsu / noeol_expression
-- Function calls need at least one word in them
inline_functioncall (FunctionCall):
- {| (inline_expression %ws*)* word (%ws* (inline_expression / word))* (%ws* inline_thunk)?|}
+ {| (inline_expression %ws*)* word (%ws* (inline_expression / word))* (%ws* inline_block)?|}
noeol_functioncall (FunctionCall):
{| (noeol_expression %ws*)* word (%ws* (noeol_expression / word))* |}
functioncall (FunctionCall):
@@ -57,7 +57,7 @@ indented_string (String):
~} / string_interpolation)*
|} ((!.) / (&(%nl+ !%gt_nodented)) / (("" -> "Error while parsing String") => error))
string_interpolation:
- "\" (variable / inline_list / inline_dict / inline_string / ("(" %ws* (inline_thunk / inline_statement) %ws* ")"))
+ "\" (variable / inline_list / inline_dict / inline_string / ("(" %ws* (inline_block / inline_statement) %ws* ")"))
number (Number): (("-"? (([0-9]+ "." [0-9]+) / ("." [0-9]+) / ([0-9]+)))-> tonumber)