aboutsummaryrefslogtreecommitdiff
path: root/lib/operators.nom
blob: d57c0615e65fc56db059efbc085b5fa0e7743a62 (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
138
139
140
141
142
143
144
use "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, e.g. "x < 5 and false or 99"
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:
    lua> ".."
        local condition = nomsu:tree_to_lua(\%condition).expr;
        local when_true = nomsu:tree_to_lua(\%when_true_expr).expr;
        local when_false = nomsu:tree_to_lua(\%when_false_expr).expr;
        local safe = {Text=true, List=true, Dict=true, Number=true};
        if safe[\%when_true_expr.type] then
            return "("..condition.." and "..when_true.." or "..when_false..")";
        else
            return ([[
        (function(nomsu)
            if %s then
                return %s;
            else
                return %s;
            end
        end)(nomsu)]]):format(condition, when_true, when_false);
        end
parse [..]
    %true if %x == %y else %false, %true if %x == %y otherwise %false
    %false unless %x == %y else %true, %false unless %x == %y otherwise %true
..as:
    %true if (%x == %y) else %false

parse [..]
    %true if %x != %y else %false, %true if %x != %y otherwise %false
    %false unless %x != %y else %true, %false unless %x != %y otherwise %true
..as:
    %true if (%x != %y) else %false

# Indexing:
compile [%obj'%key, %obj's %key, %obj -> %key] to: "(\(%obj as lua))[\(%key as lua)]"

# Substring
compile [%str |%start|] to: "\(%str as lua):sub(\(%start as lua), \(%start as lua))"
compile [%str |%start - %stop|] to: "\(%str as lua):sub(\(%start as lua), \(%stop as lua))"

# Variable assignment operator, and += type versions
compile [set %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).expr; end
            local rhs = {};
            for i,x in ipairs(\%val.value) do rhs[i] = nomsu:tree_to_lua(x).expr; 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 [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))"

# Comparison 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))"
# == and != do equivalence checking, rather than identity checking
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 "nomsu.utils.equivalent("..a_lua..", "..b_lua..")";
        end
compile [%a isn't %b, %a is 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 nomsu.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)]"

# 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))"