aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2018-01-08 18:53:57 -0800
committerBruce Hill <bitbucket@bruce-hill.com>2018-01-08 18:53:57 -0800
commitf97ab858edae5495f8ebfef46656e86150665888 (patch)
treeed47620eba2365192ba54950647d6307223cac29
parent568a44ef191e1f4072d379700e3b6f599150a92b (diff)
Modernized the codebase a bit to include "immediately:" for immediately
running code before further parsing takes place. That means that in the default case, whole files can be run at once, which makes all but the weirdest edge cases make a lot more sense and operate smoothly.
-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)