aboutsummaryrefslogtreecommitdiff
path: root/lib/operators.nom
blob: 9250214954a8c7e5afbeb53b8aacc0025bf941b1 (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
128
129
130
131
132
133
134
135
136
137
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:
    lua> ".."
        if \%var.type == 'List' and \%val.type == 'List' then
            local lhs = {};
            for i,x in ipairs(\%var.value) do lhs[i] = nomsu:tree_to_lua(x); end
            local rhs = {};
            for i,x in ipairs(\%val.value) do rhs[i] = nomsu:tree_to_lua(x); end
            return table.concat(lhs, ", ").." = "..table.concat(rhs, ", ")..";";
        else
            return \(%var as lua).." = "..\(%val as lua)..";";
        end
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(\%a).." "..op.." "..nomsu:tree_to_lua(\%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[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

# Unary operators
compile [- %] to: "-(\(% as lua))"
compile [not %] to: "not (\(% as lua))"