aboutsummaryrefslogtreecommitdiff
path: root/lib/operators.nom
blob: 1fcb42acb45a432f31ddcfa0dadfb7b8a5bb0d99 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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
#.. Note: this uses a function instead of (condition and if_expr or else_expr)
    because that breaks if %if_expr is falsey.
compile [..]
    %when_true_expr if %condition else %when_false_expr
    %when_true_expr if %condition otherwise %when_false_expr
    %when_false_expr unless %condition else %when_true_expr
    %when_false_expr unless %condition then %when_true_expr
..to: ".."
    (function(nomsu, vars)
        if \(%condition as lua) then
            return \(%when_true_expr as lua);
        else
            return \(%when_false_expr as lua);
        end
    end)(nomsu, vars)

# Indexing:
compile [%obj'%key, %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 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(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 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[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))"