nomsu/lib/operators.nom

125 lines
5.3 KiB
Plaintext
Raw Normal View History

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: ".."
2017-09-26 15:27:01 -07:00
|(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.
2017-09-26 15:27:01 -07:00
| 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)"
2017-09-29 22:04:03 -07:00
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)"
%x =: 5
# 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)..")"
2017-09-26 15:27:01 -07:00
| 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
2017-09-21 14:13:24 -07:00
# 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))"