aboutsummaryrefslogtreecommitdiff
path: root/lib/operators.nom
blob: 67383fa33d6e979bb133ad7231d330cb8a4443eb (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
require "lib/metaprogramming.nom"

# Literals
macro [true, yes] =: "true"
macro [false, no] =: "false"
macro [nil, null] =: "nil"
macro block [nop, pass] =: ""

# Ternary operator
macro [%if_expr if %condition else %else_expr] =:
    pass # TODO: Fix compiler bug that doesn't parse right here
    #.. Note: this uses a function instead of (condition and if_expr or else_expr)
        because that breaks if %if_expr is falsey.
    ".."|(function(compiler, vars)
        |    if \%condition as lua\ then
        |        return \%if_expr as lua\
        |    else
        |        return \%else_expr as lua\
        |    end
        |end)(compiler, vars)

# Variable assignment operator, and += type versions
lua block ".."
    |local function assign(callback)
    |    return function(compiler, vars, kind)
    |        if kind == "Expression" then
    |            compiler:error("Cannot use an assignment operation as an expression value.")
    |        end
    |        if vars.var.type ~= "Var" then
    |            compiler:error("Assignment operation has the wrong type for the left hand side. "
    |                .."Expected Var, but got: "..vars.var.type.."\\nMaybe you forgot a percent sign on the variable name?")
    |        end
    |        if vars.rhs.type ~= "Thunk" then
    |            compiler:error("Assignment operation has the wrong type for the right hand side. "
    |                .."Expected Thunk, but got: "..vars.rhs.type.."\\nMaybe you used '=' instead of '=:'?")
    |        end
    |        if #vars.rhs.value.value > 1 then
    |            compiler:error("Assignment operation should not have more than one value on the right hand side.")
    |        end
    |        return callback(compiler:tree_to_lua(vars.var),
    |            compiler:tree_to_lua(vars.rhs.value.value[1].value)), true
    |    end
    |end
    |compiler:defmacro("%var = %rhs", assign(function(var,result) return var.." = "..result end))
    |compiler:defmacro("%var += %rhs", assign(function(var,result) return var.." = "..var.." + "..result end))
    |compiler:defmacro("%var -= %rhs", assign(function(var,result) return var.." = "..var.." - "..result end))
    |compiler:defmacro("%var *= %rhs", assign(function(var,result) return var.." = "..var.." * "..result end))
    |compiler:defmacro("%var /= %rhs", assign(function(var,result) return var.." = "..var.." / "..result end))
    |compiler:defmacro("%var ^= %rhs", assign(function(var,result) return var.." = "..var.." ^ "..result end))
    |compiler:defmacro("%var and= %rhs", assign(function(var,result) return var.." = "..var.." and "..result end))
    |compiler:defmacro("%var or= %rhs", assign(function(var,result) return var.." = "..var.." or "..result end))
    |compiler:defmacro("%var join= %rhs", assign(function(var,result) return var.." = "..var.." .. "..result end))
    |compiler:defmacro("%var mod= %rhs", assign(function(var,result) return var.." = "..var.." % "..result end))

# Binary Operators
lua block ".."
    |local function make_binops()
    |local binops = {"+","-","*","/","<","<=",">",">=","^",{"===","=="},{"!==","~="},"and","or",{"mod","%"}}
    |for _,op in ipairs(binops) do
    |    local nomsu_alias = op
    |    if type(op) == 'table' then
    |        nomsu_alias, op = unpack(op)
    |    end
    |    compiler:defmacro("%a "..nomsu_alias.." %b", (function(compiler, vars, kind)
    |        return "("..compiler:tree_to_lua(vars.a).." "..op.." "..compiler:tree_to_lua(vars.b)..")"
    |    end), [[".."|(\\%a as lua\\ ]]..op..[[ \\%b as lua\\)]])
    |end
    |end
    |make_binops()
# == and != do equivalence checking, rather than identity checking
macro [%a == %b] =: ".."|compiler.utils.equivalent(\%a as lua\, \%b as lua\)
macro [%a != %b] =: ".."|(not compiler.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 function make_comops(max_operands)
    |local comops = {"+","*","and","or"}
    |for _,_op in ipairs(comops) do
    |    local op = _op
    |    local spec = "%1 "..op.." %2"
    |    for n=3,max_operands do
    |        spec = spec .." "..op.." %"..tostring(n)
    |        compiler:defmacro(spec, (function(compiler, vars, kind)
    |            local bits = {}
    |            for i=1,n do
    |                table.insert(bits, (compiler:tree_to_lua(vars[tostring(i)])))
    |            end
    |            return "("..table.concat(bits, " "..op.." ")..")"
    |        end))
    |    end
    |end
    |end
    |make_comops(8)

# Chained compairsions (e.g. x < y <= z) are defined up to 3 operands
lua block ".."
    |local function chained_comparisons(max_operands)
    |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 :\
    |            compiler:def(spec, function(compiler, 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
    |end
    |chained_comparisons(3)

# Unary operators
macro [- %a] =: ".."|-(\%a as lua\)
macro [not %a] =: ".."|not (\%a as lua\)