summaryrefslogtreecommitdiff
path: root/script/vm/union.lua
blob: 809dd78f3b6ce7bf5aea55b1516d41ffef91dfd8 (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
local localMgr = require 'vm.local-manager'

---@class vm.node.union
local mt = {}
mt.__index   = mt
mt.type      = 'union'
mt.optional  = nil
mt.lastInfer = nil

---@param me   vm.node
---@param node vm.node
---@return vm.node.union
local function createUnion(me, node)
    local union = setmetatable({}, mt)
    union:merge(me)
    union:merge(node)
    return union
end

---@param node vm.node
function mt:merge(node)
    if not node then
        return
    end
    if node.type == 'union' then
        for _, c in ipairs(node) do
            if not self[c] then
                self[c]       = true
                self[#self+1] = c
            end
        end
        if node:isOptional() then
            self.optional = true
        end
    else
        if not self[node] then
            self[node]    = true
            self[#self+1] = node
        end
    end
end

---@param source parser.object
function mt:subscribeLocal(source)
    for _, c in ipairs(self) do
        localMgr.subscribeLocal(source, c)
    end
end

function mt:eachNode()
    local i = 0
    return function ()
        i = i + 1
        return self[i]
    end
end

---@return vm.node.union
function mt:addOptional()
    if self:isOptional() then
        return self
    end
    self.optional = true
    return self
end

---@return vm.node.union
function mt:removeOptional()
    self.optional = nil
    if not self:isOptional() then
        return self
    end
    -- copy union
    local newUnion = createUnion()
    for _, n in ipairs(self) do
        if n.type == 'nil' then
            goto CONTINUE
        end
        if n.type == 'boolean' then
            if n[1] == false then
                goto CONTINUE
            end
        end
        if n.type == 'false' then
            goto CONTINUE
        end
        newUnion[#newUnion+1] = n
        ::CONTINUE::
    end
    newUnion.optional = false
    return newUnion
end

---@return boolean
function mt:isOptional()
    if self.optional ~= nil then
        return self.optional
    end
    for _, c in ipairs(self) do
        if c.type == 'nil' then
            self.optional = true
            return true
        end
        if c.type == 'boolean' then
            if c[1] == false then
                self.optional = true
                return true
            end
        end
        if c.type == 'false' then
            self.optional = true
            return true
        end
    end
    self.optional = false
    return false
end

return createUnion