Some overhaul of binary operators so that arbitrary math patterns work
fine.
This commit is contained in:
parent
b1c6354464
commit
c92e5fbc81
@ -7,10 +7,11 @@ compile [if %condition %if_body] to code: ".."
|
||||
if \(%condition as lua) then
|
||||
\(%if_body as lua statements)
|
||||
end --end if
|
||||
compile [unless %condition %body] to code: ".."
|
||||
if not (\(%condition as lua)) then
|
||||
\(%body as lua statements)
|
||||
end --end if
|
||||
parse [unless %condition %unless_body] as: if (not %condition) %unless_body
|
||||
parse [if %x == %y %if_body] as: if (%x == %y) %if_body
|
||||
parse [if %x != %y %if_body] as: if (%x != %y) %if_body
|
||||
parse [unless %x == %y %if_body] as: if (%x != %y) %if_body
|
||||
parse [unless %x != %y %if_body] as: if (%x == %y) %if_body
|
||||
|
||||
compile [if %condition %if_body else %else_body, unless %condition %else_body else %if_body] to code: ".."
|
||||
if \(%condition as lua) then
|
||||
@ -18,6 +19,10 @@ compile [if %condition %if_body else %else_body, unless %condition %else_body el
|
||||
else
|
||||
\(%else_body as lua statements)
|
||||
end --end if
|
||||
parse [if %x == %y %if_body else %else_body] as: if (%x == %y) %if_body else %else_body
|
||||
parse [if %x != %y %if_body else %else_body] as: if (%x != %y) %if_body else %else_body
|
||||
parse [unless %x == %y %if_body else %else_body] as: if (%x != %y) %if_body else %else_body
|
||||
parse [unless %x != %y %if_body else %else_body] as: if (%x == %y) %if_body else %else_body
|
||||
|
||||
# Return
|
||||
compile [return] to code: "do return; end"
|
||||
@ -60,6 +65,10 @@ compile [repeat while %condition %body] to code:
|
||||
return %code
|
||||
parse [repeat %body] as: repeat while (true) %body
|
||||
parse [repeat until %condition %body] as: repeat while (not %condition) %body
|
||||
parse [repeat while %x == %y %body] as: repeat while (%x == %y) %body
|
||||
parse [repeat while %x != %y %body] as: repeat while (%x != %y) %body
|
||||
parse [repeat until %x == %y %body] as: repeat while (%x != %y) %body
|
||||
parse [repeat until %x != %y %body] as: repeat while (%x == %y) %body
|
||||
|
||||
# For loop control flow:
|
||||
compile [stop for-loop] to code: "goto stop_for;"
|
||||
|
@ -57,84 +57,51 @@ compile [%var or= %val] to code: "\(%var as lua) = \(%var as lua) or \(%val as l
|
||||
compile [%var join= %val] to code: "\(%var as lua) = \(%var as lua) .. \(%val as lua);"
|
||||
compile [%var mod= %val] to code: "\(%var as lua) = \(%var as lua) % \(%val as lua);"
|
||||
|
||||
# Binary Operators
|
||||
lua do> ".."
|
||||
local binops = {"-","/","<","<=",">",">=","^",{"===","=="},{"!==","~="},{"mod","%"}};
|
||||
for _,op in ipairs(binops) do
|
||||
local nomsu_alias = op;
|
||||
if type(op) == 'table' then;
|
||||
nomsu_alias, op = unpack(op);
|
||||
end
|
||||
nomsu:defmacro("%a "..nomsu_alias.." %b", (function(nomsu, vars)
|
||||
return "("..nomsu:tree_to_lua(\%a).." "..op.." "..nomsu:tree_to_lua(\%b)..")";
|
||||
end), [["(\\%a ]]..op..[[ \\%b)"]]);
|
||||
end
|
||||
# Math Operators
|
||||
compile [%x + %y] to: "(\(%x as lua) + \(%y as lua))"
|
||||
compile [%x - %y] to: "(\(%x as lua) - \(%y as lua))"
|
||||
compile [%x * %y] to: "(\(%x as lua) * \(%y as lua))"
|
||||
compile [%x / %y] to: "(\(%x as lua) / \(%y as lua))"
|
||||
compile [%x ^ %y] to: "(\(%x as lua) ^ \(%y as lua))"
|
||||
compile [%x mod %y] to: "(\(%x as lua) % \(%y as lua))"
|
||||
|
||||
# TODO: implement OR, XOR, AND for multiple operands
|
||||
# Comparison Operators
|
||||
compile [%x < %y] to: "(\(%x as lua) < \(%y as lua))"
|
||||
compile [%x > %y] to: "(\(%x as lua) > \(%y as lua))"
|
||||
compile [%x <= %y] to: "(\(%x as lua) <= \(%y as lua))"
|
||||
compile [%x >= %y] to: "(\(%x as lua) >= \(%y as lua))"
|
||||
# == and != do equivalence checking, rather than identity checking
|
||||
compile [%a == %b] to: "nomsu.utils.equivalent(\(%a as lua), \(%b as lua))"
|
||||
compile [%a != %b] to: "(not nomsu.utils.equivalent(\(%a as lua), \(%b as lua)))"
|
||||
# For strict identity checking:
|
||||
compile [%x === %y] to: "(\(%x as lua) == \(%y as lua))"
|
||||
compile [%x !== %y] to: "(\(%x as lua) ~= \(%y as lua))"
|
||||
|
||||
# 3-part chained comparisons
|
||||
# (uses a lambda to avoid re-evaluating middle value, while still being an expression)
|
||||
parse [%x < %y < %z] as: =lua "(function(x,y,z) return x < y and y < z; end)(\%x,\%y,\%z)"
|
||||
parse [%x <= %y < %z] as: =lua "(function(x,y,z) return x <= y and y < z; end)(\%x,\%y,\%z)"
|
||||
parse [%x < %y <= %z] as: =lua "(function(x,y,z) return x < y and y <= z; end)(\%x,\%y,\%z)"
|
||||
parse [%x <= %y <= %z] as: =lua "(function(x,y,z) return x <= y and y <= z; end)(\%x,\%y,\%z)"
|
||||
parse [%x > %y > %z] as: =lua "(function(x,y,z) return x > y and y > z; end)(\%x,\%y,\%z)"
|
||||
parse [%x >= %y > %z] as: =lua "(function(x,y,z) return x >= y and y > z; end)(\%x,\%y,\%z)"
|
||||
parse [%x > %y >= %z] as: =lua "(function(x,y,z) return x > y and y >= z; end)(\%x,\%y,\%z)"
|
||||
parse [%x >= %y >= %z] as: =lua "(function(x,y,z) return x >= y and y >= z; end)(\%x,\%y,\%z)"
|
||||
# TODO: optimize for common case where x,y,z are all either variables or number literals
|
||||
|
||||
# Boolean Operators
|
||||
compile [%x and %y] to: "(\(%x as lua) and \(%y as lua))"
|
||||
compile [%x or %y] to: "(\(%x as lua) or \(%y as lua))"
|
||||
|
||||
# Bitwise Operators
|
||||
compile [%a OR %b, %a | %b] to: "bit32.bor(\(%a as lua), \(%b as lua))"
|
||||
compile [%a XOR %b] to: "bit32.bxor(\(%a as lua), \(%b as lua))"
|
||||
compile [%a AND %b, %a & %b] to: "bit32.band(\(%a as lua), \(%b as lua))"
|
||||
compile [NOT %, ~ %] to: "bit32.bnot(\(% as lua))"
|
||||
compile [%x LSHIFT %shift, %x << %shift] to: "bit32.lshift(\(%x as lua), \(%shift as lua))"
|
||||
compile [%x RSHIFT %shift] to: "bit32.rshift(\(%x as lua), \(%shift as lua))"
|
||||
compile [%x RSHIFT %shift, %x >>> %shift] to: "bit32.rshift(\(%x as lua), \(%shift as lua))"
|
||||
compile [%x ARSHIFT %shift, %x >> %shift] to: "bit32.arshift(\(%x as lua), \(%shift as lua))"
|
||||
# == and != do equivalence checking, rather than identity checking
|
||||
compile [%a == %b] to: "nomsu.utils.equivalent(\(%a as lua), \(%b as lua))"
|
||||
compile [%a != %b] to: "(not nomsu.utils.equivalent(\(%a as lua), \(%b as lua)))"
|
||||
|
||||
# Commutative Operators defined for up to 8 operands
|
||||
# TODO: work out solution for commutative operators using more clever macros
|
||||
lua do> ".."
|
||||
local max_operands = 8;
|
||||
local comops = {"+","*","and","or"};
|
||||
for _,_op in ipairs(comops) do
|
||||
local op = _op;
|
||||
local spec = "%1 ";
|
||||
for n=2,max_operands do
|
||||
spec = spec .." "..op.." %"..tostring(n);
|
||||
nomsu:defmacro(spec, (function(nomsu, vars)
|
||||
local bits = {};
|
||||
for i=1,n do
|
||||
table.insert(bits, (nomsu:tree_to_lua(vars[tostring(i)])));
|
||||
end
|
||||
return "("..table.concat(bits, " "..op.." ")..")";
|
||||
end));
|
||||
end
|
||||
end
|
||||
|
||||
# Chained compairsions (e.g. x < y <= z) are defined up to 3 operands
|
||||
lua do> ".."
|
||||
local max_operands = 3;
|
||||
for _,chainers in ipairs({{"<","<="},{">",">="}}) do
|
||||
local function recurse(chainers, 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 :
|
||||
nomsu:def(spec, function(nomsu, vars)
|
||||
for i,op in ipairs(chain) do
|
||||
local a, b, result = vars[tostring(i)], vars[tostring(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
|
||||
if #chain + 1 >= max_operands then; return; end
|
||||
for _,c in ipairs(chainers) do
|
||||
table.insert(chain, c);
|
||||
recurse(chainers, chain);
|
||||
table.remove(chain);
|
||||
end
|
||||
end
|
||||
recurse(chainers, {});
|
||||
end
|
||||
# TODO: implement OR, XOR, AND for multiple operands?
|
||||
|
||||
# Unary operators
|
||||
compile [- %] to: "-(\(% as lua))"
|
||||
|
@ -1,6 +1,7 @@
|
||||
require "lib/metaprogramming.nom"
|
||||
require "lib/operators.nom"
|
||||
require "lib/control_flow.nom"
|
||||
require "lib/collections.nom"
|
||||
|
||||
compile [<%var> = %value] to code: ".."
|
||||
nomsu.defs['#vars'][\(repr (%var's "value"))] = \(%value as lua);
|
||||
@ -12,25 +13,30 @@ compile [str %] to: "tostring(\(% as lua))"
|
||||
compile [scope] to: "nomsu.defs"
|
||||
compile [parent scope] to: "getmetatable(nomsu.defs).__index"
|
||||
|
||||
parse [using %scoped do %actions] as:
|
||||
%scope = (=lua "setmetatable({['#vars']=setmetatable({}, {__index=nomsu.defs['#vars']})}, {__index=nomsu.defs})")
|
||||
with (nomsu's "defs") = %scope:
|
||||
do %scoped
|
||||
lua> ".."
|
||||
getmetatable(nomsu.defs).__newindex = getmetatable(nomsu.defs).__index;
|
||||
getmetatable(nomsu.defs["#vars"]).__newindex = getmetatable(nomsu.defs["#vars"]).__index;
|
||||
do %actions
|
||||
compile [using %scoped do %actions] to code: ".."
|
||||
do
|
||||
local old_scope, old_vars = nomsu.defs, vars;
|
||||
local use_vars = setmetatable({}, {__index=old_scope['#vars']});
|
||||
local scope = setmetatable({['#vars']=use_vars}, {__index=old_scope});
|
||||
nomsu.defs = scope;
|
||||
local ok, ret = pcall(function(nomsu, vars)
|
||||
local ret;
|
||||
do
|
||||
\(%scoped as lua statements)
|
||||
end
|
||||
getmetatable(scope).__newindex = old_scope;
|
||||
getmetatable(use_vars).__newindex = old_vars;
|
||||
do
|
||||
\(%actions as lua statements)
|
||||
end
|
||||
return ret;
|
||||
end, nomsu, use_vars);
|
||||
nomsu.defs = old_scope;
|
||||
if not ok then nomsu:error(ret); end
|
||||
end
|
||||
|
||||
parse [scoped %actions] as: using %actions do (:pass)
|
||||
|
||||
rule [from %filename import %rules] =:
|
||||
using:
|
||||
require %filename
|
||||
..do:
|
||||
%srcs = ((%'s "src") for %_ = % in (parent scope))
|
||||
for %src in (unique %srcs):
|
||||
run %src
|
||||
|
||||
parse [wrap %signature with %body] as:
|
||||
using:
|
||||
run ((nomsu)->*["defs",nomsu "get_stub" [\%signature->*["value",1]],"src"])
|
||||
|
@ -1,23 +0,0 @@
|
||||
require "lib/core.nom"
|
||||
|
||||
compile [with secrets %block] to code: ".."
|
||||
do
|
||||
local secrets = {};
|
||||
\(%block as lua statements)
|
||||
end
|
||||
|
||||
# Access the lua variable that should be within scope
|
||||
compile [secrets] to: "secrets"
|
||||
|
||||
compile [secret %key, secret value of %key, secret value for %key] to:
|
||||
assert ((%key's "type") == "Var") ".."
|
||||
|Wrong type, expected Var, but got: \(%key's "type")
|
||||
"secrets[\(repr (%key's "value"))]"
|
||||
|
||||
compile [secret %key = %new_value] to code:
|
||||
assert ((%key's "type") == "Var") ".."
|
||||
|Wrong type, expected Var, but got: \(%key's "type")
|
||||
"secrets[\(repr (%key's "value"))] = \(%new_value as lua);"
|
||||
|
||||
rule [rules about secrecy] =: ["with secrets %"]
|
||||
|
@ -73,10 +73,6 @@ compile [e] to: "math.e"
|
||||
compile [golden ratio, phi] to: "((1 + math.sqrt(5)) / 2)"
|
||||
|
||||
# Common utility functions
|
||||
compile [sum of %items, sum %items] to: "nomsu.utils.sum(\(%items as lua))"
|
||||
compile [product of %items, product %items] to: "nomsu.utils.product(\(%items as lua))"
|
||||
compile [all of %items] to: "nomsu.utils.all(\(%items as lua))"
|
||||
compile [any of %items] to: "nomsu.utils.any(\(%items as lua))"
|
||||
rule [avg of %items, average of %items] =:
|
||||
=lua "(nomsu.utils.sum(\%items)/#\%items)"
|
||||
compile [min of %items, smallest of %items, lowest of %items] to:
|
||||
|
@ -49,3 +49,28 @@ compile [with %assignments %action] to code:
|
||||
end
|
||||
parse [with %thing = %value %action] as: with [%thing = %value] %action
|
||||
|
||||
# Any/all/none
|
||||
compile [all of %items, all %items] to:
|
||||
if (%items' "type") == "List":
|
||||
"(\(join ((% as lua) for all (%items' "value")) with glue " and "))"
|
||||
..else:
|
||||
"nomsu.utils.all(\(%items as lua))"
|
||||
parse [not all of %items, not all %items] as: not (all of %items)
|
||||
compile [any of %items, any %items] to:
|
||||
if (%items' "type") == "List":
|
||||
"(\(join ((% as lua) for all (%items' "value")) with glue " or "))"
|
||||
..else:
|
||||
"nomsu.utils.any(\(%items as lua))"
|
||||
parse [none of %items, none %items] as: not (any of %items)
|
||||
|
||||
|
||||
compile [sum of %items, sum %items] to:
|
||||
if (%items' "type") == "List":
|
||||
"(\(join ((% as lua) for all (%items' "value")) with glue " + "))"
|
||||
..else:
|
||||
"nomsu.utils.sum(\(%items as lua))"
|
||||
compile [product of %items, product %items] to:
|
||||
if (%items' "type") == "List":
|
||||
"(\(join ((% as lua) for all (%items' "value")) with glue " * "))"
|
||||
..else:
|
||||
"nomsu.utils.product(\(%items as lua))"
|
||||
|
23
nomsu.lua
23
nomsu.lua
@ -846,16 +846,22 @@ end)]]):format(concat(lua_bits, "\n"))
|
||||
local def = self.defs[tree.stub]
|
||||
if def and def.is_macro then
|
||||
local expr, statement = self:run_macro(tree)
|
||||
if def.whiteset then
|
||||
if expr then
|
||||
expr = "(nomsu:assert_permission(" .. tostring(repr(tree.stub)) .. ") and " .. tostring(expr) .. ")"
|
||||
end
|
||||
if statement then
|
||||
statement = "nomsu:assert_permission(" .. tostring(repr(tree.stub)) .. ");\n" .. statement
|
||||
end
|
||||
end
|
||||
remove(self.compilestack)
|
||||
return expr, statement
|
||||
elseif not def and self.__class.math_patt:match(tree.stub) then
|
||||
local bits = { }
|
||||
local _list_0 = tree.value
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local tok = _list_0[_index_0]
|
||||
if tok.type == "Word" then
|
||||
insert(bits, tok.value)
|
||||
else
|
||||
local expr, statement = self:tree_to_lua(tok, filename)
|
||||
self:assert(statement == nil, "non-expression value inside math expression")
|
||||
insert(bits, expr)
|
||||
end
|
||||
end
|
||||
return "(" .. tostring(concat(bits, " ")) .. ")"
|
||||
end
|
||||
local args = {
|
||||
repr(tree.stub),
|
||||
@ -1402,6 +1408,7 @@ end)]]):format(concat(lua_bits, "\n"))
|
||||
_base_0.__class = _class_0
|
||||
local self = _class_0
|
||||
self.def_number = 0
|
||||
self.math_patt = re.compile([[ "%" (" " [*/^+-] " %")+ ]])
|
||||
self.unescape_string = function(self, str)
|
||||
return Cs(((P("\\\\") / "\\") + (P("\\\"") / '"') + ESCAPE_CHAR + P(1)) ^ 0):match(str)
|
||||
end
|
||||
|
17
nomsu.moon
17
nomsu.moon
@ -533,6 +533,7 @@ end);]])\format(concat(buffer, "\n"))
|
||||
else
|
||||
error("Unsupported value_to_nomsu type: #{type(value)}")
|
||||
|
||||
@math_patt: re.compile [[ "%" (" " [*/^+-] " %")+ ]]
|
||||
tree_to_lua: (tree, filename)=>
|
||||
-- Return <lua code for value>, <additional lua code>
|
||||
@assert tree, "No tree provided."
|
||||
@ -570,13 +571,19 @@ end)]])\format(concat(lua_bits, "\n"))
|
||||
def = @defs[tree.stub]
|
||||
if def and def.is_macro
|
||||
expr, statement = @run_macro(tree)
|
||||
if def.whiteset
|
||||
if expr
|
||||
expr = "(nomsu:assert_permission(#{repr tree.stub}) and #{expr})"
|
||||
if statement
|
||||
statement = "nomsu:assert_permission(#{repr tree.stub});\n"..statement
|
||||
remove @compilestack
|
||||
return expr, statement
|
||||
elseif not def and @@math_patt\match(tree.stub)
|
||||
bits = {}
|
||||
for tok in *tree.value
|
||||
if tok.type == "Word"
|
||||
insert bits, tok.value
|
||||
else
|
||||
expr, statement = @tree_to_lua(tok, filename)
|
||||
@assert(statement == nil, "non-expression value inside math expression")
|
||||
insert bits, expr
|
||||
return "(#{concat bits, " "})"
|
||||
|
||||
args = {repr(tree.stub), repr(tree.line_no)}
|
||||
local arg_names, escaped_args
|
||||
if def
|
||||
|
Loading…
Reference in New Issue
Block a user