summaryrefslogtreecommitdiff
path: root/script/vm/node
diff options
context:
space:
mode:
Diffstat (limited to 'script/vm/node')
-rw-r--r--script/vm/node/class.lua11
-rw-r--r--script/vm/node/compiler.lua190
-rw-r--r--script/vm/node/global.lua79
-rw-r--r--script/vm/node/union.lua49
4 files changed, 329 insertions, 0 deletions
diff --git a/script/vm/node/class.lua b/script/vm/node/class.lua
new file mode 100644
index 00000000..ee8b1ca8
--- /dev/null
+++ b/script/vm/node/class.lua
@@ -0,0 +1,11 @@
+---@class vm.node.class
+local mt = {}
+mt.__index = mt
+mt.type = 'class'
+
+---@return vm.node.class
+return function ()
+ local class = setmetatable({
+ }, mt)
+ return class
+end
diff --git a/script/vm/node/compiler.lua b/script/vm/node/compiler.lua
new file mode 100644
index 00000000..dfe4bc0c
--- /dev/null
+++ b/script/vm/node/compiler.lua
@@ -0,0 +1,190 @@
+local guide = require 'parser.guide'
+local util = require 'utility'
+local state = require 'vm.state'
+local union = require 'vm.node.union'
+local localID = require 'vm.local-id'
+
+---@class parser.object
+---@field _compiledNodes boolean
+---@field _node vm.node
+
+---@class vm.node.compiler
+local m = {}
+
+---@class vm.node.cross
+
+---@alias vm.node parser.object | vm.node.union | vm.node.cross | vm.node.global
+
+function m.setNode(source, node)
+ if not node then
+ return
+ end
+ local me = source._node
+ if not me then
+ source._node = node
+ return
+ end
+ if me.type == 'union'
+ or me.type == 'cross' then
+ me:merge(node)
+ return
+ end
+ source._node = union(me, node)
+end
+
+function m.eachNode(node)
+ if node.type == 'union' then
+ return node:eachNode()
+ end
+ local first = true
+ return function ()
+ if first then
+ first = false
+ return node
+ end
+ return nil
+ end
+end
+
+local function getReturnOfFunction(func, index)
+ if not func._returns then
+ func._returns = util.defaultTable(function ()
+ return {
+ type = 'function.return',
+ parent = func,
+ index = index,
+ }
+ end)
+ end
+ return m.compileNode(func._returns[index])
+end
+
+local function getReturn(func, index)
+ local node = m.compileNode(func)
+ if not node then
+ return
+ end
+ for cnode in m.eachNode(node) do
+ if cnode.type == 'function' then
+ return getReturnOfFunction(cnode, index)
+ end
+ end
+end
+
+local function compileByLocalID(source)
+ local sources = localID.getSources(source)
+ if not sources then
+ return
+ end
+ for _, src in ipairs(sources) do
+ if src.value then
+ m.setNode(source, m.compileNode(src.value))
+ end
+ end
+end
+
+local searchFieldMap = util.switch()
+ : case 'table'
+ : call(function (node, key, pushResult)
+ for _, field in ipairs(node) do
+ if field.type == 'tablefield'
+ or field.type == 'tableindex' then
+ if guide.getKeyName(field) == key then
+ pushResult(field)
+ end
+ end
+ end
+ end)
+ : getMap()
+
+local function compileByParentNode(source)
+ local parentNode = m.compileNode(source.node)
+ if not parentNode then
+ return
+ end
+ local key = guide.getKeyName(source)
+ local f = searchFieldMap[parentNode.type]
+ if f then
+ f(parentNode, key, function (field)
+ m.setNode(source, m.compileNode(field.value))
+ end)
+ end
+end
+
+local compilerMap = util.switch()
+ : case 'boolean'
+ : case 'table'
+ : case 'integer'
+ : case 'number'
+ : case 'string'
+ : case 'function'
+ : call(function (source)
+ m.setNode(source, state.declareLiteral(source))
+ end)
+ : case 'local'
+ : call(function (source)
+ if source.value then
+ m.setNode(source, m.compileNode(source.value))
+ end
+ if source.ref then
+ for _, ref in ipairs(source.ref) do
+ if ref.type == 'setlocal' then
+ m.setNode(source, m.compileNode(ref.value))
+ end
+ end
+ end
+ end)
+ : case 'getlocal'
+ : call(function (source)
+ m.setNode(source, m.compileNode(source.node))
+ end)
+ : case 'setfield'
+ : case 'setmethod'
+ : case 'setindex'
+ : call(function (source)
+ compileByLocalID(source)
+ end)
+ : case 'getfield'
+ : case 'getmethod'
+ : case 'getindex'
+ : call(function (source)
+ compileByLocalID(source)
+ compileByParentNode(source)
+ end)
+ : case 'function.return'
+ : call(function (source)
+ local func = source.parent
+ local index = source.index
+ if func.returns then
+ for _, rtn in ipairs(func.returns) do
+ if rtn[index] then
+ m.setNode(source, m.compileNode(rtn[index]))
+ end
+ end
+ end
+ end)
+ : case 'select'
+ : call(function (source, value)
+ local vararg = value.vararg
+ if vararg.type == 'call' then
+ m.setNode(source, getReturn(vararg.node, value.sindex))
+ end
+ end)
+ : getMap()
+
+---@param source parser.object
+---@return vm.node
+function m.compileNode(source)
+ if source._node then
+ return source._node
+ end
+ source._node = false
+ local compiler = compilerMap[source.type]
+ if compiler then
+ compiler(source)
+ end
+ state.subscribeLiteral(source, source._node)
+ return source._node
+end
+
+return m
diff --git a/script/vm/node/global.lua b/script/vm/node/global.lua
new file mode 100644
index 00000000..499e526b
--- /dev/null
+++ b/script/vm/node/global.lua
@@ -0,0 +1,79 @@
+local util = require 'utility'
+
+---@class vm.node.global.link
+---@field gets parser.object[]
+---@field sets parser.object[]
+
+---@class vm.node.global
+---@field links table<uri, vm.node.global.link>
+---@field setsCache parser.object[]
+---@field getsCache parser.object[]
+local mt = {}
+mt.__index = mt
+mt.type = 'global'
+mt.name = ''
+
+---@param uri uri
+---@param source parser.object
+function mt:addSet(uri, source)
+ local link = self.links[uri]
+ link.sets[#link.sets+1] = source
+end
+
+---@param uri uri
+---@param source parser.object
+function mt:addGet(uri, source)
+ local link = self.links[uri]
+ link.gets[#link.gets+1] = source
+end
+
+---@return parser.object[]
+function mt:getSets()
+ if not self.setsCache then
+ self.setsCache = {}
+ for _, link in pairs(self.links) do
+ for _, source in ipairs(link.sets) do
+ self.setsCache[#self.setsCache+1] = source
+ end
+ end
+ end
+ return self.setsCache
+end
+
+---@return parser.object[]
+function mt:getGets()
+ if not self.getsCache then
+ self.getsCache = {}
+ for _, link in pairs(self.links) do
+ for _, source in ipairs(link.gets) do
+ self.getsCache[#self.getsCache+1] = source
+ end
+ end
+ end
+ return self.getsCache
+end
+
+---@param uri uri
+function mt:dropUri(uri)
+ self.links[uri] = nil
+ self.setsCache = nil
+ self.getsCache = nil
+end
+
+---@return string
+function mt:getName()
+ return self.name
+end
+
+---@return vm.node.global
+return function (name)
+ return setmetatable({
+ name = name,
+ links = util.defaultTable(function ()
+ return {
+ sets = {},
+ gets = {},
+ }
+ end),
+ }, mt)
+end
diff --git a/script/vm/node/union.lua b/script/vm/node/union.lua
new file mode 100644
index 00000000..a8c917d9
--- /dev/null
+++ b/script/vm/node/union.lua
@@ -0,0 +1,49 @@
+local state = require 'vm.state'
+
+---@class vm.node.union
+local mt = {}
+mt.__index = mt
+mt.type = 'union'
+
+---@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
+ self[#self+1] = c
+ end
+ else
+ self[#self+1] = node
+ end
+end
+
+---@param source parser.object
+function mt:subscribeLiteral(source)
+ for _, c in ipairs(self) do
+ state.subscribeLiteral(source, c)
+ if c.type == 'cross' then
+ c:subscribeLiteral(source)
+ end
+ end
+end
+
+function mt:eachNode()
+ local i = 0
+ return function ()
+ i = i + 1
+ return self[i]
+ end
+end
+
+---@param me parser.object
+---@param node vm.node
+---@return vm.node.union
+return function (me, node)
+ local union = setmetatable({
+ [1] = me,
+ }, mt)
+ union:merge(node)
+ return union
+end