nomsu/core.nom

397 lines
15 KiB
Plaintext
Raw Normal View History

# Rule for making rules
2017-09-12 20:00:19 -07:00
lua block ".."
|compiler:def("rule %spec %body", function(compiler, vars)
| return compiler:def(vars.spec, vars.body)
|end)
# Macros
2017-09-12 20:00:19 -07:00
rule "macro %spec %body":
lua block ".."
|compiler:defmacro(vars.spec, vars.body)
rule "macro block %spec %body":
lua block ".."
2017-09-18 12:34:10 -07:00
|local spec, body = vars.spec, vars.body
|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.")
2017-09-12 20:00:19 -07:00
| end
2017-09-18 12:34:10 -07:00
| return ("do\\n"..body(compiler, vars, kind).."\\nend"), true
2017-09-12 20:00:19 -07:00
|end
2017-09-18 12:34:10 -07:00
|compiler:defmacro(spec, wrapper)
|return nil
2017-09-12 20:00:19 -07:00
# Compiler tools
2017-09-12 20:00:19 -07:00
rule ["eval %code", "run %code"]:
lua expr "compiler:run(vars.code)"
macro "source code %code":
2017-09-18 12:34:10 -07:00
lua expr "compiler.utils.repr(vars.code.src, true)"
2017-09-12 20:00:19 -07:00
rule "run file %filename":
2017-09-14 05:04:27 -07:00
lua block ".."
2017-09-18 12:34:10 -07:00
|local file = io.open(vars.filename)
|return compiler:run(file:read("*a"))
2017-09-12 20:00:19 -07:00
# Macro helper functions
2017-09-12 20:00:19 -07:00
rule "%tree as lua block":
lua block ".."
2017-09-18 12:34:10 -07:00
|return compiler:tree_to_lua(vars.tree, 'Statement'), true
2017-09-12 20:00:19 -07:00
rule "%tree as lua expr":
lua expr ".."
|compiler:tree_to_lua(vars.tree, 'Expression')
# Moonscript!
macro block "moonscript block %moonscript_code":
lua block ".."
2017-09-18 12:34:10 -07:00
|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
2017-09-18 12:34:10 -07:00
|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 ".."
2017-09-18 12:34:10 -07:00
|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
2017-09-18 12:34:10 -07:00
|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)"
2017-09-12 20:00:19 -07:00
# String functions
2017-09-14 05:04:27 -07:00
rule "join %strs":
lua block ".."
|local str_bits = {}
2017-09-18 12:34:10 -07:00
|for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr(bit) end
|return table.concat(str_bits)
2017-09-12 20:00:19 -07:00
2017-09-14 05:04:27 -07:00
rule "join %strs with glue %glue":
lua block ".."
|local str_bits = {}
2017-09-18 12:34:10 -07:00
|for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr(bit) end
|return table.concat(str_bits, vars.glue)
2017-09-12 20:00:19 -07:00
rule ["capitalize %str", "%str capitalized"]:
lua expr ".."|vars.str:gsub("%l", string.upper, 1)
2017-09-12 21:19:19 -07:00
# Variable assignment
2017-09-12 20:00:19 -07:00
macro block "let %varname = %value":
2017-09-14 05:04:27 -07:00
".."|vars[\%varname as lua expr\] = \%value as lua expr\
2017-09-12 20:00:19 -07:00
# Operators
2017-09-12 20:00:19 -07:00
macro ["true","yes"]: "true"
macro ["false","no"]: "false"
macro ["nil","null"]: "nil"
macro block ["nop", "pass"]: ""
2017-09-14 05:04:27 -07:00
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\)
2017-09-12 20:00:19 -07:00
macro "%a and %b and %c":
2017-09-14 05:04:27 -07:00
".."|(\%a as lua expr\ and \%b as lua expr\ and \%c as lua expr\)
2017-09-12 20:00:19 -07:00
macro "%a and %b and %c and %d":
2017-09-14 05:04:27 -07:00
".."|(\%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\)
2017-09-12 20:00:19 -07:00
macro "%a or %b or %c":
2017-09-14 05:04:27 -07:00
".."|(\%a as lua expr\ or \%b as lua expr\ or \%c as lua expr\)
2017-09-12 20:00:19 -07:00
macro "%a or %b or %c or %d":
2017-09-14 05:04:27 -07:00
".."|(\%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\)
2017-09-12 20:00:19 -07:00
rule "%a == %b":
2017-09-18 12:34:10 -07:00
lua expr "((vars.a == vars.b) or compiler.utils.equivalent(vars.a, vars.b))"
rule "%a != %b":
2017-09-18 12:34:10 -07:00
lua expr "((vars.a ~= vars.b) or not compiler.utils.equivalent(vars.a, vars.b))"
2017-09-12 20:00:19 -07:00
2017-09-14 05:04:27 -07:00
macro "say %str":
2017-09-18 12:34:10 -07:00
".."|compiler:writeln(compiler.utils.repr(\%str as lua expr\))
2017-09-12 20:00:19 -07:00
# Control flow
2017-09-12 20:00:19 -07:00
rule "do %action":
lua expr "vars.action(compiler, setmetatable({}, {__index=vars}))"
macro block "return %return-value":
2017-09-18 12:34:10 -07:00
".."|return \%return-value as lua expr\
macro block "return":
2017-09-18 12:34:10 -07:00
"return nil"
# Conditionals
2017-09-12 20:00:19 -07:00
macro block "if %condition %if_body":
2017-09-14 05:04:27 -07:00
".."|if \%condition as lua expr\ then
| \(lua expr "vars.if_body.value.value") as lua block\
|end
2017-09-12 20:00:19 -07:00
macro block "if %condition %if_body else %else_body":
2017-09-14 05:04:27 -07:00
".."|if \%condition as lua expr\ then
| \(lua expr "vars.if_body.value.value") as lua block\
|else
| \(lua expr "vars.else_body.value.value") as lua block\
|end
2017-09-12 20:00:19 -07:00
# Ternary operator
2017-09-12 20:00:19 -07:00
macro "%if_expr if %condition else %else_expr":
2017-09-14 05:04:27 -07:00
".."|(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)
2017-09-12 20:00:19 -07:00
# For loop
2017-09-12 20:00:19 -07:00
macro block "for %varname in %iterable %body":
let "varname" = (%varname as lua expr)
2017-09-18 12:34:10 -07:00
".."|local old_loopval = vars[\%varname\]
|for i,value in ipairs(\%iterable as lua expr\) do
| vars[\%varname\] = value
| \(lua expr "vars.body.value.value") as lua block\
2017-09-14 05:04:27 -07:00
|end
2017-09-18 12:34:10 -07:00
|vars[\%varname\] = old_loopval
2017-09-12 20:00:19 -07:00
macro block "for all %iterable %body":
2017-09-18 12:34:10 -07:00
".."|local old_loopval = vars.it
|for i,value in ipairs(\%iterable as lua expr\) do
| vars.it = value
| \(lua expr "vars.body.value.value") as lua block\
|end
2017-09-18 12:34:10 -07:00
|vars.it = old_loopval
# List Comprehension
# TODO: maybe make this lazy, or a lazy version?
macro "%expression for %varname in %iterable":
".."|(function(game, vars)
| local comprehension = {}
| for i,value in ipairs(\%iterable as lua expr\) do
| vars[\%varname as lua expr\] = value
| comprehension[i] = \%expression as lua expr\
| end
| return comprehension
|end)(game, setmetatable({}, {__index=vars}))
2017-09-14 02:41:10 -07:00
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 %varname in %iterable":
".."|(function(game, vars)
| local comprehension = {}
| for i,value in ipairs(\%iterable as lua expr\) do
| vars[\%varname as lua expr\] = 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
2017-09-12 20:00:19 -07:00
rule "%start up to %stop":
2017-09-18 12:34:10 -07:00
lua expr "compiler.utils.range(vars.start,vars.stop-1)"
2017-09-12 20:00:19 -07:00
rule ["%start thru %stop", "%start through %stop"]:
2017-09-18 12:34:10 -07:00
lua expr "compiler.utils.range(vars.start,vars.stop)"
2017-09-12 20:00:19 -07:00
rule "%start down to %stop":
2017-09-18 12:34:10 -07:00
lua expr "compiler.utils.range(vars.start,vars.stop+1,-1)"
2017-09-12 20:00:19 -07:00
rule ["%start down thru %stop", "%start down through %stop"]:
2017-09-18 12:34:10 -07:00
lua expr "compiler.utils.range(vars.start,vars.stop,-1)"
2017-09-12 20:00:19 -07:00
rule "%start up to %stop via %step":
2017-09-18 12:34:10 -07:00
lua expr "compiler.utils.range(vars.start,vars.stop-1,vars.step)"
rule ["%start thru %stop via %step", "%start through %stop via %step"]:
2017-09-18 12:34:10 -07:00
lua expr "compiler.utils.range(vars.start,vars.stop,vars.step)"
2017-09-12 20:00:19 -07:00
rule "%start down to %stop via %step":
2017-09-18 12:34:10 -07:00
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"]:
2017-09-18 12:34:10 -07:00
lua expr "compiler.utils.range(vars.start,vars.stop,-vars.step)"
# Common utility functions
2017-09-12 20:00:19 -07:00
rule ["random number"]: lua expr "math.random()"
2017-09-18 12:34:10 -07:00
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)"
2017-09-12 20:00:19 -07:00
rule ["min of %items", "smallest of %items", "lowest of %items"]:
2017-09-18 12:34:10 -07:00
lua expr "compiler.utils.min(vars.items)"
2017-09-12 20:00:19 -07:00
rule ["max of %items", "biggest of %items", "largest of %items", "highest of %items"]:
2017-09-18 12:34:10 -07:00
lua expr "compiler.utils.min(vars.items)"
2017-09-12 20:00:19 -07:00
rule ["min of %items with respect to %keys"]:
2017-09-18 12:34:10 -07:00
lua expr "compiler.utils.min(vars.items, vars.keys)"
2017-09-12 20:00:19 -07:00
rule ["max of %items with respect to %keys"]:
2017-09-18 12:34:10 -07:00
lua expr "compiler.utils.max(vars.items, vars.keys)"
2017-09-12 20:00:19 -07:00
# List/dict functions
2017-09-14 05:04:27 -07:00
macro [..]
"%list 's %index", "%index st in %list", "%index nd in %list", "%index rd in %list"
"%index th in %list", "%index in %list"
2017-09-14 05:04:27 -07:00
..:
".."|\%list as lua expr\[\%index as lua expr\]
2017-09-14 05:16:57 -07:00
macro block [..]
2017-09-14 05:04:27 -07:00
"%list 's %index = %value", "%index st in %list = %value", "%index nd in %list = %value"
"%index rd in %list = %value", "%index th in %list = %value", "%index in %list = %value"
2017-09-14 05:04:27 -07:00
..:
2017-09-14 05:16:57 -07:00
".."|\%list as lua expr\[\%index as lua expr\] = \%value as lua expr\
2017-09-12 20:00:19 -07:00
macro ["%item is in %list", "%list contains %item"]:
2017-09-14 05:04:27 -07:00
".."|(\%list as lua expr\[\%index as lua expr\] ~= nil)
2017-09-12 20:00:19 -07:00
2017-09-12 21:10:22 -07:00
macro ["length of %list", "size of %list", "number of %list"]:
".."|#(\%list as lua expr\)
2017-09-14 05:04:27 -07:00
rule "dict %items":
let "dict" = []
for "pair" in %items:
lua block "vars.dict[vars.pair[1]] = vars.pair[2]"
return %dict
2017-09-12 20:00:19 -07:00
# Permission functions
2017-09-12 20:00:19 -07:00
rule "restrict %fn to within %whitelist":
lua block ".."
|local fns = compiler:get_invocations(vars.fn)
|local whitelist = compiler:get_invocations(vars.whitelist)
2017-09-12 20:00:19 -07:00
|local whiteset = {}
2017-09-12 21:10:22 -07:00
|for _,w in ipairs(whitelist) do
| if not compiler.defs[w] then
| compiler:error("Undefined function: "..tostring(w))
| else whiteset[w] = true
| end
|end
2017-09-12 20:00:19 -07:00
|for _,fn in ipairs(fns) do
| local fn_info = compiler.defs[fn]
| if fn_info == nil then
2017-09-12 21:10:22 -07:00
| compiler:error("Undefined function: "..tostring(fn))
2017-09-12 20:00:19 -07:00
| elseif not compiler:check_permission(fn) then
| compiler:writeln("You do not have permission to restrict function: "..tostring(fn))
2017-09-12 20:00:19 -07:00
| 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)
2017-09-12 21:10:22 -07:00
|for _,w in ipairs(whitelist) do
| if not compiler.defs[w] then
| compiler:error("Undefined function: "..tostring(w))
| end
|end
2017-09-12 20:00:19 -07:00
|for _,fn in ipairs(fns) do
| local fn_info = compiler.defs[fn]
| if fn_info == nil then
2017-09-12 21:10:22 -07:00
| compiler:error("Undefined function: "..tostring(fn))
2017-09-12 20:00:19 -07:00
| elseif fn_info.whiteset == nil then
| compiler:writeln("Function is already allowed by everyone: "..tostring(fn))
2017-09-12 20:00:19 -07:00
| elseif not compiler:check_permission(fn) then
| compiler:writeln("You do not have permission to grant permissions for function: "..tostring(fn))
2017-09-12 20:00:19 -07:00
| else
2017-09-12 21:10:22 -07:00
| for _,w in ipairs(whitelist) do
| fn_info.whiteset[w] = true
| end
2017-09-12 20:00:19 -07:00
| end
|end
rule "forbid %blacklist to use %fn":
lua block ".."
|local fns = compiler:get_invocations(vars.fn)
|local blacklist = compiler:get_invocations(vars.blacklist)
2017-09-12 21:10:22 -07:00
|for _,b in ipairs(blacklist) do
| if not compiler.defs[b] then
| compiler:error("Undefined function: "..tostring(b))
| end
|end
2017-09-12 20:00:19 -07:00
|for _,fn in ipairs(fns) do
| local fn_info = compiler.defs[fn]
| if fn_info == nil then
2017-09-12 21:10:22 -07:00
| compiler:error("Undefined function: "..tostring(fn))
2017-09-12 20:00:19 -07:00
| elseif fn_info.whiteset == nil then
| compiler:writeln("Cannot remove items from a whitelist when there is no whitelist on function: "..tostring(fn))
2017-09-12 20:00:19 -07:00
| elseif not compiler:check_permission(fn) then
| compiler:writeln("You do not have permission to restrict function: "..tostring(fn))
2017-09-12 20:00:19 -07:00
| else
| for _,b in ipairs(blacklist) do fn_info.whiteset[b] = nil end
| end
|end
2017-09-12 21:10:22 -07:00
# Error functions
2017-09-12 21:10:22 -07:00
rule "error!":
2017-09-14 18:18:42 -07:00
lua block ".."
|table.remove(compiler.callstack)
|compiler:error()
2017-09-12 21:10:22 -07:00
rule "error %msg":
2017-09-14 18:18:42 -07:00
lua block ".."
|table.remove(compiler.callstack)
|compiler:error(vars.msg)
2017-09-12 21:37:58 -07:00
macro block "test %code yields %expected":
2017-09-18 12:34:10 -07:00
let "generated" = (lua expr "compiler.utils.repr(compiler:stringify_tree(vars.code.value.value), true)")
2017-09-12 21:37:58 -07:00
let "expected" = (%expected as lua expr)
if (%generated != %expected):
say "Test failed!"
say "Expected:"
say %expected
say "But got:"
say %generated
error!
return ""