aboutsummaryrefslogtreecommitdiff
path: root/lib/operators.nom
blob: 01289effd204330f40902b0ec81aa9cf3689f246 (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
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 helper(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", helper(function(var,result) return var.." = "..result end))
    |compiler:defmacro("%var += %rhs", helper(function(var,result) return var.." = "..var.." + "..result end))
    |compiler:defmacro("%var -= %rhs", helper(function(var,result) return var.." = "..var.." - "..result end))
    |compiler:defmacro("%var *= %rhs", helper(function(var,result) return var.." = "..var.." * "..result end))
    |compiler:defmacro("%var /= %rhs", helper(function(var,result) return var.." = "..var.." / "..result end))
    |compiler:defmacro("%var ^= %rhs", helper(function(var,result) return var.." = "..var.." ^ "..result end))
    |compiler:defmacro("%var and= %rhs", helper(function(var,result) return var.." = "..var.." and "..result end))
    |compiler:defmacro("%var or= %rhs", helper(function(var,result) return var.." = "..var.." or "..result end))
    |compiler:defmacro("%var join= %rhs", helper(function(var,result) return var.." = "..var.." .. "..result end))
    |compiler:defmacro("%var mod= %rhs", helper(function(var,result) return var.." = "..var.." % "..result end))

# Binary Operators
lua block ".."
    |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
# == 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
lua block ".."
    |local comops = {"+","*","and","or"}
    |for _,_op in ipairs(comops) do
    |    local op = _op
    |    local spec = "%1 "..op.." %2"
    |    for n=3,8 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

# Chained compairsions (e.g. x < y <= z < w) are defined up to 8 operands
lua block ".."
    |for _,chainers in ipairs{{"<","<="},{">",">="}} do
    |    local function recurse(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
    # 7 operators == 8 operands, so don't continue any further
    |        if #chain >= 7 then return end
    |        for _,c in ipairs(chainers) do
    |            table.insert(chain, c)
    |            recurse(chain)
    |            table.remove(chain)
    |        end
    |    end
    |    recurse({})
    |end

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