aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/collections.nom16
-rw-r--r--lib/metaprogramming.nom70
-rw-r--r--lib/permissions.nom12
-rw-r--r--lib/utils.nom4
-rw-r--r--nomsu.lua508
-rwxr-xr-xnomsu.moon327
-rw-r--r--utils.lua8
-rw-r--r--utils.moon6
8 files changed, 521 insertions, 430 deletions
diff --git a/lib/collections.nom b/lib/collections.nom
index dbe2afa..41bf173 100644
--- a/lib/collections.nom
+++ b/lib/collections.nom
@@ -112,6 +112,22 @@ rule [entries in %dict] =:
|end
|return items
+rule [keys in %dict] =:
+ lua block ".."
+ |local items = {}
+ |for k,v in pairs(vars.dict) do
+ | table.insert(items, k)
+ |end
+ |return items
+
+rule [values in %dict] =:
+ lua block ".."
+ |local items = {}
+ |for k,v in pairs(vars.dict) do
+ | table.insert(items, v)
+ |end
+ |return items
+
# List Comprehension
macro [%expression for %var in %iterable] =:
assert ((%var's "type") == "Var") ".."
diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom
index 9b76620..fa04232 100644
--- a/lib/metaprogramming.nom
+++ b/lib/metaprogramming.nom
@@ -1,4 +1,3 @@
-
#..
This File contains rules for making rules and macros and some helper functions to make
that easier.
@@ -8,10 +7,9 @@
lua block ".."
|local function make_fn(wrap_in_block)
| return function(compiler, vars, kind)
- # Do a minimal amount of pre-processing (get the spec and the source)
- | local spec = compiler:get_invocations_from_definition(vars.spec, vars)
- | spec = compiler.utils.repr(spec)
- | local src = compiler.utils.repr(vars.user_macro.src)
+ # Do a minimal amount of pre-processing (get the aliases and the source)
+ | local aliases = compiler:repr(compiler:get_aliases(vars.macro_def))
+ | local src = compiler:repr(vars.user_macro.src)
| local user_macro = compiler:tree_to_lua(vars.user_macro)
# Then produce a block of code that creates the macro at runtime
| local lua = [[
@@ -25,57 +23,75 @@ lua block ".."
| return lua, true
| end), %s)
| ]]
- | lua = lua:format(spec, compiler.utils.repr(spec), user_macro,
+ | lua = lua:format(aliases, compiler:repr(aliases), user_macro,
| wrap_in_block and [[lua = "do\\n "..lua.."\\nend"]] or "", src)
| return lua, true
| end
|end
- |compiler:defmacro("macro statement %spec = %user_macro", make_fn(false), "N/A")
- |compiler:defmacro("macro block %spec = %user_macro", make_fn(true), "N/A")
+ |compiler:defmacro("macro statement %macro_def = %user_macro", make_fn(false), "see:lib/metaprogramming.nom")
+ |compiler:defmacro("macro block %macro_def = %user_macro", make_fn(true), "see:lib/metaprogramming.nom")
-macro block [macro %spec = %user_macro] =:
+macro block [macro %macro_def = %user_macro] =:
".."|compiler:defmacro(
- | \lua expr "compiler:get_invocations_from_definition(vars.spec, vars)"\,
+ | \lua expr "compiler:get_aliases(vars.macro_def)"\,
| \lua expr "compiler:tree_to_lua(vars.user_macro)"\,
- | \lua expr "compiler.utils.repr(vars.user_macro.src)"\)
+ | \lua expr "compiler:repr(vars.user_macro.src)"\)
macro [compiler] =: "compiler"
macro [compiler's %key] =: ".."|compiler[\%key as lua\]
macro [compiler %method %args] =:
lua block ".."
- |local args = {}
- |for i,arg in ipairs(vars.args.value) do
- | args[i] = compiler:tree_to_lua(arg)
+ |local args = {"compiler"}
+ |for _,arg in ipairs(vars.args.value) do
+ | table.insert(args, compiler:tree_to_lua(arg))
|end
- |return "compiler:"..compiler:tree_to_value(vars.method, vars).."("..table.concat(args, ", ")..")"
+ |return "compiler["..compiler:repr(compiler:tree_to_value(vars.method, vars)).."]("..table.concat(args, ", ")..")"
macro [compiler utils %method %args] =:
lua block ".."
|local args = {}
|for i,arg in ipairs(vars.args.value) do
| args[i] = compiler:tree_to_lua(arg)
|end
- |return "compiler.utils."..compiler:tree_to_value(vars.method, vars).."("..table.concat(args, ", ")..")"
+ |return "compiler.utils["..compiler:repr(compiler:tree_to_value(vars.method, vars)).."]("..table.concat(args, ", ")..")"
# Macro that lets you make new rules
#..
This is a macro instead of a rule because it needs to pre-empt processing the list of
- invocations and convert it into a list of strings (rather than call a function that
+ function calls and convert it into a list of strings (rather than call a function that
is currently in the middle of being defined). Being a macro also allows us to snatch
the source code and store that
-macro block [rule %spec = %body] =: ".."
+macro block [rule %rule_def = %body] =: ".."
|compiler:def(
- | \compiler utils "repr" [compiler "get_invocations_from_definition" [%spec, lua expr "vars"]]\,
+ | \compiler "repr" [compiler "get_aliases" [%rule_def]]\,
| \compiler "tree_to_lua" [%body]\,
- | \compiler utils "repr" [lua expr "vars.body.src"]\)
+ | \compiler "repr" [lua expr "vars.body.src"]\)
+
+rule [fart]=: lua block "print('poot')"
+
+macro block [alias %aliases = %aliased] =:
+ lua block ".."
+ |local aliases = compiler:get_aliases(vars.aliases)
+ |aliases = compiler:repr(aliases)
+ |if vars.aliased.type ~= "Thunk" then
+ | compiler:error("Right hand side of alias % = % should be a Thunk, but got "..vars.aliased.type
+ | ..". Maybe you used = instead of =: by mistake?")
+ |end
+ |local aliased = next(compiler:get_aliases(vars.aliased.value))
+ |aliased = compiler:repr(aliased)
+ |local lua = ([[
+ |compiler:add_aliases(%s, compiler.defs[%s])
+ |]]):format(aliases, aliased)
+ |return lua
# Get the source code for a function
-rule [help %invocation] =:
+rule [help %rule] =:
lua block ".."
- |local fn_info = compiler.defs[vars.invocation]
- |if not fn_info then
- | compiler:writeln("Function not found: "..compiler.utils.repr(vars.invocation))
+ |local fn_def = compiler:get_fn_def(vars.rule)
+ |if not fn_def then
+ | compiler:writeln("Rule not found: "..compiler:repr(vars.rule))
|else
- | compiler:writeln("rule "..compiler.utils.repr(fn_info.invocations).." ="..(fn_info.src or ":\\n <unknown source code>"))
+ | compiler:writeln("rule "..compiler:repr(compiler.utils.keys(fn_def.aliases))
+ | .." ="..(fn_def.src or ":\\n <unknown source code>"))
|end
@@ -103,8 +119,8 @@ rule [source code from tree %tree] =:
|end
macro [source code %body] =:
- compiler utils "repr" [compiler "call" ["source code from tree %", %body]]
+ compiler "repr" [compiler "call" ["source code from tree %", %body]]
macro [parse tree %code] =:
- compiler utils "repr" [compiler "stringify_tree" [lua expr "vars.code.value"]]
+ compiler "repr" [compiler "stringify_tree" [lua expr "vars.code.value"]]
diff --git a/lib/permissions.nom b/lib/permissions.nom
index fd55ec8..41d559e 100644
--- a/lib/permissions.nom
+++ b/lib/permissions.nom
@@ -6,8 +6,8 @@ require "lib/collections.nom"
# Permission functions
rule [restrict %rules to within %elite-rules] =:
say ".."|Restricting \%rules\ to within \%elite-rules\
- %rules =: compiler "get_invocations" [%rules]
- %elite-rules =: compiler "get_invocations" [%elite-rules]
+ %rules =: keys in (compiler "get_aliases" [%rules])
+ %elite-rules =: keys in (compiler "get_aliases" [%elite-rules])
for all (flatten [%elite-rules, %rules]):
assert ((compiler's "defs") has key %it) ".."|Undefined function: \%it\
for all %rules:
@@ -20,8 +20,8 @@ rule [restrict %rules to within %elite-rules] =:
rule [allow %elite-rules to use %rules] =:
say ".."|Allowing \%elite-rules\ to use \%rules\
- %rules =: compiler "get_invocations" [%rules]
- %elite-rules =: compiler "get_invocations" [%elite-rules]
+ %rules =: keys in (compiler "get_aliases" [%rules])
+ %elite-rules =: keys in (compiler "get_aliases" [%elite-rules])
for all (flatten [%elite-rules, %rules]):
assert ((compiler's "defs") has key %it) ".."|Undefined function: \%it\
for %fn in %rules:
@@ -33,8 +33,8 @@ rule [allow %elite-rules to use %rules] =:
rule [forbid %pleb-rules to use %rules] =:
say ".."|Forbidding \%pleb-rules\ to use \%rules\
- %rules =: compiler "get_invocations" [%rules]
- %pleb-rules =: compiler "get_invocations" [%pleb-rules]
+ %rules =: keys in (compiler "get_aliases" [%rules])
+ %pleb-rules =: keys in (compiler "get_aliases" [%pleb-rules])
for all (flatten [%pleb-rules, %used]):
assert ((compiler's "defs") has key %it) ".."|Undefined function: \%it\
for all %rules:
diff --git a/lib/utils.nom b/lib/utils.nom
index ec35c15..ff261f7 100644
--- a/lib/utils.nom
+++ b/lib/utils.nom
@@ -15,7 +15,7 @@ macro block [assert %condition %msg] =: ".."
|end
macro block [show generated lua %block] =: ".."
- |compiler:writeln(\lua expr "compiler.utils.repr(compiler:tree_to_lua(vars.block.value))"\)
+ |compiler:writeln(\lua expr "compiler:repr(compiler:tree_to_lua(vars.block.value))"\)
# String functions
@@ -35,7 +35,7 @@ macro [capitalize %str, %str capitalized] =: ".."
|(\%str as lua\):gsub("%l", string.upper, 1)
macro [repr %obj] =:
- ".."|compiler.utils.repr(\%obj as lua\)
+ ".."|compiler:repr(\%obj as lua\)
macro [%obj as string] =:
".."|compiler.utils.repr_if_not_string(\%obj as lua\)
diff --git a/nomsu.lua b/nomsu.lua
index 11ae3ae..9b1856c 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -1,7 +1,16 @@
local re = require('re')
local lpeg = require('lpeg')
local utils = require('utils')
-local insert = table.insert
+local repr = utils.repr
+local insert, remove, concat
+do
+ local _obj_0 = table
+ insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
+end
+local pcall
+pcall = function(fn, ...)
+ return true, fn(...)
+end
local INDENT = " "
lpeg.setmaxstack(10000)
local P, V, S, Cg, C, Cp, B, Cmt
@@ -15,6 +24,34 @@ local STRING_ESCAPES = {
f = "\f",
r = "\r"
}
+local parsetree_mt = {
+ __tostring = function(self)
+ return tostring(self.type) .. "(" .. tostring(repr(self.value)) .. ")"
+ end
+}
+local ParseTree
+ParseTree = function(type, src, value, errors)
+ return setmetatable({
+ type = type,
+ src = src,
+ value = value,
+ errors = errors
+ }, parsetree_mt)
+end
+local functiondef_mt = {
+ __tostring = function(self)
+ return "FunctionDef(" .. tostring(repr(self.aliases))
+ end
+}
+local FunctionDef
+FunctionDef = function(fn, aliases, src, is_macro)
+ return setmetatable({
+ fn = fn,
+ aliases = aliases,
+ src = src,
+ is_macro = is_macro
+ }, functiondef_mt)
+end
local NomsuCompiler
do
local _class_0
@@ -23,169 +60,150 @@ do
self:write(...)
return self:write("\n")
end,
- call = function(self, fn_name, ...)
- local fn_info = self.defs[fn_name]
- if fn_info == nil then
- self:error("Attempt to call undefined function: " .. tostring(fn_name))
- end
- if fn_info.is_macro then
- self:error("Attempt to call macro at runtime: " .. tostring(fn_name) .. "\nThis can be caused by using a macro in a function that is defined before the macro.")
- end
- if not (self:check_permission(fn_name)) then
- self:error("You do not have the authority to call: " .. tostring(fn_name))
+ def = function(self, aliases, fn, src, is_macro)
+ if is_macro == nil then
+ is_macro = false
end
- insert(self.callstack, fn_name)
- local fn, arg_names
- fn, arg_names = fn_info.fn, fn_info.arg_names
- local args
- do
- local _tbl_0 = { }
- for i, name in ipairs(arg_names[fn_name]) do
- _tbl_0[name] = select(i, ...)
- end
- args = _tbl_0
+ if type(aliases) == 'string' then
+ aliases = self:get_aliases(aliases)
end
if self.debug then
- self:writeln("Calling " .. tostring(fn_name) .. " with args: " .. tostring(utils.repr(args)))
+ self:writeln("Defining rule: " .. tostring(aliases))
end
- local ret = fn(self, args)
- table.remove(self.callstack)
- return ret
+ local fn_def = FunctionDef(fn, { }, src, is_macro)
+ return self:add_aliases(aliases, fn_def)
end,
- check_permission = function(self, fn_name)
- local fn_info = self.defs[fn_name]
- if fn_info == nil then
- self:error("Undefined function: " .. tostring(fn_name))
- end
- if fn_info.whiteset == nil then
- return true
- end
- local _list_0 = self.callstack
- for _index_0 = 1, #_list_0 do
- local caller = _list_0[_index_0]
- if fn_info.whiteset[caller] then
- return true
- end
- end
- return false
+ defmacro = function(self, aliases, fn, src)
+ return self:def(aliases, fn, src, true)
end,
- def = function(self, spec, fn, src)
- if self.debug then
- self:writeln("Defining rule: " .. tostring(spec))
+ add_aliases = function(self, aliases, fn_def)
+ local first_alias, first_args = next(fn_def.aliases)
+ if not first_alias then
+ first_alias, first_args = next(aliases)
end
- local invocations, arg_names = self:get_invocations(spec)
- for i = 2, #invocations do
- if not utils.equivalent(utils.set(arg_names[invocations[1]]), utils.set(arg_names[invocations[i]])) then
- self:error("Conflicting argument names " .. tostring(utils.repr(invocations[1])) .. " and " .. tostring(utils.repr(invocations[i])) .. " for " .. tostring(utils.repr(spec)))
- end
- end
- local fn_info = {
- fn = fn,
- arg_names = arg_names,
- invocations = invocations,
- src = src,
- is_macro = false
- }
- for _index_0 = 1, #invocations do
- local invocation = invocations[_index_0]
- self.defs[invocation] = fn_info
- end
- end,
- get_invocations_from_definition = function(self, def, vars)
- if def.type == "String" then
- return self.__class:unescape_string(def.value)
- end
- if def.type ~= "List" then
- self:error("Trying to get invocations from " .. tostring(def.type) .. ", but expected List or String.")
- end
- local invocations = { }
- local _list_0 = def.value
- for _index_0 = 1, #_list_0 do
+ for alias, args in pairs(aliases) do
local _continue_0 = false
repeat
- local item = _list_0[_index_0]
- if item.type == "String" then
- insert(invocations, item.value)
+ if fn_def[alias] then
_continue_0 = true
break
end
- if item.type ~= "FunctionCall" then
- self:error("Invalid list item: " .. tostring(item.type) .. ", expected FunctionCall or String")
+ if self.defs[alias] then
+ self:remove_alias(alias)
end
- local name_bits = { }
- local _list_1 = item.value
- for _index_1 = 1, #_list_1 do
- local token = _list_1[_index_1]
- if token.type == "Word" then
- insert(name_bits, token.value)
- elseif token.type == "Var" then
- insert(name_bits, token.src)
- else
- self:error("Unexpected token type in definition: " .. tostring(token.type) .. " (expected Word or Var)")
- end
+ if alias ~= first_alias and not utils.equivalent(utils.set(args), utils.set(first_args)) then
+ self:error("Conflicting argument names between " .. tostring(first_alias) .. " and " .. tostring(alias))
end
- insert(invocations, table.concat(name_bits, " "))
+ fn_def.aliases[alias] = args
+ self.defs[alias] = fn_def
_continue_0 = true
until true
if not _continue_0 then
break
end
end
- return invocations
end,
- get_invocations = function(self, text)
- if not text then
- self:error("No text provided!")
+ remove_alias = function(self, alias)
+ local fn_def = self.defs[alias]
+ if not fn_def then
+ return
end
- if type(text) == 'function' then
- error("Function passed to get_invocations")
+ fn_def.aliases[alias] = nil
+ self.defs[alias] = nil
+ end,
+ remove_aliases = function(self, aliases)
+ for alias in pairs(aliases) do
+ self:remove_alias(alias)
end
- if type(text) == 'string' then
- text = {
- text
- }
+ end,
+ get_fn_def = function(self, x)
+ if not x then
+ self:error("Nothing to get function def from")
end
- local invocations = { }
- local arg_names = { }
- for _index_0 = 1, #text do
- local _text = text[_index_0]
- local invocation = _text:gsub("'", " '"):gsub("%%%S+", "%%"):gsub("%s+", " ")
- local _arg_names
- do
- local _accum_0 = { }
- local _len_0 = 1
- for arg in _text:gmatch("%%(%S[^%s']*)") do
- _accum_0[_len_0] = arg
+ local aliases = self:get_aliases(x)
+ local alias, _ = next(aliases)
+ return self.defs[alias]
+ end,
+ call = function(self, alias, ...)
+ local fn_def = self.defs[alias]
+ if fn_def == nil then
+ self:error("Attempt to call undefined function: " .. tostring(alias))
+ end
+ if fn_def.is_macro and self.callstack[#self.callstack] ~= "__macro__" then
+ self:error("Attempt to call macro at runtime: " .. tostring(alias) .. "\nThis can be caused by using a macro in a function that is defined before the macro.")
+ end
+ if not (self:check_permission(alias)) then
+ self:error("You do not have the authority to call: " .. tostring(alias))
+ end
+ insert(self.callstack, alias)
+ local fn, aliases
+ fn, aliases = fn_def.fn, fn_def.aliases
+ local args
+ do
+ local _tbl_0 = { }
+ for i, name in ipairs(aliases[alias]) do
+ _tbl_0[name] = select(i, ...)
+ end
+ args = _tbl_0
+ end
+ if self.debug then
+ self:writeln("Calling " .. tostring(alias) .. " with args: " .. tostring(repr(args)))
+ end
+ local rets = {
+ fn(self, args)
+ }
+ remove(self.callstack)
+ return unpack(rets)
+ end,
+ run_macro = function(self, tree, kind)
+ if kind == nil then
+ kind = "Expression"
+ end
+ local args
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ local _list_0 = tree.value
+ for _index_0 = 1, #_list_0 do
+ local a = _list_0[_index_0]
+ if a.type ~= "Word" then
+ _accum_0[_len_0] = a
_len_0 = _len_0 + 1
end
- _arg_names = _accum_0
end
- insert(invocations, invocation)
- arg_names[invocation] = _arg_names
+ args = _accum_0
end
- return invocations, arg_names
- end,
- defmacro = function(self, spec, lua_gen_fn, src)
- if self.debug then
- self:writeln("DEFINING MACRO: " .. tostring(spec) .. tostring(src or ""))
+ local alias, _ = self:get_alias(tree)
+ insert(self.callstack, "__macro__")
+ local ret, manual_mode = self:call(alias, unpack(args))
+ remove(self.callstack)
+ if not ret then
+ self:error("No return value for macro: " .. tostring(name))
end
- local invocations, arg_names = self:get_invocations(spec)
- for i = 2, #invocations do
- if not utils.equivalent(utils.set(arg_names[invocations[1]]), utils.set(arg_names[invocations[i]])) then
- self:error("Conflicting argument names " .. tostring(utils.repr(invocations[1])) .. " and " .. tostring(utils.repr(invocations[i])) .. " for " .. tostring(utils.repr(spec)))
+ if kind == "Statement" and not manual_mode then
+ if ret:match("^do\n") then
+ error("Attempting to use macro return value as an expression, when it looks like a block:\n" .. tostring(ret))
end
+ ret = "ret = " .. ret
end
- local fn_info = {
- fn = lua_gen_fn,
- arg_names = arg_names,
- invocations = invocations,
- src = src,
- is_macro = true
- }
- for _index_0 = 1, #invocations do
- local invocation = invocations[_index_0]
- self.defs[invocation] = fn_info
+ return ret
+ end,
+ check_permission = function(self, fn_name)
+ local fn_def = self.defs[fn_name]
+ if fn_def == nil then
+ self:error("Undefined function: " .. tostring(fn_name))
end
+ if fn_def.whiteset == nil then
+ return true
+ end
+ local _list_0 = self.callstack
+ for _index_0 = 1, #_list_0 do
+ local caller = _list_0[_index_0]
+ if fn_def.whiteset[caller] then
+ return true
+ end
+ end
+ return false
end,
parse = function(self, str, filename)
if self.debug then
@@ -204,7 +222,7 @@ do
local check_dedent
check_dedent = function(subject, end_pos, spaces)
if #spaces < indent_stack[#indent_stack] then
- table.remove(indent_stack)
+ remove(indent_stack)
return end_pos
end
end
@@ -319,17 +337,21 @@ do
return error("\n" .. tostring(err_msg or "Parse error") .. " in " .. tostring(filename) .. " on line " .. tostring(line_no) .. ":\n\n" .. tostring(prev_line) .. "\n" .. tostring(err_line) .. "\n" .. tostring(pointer) .. "\n" .. tostring(next_line) .. "\n")
end
}
+ local tree_mt = {
+ __tostring = function(self)
+ return tostring(self.type) .. "(" .. tostring(repr(self.value)) .. ")"
+ end
+ }
setmetatable(defs, {
__index = function(t, key)
local fn
fn = function(src, value, errors)
- local token = {
+ return setmetatable({
type = key,
src = src,
value = value,
errors = errors
- }
- return token
+ }, tree_mt)
end
t[key] = fn
return fn
@@ -355,7 +377,7 @@ do
tree_to_lua = function(self, tree)
assert(tree, "No tree provided.")
if not tree.type then
- self:error("Invalid tree: " .. tostring(utils.repr(tree)))
+ self:error("Invalid tree: " .. tostring(repr(tree)))
end
local _exp_0 = tree.type
if "File" == _exp_0 then
@@ -375,7 +397,7 @@ do
local lua_code = "\n return (function(compiler, vars)\n" .. tostring(code) .. "\nend)"
local lua_thunk, err = load(lua_code)
if not lua_thunk then
- error("Failed to compile generated code:\n" .. tostring(code) .. "\n\n" .. tostring(err) .. "\n\nProduced by statement:\n" .. tostring(utils.repr(statement)))
+ error("Failed to compile generated code:\n" .. tostring(code) .. "\n\n" .. tostring(err) .. "\n\nProduced by statement:\n" .. tostring(repr(statement)))
end
local value = lua_thunk()
local return_value
@@ -389,7 +411,7 @@ do
insert(buffer, [[ return ret
end)
]])
- return table.concat(buffer, "\n"), return_value
+ return concat(buffer, "\n"), return_value
elseif "Block" == _exp_0 then
local buffer = { }
local _list_0 = tree.value
@@ -397,7 +419,7 @@ do
local statement = _list_0[_index_0]
insert(buffer, self:tree_to_lua(statement))
end
- return table.concat(buffer, "\n")
+ return concat(buffer, "\n")
elseif "Thunk" == _exp_0 then
assert(tree.value.type == "Block", "Non-block value in Thunk")
return [[ (function(compiler, vars)
@@ -407,15 +429,15 @@ do
]]
elseif "Statement" == _exp_0 then
if tree.value.type == "FunctionCall" then
- local name = self:fn_name_from_tree(tree.value)
- if self.defs[name] and self.defs[name].is_macro then
+ local alias = self:get_alias(tree.value)
+ if self.defs[alias] and self.defs[alias].is_macro then
return self:run_macro(tree.value, "Statement")
end
end
return "ret = " .. (self:tree_to_lua(tree.value))
elseif "FunctionCall" == _exp_0 then
- local name = self:fn_name_from_tree(tree)
- if self.defs[name] and self.defs[name].is_macro then
+ local alias = self:get_alias(tree)
+ if self.defs[alias] and self.defs[alias].is_macro then
return self:run_macro(tree, "Expression")
else
local args
@@ -432,11 +454,11 @@ do
end
args = _accum_0
end
- insert(args, 1, utils.repr(name))
+ insert(args, 1, repr(alias))
return self.__class:comma_separated_items("compiler:call(", args, ")")
end
elseif "String" == _exp_0 then
- return utils.repr(self.__class:unescape_string(tree.value))
+ return repr(self.__class:unescape_string(tree.value))
elseif "Longstring" == _exp_0 then
local concat_parts = { }
local string_buffer = ""
@@ -450,7 +472,7 @@ do
string_buffer = string_buffer .. bit:gsub("\\\\", "\\")
else
if string_buffer ~= "" then
- insert(concat_parts, utils.repr(string_buffer))
+ insert(concat_parts, repr(string_buffer))
string_buffer = ""
end
insert(concat_parts, "compiler.utils.repr_if_not_string(" .. tostring(self:tree_to_lua(bit)) .. ")")
@@ -458,14 +480,14 @@ do
end
end
if string_buffer ~= "" then
- insert(concat_parts, utils.repr(string_buffer))
+ insert(concat_parts, repr(string_buffer))
end
if #concat_parts == 0 then
return "''"
elseif #concat_parts == 1 then
return concat_parts[1]
else
- return "(" .. tostring(table.concat(concat_parts, "..")) .. ")"
+ return "(" .. tostring(concat(concat_parts, "..")) .. ")"
end
elseif "Number" == _exp_0 then
return tree.value
@@ -488,26 +510,94 @@ do
end)(), "}")
end
elseif "Var" == _exp_0 then
- return "vars[" .. tostring(utils.repr(tree.value)) .. "]"
+ return "vars[" .. tostring(repr(tree.value)) .. "]"
else
return self:error("Unknown/unimplemented thingy: " .. tostring(tree.type))
end
end,
- fn_name_from_tree = function(self, tree)
- assert(tree.type == "FunctionCall", "Attempt to get fn name from non-functioncall tree: " .. tostring(tree.type))
- local name_bits = { }
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local token = _list_0[_index_0]
- insert(name_bits, (function()
- if token.type == "Word" then
- return token.value
+ get_alias = function(self, x)
+ if not x then
+ self:error("Nothing to get alias from")
+ end
+ if type(x) == 'string' then
+ local alias = x:gsub("'", " '"):gsub("%%%S+", "%%"):gsub("%s+", " ")
+ local args
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ for arg in x:gmatch("%%(%S[^%s']*)") do
+ _accum_0[_len_0] = arg
+ _len_0 = _len_0 + 1
+ end
+ args = _accum_0
+ end
+ return alias, args
+ end
+ local _exp_0 = x.type
+ if "String" == _exp_0 then
+ return self:get_alias(x.value)
+ elseif "Statement" == _exp_0 then
+ return self:get_alias(x.value)
+ elseif "FunctionCall" == _exp_0 then
+ local alias, args = { }, { }, { }
+ local _list_0 = x.value
+ for _index_0 = 1, #_list_0 do
+ local token = _list_0[_index_0]
+ local _exp_1 = token.type
+ if "Word" == _exp_1 then
+ insert(alias, token.value)
+ elseif "Var" == _exp_1 then
+ insert(alias, "%")
+ insert(args, token.value)
else
- return "%"
+ insert(alias, "%")
+ insert(args, token)
end
- end)())
+ end
+ return concat(alias, " "), args
+ end
+ end,
+ get_aliases = function(self, x)
+ if self.value then
+ print(self)
+ error("WTF")
+ end
+ if not x then
+ self:error("Nothing to get aliases from")
+ end
+ if type(x) == 'string' then
+ local alias, args = self:get_alias(x)
+ return {
+ [alias] = args
+ }
+ end
+ local _exp_0 = x.type
+ if "String" == _exp_0 then
+ return self:get_aliases({
+ x.value
+ })
+ elseif "Statement" == _exp_0 then
+ return self:get_aliases({
+ x.value
+ })
+ elseif "FunctionCall" == _exp_0 then
+ return self:get_aliases({
+ x
+ })
+ elseif "List" == _exp_0 then
+ x = x.value
+ elseif "Block" == _exp_0 then
+ x = x.value
+ end
+ do
+ local _with_0 = { }
+ for _index_0 = 1, #x do
+ local y = x[_index_0]
+ local alias, args = self:get_alias(y)
+ _with_0[alias] = args
+ end
+ return _with_0
end
- return table.concat(name_bits, " ")
end,
var_to_lua_identifier = function(self, var)
if var.type ~= "Var" then
@@ -521,54 +611,6 @@ do
end
end))
end,
- run_macro = function(self, tree, kind)
- if kind == nil then
- kind = "Expression"
- end
- local name = self:fn_name_from_tree(tree)
- if not (self.defs[name] and self.defs[name].is_macro) then
- self:error("Macro not found: " .. tostring(name))
- end
- if not (self:check_permission(name)) then
- self:error("You do not have the authority to call: " .. tostring(name))
- end
- local fn, arg_names
- do
- local _obj_0 = self.defs[name]
- fn, arg_names = _obj_0.fn, _obj_0.arg_names
- end
- local args
- do
- local _accum_0 = { }
- local _len_0 = 1
- local _list_0 = tree.value
- for _index_0 = 1, #_list_0 do
- local a = _list_0[_index_0]
- if a.type ~= "Word" then
- _accum_0[_len_0] = a
- _len_0 = _len_0 + 1
- end
- end
- args = _accum_0
- end
- do
- local _tbl_0 = { }
- for i, name in ipairs(arg_names[name]) do
- _tbl_0[name] = args[i]
- end
- args = _tbl_0
- end
- insert(self.callstack, name)
- local ret, manual_mode = fn(self, args, kind)
- table.remove(self.callstack)
- if not ret then
- self:error("No return value for macro: " .. tostring(name))
- end
- if kind == "Statement" and not manual_mode then
- ret = "ret = " .. ret
- end
- return ret
- end,
_yield_tree = function(self, tree, indent_level)
if indent_level == nil then
indent_level = 0
@@ -580,9 +622,9 @@ do
local _exp_0 = tree.type
if "File" == _exp_0 then
coroutine.yield(ind("File:"))
- self:_yield_tree(tree.value.body, indent_level + 1)
+ return self:_yield_tree(tree.value.body, indent_level + 1)
elseif "Errors" == _exp_0 then
- coroutine.yield(ind("Error:\n" .. tostring(tree.value)))
+ return coroutine.yield(ind("Error:\n" .. tostring(tree.value)))
elseif "Block" == _exp_0 then
local _list_0 = tree.value
for _index_0 = 1, #_list_0 do
@@ -591,11 +633,11 @@ do
end
elseif "Thunk" == _exp_0 then
coroutine.yield(ind("Thunk:"))
- self:_yield_tree(tree.value, indent_level + 1)
+ return self:_yield_tree(tree.value, indent_level + 1)
elseif "Statement" == _exp_0 then
- self:_yield_tree(tree.value, indent_level)
+ return self:_yield_tree(tree.value, indent_level)
elseif "FunctionCall" == _exp_0 then
- local name = self:fn_name_from_tree(tree)
+ local alias = self:get_alias(tree)
local args
do
local _accum_0 = { }
@@ -611,23 +653,25 @@ do
args = _accum_0
end
if #args == 0 then
- coroutine.yield(ind("Call [" .. tostring(name) .. "]!"))
+ return coroutine.yield(ind("Call [" .. tostring(alias) .. "]!"))
else
- coroutine.yield(ind("Call [" .. tostring(name) .. "]:"))
+ coroutine.yield(ind("Call [" .. tostring(alias) .. "]:"))
for _index_0 = 1, #args do
local a = args[_index_0]
self:_yield_tree(a, indent_level + 1)
end
end
elseif "String" == _exp_0 then
- coroutine.yield(ind(utils.repr(tree.value)))
+ return coroutine.yield(ind(repr(tree.value)))
elseif "Longstring" == _exp_0 then
- coroutine.yield(ind(utils.repr(tree.value)))
+ return coroutine.yield(ind(repr(tree.value)))
elseif "Number" == _exp_0 then
- coroutine.yield(ind(tree.value))
+ return coroutine.yield(ind(tree.value))
+ elseif "Var" == _exp_0 then
+ return coroutine.yield(ind("Var[" .. tostring(repr(tree.value)) .. "]"))
elseif "List" == _exp_0 then
if #tree.value == 0 then
- coroutine.yield(ind("<Empty List>"))
+ return coroutine.yield(ind("<Empty List>"))
else
coroutine.yield(ind("List:"))
local _list_0 = tree.value
@@ -636,12 +680,9 @@ do
self:_yield_tree(item, indent_level + 1)
end
end
- elseif "Var" == _exp_0 then
- coroutine.yield(ind("Var[" .. tostring(utils.repr(tree.value)) .. "]"))
else
- error("Unknown/unimplemented thingy: " .. tostring(tree.type))
+ return error("Unknown/unimplemented thingy: " .. tostring(tree.type))
end
- return nil
end,
print_tree = function(self, tree)
for line in coroutine.wrap(function()
@@ -657,7 +698,7 @@ do
end) do
insert(result, line)
end
- return table.concat(result, "\n")
+ return concat(result, "\n")
end,
run = function(self, src, filename, output_file)
if output_file == nil then
@@ -707,22 +748,22 @@ do
end
end,
initialize_core = function(self)
- self:defmacro([[lua block %lua_code]], function(self, vars, kind)
+ self:defmacro("lua block %lua_code", function(self, vars, kind)
if kind == "Expression" then
error("Expected to be in statement.")
end
local inner_vars = setmetatable({ }, {
__index = function(_, key)
- return error("vars[" .. tostring(utils.repr(key)) .. "]")
+ return error("vars[" .. tostring(repr(key)) .. "]")
end
})
return "do\n" .. self:tree_to_value(vars.lua_code, inner_vars) .. "\nend", true
end)
- self:defmacro([[lua expr %lua_code]], function(self, vars, kind)
+ self:defmacro("lua expr %lua_code", function(self, vars, kind)
local lua_code = vars.lua_code.value
local inner_vars = setmetatable({ }, {
__index = function(_, key)
- return error("vars[" .. tostring(utils.repr(key)) .. "]")
+ return error("vars[" .. tostring(repr(key)) .. "]")
end
})
return self:tree_to_value(vars.lua_code, inner_vars)
@@ -759,6 +800,9 @@ do
self.debug = false
self:initialize_core()
self.utils = utils
+ self.repr = function(self, ...)
+ return repr(...)
+ end
self.loaded_files = { }
end,
__base = _base_0,
@@ -795,7 +839,7 @@ do
end
end
insert(bits, close)
- return table.concat(bits)
+ return concat(bits)
end
NomsuCompiler = _class_0
end
@@ -845,7 +889,7 @@ elseif arg then
return c:run(buff)
end)
if ok and ret ~= nil then
- print("= " .. utils.repr(ret))
+ print("= " .. repr(ret))
end
end
end
diff --git a/nomsu.moon b/nomsu.moon
index 6dce00b..ed4b955 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -2,7 +2,9 @@
re = require 're'
lpeg = require 'lpeg'
utils = require 'utils'
-insert = table.insert
+repr = utils.repr
+{:insert, :remove, :concat} = table
+pcall = (fn,...)-> true, fn(...)
-- TODO:
-- improve indentation of generated lua code
@@ -19,6 +21,14 @@ lpeg.setmaxstack 10000 -- whoa
{:P,:V,:S,:Cg,:C,:Cp,:B,:Cmt} = lpeg
STRING_ESCAPES = n:"\n", t:"\t", b:"\b", a:"\a", v:"\v", f:"\f", r:"\r"
+-- Helper "classes"
+parsetree_mt = {__tostring:=> "#{@type}(#{repr(@value)})"}
+ParseTree = (type, src, value, errors)->
+ setmetatable({:type, :src, :value, :errors}, parsetree_mt)
+
+functiondef_mt = {__tostring:=> "FunctionDef(#{repr(@aliases)}"}
+FunctionDef = (fn, aliases, src, is_macro)->
+ setmetatable({:fn, :aliases, :src, :is_macro}, functiondef_mt)
class NomsuCompiler
new:(parent)=>
@@ -28,102 +38,93 @@ class NomsuCompiler
@debug = false
@initialize_core!
@utils = utils
+ @repr = (...)=> repr(...)
@loaded_files = {}
writeln:(...)=>
@write(...)
@write("\n")
-
- call: (fn_name,...)=>
- fn_info = @defs[fn_name]
- if fn_info == nil
- @error "Attempt to call undefined function: #{fn_name}"
- if fn_info.is_macro
- @error "Attempt to call macro at runtime: #{fn_name}\nThis can be caused by using a macro in a function that is defined before the macro."
- unless @check_permission(fn_name)
- @error "You do not have the authority to call: #{fn_name}"
- insert @callstack, fn_name
- {:fn, :arg_names} = fn_info
- args = {name, select(i,...) for i,name in ipairs(arg_names[fn_name])}
+
+ def: (aliases, fn, src, is_macro=false)=>
+ if type(aliases) == 'string'
+ aliases = @get_aliases aliases
if @debug
- @writeln "Calling #{fn_name} with args: #{utils.repr(args)}"
- ret = fn(self, args)
- table.remove @callstack
+ @writeln "Defining rule: #{aliases}"
+ fn_def = FunctionDef(fn, {}, src, is_macro)
+ @add_aliases aliases, fn_def
+
+ defmacro: (aliases, fn, src)=> @def(aliases, fn, src, true)
+
+ add_aliases: (aliases, fn_def)=>
+ first_alias,first_args = next(fn_def.aliases)
+ if not first_alias
+ first_alias,first_args = next(aliases)
+ for alias,args in pairs(aliases)
+ if fn_def[alias] then continue
+ if @defs[alias] then @remove_alias(alias)
+ if alias != first_alias and not utils.equivalent(utils.set(args), utils.set(first_args))
+ @error "Conflicting argument names between #{first_alias} and #{alias}"
+ fn_def.aliases[alias] = args
+ @defs[alias] = fn_def
+
+ remove_alias: (alias)=>
+ fn_def = @defs[alias]
+ if not fn_def then return
+ fn_def.aliases[alias] = nil
+ @defs[alias] = nil
+
+ remove_aliases: (aliases)=>
+ for alias in pairs(aliases) do @remove_alias(alias)
+
+ get_fn_def: (x)=>
+ if not x then @error "Nothing to get function def from"
+ aliases = @get_aliases x
+ alias,_ = next(aliases)
+ return @defs[alias]
+
+ call: (alias,...)=>
+ fn_def = @defs[alias]
+ if fn_def == nil
+ @error "Attempt to call undefined function: #{alias}"
+ if fn_def.is_macro and @callstack[#@callstack] != "__macro__"
+ @error "Attempt to call macro at runtime: #{alias}\nThis can be caused by using a macro in a function that is defined before the macro."
+ unless @check_permission(alias)
+ @error "You do not have the authority to call: #{alias}"
+ insert @callstack, alias
+ {:fn, :aliases} = fn_def
+ args = {name, select(i,...) for i,name in ipairs(aliases[alias])}
+ if @debug
+ @writeln "Calling #{alias} with args: #{repr(args)}"
+ -- TODO: optimize, but still allow multiple return values?
+ rets = {fn(self,args)}
+ remove @callstack
+ return unpack(rets)
+
+ run_macro: (tree, kind="Expression")=>
+ args = [a for a in *tree.value when a.type != "Word"]
+ alias,_ = @get_alias tree
+ insert @callstack, "__macro__"
+ ret, manual_mode = @call(alias, unpack(args))
+ remove @callstack
+ if not ret
+ @error("No return value for macro: #{name}")
+ if kind == "Statement" and not manual_mode
+ if ret\match("^do\n")
+ error "Attempting to use macro return value as an expression, when it looks like a block:\n#{ret}"
+ ret = "ret = "..ret
return ret
check_permission: (fn_name)=>
- fn_info = @defs[fn_name]
- if fn_info == nil
+ fn_def = @defs[fn_name]
+ if fn_def == nil
@error "Undefined function: #{fn_name}"
- if fn_info.whiteset == nil then return true
+ if fn_def.whiteset == nil then return true
-- TODO: optimize this, maybe by making the callstack a Counter and having a move-to-front optimization on the whitelist
for caller in *@callstack
- if fn_info.whiteset[caller]
+ if fn_def.whiteset[caller]
return true
return false
- def: (spec, fn, src)=>
- if @debug
- @writeln "Defining rule: #{spec}"
- invocations,arg_names = @get_invocations spec
- for i=2,#invocations
- if not utils.equivalent(utils.set(arg_names[invocations[1]]), utils.set(arg_names[invocations[i]]))
- @error("Conflicting argument names #{utils.repr(invocations[1])} and #{utils.repr(invocations[i])} for #{utils.repr(spec)}")
- fn_info = {:fn, :arg_names, :invocations, :src, is_macro:false}
- for invocation in *invocations
- @defs[invocation] = fn_info
-
- get_invocations_from_definition:(def, vars)=>
- if def.type == "String"
- return @@unescape_string(def.value)
-
- if def.type != "List"
- @error "Trying to get invocations from #{def.type}, but expected List or String."
-
- invocations = {}
- for item in *def.value
- if item.type == "String"
- insert invocations, item.value
- continue
- if item.type != "FunctionCall"
- @error "Invalid list item: #{item.type}, expected FunctionCall or String"
- name_bits = {}
- for token in *item.value
- if token.type == "Word"
- insert name_bits, token.value
- elseif token.type == "Var"
- insert name_bits, token.src
- else
- @error "Unexpected token type in definition: #{token.type} (expected Word or Var)"
- insert invocations, table.concat(name_bits, " ")
- return invocations
-
- get_invocations:(text)=>
- if not text
- @error "No text provided!"
- if type(text) == 'function'
- error "Function passed to get_invocations"
- if type(text) == 'string' then text = {text}
- invocations = {}
- arg_names = {}
- for _text in *text
- invocation = _text\gsub("'"," '")\gsub("%%%S+","%%")\gsub("%s+"," ")
- _arg_names = [arg for arg in _text\gmatch("%%(%S[^%s']*)")]
- insert invocations, invocation
- arg_names[invocation] = _arg_names
- return invocations, arg_names
-
- defmacro: (spec, lua_gen_fn, src)=>
- if @debug
- @writeln("DEFINING MACRO: #{spec}#{src or ""}")
- invocations,arg_names = @get_invocations spec
- for i=2,#invocations
- if not utils.equivalent(utils.set(arg_names[invocations[1]]), utils.set(arg_names[invocations[i]]))
- @error("Conflicting argument names #{utils.repr(invocations[1])} and #{utils.repr(invocations[i])} for #{utils.repr(spec)}")
- fn_info = {fn:lua_gen_fn, :arg_names, :invocations, :src, is_macro:true}
- for invocation in *invocations
- @defs[invocation] = fn_info
-
parse: (str, filename)=>
if @debug
@writeln("PARSING:\n#{str}")
@@ -136,7 +137,7 @@ class NomsuCompiler
return end_pos
check_dedent = (subject,end_pos,spaces)->
if #spaces < indent_stack[#indent_stack]
- table.remove(indent_stack)
+ remove(indent_stack)
return end_pos
check_nodent = (subject,end_pos,spaces)->
if #spaces == indent_stack[#indent_stack]
@@ -245,11 +246,10 @@ class NomsuCompiler
pointer = ("-")\rep(err_pos - start_of_err_line + 0) .. "^"
error("\n#{err_msg or "Parse error"} in #{filename} on line #{line_no}:\n\n#{prev_line}\n#{err_line}\n#{pointer}\n#{next_line}\n")
+ tree_mt = {__tostring:=> "#{@type}(#{repr(@value)})"}
setmetatable(defs, {
__index: (t,key)->
- fn = (src, value, errors)->
- token = {type: key, :src, :value, :errors}
- return token
+ fn = (src, value, errors)-> setmetatable({type: key, :src, :value, :errors}, tree_mt)
t[key] = fn
return fn
})
@@ -272,7 +272,7 @@ class NomsuCompiler
tree_to_lua: (tree)=>
assert tree, "No tree provided."
if not tree.type
- @error "Invalid tree: #{utils.repr(tree)}"
+ @error "Invalid tree: #{repr(tree)}"
switch tree.type
when "File"
buffer = {[[return (function(compiler, vars)
@@ -288,7 +288,7 @@ class NomsuCompiler
return (function(compiler, vars)\n#{code}\nend)"
lua_thunk, err = load(lua_code)
if not lua_thunk
- error("Failed to compile generated code:\n#{code}\n\n#{err}\n\nProduced by statement:\n#{utils.repr(statement)}")
+ error("Failed to compile generated code:\n#{code}\n\n#{err}\n\nProduced by statement:\n#{repr(statement)}")
value = lua_thunk!
ok,return_value = pcall(value, self, vars)
if not ok
@@ -299,13 +299,13 @@ class NomsuCompiler
return ret
end)
]]
- return table.concat(buffer, "\n"), return_value
+ return concat(buffer, "\n"), return_value
when "Block"
buffer = {}
for statement in *tree.value
insert buffer, @tree_to_lua(statement)
- return table.concat(buffer, "\n")
+ return concat(buffer, "\n")
when "Thunk"
assert tree.value.type == "Block", "Non-block value in Thunk"
@@ -320,22 +320,22 @@ class NomsuCompiler
when "Statement"
-- This case here is to prevent "ret =" from getting prepended when the macro might not want it
if tree.value.type == "FunctionCall"
- name = @fn_name_from_tree(tree.value)
- if @defs[name] and @defs[name].is_macro
+ alias = @get_alias(tree.value)
+ if @defs[alias] and @defs[alias].is_macro
return @run_macro(tree.value, "Statement")
return "ret = "..(@tree_to_lua(tree.value))
when "FunctionCall"
- name = @fn_name_from_tree(tree)
- if @defs[name] and @defs[name].is_macro
+ alias = @get_alias(tree)
+ if @defs[alias] and @defs[alias].is_macro
return @run_macro(tree, "Expression")
else
args = [@tree_to_lua(a) for a in *tree.value when a.type != "Word"]
- insert args, 1, utils.repr(name)
+ insert args, 1, repr(alias)
return @@comma_separated_items("compiler:call(", args, ")")
when "String"
- return utils.repr(@@unescape_string(tree.value))
+ return repr(@@unescape_string(tree.value))
when "Longstring"
concat_parts = {}
@@ -347,19 +347,19 @@ class NomsuCompiler
string_buffer ..= bit\gsub("\\\\","\\")
else
if string_buffer ~= ""
- insert concat_parts, utils.repr(string_buffer)
+ insert concat_parts, repr(string_buffer)
string_buffer = ""
insert concat_parts, "compiler.utils.repr_if_not_string(#{@tree_to_lua(bit)})"
if string_buffer ~= ""
- insert concat_parts, utils.repr(string_buffer)
+ insert concat_parts, repr(string_buffer)
if #concat_parts == 0
return "''"
elseif #concat_parts == 1
return concat_parts[1]
else
- return "(#{table.concat(concat_parts, "..")})"
+ return "(#{concat(concat_parts, "..")})"
when "Number"
return tree.value
@@ -373,7 +373,7 @@ class NomsuCompiler
return @@comma_separated_items("{", [@tree_to_lua(item) for item in *tree.value], "}")
when "Var"
- return "vars[#{utils.repr(tree.value)}]"
+ return "vars[#{repr(tree.value)}]"
else
@error("Unknown/unimplemented thingy: #{tree.type}")
@@ -392,38 +392,60 @@ class NomsuCompiler
insert bits, "\n"
so_far = 0
insert bits, close
- return table.concat(bits)
-
- fn_name_from_tree: (tree)=>
- assert(tree.type == "FunctionCall", "Attempt to get fn name from non-functioncall tree: #{tree.type}")
- name_bits = {}
- for token in *tree.value
- insert name_bits, if token.type == "Word" then token.value else "%"
- table.concat(name_bits, " ")
+ return concat(bits)
+
+ get_alias: (x)=>
+ if not x then @error "Nothing to get alias from"
+ -- Returns a single alias ("say %"), and list of args ({msg}) from a single rule def
+ -- (e.g. "say %msg") or function call (e.g. FunctionCall({Word("say"), Var("msg")))
+ if type(x) == 'string'
+ -- TODO
+ alias = x\gsub("'"," '")\gsub("%%%S+","%%")\gsub("%s+"," ")
+ args = [arg for arg in x\gmatch("%%(%S[^%s']*)")]
+ return alias, args
+ switch x.type
+ when "String" then return @get_alias(x.value)
+ when "Statement" then return @get_alias(x.value)
+ when "FunctionCall"
+ alias, args = {}, {}, {}
+ for token in *x.value
+ switch token.type
+ when "Word"
+ insert alias, token.value
+ when "Var"
+ insert alias, "%"
+ insert args, token.value
+ else
+ insert alias, "%"
+ insert args, token
+ return concat(alias," "), args
+
+ get_aliases:(x)=>
+ if self.value
+ print self
+ error "WTF"
+ if not x then @error "Nothing to get aliases from"
+ if type(x) == 'string'
+ alias, args = @get_alias(x)
+ return {[alias]: args}
+ switch x.type
+ when "String" then return @get_aliases({x.value})
+ when "Statement" then return @get_aliases({x.value})
+ when "FunctionCall" then return @get_aliases({x})
+ when "List" then x = x.value
+ when "Block" then x = x.value
+ with {}
+ for y in *x
+ alias,args = @get_alias(y)
+ [alias] = args
var_to_lua_identifier: (var)=>
+ -- Converts arbitrary nomsu vars to valid lua identifiers by replacing illegal
+ -- characters with escape sequences
if var.type != "Var"
@error("Tried to convert something that wasn't a Var into a lua identifier: it was not a Var, it was: "..label.type)
"var"..(var.value\gsub "%W", (verboten)->
if verboten == "_" then "__" else ("_%x")\format(verboten\byte!))
-
- run_macro: (tree, kind="Expression")=>
- name = @fn_name_from_tree(tree)
- unless @defs[name] and @defs[name].is_macro
- @error("Macro not found: #{name}")
- unless @check_permission(name)
- @error "You do not have the authority to call: #{name}"
- {:fn, :arg_names} = @defs[name]
- args = [a for a in *tree.value when a.type != "Word"]
- args = {name,args[i] for i,name in ipairs(arg_names[name])}
- insert @callstack, name
- ret, manual_mode = fn(self, args, kind)
- table.remove @callstack
- if not ret
- @error("No return value for macro: #{name}")
- if kind == "Statement" and not manual_mode
- ret = "ret = "..ret
- return ret
_yield_tree: (tree, indent_level=0)=>
ind = (s) -> INDENT\rep(indent_level)..s
@@ -431,42 +453,27 @@ class NomsuCompiler
when "File"
coroutine.yield(ind"File:")
@_yield_tree(tree.value.body, indent_level+1)
-
- when "Errors"
- coroutine.yield(ind"Error:\n#{tree.value}")
-
+ when "Errors" then coroutine.yield(ind"Error:\n#{tree.value}")
when "Block"
for chunk in *tree.value
@_yield_tree(chunk, indent_level)
-
when "Thunk"
coroutine.yield(ind"Thunk:")
@_yield_tree(tree.value, indent_level+1)
-
- when "Statement"
- @_yield_tree(tree.value, indent_level)
-
+ when "Statement" then @_yield_tree(tree.value, indent_level)
when "FunctionCall"
- name = @fn_name_from_tree(tree)
+ alias = @get_alias tree
args = [a for a in *tree.value when a.type != "Word"]
if #args == 0
- coroutine.yield(ind"Call [#{name}]!")
+ coroutine.yield(ind"Call [#{alias}]!")
else
- coroutine.yield(ind"Call [#{name}]:")
+ coroutine.yield(ind"Call [#{alias}]:")
for a in *args
@_yield_tree(a, indent_level+1)
-
- when "String"
- -- TODO: Better implement
- coroutine.yield(ind(utils.repr(tree.value)))
-
- when "Longstring"
- -- TODO: Better implement
- coroutine.yield(ind(utils.repr(tree.value)))
-
- when "Number"
- coroutine.yield(ind(tree.value))
-
+ when "String" then coroutine.yield(ind(repr(tree.value)))
+ when "Longstring" then coroutine.yield(ind(repr(tree.value)))
+ when "Number" then coroutine.yield(ind(tree.value))
+ when "Var" then coroutine.yield ind"Var[#{repr(tree.value)}]"
when "List"
if #tree.value == 0
coroutine.yield(ind("<Empty List>"))
@@ -474,13 +481,7 @@ class NomsuCompiler
coroutine.yield(ind"List:")
for item in *tree.value
@_yield_tree(item, indent_level+1)
-
- when "Var"
- coroutine.yield ind"Var[#{utils.repr(tree.value)}]"
-
- else
- error("Unknown/unimplemented thingy: #{tree.type}")
- return nil -- to prevent tail calls
+ else error("Unknown/unimplemented thingy: #{tree.type}")
print_tree:(tree)=>
for line in coroutine.wrap(-> @_yield_tree(tree))
@@ -490,7 +491,7 @@ class NomsuCompiler
result = {}
for line in coroutine.wrap(-> @_yield_tree(tree))
insert(result, line)
- return table.concat result, "\n"
+ return concat result, "\n"
run: (src, filename, output_file=nil)=>
if @debug
@@ -533,14 +534,14 @@ class NomsuCompiler
initialize_core: =>
-- Sets up some core functionality
- @defmacro [[lua block %lua_code]], (vars, kind)=>
+ @defmacro "lua block %lua_code", (vars, kind)=>
if kind == "Expression" then error("Expected to be in statement.")
- inner_vars = setmetatable({}, {__index:(_,key)-> error"vars[#{utils.repr(key)}]"})
+ inner_vars = setmetatable({}, {__index:(_,key)-> error"vars[#{repr(key)}]"})
return "do\n"..@tree_to_value(vars.lua_code, inner_vars).."\nend", true
- @defmacro [[lua expr %lua_code]], (vars, kind)=>
+ @defmacro "lua expr %lua_code", (vars, kind)=>
lua_code = vars.lua_code.value
- inner_vars = setmetatable({}, {__index:(_,key)-> error"vars[#{utils.repr(key)}]"})
+ inner_vars = setmetatable({}, {__index:(_,key)-> error"vars[#{repr(key)}]"})
return @tree_to_value(vars.lua_code, inner_vars)
@def "require %filename", (vars)=>
@@ -607,6 +608,6 @@ elseif arg
break
ok, ret = pcall(-> c\run(buff))
if ok and ret != nil
- print "= "..utils.repr(ret)
+ print "= "..repr(ret)
return NomsuCompiler
diff --git a/utils.lua b/utils.lua
index ddd2c3c..e058645 100644
--- a/utils.lua
+++ b/utils.lua
@@ -78,6 +78,14 @@ utils = {
end
return _accum_0
end,
+ remove_from_list = function(list, item)
+ for i, list_item in ipairs(list) do
+ if list_item == item then
+ table.remove(list, i)
+ return
+ end
+ end
+ end,
accumulate = function(glue, co)
if co == nil then
glue, co = "", glue
diff --git a/utils.moon b/utils.moon
index 3b3573c..f267c7d 100644
--- a/utils.moon
+++ b/utils.moon
@@ -41,6 +41,12 @@ utils = {
split: (str, sep="%s")->
[chunk for chunk in str\gmatch("[^#{sep}]+")]
+ remove_from_list: (list, item)->
+ for i,list_item in ipairs(list)
+ if list_item == item
+ table.remove list, i
+ return
+
accumulate: (glue, co)->
if co == nil then glue, co = "", glue
bits = {}