aboutsummaryrefslogtreecommitdiff
path: root/lib/operators.nom
blob: 7a926f31f657857be2be7b041dad8810567425b6 (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
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: ".."
    |(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.
    |    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)"
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)..")"
    |    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

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