aboutsummaryrefslogtreecommitdiff
path: root/lib/operators.nom
blob: 91410bea1ff9cb795566667d5e40873b9a59129d (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
#..
    This file contains definitions of operators like "+" and "and".

use "lib/metaprogramming.nom"

# Indexing:
immediately
    #.. NOTE!!! It's critical that there are spaces around %key if it's a string,
        otherwise, Lua will get confused and interpret %obj[[[foo]]] as %obj("[foo]")
        instead of %obj[ "foo" ].
        It's also critical to have parens around %obj, otherwise Lua is too dumb to
        realize that {x=1}["x"] is the same as ({x=1})["x"] or that
        {x=1}.x is the same as ({x=1}).x
    compile [%obj'%key, %obj's %key, %obj -> %key] to
        lua> ".."
            local obj_lua = \(%obj as lua);
            if not obj_lua:sub(-1,-1):match("[a-zA-Z)]") then
                obj_lua = "("..obj_lua..")";
            end
            local key_lua = \(%key as lua);
            local key_attr = (key_lua:match("'([a-zA-Z][a-zA-Z0-9]*)'")
                           or key_lua:match('"([a-zA-Z][a-zA-Z0-9]*)"'));
            if key_attr then
                return obj_lua.."."..key_attr;
            elseif key_lua:sub(1,1) == "[" then
                key_lua = " "..key_lua.." ";
            end
            return obj_lua.."["..key_lua.."]";

# Comparison Operators
immediately
    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 [%a is %b, %a = %b, %a == %b] to
        lua> ".."
            local safe = {Text=true, Number=true};
            local a_lua, b_lua = nomsu:tree_to_lua(\%a).expr, nomsu:tree_to_lua(\%b).expr;
            if safe[\%a.type] or safe[\%b.type] then
                return "("..a_lua.." == "..b_lua..")";
            else
                return "utils.equivalent("..a_lua..", "..b_lua..")";
            end
    compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to
        lua> ".."
            local safe = {Text=true, Number=true};
            local a_lua, b_lua = nomsu:tree_to_lua(\%a).expr, nomsu:tree_to_lua(\%b).expr;
            if safe[\%a.type] or safe[\%b.type] then
                return "("..a_lua.." ~= "..b_lua..")";
            else
                return "(not utils.equivalent("..a_lua..", "..b_lua.."))";
            end
    # For strict identity checking, use (%x's id) is (%y's id)
    compile [%'s id, id of %] to "nomsu.ids[\(% as lua)]"

# Variable assignment operator, and += type versions
immediately
    compile [local %vars] to code
        lua> ".."
            local locals = \%vars.type == "List" and \%vars.value or {\%vars};
            local identifiers = {};
            for i,x in ipairs(locals) do
                identifiers[i] = nomsu:tree_to_lua(x).expr;
            end
            return "local "..table.concat(identifiers, ", ");
    compile [set %var = %val] to code "\(%var as lua) = \(%val as lua);"
    compile [set %assignments] to code
        assume ((%assignments' "type") is "Dict") or barf "Expected Dict, but got \(%assignments' "type")"
        lua> ".."
            local lhs, rhs = {}, {};
            for i,entry in ipairs(\%assignments.value) do
                lhs[i] = nomsu:tree_to_lua(entry.dict_key).expr;
                rhs[i] = nomsu:tree_to_lua(entry.dict_value).expr;
            end
            return table.concat(lhs, ", ").." = "..table.concat(rhs, ", ")..";";

# Update assignment operators
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 [wrap %var around %val] to code "\(%var as lua) = \(%var as lua) % \(%val as lua);"

# 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 wrapped around %y, %x mod %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, %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))"
# TODO: implement OR, XOR, AND for multiple operands?

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