aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2017-09-21 00:10:26 -0700
committerBruce Hill <bitbucket@bruce-hill.com>2017-09-21 00:10:26 -0700
commit371548150618d5b3501f388972077b5d035f7d8a (patch)
tree8a1cdf99dc25536e21a5a571e5d54607a50848f4
parent0750d642624b2262afdb4dd17b275a16e96971b5 (diff)
Another overhaul, this time pulling all the chunks of the core lib into
their own files.
-rw-r--r--lib/collections.nom139
-rw-r--r--lib/control_flow.nom193
-rw-r--r--lib/core.nom689
-rw-r--r--lib/metaprogramming.nom105
-rw-r--r--lib/moonscript.nom30
-rw-r--r--lib/operators.nom125
-rw-r--r--lib/permissions.nom42
-rw-r--r--lib/testing.nom14
-rw-r--r--lib/utils.nom85
-rw-r--r--nomsu.lua56
-rwxr-xr-xnomsu.moon54
-rw-r--r--utils.lua22
-rw-r--r--utils.moon16
13 files changed, 833 insertions, 737 deletions
diff --git a/lib/collections.nom b/lib/collections.nom
new file mode 100644
index 0000000..3751d27
--- /dev/null
+++ b/lib/collections.nom
@@ -0,0 +1,139 @@
+require "lib/metaprogramming.nom"
+require "lib/control_flow.nom"
+require "lib/operators.nom"
+
+# List/dict functions:
+
+# Indexing
+macro [..]
+ %list's %index, %index st in %list, %index nd in %list, %index rd in %list
+ %index th in %list, %index in %list, %list -> %index
+..=:
+ ".."|\%list as lua expr\[\%index as lua expr\]
+ ".."|\%list as lua expr\[\%index as lua expr\]
+macro [..]
+ %index st to last in %list, %index nd to last in %list, %index rd to last in %list
+ %index th to last in %list
+..=:
+ ".."|compiler.utils.nth_to_last(\%list as lua expr\, \%index as lua expr\)
+
+macro [first in %list, first %list] =:
+ ".."|\%list as lua expr\[1]
+macro [last in %list, last %list] =:
+ ".."|compiler.utils.nth_to_last(\%list as lua expr\, 1)
+
+# Dict iteration convenience function. This could also be accomplished with: for all (entries in %dict): ...
+macro block [for %key -> %value in %dict %body] =:
+ assert ((%key's "type") == "Var") ".."
+ |For loop has the wrong type for the key variable. Expected Var, but got: \%key's "type"\
+ assert ((%value's "type") == "Var") ".."
+ |For loop has the wrong type for the value variable. Expected Var, but got: \%value's "type"\
+ ".."
+ |do
+ | local vars = setmetatable({}, {__index=vars})
+ | for k, v in pairs(\%dict as lua expr\) do
+ | \%key as lua expr\, \%value as lua expr\ = k, v
+ | \%body as lua block\
+ | end
+ |end
+
+# Membership testing
+rule [%item is in %list, %list contains %item, %list has %item] =:
+ for %key -> %value in %list:
+ if (%key == %item): return (yes)
+ return (no)
+macro [%list has key %index, %list has index %index] =: ".."
+ |(\%list as lua expr\[\%index as lua expr\] ~= nil)
+
+rule [..]
+ %item isn't in %list, %item is not in %list
+ %list doesn't contain %item, %list does not contain %item
+ %list doesn't have %item, %list does not have %item
+..=:
+ for %key -> %value in %list:
+ if (%key == %item): return (no)
+ return (yes)
+
+macro [..]
+ %list doesn't have key %index, %list does not have key %index
+ %list doesn't have index %index, %list does not have index %index
+..=: ".."|(\%list as lua expr\[\%index as lua expr\] ~= nil)
+
+macro [length of %list, size of %list, number of %list, len %list] =:
+ ".."|#(\%list as lua expr\)
+
+# Chained lookup
+macro [%list ->* %indices] =:
+ %ret =: %list as lua expr
+ for %index in %indices:
+ %ret join=: ".."|[\%index as lua expr\]
+ %ret
+
+# Assignment
+macro block [..]
+ %list's %index = %new_value, %index st in %list = %new_value, %index nd in %list = %new_value
+ %index rd in %list = %new_value, %index th in %list = %new_value, %index in %list = %new_value
+ %list -> %index = %new_value
+..=:
+ assert ((%new_value's "type") == "Thunk") ".."
+ |Dict assignment operation has the wrong type for the right hand side.
+ |Expected Thunk, but got \%new_value's "type"\.
+ |Maybe you used "=" instead of "=:"?
+ if ((size of (%new_value ->*["value","value"])) > 1):
+ error ".."|Dict assignment operation has too many values on the right hand side.
+ ".."|\%list as lua expr\[\%index as lua expr\] = \(%new_value ->*["value","value",1]) as lua expr\
+
+macro [append %item to %list, add %item to %list] =:
+ ".."|table.insert(\%list as lua expr\, \%item as lua expr\)
+
+rule [flatten %lists] =:
+ %flat =: []
+ for %list in %lists:
+ for %item in %list:
+ add %item to %flat
+ %flat
+
+rule [dict %items] =:
+ %dict =: []
+ for %pair in %items:
+ lua block "vars.dict[vars.pair[1]] = vars.pair[2]"
+ return %dict
+
+rule [entries in %dict] =:
+ lua block ".."
+ |local items = {}
+ |for k,v in pairs(vars.dict) do
+ | table.insert(items, {key=k,value=v})
+ |end
+ |return items
+
+# List Comprehension
+macro [%expression for %var in %iterable] =:
+ assert ((%var's "type") == "Var") ".."
+ |List comprehension has the wrong type for the loop variable. Expected Var, but got: \%var's "type"\
+ ".."
+ |(function(game, vars)
+ | local comprehension = {}
+ | for i,value in ipairs(\%iterable as lua expr\) do
+ | \%var as lua expr\ = value
+ | comprehension[i] = \%expression as lua expr\
+ | end
+ | return comprehension
+ |end)(game, setmetatable({}, {__index=vars}))
+macro [%expression for all %iterable] =:
+ ".."|(function(game, vars)
+ | local comprehension = {}
+ | for i,value in ipairs(\%iterable as lua expr\) do
+ | vars.it = value
+ | comprehension[i] = \%expression as lua expr\
+ | end
+ | return comprehension
+ |end)(game, setmetatable({}, {__index=vars}))
+
+# TODO: maybe make a generator/coroutine?
+
+#.. Dict comprehensions can be accomplished okay by doing:
+ dict ([new_key using (%it's "key"), new_value using (%it's "value")] for all (entries in %dict))
+ or something similar
+# TODO: fix compiler bugs
+pass
diff --git a/lib/control_flow.nom b/lib/control_flow.nom
new file mode 100644
index 0000000..e2caa2d
--- /dev/null
+++ b/lib/control_flow.nom
@@ -0,0 +1,193 @@
+require "lib/metaprogramming.nom"
+require "lib/operators.nom"
+require "lib/utils.nom"
+
+# Conditionals
+macro block [if %condition %if_body] =:
+ ".."|if \%condition as lua expr\ then
+ | \(lua expr "vars.if_body.value") as lua block\
+ |end
+
+macro block [if %condition %if_body else %else_body] =:
+ ".."|if \%condition as lua expr\ then
+ | \(lua expr "vars.if_body.value") as lua block\
+ |else
+ | \(lua expr "vars.else_body.value") as lua block\
+ |end
+
+# Return
+macro block [return] =: "return nil"
+macro block [return %return-value] =: ".."
+ |do return \%return-value as lua expr\ end
+
+macro [do %action] =: ".."
+ |(\%action as lua expr\)(compiler, setmetatable({}, {__index=vars}))
+
+
+# GOTOs
+macro block [-> %label] =: ".."
+ |::label_\compiler "var_to_lua_identifier" [%label]\::
+macro block [go to %label] =: ".."
+ |goto label_\compiler "var_to_lua_identifier" [%label]\
+
+# Loop control flow
+macro block [break] =: "break"
+macro block [break for] =: "goto break_for"
+macro block [break for-all] =: "goto break_for_all"
+macro block [break repeat] =: "goto break_repeat"
+macro block [break repeat-until] =: "goto break_repeat_until"
+macro block [break repeat-while] =: "goto break_repeat_while"
+macro block [break %var, stop getting %var, no more %var] =: ".."
+ |goto break_\compiler "var_to_lua_identifier" [%var]\
+
+macro block [continue] =: "continue"
+macro block [continue for] =: "goto continue_for"
+macro block [continue for-all] =: "goto continue_for_all"
+macro block [continue repeat] =: "goto continue_repeat"
+macro block [continue repeat-until] =: "goto continue_repeat_until"
+macro block [continue repeat-while] =: "goto continue_repeat_while"
+macro block [continue %var, go to next %var, on to the next %var] =: ".."
+ |goto continue_\compiler "var_to_lua_identifier" [%var]\
+# TODO: add labeled break/continue?
+
+# While loops
+macro block [repeat %body] =:
+ ".."|do
+ | while true do
+ | \(lua expr "vars.body.value") as lua block\
+ | ::continue_repeat::
+ | end
+ | ::break_repeat::
+ |end
+macro block [repeat while %condition %body] =:
+ ".."|do
+ | while \%condition as lua expr\ do
+ | \(lua expr "vars.body.value") as lua block\
+ | ::continue_repeat_while::
+ | end
+ | ::break_repeat_while::
+ |end
+macro block [repeat until %condition %body] =:
+ ".."|do
+ | while not (\%condition as lua expr\) do
+ | \(lua expr "vars.body.value") as lua block\
+ | ::continue_repeat_until::
+ | end
+ | ::break_repeat_until::
+ |end
+
+# For loops
+macro block [for %var in %iterable %body] =:
+ %var-type =: lua expr "vars.var.type"
+ assert (%var-type == "Var") ".."
+ |For loop has the wrong type for the loop variable. Expected Var, but got: \%var-type\
+ ".."
+ |do
+ | local vars = setmetatable({}, {__index=vars})
+ | for i,value in ipairs(\%iterable as lua expr\) do
+ | \%var as lua expr\ = value
+ | \(lua expr "vars.body.value") as lua block\
+ | ::continue_for::
+ | ::continue_\compiler "var_to_lua_identifier" [%var]\::
+ | end
+ | ::break_for::
+ | ::break_\compiler "var_to_lua_identifier" [%var]\::
+ |end
+
+macro block [for all %iterable %body] =:
+ ".."|do
+ | local vars = setmetatable({}, {__index=vars})
+ | for i,value in ipairs(\%iterable as lua expr\) do
+ | vars.it = value
+ | \(lua expr "vars.body.value") as lua block\
+ | ::continue_for_all::
+ | end
+ | ::break_for_all::
+ |end
+
+# Switch statement/multi-branch if
+macro block [when %body] =:
+ %result =: ""
+ for %statement in (lua expr "vars.body.value.value"):
+ %func-call =: lua expr "vars.statement.value"
+ assert ((lua expr "vars['func-call'].type") != "FunctionCall") ".."
+ |Invalid format for 'when' statement. Only '?' blocks are allowed.
+ %tokens =: lua expr "vars['func-call'].value"
+ %q =: lua expr "vars.tokens[1]"
+ assert (((lua expr "vars.q.type") != "Word") or ((lua expr "vars.q.value") != "?")) ".."
+ |Invalid format for 'when' statement. Lines must begin with '?'
+ %thunk =: lua expr "vars.tokens[#vars.tokens]"
+ assert ((lua expr "vars.thunk.type") != "Thunk") ".."
+ |Invalid format for 'when' statement. Lines must have a body.
+ %condition_bits =: []
+ for %i in (2 up to (lua expr "#vars.tokens")):
+ lua block "table.insert(vars['condition_bits'], vars.tokens[vars.i])"
+
+ if (lua expr "#vars.condition_bits == 0"):
+ %result join=: ".."
+ |
+ |do
+ | local ret
+ | \(lua expr "vars.thunk.value") as lua block\
+ | return ret
+ |end
+ ..else:
+ if (lua expr "#vars.condition_bits == 1 and vars.condition_bits[1].type ~= 'Word'"):
+ %condition =: lua expr "vars.condition_bits[1]"
+ ..else:
+ %condition =: dict [..]
+ ["type",lua expr "vars['func-call'].type"]
+ ["src",lua expr "vars['func-call'].src"]
+ ["value", %condition_bits]
+ %result join=: ".."
+ |
+ |if \%condition as lua expr\ then
+ | local ret
+ | \(lua expr "vars.thunk.value") as lua block\
+ | return ret
+ |end
+
+ %result
+
+# Switch statement
+macro block [when %branch-value %body] =:
+ %result =: ".."|local branch_value = \%branch-value as lua expr\
+ for %statement in (lua expr "vars.body.value.value"):
+ %func-call =: lua expr "vars.statement.value"
+ assert ((lua expr "vars['func-call'].type") != "FunctionCall") ".."
+ |Invalid format for 'when' statement. Only == blocks are allowed.
+ %tokens =: lua expr "vars['func-call'].value"
+ %eq =: lua expr "vars.tokens[1]"
+ assert (((lua expr "vars.eq.type") != "Word") or ((lua expr "vars.eq.value") != "==")) ".."
+ |Invalid format for 'when' statement. Lines must begin with '=='
+ %thunk =: lua expr "vars.tokens[#vars.tokens]"
+ assert ((lua expr "vars.thunk.type") != "Thunk") ".."
+ |Invalid format for 'when' statement. Lines must have a body.
+ %condition_bits =: []
+ for %i in (2 up to (lua expr "#vars.tokens")):
+ lua block "table.insert(vars.condition_bits, vars.tokens[vars.i])"
+ if (lua expr "#vars.condition_bits == 0"):
+ %result join=: ".."
+ |
+ |do
+ | local ret
+ | \(lua expr "vars.thunk.value") as lua block\
+ | return ret
+ |end
+ ..else:
+ if (lua expr "#vars.condition_bits == 1 and vars.condition_bits[1].type ~= 'Word'"):
+ %condition =: (lua expr "vars.condition_bits[1]")
+ ..else:
+ %condition =: dict [..]
+ ["type",lua expr "vars['func-call'].type"]
+ ["src",lua expr "vars['func-call'].src"]
+ ["value", %condition_bits]
+ %result join=: ".."
+ |
+ |if compiler.utils.equivalent(branch_condition, \%condition as lua expr\) then
+ | local ret
+ | \(lua expr "vars.thunk.value") as lua block\
+ | return ret
+ |end
+ %result
+
diff --git a/lib/core.nom b/lib/core.nom
index f0cad0d..f00bd22 100644
--- a/lib/core.nom
+++ b/lib/core.nom
@@ -1,683 +1,6 @@
-# Rule for making rules
-lua block ".."
- |compiler:defmacro("rule %spec = %body", function(compiler, vars, kind)
- | if kind == "Expression" then
- | compiler:error("Cannot use rule definitions as an expression.")
- | end
- | local spec = compiler:get_invocations_from_definition(vars.spec, vars)
- | return ("compiler:def("..compiler.utils.repr(spec,true)..", "..compiler:tree_to_lua(vars.body, vars)..")"), true
- |end)
-
-rule [help %invocation] =:
- lua block ".."
- |local fn_info = compiler.defs[vars.invocation]
- |if not fn_info then
- | compiler:writeln("Function not found: "..compiler.utils.repr(vars.invocation, true))
- |else
- | compiler:writeln("rule "..compiler.utils.repr(fn_info.invocations).." ="..(fn_info.src or ":\\n <unknown source code>"))
- |end
-
-# Macros
-lua block ".."
- |local add_macro = function(compiler, vars, kind)
- | local spec = compiler:get_invocations_from_definition(vars.spec, vars)
- | local fn = compiler:tree_to_value(vars.body, vars)
- | compiler:defmacro(spec, fn, vars.body.src)
- | return "", true
- |end
- |compiler:defmacro("macro %spec = %body", add_macro)
- |
- |local add_macro_block = function(compiler, vars, kind)
- | local spec = compiler:get_invocations_from_definition(vars.spec, vars)
- | local fn = compiler:tree_to_value(vars.body, vars)
- | local wrapper = function(compiler, vars, kind)
- | if kind == "Expression" then
- | compiler:error("Macro: "..spec.." was defined to be a block, but is being used as an expression.")
- | end
- | return ("do\\n"..fn(compiler, vars, kind).."\\nend"), true
- | end
- | compiler:defmacro(spec, wrapper, vars.body.src)
- | return "", true
- |end
- |compiler:defmacro("macro block %spec = %body", add_macro_block)
-
-# Compiler tools
-rule [eval %code, run %code] =:
- lua expr "compiler:run(vars.code)"
-
-rule [source code from tree %tree] =:
- lua block ".."
- |local _,_,leading_space = vars.tree.src:find("\\n(%s*)%S")
- |if leading_space then
- | local chunk1, chunk2 = vars.tree.src:match(":%s*([^\\n]*)(\\n.*)")
- | chunk2 = chunk2:gsub("\\n"..leading_space, "\\n")
- | vars.source = chunk1..chunk2.."\\n"
- |else
- | vars.source = vars.tree.src:match(":%s*(%S.*)").."\\n"
- |end
-
- %source
-
-macro [source code %body] =:
- lua expr ".."
- |compiler.utils.repr(compiler:call("source code from tree %", vars.body), true)
-
-rule [run file %filename] =:
- lua block ".."
- |local file = io.open(vars.filename)
- |return compiler:run(file:read("*a"))
-
-# Error functions
-rule [error!] =:
- lua block ".."
- |table.remove(compiler.callstack)
- |compiler:error()
-
-rule [error %msg] =:
- lua block ".."
- |table.remove(compiler.callstack)
- |compiler:error(vars.msg)
-
-# TODO: Make useful
-macro [as lua %block] =:
- lua expr "compiler.utils.repr(compiler:tree_to_lua(vars.block.value, 'Statement'), true)"
-
-macro block [show generated lua %block] =:
- ".."|compiler:writeln(\lua expr "compiler.utils.repr(compiler:tree_to_lua(vars.block.value, 'Statement'), true)"\)
-
-# Macro helper functions
-rule [%tree as value] =:
- lua expr ".."
- |compiler:tree_to_value(vars.tree, vars)
-
-rule [%tree as lua block] =:
- lua block ".."
- |return compiler:tree_to_lua(vars.tree, 'Statement'), true
-
-rule [%tree as lua expr] =:
- lua expr ".."
- |compiler:tree_to_lua(vars.tree, 'Expression')
-
-# Moonscript!
-macro block [moonscript block %moonscript_code] =:
- lua block ".."
- |local parse, compile = require('moonscript.parse'), require('moonscript.compile')
- |local moon_code = compiler:tree_to_value(vars.moonscript_code, vars)
- |local tree, err = parse.string(moon_code)
- |if not tree then
- | compiler:error("Failed to parse moonscript: "..err)
- |end
- |local lua_code, err, pos = compile.tree(tree)
- |if not lua_code then
- | compiler:error(compile.format_error(err, pos, moon_code))
- |end
- |return "do\\n"..lua_code.."\\nend"
-
-macro [moonscript %moonscript_code] =:
- lua block ".."
- |local parse, compile = require('moonscript.parse'), require('moonscript.compile')
- |local moon_code = compiler:tree_to_value(vars.moonscript_code, vars)
- |local tree, err = parse.string(moon_code)
- |if not tree then
- | compiler:error("Failed to parse moonscript: "..err)
- |end
- |local lua_code, err, pos = compile.tree(tree)
- |if not lua_code then
- | compiler:error(compile.format_error(err, pos, moon_code))
- |end
- |return "(function(compiler, vars)\\n"..lua_code.."\\nend)(compiler, vars)"
-
-# String functions
-rule [join %strs] =:
- lua block ".."
- |local str_bits = {}
- |for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr(bit) end
- |return table.concat(str_bits)
-
-rule [join %strs with glue %glue] =:
- lua block ".."
- |local str_bits = {}
- |for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr(bit) end
- |return table.concat(str_bits, vars.glue)
-
-rule [capitalize %str, %str capitalized] =:
- lua expr ".."|vars.str:gsub("%l", string.upper, 1)
-
-# Variable assignment
-#..
- macro block [%var = %value] =:
- lua block ".."
- |if vars.var.type ~= "Var" then
- | compiler:error("Assignment operation has the wrong type for the left hand side. "
- | .."Expected Var, but got: "..vars.var.type)
- |end
- ".."|\%var as lua expr\ = \%value as lua expr\
-
-lua block ".."
- |local function helper(callback)
- | return function(compiler, vars, kind)
- | if kind == "Expression" then
- | compiler:error("Cannot use an assignment operation as an expression value.")
- | end
- | if vars.var.type ~= "Var" then
- | compiler:error("Assignment operation has the wrong type for the left hand side. "
- | .."Expected Var, but got: "..vars.var.type)
- | end
- | if vars.rhs.type ~= "Thunk" then
- | compiler:error("Assignment operation has the wrong type for the right hand side. "
- | .."Expected Thunk, but got: "..vars.rhs.type.."\\nMaybe you used '=' instead of '=:'?")
- | end
- | if #vars.rhs.value.value == 1 then
- | return callback(compiler:tree_to_lua(vars.var, "Expression"),
- | compiler:tree_to_lua(vars.rhs.value.value[1].value, "Expression")), true
- | else
- | local ret = "do\\n local ret"
- | ret = ret .. "\\n "..compiler:tree_to_lua(vars.rhs.value, "Statement")
- | ret = ret .. "\\n "..callback(compiler:tree_to_lua(vars.var, "Expression"), "ret")
- | return (ret.."\\nend"), true
- | end
- | end
- |end
- |compiler:defmacro("%var = %rhs", helper(function(var,result) return var.." = "..result end))
- |compiler:defmacro("%var += %rhs", helper(function(var,result) return var.." = "..var.." + "..result end))
- |compiler:defmacro("%var -= %rhs", helper(function(var,result) return var.." = "..var.." - "..result end))
- |compiler:defmacro("%var *= %rhs", helper(function(var,result) return var.." = "..var.." * "..result end))
- |compiler:defmacro("%var /= %rhs", helper(function(var,result) return var.." = "..var.." / "..result end))
- |compiler:defmacro("%var ^= %rhs", helper(function(var,result) return var.." = "..var.." ^ "..result end))
- |compiler:defmacro("%var and= %rhs", helper(function(var,result) return var.." = "..var.." and "..result end))
- |compiler:defmacro("%var or= %rhs", helper(function(var,result) return var.." = "..var.." or "..result end))
- |compiler:defmacro("%var concat= %rhs", helper(function(var,result) return var.." = "..var.." .. "..result end))
- |compiler:defmacro("%var mod= %rhs", helper(function(var,result) return var.." = "..var.." % "..result end))
-
-# Operators
-macro [true, yes] =: "true"
-macro [false, no] =: "false"
-macro [nil, null] =: "nil"
-macro block [nop, pass] =: ""
-macro [%a + %b] =: ".."|(\%a as lua expr\ + \%b as lua expr\)
-macro [%a + %b + %c] =: ".."|(\%a as lua expr\ + \%b as lua expr\ + \%c as lua expr\)
-macro [%a + %b + %c + %d] =: ".."|(\%a as lua expr\ + \%b as lua expr\ + \%c as lua expr\ + \%d as lua expr\)
-macro [%a - %b] =: ".."|(\%a as lua expr\ - \%b as lua expr\)
-macro [%a * %b] =: ".."|(\%a as lua expr\ * \%b as lua expr\)
-macro [%a * %b * %c] =: ".."|(\%a as lua expr\ * \%b as lua expr\ * \%c as lua expr\)
-macro [%a * %b * %c * %d] =: ".."|(\%a as lua expr\ * \%b as lua expr\ * \%c as lua expr\ * \%d as lua expr\)
-macro [%a / %b] =: ".."|(\%a as lua expr\ / \%b as lua expr\)
-macro [%a === %b] =: ".."|(\%a as lua expr\ == \%b as lua expr\)
-macro [%a !== %b] =: ".."|(\%a as lua expr\ ~= \%b as lua expr\)
-macro [%a < %b] =: ".."|(\%a as lua expr\ < \%b as lua expr\)
-macro [%a < %b < %c] =: ".."|((\%a as lua expr\ < \%b as lua expr\) and (\%b as lua expr\ < \%c as lua expr\))
-macro [%a <= %b < %c] =: ".."|((\%a as lua expr\ <= \%b as lua expr\) and (\%b as lua expr\ < \%c as lua expr\))
-macro [%a <= %b <= %c] =: ".."|((\%a as lua expr\ <= \%b as lua expr\) and (\%b as lua expr\ <= \%c as lua expr\))
-macro [%a < %b <= %c] =: ".."|((\%a as lua expr\ < \%b as lua expr\) and (\%b as lua expr\ <= \%c as lua expr\))
-macro [%a > %b > %c] =: ".."|((\%a as lua expr\ > \%b as lua expr\) and (\%b as lua expr\ > \%c as lua expr\))
-macro [%a >= %b > %c] =: ".."|((\%a as lua expr\ >= \%b as lua expr\) and (\%b as lua expr\ > \%c as lua expr\))
-macro [%a >= %b >= %c] =: ".."|((\%a as lua expr\ >= \%b as lua expr\) and (\%b as lua expr\ >= \%c as lua expr\))
-macro [%a > %b >= %c] =: ".."|((\%a as lua expr\ > \%b as lua expr\) and (\%b as lua expr\ >= \%c as lua expr\))
-macro [%a <= %b] =: ".."|(\%a as lua expr\ <= \%b as lua expr\)
-macro [%a > %b] =: ".."|(\%a as lua expr\ > \%b as lua expr\)
-macro [%a >= %b] =: ".."|(\%a as lua expr\ >= \%b as lua expr\)
-macro [%a ^ %b] =: ".."|(\%a as lua expr\ ^ \%b as lua expr\)
-macro [%a and %b] =: ".."|(\%a as lua expr\ and \%b as lua expr\)
-macro [%a and %b and %c] =:
- ".."|(\%a as lua expr\ and \%b as lua expr\ and \%c as lua expr\)
-macro [%a and %b and %c and %d] =:
- ".."|(\%a as lua expr\ and \%b as lua expr\ and \%c as lua expr\ and \%d as lua expr\)
-macro [%a or %b] =: ".."|(\%a as lua expr\ or \%b as lua expr\)
-macro [%a or %b or %c] =:
- ".."|(\%a as lua expr\ or \%b as lua expr\ or \%c as lua expr\)
-macro [%a or %b or %c or %d] =:
- ".."|(\%a as lua expr\ or \%b as lua expr\ or \%c as lua expr\ or \%d as lua expr\)
-macro [%a mod %b] =: ".."|(\%a as lua expr\ mod \%b as lua expr\)
-macro [- %a] =: ".."|-(\%a as lua expr\)
-macro [not %a] =: ".."|not (\%a as lua expr\)
-
-rule [%a == %b] =:
- lua expr "((vars.a == vars.b) or compiler.utils.equivalent(vars.a, vars.b))"
-rule [%a != %b] =:
- lua expr "((vars.a ~= vars.b) or not compiler.utils.equivalent(vars.a, vars.b))"
-
-macro [repr %obj] =:
- ".."|compiler.utils.repr(\%obj as lua expr\, true)
-
-macro [say %str] =:
- ".."|compiler:writeln(compiler.utils.repr(\%str as lua expr\))
-
-# Control flow
-rule [do %action] =:
- lua expr "vars.action(compiler, setmetatable({}, {__index=vars}))"
-
-macro block [return %return_value] =:
- lua block ".."
- |if vars.return_value.type ~= "Thunk" then
- | compiler:error("Assignment operation has the wrong type for the right hand side. "
- | .."Expected Thunk, but got: "..vars.return_value.type.."\\nMaybe you used '=' instead of '=:'?")
- |end
- ".."|do
- | local ret
- | \lua expr "compiler:tree_to_lua(vars.return_value.value, 'Statement')"\
- | return ret
- |end
-
-macro block [return] =:
- "return nil"
-
-# Conditionals
-macro block [if %condition %if_body] =:
- ".."|if \%condition as lua expr\ then
- | \(lua expr "vars.if_body.value") as lua block\
- |end
-
-macro block [if %condition %if_body else %else_body] =:
- ".."|if \%condition as lua expr\ then
- | \(lua expr "vars.if_body.value") as lua block\
- |else
- | \(lua expr "vars.else_body.value") as lua block\
- |end
-
-# Ternary operator
-macro [%if_expr if %condition else %else_expr] =:
- ".."|(function(compiler, vars)
- | if \%condition as lua expr\ then
- | return \%if_expr as lua expr\
- | else
- | return \%else_expr as lua expr\
- | end
- |end)(compiler, vars)
-
-
-# Loop control flow
-macro block [break] =: "break"
-macro block [continue] =: "continue"
-# TODO: add labeled break/continue?
-
-# GOTOs
-lua block ".."
- |local function lua_label(label)
- | if label.type ~= "Var" then
- | compiler:error("Goto label has the wrong type for the label. Expected Var, but got: "..label.type)
- | end
- | local bits = "abcdefghijklmnop"
- | local lua_identifier = "label_"
- | for i=1,#label.value do
- | local byte = string.byte(label.value, i)
- | local low = byte % 16
- | local high = (byte - low) / 16
- | lua_identifier = lua_identifier .. bits:sub(low+1,low+1) .. bits:sub(high+1,high+1)
- | end
- | return lua_identifier
- |end
- |
- |compiler:defmacro("-> %label", function(compiler, vars, kind)
- | return "::"..lua_label(vars.label).."::", true
- |end)
- |compiler:defmacro("go to %label", function(compiler, vars, kind)
- | return "goto "..lua_label(vars.label), true
- |end)
-
-# While loops
-macro block [repeat %body] =:
- ".."|while true do
- | \(lua expr "vars.body.value") as lua block\
- |end
-macro block [repeat while %condition %body] =:
- ".."|while \%condition as lua expr\ do
- | \(lua expr "vars.body.value") as lua block\
- |end
-macro block [repeat until %condition %body] =:
- ".."|while not (\%condition as lua expr\) do
- | \(lua expr "vars.body.value") as lua block\
- |end
-
-# For loops
-macro block [for %var in %iterable %body] =:
- %var-type =: lua expr "vars.var.type"
- if (%var-type != "Var"):
- error ".."
- |For loop has the wrong type for the loop variable. Expected Var, but got: \%var-type\
- %var-code =: %var as lua expr
- ".."|local old_loopval = \%var-code\
- |for i,value in ipairs(\%iterable as lua expr\) do
- | \%var-code\ = value
- | \(lua expr "vars.body.value") as lua block\
- |end
- |\%var-code\ = old_loopval
-
-macro block [for all %iterable %body] =:
- ".."|local old_loopval = vars.it
- |for i,value in ipairs(\%iterable as lua expr\) do
- | vars.it = value
- | \(lua expr "vars.body.value") as lua block\
- |end
- |vars.it = old_loopval
-
-# Switch statement/multi-branch if
-macro block [when %body] =:
- %result =: ""
- for %statement in (lua expr "vars.body.value.value"):
- %func-call =: lua expr "vars.statement.value"
- if ((lua expr "vars['func-call'].type") != "FunctionCall"):
- error "Invalid format for 'when' statement. Only '?' blocks are allowed."
- %tokens =: lua expr "vars['func-call'].value"
- %q =: lua expr "vars.tokens[1]"
- if (((lua expr "vars.q.type") != "Word") or ((lua expr "vars.q.value") != "?")):
- error "Invalid format for 'when' statement. Lines must begin with '?'"
- %thunk =: lua expr "vars.tokens[#vars.tokens]"
- if ((lua expr "vars.thunk.type") != "Thunk"):
- error "Invalid format for 'when' statement. Lines must have a body."
- %condition_bits =: []
- for %i in (2 up to (lua expr "#vars.tokens")):
- lua block "table.insert(vars['condition_bits'], vars.tokens[vars.i])"
-
- if (lua expr "#vars.condition_bits == 0"):
- %result concat=: ".."
- |
- |do
- | local ret
- | \(lua expr "vars.thunk.value") as lua block\
- | return ret
- |end
- ..else:
- if (lua expr "#vars.condition_bits == 1 and vars.condition_bits[1].type ~= 'Word'"):
- %condition =: lua expr "vars.condition_bits[1]"
- ..else:
- %condition =: dict [..]
- ["type",lua expr "vars['func-call'].type"]
- ["src",lua expr "vars['func-call'].src"]
- ["value", %condition_bits]
- %result concat=: ".."
- |
- |if \%condition as lua expr\ then
- | local ret
- | \(lua expr "vars.thunk.value") as lua block\
- | return ret
- |end
-
- %result
-
-# Switch statement
-macro block [when %branch-value %body] =:
- %result =: ".."|local branch_value = \%branch-value as lua expr\
- for %statement in (lua expr "vars.body.value.value"):
- %func-call =: lua expr "vars.statement.value"
- if ((lua expr "vars['func-call'].type") != "FunctionCall"):
- error "Invalid format for 'when' statement. Only == blocks are allowed."
- %tokens =: lua expr "vars['func-call'].value"
- %eq =: lua expr "vars.tokens[1]"
- if (((lua expr "vars.eq.type") != "Word") or ((lua expr "vars.eq.value") != "==")):
- error "Invalid format for 'when' statement. Lines must begin with '=='"
- %thunk =: lua expr "vars.tokens[#vars.tokens]"
- if ((lua expr "vars.thunk.type") != "Thunk"):
- error "Invalid format for 'when' statement. Lines must have a body."
- %condition_bits =: []
- for %i in (2 up to (lua expr "#vars.tokens")):
- lua block "table.insert(vars.condition_bits, vars.tokens[vars.i])"
- if (lua expr "#vars.condition_bits == 0"):
- %result concat=: ".."
- |
- |do
- | local ret
- | \(lua expr "vars.thunk.value") as lua block\
- | return ret
- |end
- ..else:
- if (lua expr "#vars.condition_bits == 1 and vars.condition_bits[1].type ~= 'Word'"):
- %condition =: (lua expr "vars.condition_bits[1]")
- ..else:
- %condition =: dict [..]
- ["type",lua expr "vars['func-call'].type"]
- ["src",lua expr "vars['func-call'].src"]
- ["value", %condition_bits]
- %result concat=: ".."
- |
- |if compiler.utils.equivalent(branch_condition, \%condition as lua expr\) then
- | local ret
- | \(lua expr "vars.thunk.value") as lua block\
- | return ret
- |end
- %result
-
-# List Comprehension
-# TODO: maybe make this lazy, or a lazy version?
-macro [%expression for %var in %iterable] =:
- %var-type =: lua expr "vars.var.type"
- if (%var-type != "Var"):
- error ".."
- |List comprehension has the wrong type for the loop variable. Expected Var, but got: \%var-type\
- %var-code =: %var as lua expr
- ".."|(function(game, vars)
- | local comprehension = {}
- | for i,value in ipairs(\%iterable as lua expr\) do
- | \%var-code\ = value
- | comprehension[i] = \%expression as lua expr\
- | end
- | return comprehension
- |end)(game, setmetatable({}, {__index=vars}))
-
-macro [%expression for all %iterable] =:
- ".."|(function(game, vars)
- | local comprehension = {}
- | for i,value in ipairs(\%iterable as lua expr\) do
- | vars.it = value
- | comprehension[i] = \%expression as lua expr\
- | end
- | return comprehension
- |end)(game, setmetatable({}, {__index=vars}))
-
-# Dict comprehension
-macro [%key -> %value for %var in %iterable] =:
- %var-type =: lua expr "vars.var.type"
- if (%var-type != "Var"):
- error ".."
- |Dict comprehension has the wrong type for the loop variable. Expected Var, but got: \%var-type\
- %var-code =: %var as lua expr
- ".."|(function(game, vars)
- | local comprehension = {}
- | for i,value in ipairs(\%iterable as lua expr\) do
- | \%var-code\ = value
- | comprehension[\%key as lua expr\] = \%value as lua expr\
- | end
- | return comprehension
- |end)(game, setmetatable({}, {__index=vars}))
-
-macro [%key -> %value for all %iterable] =:
- ".."|(function(game, vars)
- | local comprehension = {}
- | for i,value in ipairs(\%iterable as lua expr\) do
- | vars.it = value
- | comprehension[\%key as lua expr\] = \%value as lua expr\
- | end
- | return comprehension
- |end)(game, setmetatable({}, {__index=vars}))
-
-# Number ranges
-rule [%start up to %stop] =:
- lua expr "compiler.utils.range(vars.start,vars.stop-1)"
-
-rule [%start thru %stop, %start through %stop] =:
- lua expr "compiler.utils.range(vars.start,vars.stop)"
-
-rule [%start down to %stop] =:
- lua expr "compiler.utils.range(vars.start,vars.stop+1,-1)"
-
-rule [%start down thru %stop, %start down through %stop] =:
- lua expr "compiler.utils.range(vars.start,vars.stop,-1)"
-
-rule [%start up to %stop via %step] =:
- lua expr "compiler.utils.range(vars.start,vars.stop-1,vars.step)"
-
-rule [%start thru %stop via %step, %start through %stop via %step] =:
- lua expr "compiler.utils.range(vars.start,vars.stop,vars.step)"
-
-rule [%start down to %stop via %step] =:
- lua expr "compiler.utils.range(vars.start,vars.stop+1,-vars.step)"
-
-rule [%start down thru %stop via %step, %start down through %stop via %step] =:
- lua expr "compiler.utils.range(vars.start,vars.stop,-vars.step)"
-
-# Common utility functions
-rule [random number] =: lua expr "math.random()"
-rule [sum of %items] =: lua expr "compiler.utils.sum(vars.items)"
-rule [product of %items] =: lua expr "compiler.utils.product(vars.items)"
-rule [all of %items] =: lua expr "compiler.utils.all(vars.items)"
-rule [any of %items] =: lua expr "compiler.utils.any(vars.items)"
-rule [avg of %items, average of %items] =:
- lua expr "(compiler.utils.sum(vars.items)/#vars.items)"
-rule [min of %items, smallest of %items, lowest of %items] =:
- lua expr "compiler.utils.min(vars.items)"
-rule [max of %items, biggest of %items, largest of %items, highest of %items] =:
- lua expr "compiler.utils.min(vars.items)"
-
-rule [min of %items with respect to %keys] =:
- lua expr "compiler.utils.min(vars.items, vars.keys)"
-rule [max of %items with respect to %keys] =:
- lua expr "compiler.utils.max(vars.items, vars.keys)"
-
-# List/dict functions
-macro [..]
- %list's %index, %index st in %list, %index nd in %list, %index rd in %list
- %index th in %list, %index in %list, %list -> %index
-..=:
- ".."|\%list as lua expr\[\%index as lua expr\]
- ".."|\%list as lua expr\[\%index as lua expr\]
-macro [first in %list] =:
- ".."|\%list as lua expr\[1]
-
-macro [..]
- %index st to last in %list, %index nd to last in %list, %index rd to last in %list
- %index th to last in %list
-..=:
- ".."|compiler.utils.nth_to_last(\%list as lua expr\, \%index as lua expr\)
-macro [last in %list] =:
- ".."|compiler.utils.nth_to_last(\%list as lua expr\, 1)
-
-macro block [..]
- %list's %index = %new_value, %index st in %list = %new_value, %index nd in %list = %new_value
- %index rd in %list = %new_value, %index th in %list = %new_value, %index in %list = %new_value
- %list -> %index = %new_value
-..=:
- lua block ".."
- |if vars.new_value.type ~= "Thunk" then
- | compiler:error("Dict assignment operation has the wrong type for the right hand side. "
- | .."Expected Thunk, but got: "..vars.new_value.type.."\\nMaybe you used '=' instead of '=:'?")
- |end
- ".."|do
- | local ret
- | \lua expr "compiler:tree_to_lua(vars.new_value.value, 'Statement')"\
- | \%list as lua expr\[\%index as lua expr\] = ret
- |end
-
-macro [append %item to %list, add %item to %list] =:
- ".."|table.insert(\%list as lua expr\, \%item as lua expr\)
-
-rule [flatten %lists] =:
- %flat =: []
- for %list in %lists:
- for %item in %list:
- add %item to %flat
- %flat
-
-macro [%item is in %list, %list contains %item] =:
- ".."|(\%list as lua expr\[\%index as lua expr\] ~= nil)
-
-macro [length of %list, size of %list, number of %list] =:
- ".."|#(\%list as lua expr\)
-
-rule [dict %items] =:
- %dict =: []
- for %pair in %items:
- lua block "vars.dict[vars.pair[1]] = vars.pair[2]"
- return: %dict
-
-rule [entries in %dict] =:
- lua block ".."
- |local items = {}
- |for k,v in pairs(vars.dict) do
- | table.insert(items, {key=k,value=v})
- |end
- |return items
-
-# Permission functions
-rule [restrict %fn to within %whitelist] =:
- lua block ".."
- |local fns = compiler:get_invocations(vars.fn)
- |local whitelist = compiler:get_invocations(vars.whitelist)
- |local whiteset = {}
- |for _,w in ipairs(whitelist) do
- | if not compiler.defs[w] then
- | compiler:error("Undefined function: "..tostring(w))
- | else whiteset[w] = true
- | end
- |end
- |for _,fn in ipairs(fns) do
- | local fn_info = compiler.defs[fn]
- | if fn_info == nil then
- | compiler:error("Undefined function: "..tostring(fn))
- | elseif not compiler:check_permission(fn) then
- | compiler:writeln("You do not have permission to restrict function: "..tostring(fn))
- | else
- | compiler.defs[fn].whiteset = whiteset
- | end
- |end
-
-rule [allow %whitelist to use %fn] =:
- lua block ".."
- |local fns = compiler:get_invocations(vars.fn)
- |local whitelist = compiler:get_invocations(vars.whitelist)
- |for _,w in ipairs(whitelist) do
- | if not compiler.defs[w] then
- | compiler:error("Undefined function: "..tostring(w))
- | end
- |end
- |for _,fn in ipairs(fns) do
- | local fn_info = compiler.defs[fn]
- | if fn_info == nil then
- | compiler:error("Undefined function: "..tostring(fn))
- | elseif fn_info.whiteset == nil then
- | compiler:writeln("Function is already allowed by everyone: "..tostring(fn))
- | elseif not compiler:check_permission(fn) then
- | compiler:writeln("You do not have permission to grant permissions for function: "..tostring(fn))
- | else
- | for _,w in ipairs(whitelist) do
- | fn_info.whiteset[w] = true
- | end
- | end
- |end
-
-rule [forbid %blacklist to use %fn] =:
- lua block ".."
- |local fns = compiler:get_invocations(vars.fn)
- |local blacklist = compiler:get_invocations(vars.blacklist)
- |for _,b in ipairs(blacklist) do
- | if not compiler.defs[b] then
- | compiler:error("Undefined function: "..tostring(b))
- | end
- |end
- |for _,fn in ipairs(fns) do
- | local fn_info = compiler.defs[fn]
- | if fn_info == nil then
- | compiler:error("Undefined function: "..tostring(fn))
- | elseif fn_info.whiteset == nil then
- | compiler:writeln("Cannot remove items from a whitelist when there is no whitelist on function: "..tostring(fn))
- | elseif not compiler:check_permission(fn) then
- | compiler:writeln("You do not have permission to restrict function: "..tostring(fn))
- | else
- | for _,b in ipairs(blacklist) do fn_info.whiteset[b] = nil end
- | end
- |end
-
-# For unit testing
-macro [parse tree %code] =:
- lua expr "compiler.utils.repr(compiler:stringify_tree(vars.code.value), true)"
-
-macro block [test %code yields %expected] =:
- %generated =: lua expr "compiler.utils.repr(compiler:stringify_tree(vars.code.value), true)"
- %expected =: %expected as lua expr
- if (%generated != %expected):
- say "Test failed!"
- say "Expected:"
- say %expected
- say "But got:"
- say %generated
- error!
- return: ""
-
+require "lib/metaprogramming.nom"
+require "lib/utils.nom"
+require "lib/operators.nom"
+require "lib/control_flow.nom"
+require "lib/collections.nom"
+require "lib/permissions.nom"
diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom
new file mode 100644
index 0000000..d2f8fb6
--- /dev/null
+++ b/lib/metaprogramming.nom
@@ -0,0 +1,105 @@
+
+#..
+ This File contains rules for making rules and macros and some helper functions to make
+ that easier.
+
+# Macros:
+
+# macro block [macro block %spec = %user_macro] =: ..
+lua block ".."
+ |compiler:defmacro("macro block %spec = %user_macro", (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)
+ | local user_macro = compiler:tree_to_lua(vars.user_macro)
+ # Then produce a block of code that creates the macro at runtime
+ | local lua = [[
+ |compiler:defmacro(%s, (function(compiler, vars, kind)
+ | if kind == "Expression" then
+ | compiler:error("Macro "..%s.." was defined to be a block, but is being used as an expression")
+ | end
+ | local user_macro = %s
+ | return ("do\\n"..user_macro(compiler, vars).."\\nend"), true
+ |end), %s)
+ |]]
+ | lua = lua:format(spec, compiler.utils.repr(spec), user_macro, src)
+ | return lua, true
+ |end), "N/A")
+
+macro block [macro %spec = %user_macro] =:
+ ".."|compiler:defmacro(
+ | \lua expr "compiler:get_invocations_from_definition(vars.spec, vars)"\,
+ | \lua expr "compiler:tree_to_lua(vars.user_macro, 'Expression')"\,
+ | \lua expr "compiler.utils.repr(vars.user_macro.src)"\)
+
+macro [compiler] =: "compiler"
+macro [compiler's %key] =: ".."|compiler[\%key as lua expr\]
+macro [compiler %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:"..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, ", ")..")"
+
+# 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
+ 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] =: ".."
+ |compiler:def(
+ | \compiler utils "repr" [compiler "get_invocations_from_definition" [%spec, lua expr "vars"]]\,
+ | \compiler "tree_to_lua" [%body, lua expr "vars"]\,
+ | \compiler utils "repr" [lua expr "vars.body.src"]\)
+
+# Get the source code for a function
+rule [help %invocation] =:
+ lua block ".."
+ |local fn_info = compiler.defs[vars.invocation]
+ |if not fn_info then
+ | compiler:writeln("Function not found: "..compiler.utils.repr(vars.invocation))
+ |else
+ | compiler:writeln("rule "..compiler.utils.repr(fn_info.invocations).." ="..(fn_info.src or ":\\n <unknown source code>"))
+ |end
+
+
+# Macro helper functions
+rule [%tree as value] =:
+ lua expr "compiler:tree_to_value(vars.tree, vars)"
+
+rule [%tree as lua block] =:
+ lua expr "compiler:tree_to_lua(vars.tree, 'Statement')"
+
+rule [%tree as lua expr] =:
+ lua expr "compiler:tree_to_lua(vars.tree, 'Expression')"
+
+# Compiler tools
+rule [eval %code, run %code] =: compiler "run" [%code]
+
+rule [source code from tree %tree] =:
+ lua block ".."
+ |local _,_,leading_space = vars.tree.src:find("\\n(%s*)%S")
+ |if leading_space then
+ | local chunk1, chunk2 = vars.tree.src:match(":%s*([^\\n]*)(\\n.*)")
+ | chunk2 = chunk2:gsub("\\n"..leading_space, "\\n")
+ | return chunk1..chunk2.."\\n"
+ |else
+ | return vars.tree.src:match(":%s*(%S.*)").."\\n"
+ |end
+
+macro [source code %body] =:
+ compiler utils "repr" [compiler "call" ["source code from tree %", %body]]
+
+macro [parse tree %code] =:
+ compiler utils "repr" [compiler "stringify_tree" [lua expr "vars.code.value"]]
+
diff --git a/lib/moonscript.nom b/lib/moonscript.nom
new file mode 100644
index 0000000..1b7bf2d
--- /dev/null
+++ b/lib/moonscript.nom
@@ -0,0 +1,30 @@
+require "lib/metaprogramming.nom"
+
+# Moonscript!
+macro block [moonscript block %moonscript_code] =:
+ lua block ".."
+ |local parse, compile = require('moonscript.parse'), require('moonscript.compile')
+ |local moon_code = compiler:tree_to_value(vars.moonscript_code, vars)
+ |local tree, err = parse.string(moon_code)
+ |if not tree then
+ | compiler:error("Failed to parse moonscript: "..err)
+ |end
+ |local lua_code, err, pos = compile.tree(tree)
+ |if not lua_code then
+ | compiler:error(compile.format_error(err, pos, moon_code))
+ |end
+ |return "do\\n"..lua_code.."\\nend"
+
+macro [moonscript %moonscript_code] =:
+ lua block ".."
+ |local parse, compile = require('moonscript.parse'), require('moonscript.compile')
+ |local moon_code = compiler:tree_to_value(vars.moonscript_code, vars)
+ |local tree, err = parse.string(moon_code)
+ |if not tree then
+ | compiler:error("Failed to parse moonscript: "..err)
+ |end
+ |local lua_code, err, pos = compile.tree(tree)
+ |if not lua_code then
+ | compiler:error(compile.format_error(err, pos, moon_code))
+ |end
+ |return "(function(compiler, vars)\\n"..lua_code.."\\nend)(compiler, vars)"
diff --git a/lib/operators.nom b/lib/operators.nom
new file mode 100644
index 0000000..f6fea36
--- /dev/null
+++ b/lib/operators.nom
@@ -0,0 +1,125 @@
+require "lib/metaprogramming.nom"
+
+# Literals
+macro [true, yes] =: "true"
+macro [false, no] =: "false"
+macro [nil, null] =: "nil"
+macro block [nop, pass] =: ""
+
+# Ternary operator
+macro [%if_expr if %condition else %else_expr] =:
+ pass # TODO: Fix compiler bug that doesn't parse right here
+ #.. Note: this uses a function instead of (condition and if_expr or else_expr)
+ because that breaks if %if_expr is falsey.
+ ".."|(function(compiler, vars)
+ | if \%condition as lua expr\ then
+ | return \%if_expr as lua expr\
+ | else
+ | return \%else_expr as lua expr\
+ | end
+ |end)(compiler, vars)
+
+# Variable assignment operator, and += type versions
+lua block ".."
+ |local function helper(callback)
+ | return function(compiler, vars, kind)
+ | if kind == "Expression" then
+ | compiler:error("Cannot use an assignment operation as an expression value.")
+ | end
+ | if vars.var.type ~= "Var" then
+ | compiler:error("Assignment operation has the wrong type for the left hand side. "
+ | .."Expected Var, but got: "..vars.var.type.."\\nMaybe you forgot a percent sign on the variable name?")
+ | end
+ | if vars.rhs.type ~= "Thunk" then
+ | compiler:error("Assignment operation has the wrong type for the right hand side. "
+ | .."Expected Thunk, but got: "..vars.rhs.type.."\\nMaybe you used '=' instead of '=:'?")
+ | end
+ | if #vars.rhs.value.value > 1 then
+ | compiler:error("Assignment operation should not have more than one value on the right hand side.")
+ | end
+ | return callback(compiler:tree_to_lua(vars.var, "Expression"),
+ | compiler:tree_to_lua(vars.rhs.value.value[1].value, "Expression")), true
+ | end
+ |end
+ |compiler:defmacro("%var = %rhs", helper(function(var,result) return var.." = "..result end))
+ |compiler:defmacro("%var += %rhs", helper(function(var,result) return var.." = "..var.." + "..result end))
+ |compiler:defmacro("%var -= %rhs", helper(function(var,result) return var.." = "..var.." - "..result end))
+ |compiler:defmacro("%var *= %rhs", helper(function(var,result) return var.." = "..var.." * "..result end))
+ |compiler:defmacro("%var /= %rhs", helper(function(var,result) return var.." = "..var.." / "..result end))
+ |compiler:defmacro("%var ^= %rhs", helper(function(var,result) return var.." = "..var.." ^ "..result end))
+ |compiler:defmacro("%var and= %rhs", helper(function(var,result) return var.." = "..var.." and "..result end))
+ |compiler:defmacro("%var or= %rhs", helper(function(var,result) return var.." = "..var.." or "..result end))
+ |compiler:defmacro("%var join= %rhs", helper(function(var,result) return var.." = "..var.." .. "..result end))
+ |compiler:defmacro("%var mod= %rhs", helper(function(var,result) return var.." = "..var.." % "..result end))
+
+# Binary Operators
+lua block ".."
+ |local binops = {"+","-","*","/","<","<=",">",">=","^",{"===","=="},{"!==","~="},"and","or",{"mod","%"}}
+ |for _,op in ipairs(binops) do
+ | local nomsu_alias = op
+ | if type(op) == 'table' then
+ | nomsu_alias, op = unpack(op)
+ | end
+ | compiler:defmacro("%a "..nomsu_alias.." %b", (function(compiler, vars, kind)
+ | return "("..compiler:tree_to_lua(vars.a).." "..op.." "..compiler:tree_to_lua(vars.b)..")"
+ | end), [[".."|(\\%a as lua expr\\ ]]..op..[[ \\%b as lua expr\\)]])
+ |end
+# == and != do equivalence checking, rather than identity checking
+macro [%a == %b] =: ".."|compiler.utils.equivalent(\%a as lua expr\, \%b as lua expr\)
+macro [%a != %b] =: ".."|(not compiler.utils.equivalent(\%a as lua expr\, \%b as lua expr\))
+
+# Commutative Operators defined for up to 10 operands
+lua block ".."
+ |local comops = {"+","*","and","or"}
+ |for _,_op in ipairs(comops) do
+ | local op = _op
+ | local spec = "%1 "..op.." %2"
+ | for i=3,10 do
+ | spec = spec .. " %"..tostring(i)
+ | compiler:defmacro(spec, (function(compiler, vars, kind)
+ | local bits = {}
+ | for _,v in ipairs(vars) do
+ | table.insert(bits, compiler:tree_to_lua(v))
+ | end
+ | return "("..table.concat(bits, " "..op.." ")..")"
+ | end))
+ | end
+ |end
+
+# Chained compairsions (e.g. x < y <= z < w) are defined up to 10 operands
+lua block ".."
+ |for _,chainers in ipairs{{"<","<="},{">",">="}} do
+ | local function recurse(chain)
+ # The 1-op versions are already more efficiently defined, and a 0-op version doesnt make sense
+ | if #chain >= 2 then
+ | local spec = "%1"
+ | for i,op in ipairs(chain) do
+ | spec = spec .. " "..op.." %"..tostring(i+1)
+ | end
+ # Chained comparisons need to be functions to avoid re-evaluating their arguments :\
+ | compiler:def(spec, function(compiler, vars)
+ | for i,op in ipairs(chain) do
+ | local a, b, result = vars[i], vars[i+1]
+ | if op == "<" then result = a < b
+ | elseif op == "<=" then result = a <= b
+ | elseif op == ">" then result = a > b
+ | elseif op == ">=" then result = a >= b end
+ # Short circuit
+ | if not result then return false end
+ | end
+ | end)
+ | end
+ # 9 operators == 10 operands, so don't continue any further
+ | if #chain >= 9 then return end
+ | for _,c in ipairs(chainers) do
+ | table.insert(chain, c)
+ | recurse(chain)
+ | table.remove(chain)
+ | end
+ | end
+ | recurse({})
+ |end
+
+# Unary operators
+macro [- %a] =: ".."|-(\%a as lua expr\)
+macro [not %a] =: ".."|not (\%a as lua expr\)
diff --git a/lib/permissions.nom b/lib/permissions.nom
new file mode 100644
index 0000000..4b6c428
--- /dev/null
+++ b/lib/permissions.nom
@@ -0,0 +1,42 @@
+require "lib/metaprogramming.nom"
+require "lib/control_flow.nom"
+require "lib/operators.nom"
+require "lib/collections.nom"
+
+# Permission functions
+rule [restrict %rules to within %elite-rules] =:
+ %rules =: compiler "get_invocations" [%rules]
+ %elite-rules =: compiler "get_invocations" [%elite-rules]
+ for all (flatten [%elite-rules, %rules]):
+ assert ((compiler's "defs") has %it) ".."|Undefined function: \%it\
+ for all %rules:
+ assert (not (compiler "check_permission" [%it])) ".."
+ |You do not have permission to restrict permissions for function: \%it\
+ ((compiler's "defs")'s %it)'s "whiteset" =: dict (..)
+ [%it, (yes)] for %it in %elite-rules
+
+rule [allow %elite-rules to use %rules] =:
+ %rules =: compiler "get_invocations" [%rules]
+ %elite-rules =: compiler "get_invocations" [%elite-rules]
+ for all (flatten [%elite-rules, %rules]):
+ assert ((compiler's "defs") has %it) ".."|Undefined function: \%it\
+ for %fn in %rules:
+ assert (not (compiler "check_permission" [%fn])) ".."
+ |You do not have permission to grant permissions for function: \%fn\
+ %whiteset =: ((compiler's "defs")'s %fn)'s "whiteset"
+ if (not %whiteset): on to the next %fn
+ for all %elite-rules: %whiteset's %it =: yes
+
+rule [forbid %pleb-rules to use %rules] =:
+ %rules =: compiler "get_invocations" [%rules]
+ %pleb-rules =: compiler "get_invocations" [%pleb-rules]
+ for all (flatten [%pleb-rules, %used]):
+ assert ((compiler's "defs") has %it) ".."|Undefined function: \%it\
+ for all %rules:
+ assert (not (compiler "check_permission" [%it])) ".."
+ |You do not have permission to grant permissions for function: \%it\
+ %whiteset =: ((compiler's "defs")'s %it)'s "whiteset"
+ assert %whiteset ".."
+ |Cannot individually restrict permissions for \%it\ because it is currently
+ |available to everyone. Perhaps you meant to use "restrict % to within %" instead?
+ for all %pleb-rules: %whiteset's %it =: nil
diff --git a/lib/testing.nom b/lib/testing.nom
new file mode 100644
index 0000000..48b311f
--- /dev/null
+++ b/lib/testing.nom
@@ -0,0 +1,14 @@
+require "lib/metaprogramming.nom"
+
+# For unit testing
+macro block [test %code yields %expected] =:
+ %generated =: lua expr "compiler.utils.repr(compiler:stringify_tree(vars.code.value))"
+ %expected =: %expected as lua expr
+ if (%generated != %expected):
+ say "Test failed!"
+ say "Expected:"
+ say %expected
+ say "But got:"
+ say %generated
+ error!
+ return ""
diff --git a/lib/utils.nom b/lib/utils.nom
new file mode 100644
index 0000000..57b8dfc
--- /dev/null
+++ b/lib/utils.nom
@@ -0,0 +1,85 @@
+require "lib/metaprogramming.nom"
+
+# Error functions
+rule [error!, panic!, fail!, abort!] =:
+ compiler "error"[]
+rule [error %msg] =:
+ compiler "error"[%msg]
+macro block [assert %condition] =: ".."
+ |if not (\%condition as lua expr\) then
+ | compiler:error()
+ |end
+macro block [assert %condition %msg] =: ".."
+ |if not (\%condition as lua expr\) then
+ | compiler:error(\%msg as lua expr\)
+ |end
+
+macro block [show generated lua %block] =: ".."
+ |compiler:writeln(\lua expr "compiler.utils.repr(compiler:tree_to_lua(vars.block.value, 'Statement'))"\)
+
+
+# String functions
+rule [join %strs] =:
+ lua block ".."
+ |local str_bits = {}
+ |for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr_if_not_string(bit) end
+ |return table.concat(str_bits)
+
+rule [join %strs with glue %glue] =:
+ lua block ".."
+ |local str_bits = {}
+ |for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr_if_not_string(bit) end
+ |return table.concat(str_bits, vars.glue)
+
+macro [capitalize %str, %str capitalized] =: ".."
+ |(\%str as lua expr\):gsub("%l", string.upper, 1)
+
+macro [repr %obj] =:
+ ".."|compiler.utils.repr(\%obj as lua expr\)
+
+macro [%obj as string] =:
+ ".."|compiler.utils.repr_if_not_string(\%obj as lua expr\)
+
+macro [say %str] =:
+ ".."|compiler:writeln(compiler.utils.repr_if_not_string(\%str as lua expr\))
+
+# Number ranges
+macro [%start up to %stop] =: ".."
+ |compiler.utils.range(\%start as lua expr\, \%stop as lua expr\-1)
+macro [%start thru %stop, %start through %stop] =: ".."
+ |compiler.utils.range(\%start as lua expr\, \%stop as lua expr\)
+macro [%start down to %stop] =: ".."
+ |compiler.utils.range(\%start as lua expr\, \%stop as lua expr\+1,-1)
+macro [%start down thru %stop, %start down through %stop] =: ".."
+ |compiler.utils.range(\%start as lua expr\, \%stop as lua expr\,-1)
+macro [%start up to %stop via %step] =: ".."
+ |compiler.utils.range(\%start as lua expr\,\%stop as lua expr\-1,vars.step)
+macro [%start thru %stop via %step, %start through %stop via %step] =: ".."
+ |compiler.utils.range(\%start as lua expr\,\%stop as lua expr\,vars.step)
+macro [%start down to %stop via %step] =: ".."
+ |compiler.utils.range(\%start as lua expr\,\%stop as lua expr\+1,-vars.step)
+macro [%start down thru %stop via %step, %start down through %stop via %step] =: ".."
+ |compiler.utils.range(\%start as lua expr\,\%stop as lua expr\,-vars.step)
+
+# Common utility functions
+macro [random number, random, rand] =: "math.random()"
+macro [random int %n, random integer %n, randint %n] =: ".."|math.random(\%n as lua expr\)
+macro [random from %low to %high, random number from %low to %high, rand %low %high] =: ".."
+ |math.random(\%low as lua expr\, \%high as lua expr\)
+rule [random choice from %elements, random choice %elements, random %elements] =:
+ lua expr ".."|vars.elements[math.random(#vars.elements)]
+macro [sum of %items, sum %items] =: ".."|compiler.utils.sum(\%items as lua expr\)
+macro [product of %items, product %items] =: ".."|compiler.utils.product(\%items as lua expr\)
+macro [all of %items] =: ".."|compiler.utils.all(\%items as lua expr\)
+macro [any of %items] =: ".."|compiler.utils.any(\%items as lua expr\)
+# This is a rule, not a macro so we can use vars.items twice without running it twice.
+rule [avg of %items, average of %items] =:
+ lua expr ".."|(compiler.utils.sum(vars.items)/#vars.items)
+macro [min of %items, smallest of %items, lowest of %items] =:
+ ".."|compiler.utils.min(\%items as lua expr\)
+macro [max of %items, biggest of %items, largest of %items, highest of %items] =:
+ ".."|compiler.utils.min(\%items as lua expr\)
+macro [min of %items with respect to %keys] =:
+ ".."|compiler.utils.min(\%items as lua expr\, \%keys as lua expr\)
+macro [max of %items with respect to %keys] =:
+ ".."|compiler.utils.max(\%items as lua expr\, \%keys as lua expr\)
diff --git a/nomsu.lua b/nomsu.lua
index c2b1004..108cbd3 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -38,8 +38,11 @@ do
if self.debug then
self:writeln("Calling " .. tostring(fn_name) .. " with args: " .. tostring(utils.repr(args)))
end
- local ret = fn(self, args)
+ local ok, ret = pcall(fn, self, args)
table.remove(self.callstack)
+ if not ok then
+ error(ret)
+ end
return ret
end,
check_permission = function(self, fn_name)
@@ -81,7 +84,6 @@ do
return self:tree_to_value(def, vars)
end
if def.type ~= "List" then
- error("DEF IS: " .. tostring(utils.repr(def)))
self:error("Trying to get invocations from " .. tostring(def.type) .. ", but expected List or String.")
end
local invocations = { }
@@ -191,7 +193,7 @@ do
local _exp_0 = type(obj)
if "function" == _exp_0 then
error("Function serialization is not yet implemented.")
- return "assert(load(" .. utils.repr(string.dump(obj), true) .. "))"
+ return "assert(load(" .. utils.repr(string.dump(obj)) .. "))"
elseif "table" == _exp_0 then
if utils.is_list(obj) then
return "{" .. tostring(table.concat((function()
@@ -216,9 +218,9 @@ do
end)(), ", ")) .. "}"
end
elseif "number" == _exp_0 then
- return utils.repr(obj, true)
+ return utils.repr(obj)
elseif "string" == _exp_0 then
- return utils.repr(obj, true)
+ return utils.repr(obj)
else
return error("Serialization not implemented for: " .. tostring(type(obj)))
end
@@ -441,14 +443,22 @@ do
local _list_0 = tree.value.body.value
for _index_0 = 1, #_list_0 do
local statement = _list_0[_index_0]
- local code = to_lua(statement, "Statement")
+ local ok, code = pcall(to_lua, statement, "Statement")
+ if not ok then
+ self:writeln("Error occurred in statement:\n" .. tostring(statement.src))
+ error(code)
+ end
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)))
end
local value = lua_thunk()
- return_value = value(self, vars)
+ ok, return_value = pcall(value, self, vars)
+ if not ok then
+ self:writeln("Error occurred in statement:\n" .. tostring(statement.src))
+ error(return_value)
+ end
add(code)
end
add([[ return ret
@@ -497,7 +507,7 @@ do
end
args = _accum_0
end
- table.insert(args, 1, utils.repr(name, true))
+ table.insert(args, 1, utils.repr(name))
add(self.__class:comma_separated_items("compiler:call(", args, ")"))
end
elseif "String" == _exp_0 then
@@ -513,7 +523,7 @@ do
local unescaped = tree.value:gsub("\\(.)", (function(c)
return escapes[c] or c
end))
- add(utils.repr(unescaped, true))
+ add(utils.repr(unescaped))
elseif "Longstring" == _exp_0 then
local concat_parts = { }
local string_buffer = ""
@@ -527,15 +537,15 @@ do
string_buffer = string_buffer .. bit:gsub("\\\\", "\\")
else
if string_buffer ~= "" then
- table.insert(concat_parts, utils.repr(string_buffer, true))
+ table.insert(concat_parts, utils.repr(string_buffer))
string_buffer = ""
end
- table.insert(concat_parts, "compiler.utils.repr(" .. tostring(to_lua(bit)) .. ")")
+ table.insert(concat_parts, "compiler.utils.repr_if_not_string(" .. tostring(to_lua(bit)) .. ")")
end
end
end
if string_buffer ~= "" then
- table.insert(concat_parts, utils.repr(string_buffer, true))
+ table.insert(concat_parts, utils.repr(string_buffer))
end
if #concat_parts == 0 then
add("''")
@@ -565,7 +575,7 @@ do
end)(), "}"))
end
elseif "Var" == _exp_0 then
- add("vars[" .. tostring(utils.repr(tree.value, true)) .. "]")
+ add("vars[" .. tostring(utils.repr(tree.value)) .. "]")
else
self:error("Unknown/unimplemented thingy: " .. tostring(tree.type))
end
@@ -588,6 +598,16 @@ do
end
return table.concat(name_bits, " ")
end,
+ var_to_lua_identifier = function(self, var)
+ if var.type ~= "Var" then
+ self:error("Tried to convert something that wasn't a Var into a lua identifier: it was not a Var, it was: " .. label.type)
+ end
+ local identifier = "var_"
+ for i = 1, #var.value do
+ identifier = identifier .. ("%x"):format(string.byte(var.value, i))
+ end
+ return identifier
+ end,
run_macro = function(self, tree, kind)
if kind == nil then
kind = "Expression"
@@ -687,9 +707,9 @@ do
end
end
elseif "String" == _exp_0 then
- coroutine.yield(ind(utils.repr(tree.value, true)))
+ coroutine.yield(ind(utils.repr(tree.value)))
elseif "Longstring" == _exp_0 then
- coroutine.yield(ind(utils.repr(tree.value, true)))
+ coroutine.yield(ind(utils.repr(tree.value)))
elseif "Number" == _exp_0 then
coroutine.yield(ind(tree.value))
elseif "List" == _exp_0 then
@@ -791,7 +811,7 @@ do
end
local inner_vars = setmetatable({ }, {
__index = function(_, key)
- return "vars[" .. tostring(utils.repr(key, true)) .. "]"
+ return "vars[" .. tostring(utils.repr(key)) .. "]"
end
})
return "do\n" .. self:tree_to_value(vars.lua_code, inner_vars) .. "\nend", true
@@ -800,7 +820,7 @@ do
local lua_code = vars.lua_code.value
local inner_vars = setmetatable({ }, {
__index = function(_, key)
- return "vars[" .. tostring(utils.repr(key, true)) .. "]"
+ return "vars[" .. tostring(utils.repr(key)) .. "]"
end
})
return self:tree_to_value(vars.lua_code, inner_vars)
@@ -920,7 +940,7 @@ elseif arg then
return c:run(buff)
end)
if ok and ret ~= nil then
- print("= " .. utils.repr(ret, true))
+ print("= " .. utils.repr(ret))
end
end
end
diff --git a/nomsu.moon b/nomsu.moon
index 7f623cf..549684f 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -7,8 +7,9 @@ utils = require 'utils'
-- improve indentation of generated lua code
-- provide way to run precompiled nomsu -> lua code
-- better scoping?
--- first-class functions
+-- first-class rules
-- better error reporting
+-- add line numbers of function calls
-- versions of rules with auto-supplied arguments
-- type checking?
@@ -44,8 +45,10 @@ class NomsuCompiler
args = {name, select(i,...) for i,name in ipairs(arg_names[fn_name])}
if @debug
@writeln "Calling #{fn_name} with args: #{utils.repr(args)}"
- ret = fn(self, args)
+ ok,ret = pcall(fn, self, args)
table.remove @callstack
+ if not ok
+ error(ret)
return ret
check_permission: (fn_name)=>
@@ -71,7 +74,6 @@ class NomsuCompiler
return @tree_to_value(def, vars)
if def.type != "List"
- error "DEF IS: #{utils.repr(def)}"
@error "Trying to get invocations from #{def.type}, but expected List or String."
invocations = {}
@@ -134,16 +136,16 @@ class NomsuCompiler
switch type(obj)
when "function"
error("Function serialization is not yet implemented.")
- "assert(load("..utils.repr(string.dump(obj), true).."))"
+ "assert(load("..utils.repr(string.dump(obj)).."))"
when "table"
if utils.is_list obj
"{#{table.concat([@serialize(i) for i in *obj], ", ")}}"
else
"{#{table.concat(["[#{@serialize(k)}]= #{@serialize(v)}" for k,v in pairs(obj)], ", ")}}"
when "number"
- utils.repr(obj, true)
+ utils.repr(obj)
when "string"
- utils.repr(obj, true)
+ utils.repr(obj)
else
error "Serialization not implemented for: #{type(obj)}"
@@ -329,7 +331,10 @@ class NomsuCompiler
local ret]]
vars = {}
for statement in *tree.value.body.value
- code = to_lua(statement, "Statement")
+ ok,code = pcall(to_lua, statement, "Statement")
+ if not ok
+ @writeln "Error occurred in statement:\n#{statement.src}"
+ error(code)
-- Run the fuckers as we go
lua_code = "
return (function(compiler, vars)\n#{code}\nend)"
@@ -337,7 +342,10 @@ class NomsuCompiler
if not lua_thunk
error("Failed to compile generated code:\n#{code}\n\n#{err}\n\nProduced by statement:\n#{utils.repr(statement)}")
value = lua_thunk!
- return_value = value(self, vars)
+ ok,return_value = pcall(value, self, vars)
+ if not ok
+ @writeln "Error occurred in statement:\n#{statement.src}"
+ error(return_value)
add code
add [[
return ret
@@ -375,13 +383,13 @@ class NomsuCompiler
add @run_macro(tree, "Expression")
else
args = [to_lua(a) for a in *tree.value when a.type != "Word"]
- table.insert args, 1, utils.repr(name, true)
+ table.insert args, 1, utils.repr(name)
add @@comma_separated_items("compiler:call(", args, ")")
when "String"
escapes = n:"\n", t:"\t", b:"\b", a:"\a", v:"\v", f:"\f", r:"\r"
unescaped = tree.value\gsub("\\(.)", ((c)-> escapes[c] or c))
- add utils.repr(unescaped, true)
+ add utils.repr(unescaped)
when "Longstring"
concat_parts = {}
@@ -393,12 +401,12 @@ class NomsuCompiler
string_buffer ..= bit\gsub("\\\\","\\")
else
if string_buffer ~= ""
- table.insert concat_parts, utils.repr(string_buffer, true)
+ table.insert concat_parts, utils.repr(string_buffer)
string_buffer = ""
- table.insert concat_parts, "compiler.utils.repr(#{to_lua(bit)})"
+ table.insert concat_parts, "compiler.utils.repr_if_not_string(#{to_lua(bit)})"
if string_buffer ~= ""
- table.insert concat_parts, utils.repr(string_buffer, true)
+ table.insert concat_parts, utils.repr(string_buffer)
if #concat_parts == 0
add "''"
@@ -419,7 +427,7 @@ class NomsuCompiler
add @@comma_separated_items("{", [to_lua(item) for item in *tree.value], "}")
when "Var"
- add "vars[#{utils.repr(tree.value,true)}]"
+ add "vars[#{utils.repr(tree.value)}]"
else
@error("Unknown/unimplemented thingy: #{tree.type}")
@@ -450,6 +458,14 @@ class NomsuCompiler
for token in *tree.value
table.insert name_bits, if token.type == "Word" then token.value else "%"
table.concat(name_bits, " ")
+
+ var_to_lua_identifier: (var)=>
+ 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)
+ identifier = "var_"
+ for i=1,#var.value
+ identifier ..= ("%x")\format(string.byte(var.value, i))
+ return identifier
run_macro: (tree, kind="Expression")=>
name = @fn_name_from_tree(tree)
@@ -502,11 +518,11 @@ class NomsuCompiler
when "String"
-- TODO: Better implement
- coroutine.yield(ind(utils.repr(tree.value, true)))
+ coroutine.yield(ind(utils.repr(tree.value)))
when "Longstring"
-- TODO: Better implement
- coroutine.yield(ind(utils.repr(tree.value, true)))
+ coroutine.yield(ind(utils.repr(tree.value)))
when "Number"
coroutine.yield(ind(tree.value))
@@ -588,12 +604,12 @@ class NomsuCompiler
@defmacro [[lua block %lua_code]], (vars, kind)=>
if kind == "Expression" then error("Expected to be in statement.")
- inner_vars = setmetatable({}, {__index:(_,key)-> "vars[#{utils.repr(key,true)}]"})
+ inner_vars = setmetatable({}, {__index:(_,key)-> "vars[#{utils.repr(key)}]"})
return "do\n"..@tree_to_value(vars.lua_code, inner_vars).."\nend", true
@defmacro [[lua expr %lua_code]], (vars, kind)=>
lua_code = vars.lua_code.value
- inner_vars = setmetatable({}, {__index:(_,key)-> "vars[#{utils.repr(key,true)}]"})
+ inner_vars = setmetatable({}, {__index:(_,key)-> "vars[#{utils.repr(key)}]"})
return @tree_to_value(vars.lua_code, inner_vars)
@def "require %filename", (vars)=>
@@ -655,6 +671,6 @@ elseif arg
break
ok, ret = pcall(-> c\run(buff))
if ok and ret != nil
- print "= "..utils.repr(ret, true)
+ print "= "..utils.repr(ret)
return NomsuCompiler
diff --git a/utils.lua b/utils.lua
index b50a88f..ddd2c3c 100644
--- a/utils.lua
+++ b/utils.lua
@@ -10,10 +10,7 @@ utils = {
end
return true
end,
- repr = function(x, add_quotes)
- if add_quotes == nil then
- add_quotes = false
- end
+ repr = function(x)
local _exp_0 = type(x)
if 'table' == _exp_0 then
local mt = getmetatable(x)
@@ -25,7 +22,7 @@ utils = {
local _len_0 = 1
for _index_0 = 1, #x do
local i = x[_index_0]
- _accum_0[_len_0] = utils.repr(i, true)
+ _accum_0[_len_0] = utils.repr(i)
_len_0 = _len_0 + 1
end
return _accum_0
@@ -35,18 +32,16 @@ utils = {
local _accum_0 = { }
local _len_0 = 1
for k, v in pairs(x) do
- _accum_0[_len_0] = "[" .. tostring(utils.repr(k, true)) .. "]= " .. tostring(utils.repr(v, true))
+ _accum_0[_len_0] = "[" .. tostring(utils.repr(k)) .. "]= " .. tostring(utils.repr(v))
_len_0 = _len_0 + 1
end
return _accum_0
end)(), ", ")) .. "}"
end
elseif 'string' == _exp_0 then
- if not add_quotes then
- return x
- elseif not x:find([["]]) and not x:find("\n") then
+ if not x:find([["]]) and not x:find("\n") and not x:find("\\") then
return "\"" .. x .. "\""
- elseif not x:find([[']]) and not x:find("\n") then
+ elseif not x:find([[']]) and not x:find("\n") and not x:find("\\") then
return "\'" .. x .. "\'"
else
for i = 0, math.huge do
@@ -64,6 +59,13 @@ utils = {
return tostring(x)
end
end,
+ repr_if_not_string = function(x)
+ if type(x) == 'string' then
+ return x
+ else
+ return utils.repr(x)
+ end
+ end,
split = function(str, sep)
if sep == nil then
sep = "%s"
diff --git a/utils.moon b/utils.moon
index b46bf44..3b3573c 100644
--- a/utils.moon
+++ b/utils.moon
@@ -7,22 +7,20 @@ utils = {
i += 1
return true
- repr: (x, add_quotes=false)->
+ repr: (x)->
switch type(x)
when 'table'
mt = getmetatable(x)
if mt and mt.__tostring
mt.__tostring(x)
elseif utils.is_list x
- "{#{table.concat([utils.repr(i, true) for i in *x], ", ")}}"
+ "{#{table.concat([utils.repr(i) for i in *x], ", ")}}"
else
- "{#{table.concat(["[#{utils.repr(k, true)}]= #{utils.repr(v, true)}" for k,v in pairs x], ", ")}}"
+ "{#{table.concat(["[#{utils.repr(k)}]= #{utils.repr(v)}" for k,v in pairs x], ", ")}}"
when 'string'
- if not add_quotes
- x
- elseif not x\find[["]] and not x\find"\n"
+ if not x\find[["]] and not x\find"\n" and not x\find"\\"
"\""..x.."\""
- elseif not x\find[[']] and not x\find"\n"
+ elseif not x\find[[']] and not x\find"\n" and not x\find"\\"
"\'"..x.."\'"
else
for i=0,math.huge
@@ -36,6 +34,10 @@ utils = {
else
tostring(x)
+ repr_if_not_string: (x)->
+ if type(x) == 'string' then x
+ else utils.repr(x)
+
split: (str, sep="%s")->
[chunk for chunk in str\gmatch("[^#{sep}]+")]