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

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

---@param me   vm.node
---@param node vm.node
---@return vm.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 == 'vm.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

---@return vm.node
function mt:copy()
    return createUnion(self, nil)
end

---@return boolean
function mt:isEmpty()
    return #self == 0
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.union
function mt:addOptional()
    if self:isOptional() then
        return self
    end
    self.optional = true
    return self
end

---@return vm.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