nomsu/lib/operators.nom
Bruce Hill dcd3391b36 Updated to undo some of the block/thunk stuff. Thunks are thunks, and
expressions can be grouped with parens, and they have a clear
distinction.
2017-10-02 17:21:22 -07:00

123 lines
5.4 KiB
Plaintext

require "lib/metaprogramming.nom"
# Literals
compile [true, yes] to: "true"
compile [false, no] to: "false"
compile [nil, null] to: "nil"
compile [inf, infinity] to: "math.huge"
compile [nan, NaN, not a number] to: "(0/0)"
compile [pi, PI] to: "math.pi"
compile [tau, TAU] to: "(2*math.pi)"
compile [phi, PHI, golden ratio] to: "((1+math.sqrt(5))/2)"
compile [nop, pass] to code: ""
# Ternary operator
compile [%if_expr if %condition else %else_expr] to: ".."
|(function(nomsu, vars)
# TODO: fix compiler bug that breaks this code if comments immediately follow ".."
#.. Note: this uses a function instead of (condition and if_expr or else_expr)
because that breaks if %if_expr is falsey.
| if \(%condition) then;
| return \(%if_expr);
| else;
| return \(%else_expr);
| end;
|end)(nomsu, vars)
# Indexing:
compile [%obj's %key, %obj -> %key] to: "(\(%obj as lua))[\(%key as lua)]"
# Variable assignment operator, and += type versions
compile [%var = %val] to code: "\(%var as lua) = \(%val as lua);"
compile [%var += %val] to code: "\(%var as lua) = \(%var as lua) + \(%val as lua);"
compile [%var -= %val] to code: "\(%var as lua) = \(%var as lua) - \(%val as lua);"
compile [%var *= %val] to code: "\(%var as lua) = \(%var as lua) * \(%val as lua);"
compile [%var /= %val] to code: "\(%var as lua) = \(%var as lua) / \(%val as lua);"
compile [%var ^= %val] to code: "\(%var as lua) = \(%var as lua) ^ \(%val as lua);"
compile [%var and= %val] to code: "\(%var as lua) = \(%var as lua) and\(%val as lua);"
compile [%var or= %val] to code: "\(%var as lua) = \(%var as lua) or \(%val as lua);"
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 block ".."
|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(vars.a).." "..op.." "..nomsu:tree_to_lua(vars.b)..")";
| end), [["(\\(%a) ]]..op..[[ \\(%b))"]]);
|end;
# TODO: implement OR, XOR, AND for multiple operands
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 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 block ".."
|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 block ".."
|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[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;
| 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;
# Unary operators
compile [- %] to: "-(\(% as lua))"
compile [not %] to: "not (\(% as lua))"