diff options
Diffstat (limited to 'script/vm/node')
-rw-r--r-- | script/vm/node/class.lua | 11 | ||||
-rw-r--r-- | script/vm/node/compiler.lua | 190 | ||||
-rw-r--r-- | script/vm/node/global.lua | 79 | ||||
-rw-r--r-- | script/vm/node/union.lua | 49 |
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 |