summaryrefslogtreecommitdiff
path: root/script/vm/runner.lua
blob: 2d3309d58d8152174e9edc5c389e730867566f94 (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
---@class vm
local vm    = require 'vm.vm'
local guide = require 'parser.guide'

---@class vm.runner
---@field loc       parser.object
---@field mainBlock parser.object
---@field blocks    table<parser.object, true>
---@field steps     vm.runner.step[]
local mt = {}
mt.__index = mt
mt.index = 1

---@class parser.object
---@field _hasSorted boolean

---@class vm.runner.step
---@field type    'truly' | 'as' | 'object' | 'save' | 'load'
---@field pos     integer
---@field node?   vm.node
---@field object? parser.object
---@field ref?    vm.runner.step

---@param filter parser.object
---@param pos    integer
function mt:_compileNarrowByFilter(filter, pos)
    if filter.type == 'unary' then
    elseif filter.type == 'binary' then
    else
        if filter.type == 'getlocal' and filter.node == self.loc then
            self.steps[#self.steps+1] = {
                type = 'truly',
                pos = pos,
            }
        end
    end
end

function mt:_dropBlock(block)
    local savePoint = {
        type = 'save',
        pos  = block.start,
    }
    self.steps[#self.steps+1] = savePoint
    self.steps[#self.steps+1] = {
        type = 'load',
        pos  = block.finish,
        ref  = savePoint,
    }
end

---@param block parser.object
function mt:_compileBlock(block)
    if self.blocks[block] then
        return
    end
    self.blocks[block] = true
    if block == self.mainBlock then
        return
    end

    local parentBlock = guide.getParentBlock(block)
    self:_compileBlock(parentBlock)

    if block.type == 'ifblock' then
        if block[1] then
            self:_compileNarrowByFilter(block.filter, block[1].start)
        end
    end

    if block.type == 'if' then
        self:_dropBlock(block)
    end

    if block.type == 'function' then
        self:_dropBlock(block)
    end
end

function mt:_preCompile()
    for _, ref in ipairs(self.loc.ref) do
        self.steps[#self.steps+1] = {
            type   = 'object',
            object = ref,
            pos  = ref.start,
        }
        local block = guide.getParentBlock(ref)
        self:_compileBlock(block)
    end
    table.sort(self.steps, function (a, b)
        return a.pos < b.pos
    end)
end

---@param callback    fun(src: parser.object, node: vm.node)
function mt:launch(callback)
    local node = vm.getNode(self.loc)
    for _, step in ipairs(self.steps) do
        if     step.type == 'truly' then
            node = node:copyTruly()
        elseif step.type == 'as' then
        elseif step.type == 'object' then
            node = callback(step.object, node) or node
        elseif step.type == 'save' then
            -- Nothing need to do
        elseif step.type == 'load' then
            node = step.ref.node
        end
        step.node = node
    end
end

---@param loc parser.object
---@return vm.runner
function vm.createRunner(loc)
    local self = setmetatable({
        loc       = loc,
        mainBlock = guide.getParentBlock(loc),
        blocks    = {},
        steps     = {},
    }, mt)

    self:_preCompile()

    return self
end