summaryrefslogtreecommitdiff
path: root/script/vm/compiler.lua
diff options
context:
space:
mode:
Diffstat (limited to 'script/vm/compiler.lua')
-rw-r--r--script/vm/compiler.lua1342
1 files changed, 1342 insertions, 0 deletions
diff --git a/script/vm/compiler.lua b/script/vm/compiler.lua
new file mode 100644
index 00000000..bfed8ffb
--- /dev/null
+++ b/script/vm/compiler.lua
@@ -0,0 +1,1342 @@
+local guide = require 'parser.guide'
+local util = require 'utility'
+local localID = require 'vm.local-id'
+local globalMgr = require 'vm.global-manager'
+local nodeMgr = require 'vm.node'
+local signMgr = require 'vm.sign'
+local config = require 'config'
+local union = require 'vm.union'
+local genericMgr = require 'vm.generic'
+local rpath = require 'workspace.require-path'
+local files = require 'files'
+
+---@class parser.object
+---@field _compiledNodes boolean
+---@field _node vm.node
+
+---@class vm.node.compiler
+local m = {}
+
+local searchFieldSwitch = util.switch()
+ : case 'table'
+ : call(function (node, key, pushResult)
+ local hasFiled = false
+ for _, field in ipairs(node) do
+ if field.type == 'tablefield'
+ or field.type == 'tableindex' then
+ if key == nil
+ or key == guide.getKeyName(field) then
+ hasFiled = true
+ pushResult(field)
+ end
+ end
+ if field.type == 'tableexp' then
+ if key == nil
+ or key == field.tindex then
+ hasFiled = true
+ pushResult(field)
+ end
+ end
+ if field.type == 'varargs' then
+ if not hasFiled
+ and type(key) == 'number'
+ and key >= 1
+ and math.tointeger(key) then
+ hasFiled = true
+ pushResult(field)
+ end
+ if key == nil then
+ pushResult(field)
+ end
+ end
+ end
+ end)
+ : case 'global'
+ ---@param node vm.node.global
+ : call(function (node, key, pushResult)
+ if node.cate == 'variable' then
+ if key then
+ local global = globalMgr.getGlobal('variable', node.name, key)
+ if global then
+ pushResult(global)
+ end
+ else
+ local globals = globalMgr.getFields('variable', node.name)
+ for _, global in ipairs(globals) do
+ pushResult(global)
+ end
+ end
+ end
+ if node.cate == 'type' then
+ m.getClassFields(node, key, pushResult)
+ end
+ end)
+ : case 'string'
+ : call(function (node, key, pushResult)
+ -- change to `string: stringlib` ?
+ local stringlib = globalMgr.getGlobal('type', 'stringlib')
+ m.getClassFields(stringlib, key, pushResult)
+ end)
+ : case 'local'
+ : call(function (node, key, pushResult)
+ local fields = key and localID.getSources(node, key) or localID.getFields(node)
+ if fields then
+ for _, src in ipairs(fields) do
+ pushResult(src)
+ end
+ end
+ end)
+ : case 'doc.type.array'
+ : call(function (node, key, pushResult)
+ if type(key) == 'number'
+ and key >= 1
+ and math.tointeger(key) then
+ pushResult(node.node)
+ end
+ if key == nil then
+ pushResult(node.node)
+ end
+ end)
+ : case 'doc.type.table'
+ : call(function (node, key, pushResult)
+ for _, field in ipairs(node.fields) do
+ local fieldKey = field.name
+ if fieldKey.type == 'doc.type' then
+ local fieldNode = m.compileNode(fieldKey)
+ for fn in nodeMgr.eachNode(fieldNode) do
+ if fn.type == 'global' and fn.cate == 'type' then
+ if key == nil
+ or fn.name == 'any'
+ or (fn.name == 'boolean' and type(key) == 'boolean')
+ or (fn.name == 'number' and type(key) == 'number')
+ or (fn.name == 'integer' and math.tointeger(key))
+ or (fn.name == 'string' and type(key) == 'string') then
+ pushResult(field)
+ end
+ end
+ end
+ end
+ if fieldKey.type == 'doc.field.name' then
+ if key == nil or fieldKey[1] == key then
+ pushResult(field)
+ end
+ end
+ end
+ end)
+
+
+function m.getClassFields(node, key, pushResult)
+ local mark = {}
+
+ local function searchClass(class)
+ local name = class.name
+ if mark[name] then
+ return
+ end
+ mark[name] = true
+ for _, set in ipairs(class:getSets()) do
+ if set.type == 'doc.class' then
+ -- check ---@field
+ local hasFounded
+ for _, field in ipairs(set.fields) do
+ if key == nil
+ or guide.getKeyName(field) == key then
+ hasFounded = true
+ pushResult(field)
+ end
+ end
+ -- check local field and global field
+ if set.bindSources then
+ for _, src in ipairs(set.bindSources) do
+ searchFieldSwitch(src.type, src, key, function (field)
+ if guide.isSet(field) then
+ hasFounded = true
+ pushResult(field)
+ end
+ end)
+ if src._globalNode then
+ searchFieldSwitch('global', src._globalNode, key, function (field)
+ hasFounded = true
+ pushResult(field)
+ end)
+ end
+ end
+ end
+ -- look into extends(if field not found)
+ if not hasFounded and set.extends then
+ for _, extend in ipairs(set.extends) do
+ if extend.type == 'doc.extends.name' then
+ local extendType = globalMgr.getGlobal('type', extend[1])
+ if extendType then
+ searchClass(extendType)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ local function searchGlobal(class)
+ if class.cate == 'type' and class.name == '_G' then
+ local globals = globalMgr.getGlobals('variable')
+ for _, global in ipairs(globals) do
+ local sets = global:getSets()
+ for _, set in ipairs(sets) do
+ pushResult(set)
+ end
+ end
+ end
+ end
+
+ searchClass(node)
+ searchGlobal(node)
+end
+
+---@class parser.object
+---@field _sign? vm.sign
+
+---@param source parser.object
+---@return vm.sign?
+local function getObjectSign(source)
+ if source._sign ~= nil then
+ return source._sign
+ end
+ source._sign = false
+ if source.type == 'function' then
+ for _, doc in ipairs(source.bindDocs) do
+ if doc.type == 'doc.generic' then
+ if not source._sign then
+ source._sign = signMgr()
+ break
+ end
+ end
+ end
+ if not source._sign then
+ return false
+ end
+ if source.args then
+ for _, arg in ipairs(source.args) do
+ local argNode = m.compileNode(arg)
+ if arg.optional then
+ argNode = nodeMgr.addOptional(argNode)
+ end
+ source._sign:addSign(argNode)
+ end
+ end
+ end
+ if source.type == 'doc.type.function'
+ or source.type == 'doc.type.table' then
+ local hasGeneric
+ guide.eachSourceType(source, 'doc.generic.name', function ()
+ hasGeneric = true
+ end)
+ if not hasGeneric then
+ return false
+ end
+ source._sign = signMgr()
+ if source.type == 'doc.type.function' then
+ for _, arg in ipairs(source.args) do
+ local argNode = m.compileNode(arg.extends)
+ if arg.optional then
+ argNode = nodeMgr.addOptional(argNode)
+ end
+ source._sign:addSign(argNode)
+ end
+ end
+ end
+ return source._sign
+end
+
+function m.getReturnOfFunction(func, index)
+ if func.type == 'function' then
+ if not func._returns then
+ func._returns = {}
+ end
+ if not func._returns[index] then
+ func._returns[index] = {
+ type = 'function.return',
+ parent = func,
+ index = index,
+ }
+ end
+ return m.compileNode(func._returns[index])
+ end
+ if func.type == 'doc.type.function' then
+ local rtn = func.returns[index]
+ if not rtn then
+ return nil
+ end
+ local rtnNode = m.compileNode(rtn)
+ local sign = getObjectSign(func)
+ if not sign then
+ return rtnNode
+ end
+ return genericMgr(rtnNode, sign)
+ end
+end
+
+local function getReturnOfSetMetaTable(args)
+ local tbl = args and args[1]
+ local mt = args and args[2]
+ local node = union()
+ if tbl then
+ node:merge(m.compileNode(tbl))
+ end
+ if mt then
+ m.compileByParentNode(mt, '__index', function (src)
+ for n in nodeMgr.eachNode(m.compileNode(src)) do
+ if n.type == 'global'
+ or n.type == 'local'
+ or n.type == 'table'
+ or n.type == 'doc.type.table' then
+ node:merge(n)
+ end
+ end
+ end)
+ end
+ return node
+end
+
+local function getReturn(func, index, args)
+ if func.special == 'setmetatable' then
+ return getReturnOfSetMetaTable(args)
+ end
+ if func.special == 'pcall' and index > 1 then
+ local newArgs = {}
+ for i = 2, #args do
+ newArgs[#newArgs+1] = args[i]
+ end
+ return getReturn(args[1], index - 1, newArgs)
+ end
+ if func.special == 'xpcall' and index > 1 then
+ local newArgs = {}
+ for i = 3, #args do
+ newArgs[#newArgs+1] = args[i]
+ end
+ return getReturn(args[1], index - 1, newArgs)
+ end
+ if func.special == 'require' then
+ local nameArg = args[1]
+ if not nameArg or nameArg.type ~= 'string' then
+ return nil
+ end
+ local name = nameArg[1]
+ if not name or type(name) ~= 'string' then
+ return nil
+ end
+ local uri = rpath.findUrisByRequirePath(guide.getUri(func), name)[1]
+ if not uri then
+ return nil
+ end
+ local state = files.getState(uri)
+ local ast = state and state.ast
+ if not ast then
+ return nil
+ end
+ return m.compileNode(ast)
+ end
+ local node = m.compileNode(func)
+ ---@type vm.node.union
+ local result, hasCalled
+ if node then
+ for cnode in nodeMgr.eachNode(node) do
+ if cnode.type == 'function'
+ or cnode.type == 'doc.type.function' then
+ hasCalled = true
+ local returnNode = m.getReturnOfFunction(cnode, index)
+ if returnNode and returnNode.type == 'generic' then
+ returnNode = returnNode:resolve(args)
+ end
+ if returnNode and returnNode.type ~= 'doc.generic.name' then
+ result = result or union()
+ result:merge(m.compileNode(returnNode))
+ end
+ end
+ end
+ end
+ if not hasCalled then
+ result = globalMgr.getGlobal('type', 'unknown')
+ end
+ return result
+end
+
+local function bindDocs(source)
+ local hasFounded = false
+ local isParam = source.parent.type == 'funcargs'
+ or source.parent.type == 'in'
+ local docs = source.bindDocs
+ for i = #docs, 1, -1 do
+ local doc = docs[i]
+ if doc.type == 'doc.type' then
+ if not isParam then
+ nodeMgr.setNode(source, m.compileNode(doc))
+ return true
+ end
+ end
+ if doc.type == 'doc.class' then
+ if (source.type == 'local' and not isParam)
+ or (source._globalNode and guide.isSet(source))
+ or source.type == 'tablefield'
+ or source.type == 'tableindex' then
+ nodeMgr.setNode(source, m.compileNode(doc))
+ return true
+ end
+ end
+ if doc.type == 'doc.param' then
+ if isParam and source[1] == doc.param[1] then
+ nodeMgr.setNode(source, m.compileNode(doc))
+ return true
+ end
+ end
+ end
+ return false
+end
+
+local function compileByLocalID(source)
+ local sources = localID.getSources(source)
+ if not sources then
+ return
+ end
+ local hasMarkDoc
+ for _, src in ipairs(sources) do
+ if src.bindDocs then
+ if bindDocs(src) then
+ hasMarkDoc = true
+ nodeMgr.setNode(source, m.compileNode(src))
+ end
+ end
+ end
+ for _, src in ipairs(sources) do
+ if src.value then
+ if not hasMarkDoc or guide.isLiteral(src.value) then
+ nodeMgr.setNode(source, m.compileNode(src.value))
+ end
+ end
+ end
+end
+
+---@param source vm.node
+---@param key? any
+---@param pushResult fun(source: parser.object)
+function m.compileByParentNode(source, key, pushResult)
+ local parentNode = m.compileNode(source)
+ if not parentNode then
+ return
+ end
+ for node in nodeMgr.eachNode(parentNode) do
+ searchFieldSwitch(node.type, node, key, pushResult)
+ end
+end
+
+local function selectNode(source, list, index)
+ local exp
+ if list[index] then
+ exp = list[index]
+ else
+ for i = index, 1, -1 do
+ if list[i] then
+ local last = list[i]
+ if last.type == 'call'
+ or last.type == '...' then
+ index = index - i + 1
+ exp = last
+ end
+ break
+ end
+ end
+ end
+ if not exp then
+ return nil
+ end
+ local result
+ if exp.type == 'call' then
+ result = getReturn(exp.node, index, exp.args)
+ else
+ result = m.compileNode(exp)
+ end
+ if not result then
+ return nodeMgr.setNode(source, result)
+ end
+ local hasKnownType
+ for n in nodeMgr.eachNode(result) do
+ if guide.isLiteral(n)
+ or (n.type == 'global' and n.cate == 'type') then
+ hasKnownType = true
+ break
+ end
+ end
+ if not hasKnownType then
+ result = nodeMgr.mergeNode(result, globalMgr.getGlobal('type', 'unknown'))
+ end
+ return nodeMgr.setNode(source, result)
+end
+
+---@param source parser.object
+---@param node vm.node
+---@return boolean
+local function isValidCallArgNode(source, node)
+ if source.type == 'function' then
+ return node.type == 'doc.type.function'
+ end
+ if source.type == 'table' then
+ return node.type == 'doc.type.table'
+ or (node.type == 'global' and node.cate == 'type' and not guide.isBasicType(node.name))
+ end
+ return false
+end
+
+local function setCallArgNode(source, call, callNode, fixIndex)
+ local valueMgr = require 'vm.value'
+ local myIndex
+ for i, arg in ipairs(call.args) do
+ if arg == source then
+ myIndex = i - fixIndex
+ break
+ end
+ end
+
+ local eventIndex = 1
+ local eventArg = call.args[eventIndex + fixIndex]
+ if eventArg and eventArg.dummy then
+ eventIndex = 2
+ eventArg = call.args[eventIndex + fixIndex]
+ end
+ local eventMap = valueMgr.getLiterals(eventArg)
+
+ for n in nodeMgr.eachNode(callNode) do
+ if n.type == 'function' then
+ local arg = n.args[myIndex]
+ for fn in nodeMgr.eachNode(m.compileNode(arg)) do
+ if isValidCallArgNode(source, fn) then
+ nodeMgr.setNode(source, fn)
+ end
+ end
+ end
+ if n.type == 'doc.type.function' then
+ local event = m.compileNode(n.args[eventIndex])
+ if not event
+ or not eventMap
+ or event.type ~= 'doc.type.string'
+ or eventMap[event[1]] then
+ local arg = n.args[myIndex]
+ for fn in nodeMgr.eachNode(m.compileNode(arg)) do
+ if isValidCallArgNode(source, fn) then
+ nodeMgr.setNode(source, fn)
+ end
+ end
+ end
+ end
+ end
+end
+
+local compilerSwitch = util.switch()
+ : case 'nil'
+ : case 'boolean'
+ : case 'integer'
+ : case 'number'
+ : case 'string'
+ : case 'union'
+ : case 'doc.type.function'
+ : case 'doc.type.table'
+ : case 'doc.type.array'
+ : call(function (source)
+ nodeMgr.setNode(source, source)
+ end)
+ : case 'table'
+ : call(function (source)
+ nodeMgr.setNode(source, source)
+
+ if source.parent.type == 'callargs' then
+ local call = source.parent.parent
+ local callNode = m.compileNode(call.node)
+ setCallArgNode(source, call, callNode, 0)
+
+ if call.node.special == 'pcall'
+ or call.node.special == 'xpcall' then
+ local fixIndex = call.node.special == 'pcall' and 1 or 2
+ callNode = m.compileNode(call.args[1])
+ setCallArgNode(source, call, callNode, fixIndex)
+ end
+ end
+
+ if source.parent.type == 'setglobal'
+ or source.parent.type == 'setlocal'
+ or source.parent.type == 'tablefield'
+ or source.parent.type == 'tableindex'
+ or source.parent.type == 'setfield'
+ or source.parent.type == 'setindex' then
+ nodeMgr.setNode(source, m.compileNode(source.parent))
+ end
+ end)
+ : case 'function'
+ : call(function (source)
+ nodeMgr.setNode(source, source)
+
+ if source.bindDocs then
+ for _, doc in ipairs(source.bindDocs) do
+ if doc.type == 'doc.overload' then
+ nodeMgr.setNode(source, m.compileNode(doc))
+ end
+ end
+ end
+
+ -- table.sort(string[], function (<?x?>) end)
+ if source.parent.type == 'callargs' then
+ local call = source.parent.parent
+ local callNode = m.compileNode(call.node)
+ setCallArgNode(source, call, callNode, 0)
+
+ if call.node.special == 'pcall'
+ or call.node.special == 'xpcall' then
+ local fixIndex = call.node.special == 'pcall' and 1 or 2
+ callNode = m.compileNode(call.args[1])
+ setCallArgNode(source, call, callNode, fixIndex)
+ end
+ end
+ end)
+ : case 'paren'
+ : call(function (source)
+ nodeMgr.setNode(source, m.compileNode(source.exp))
+ end)
+ : case 'local'
+ : call(function (source)
+ --localMgr.declareLocal(source)
+ nodeMgr.setNode(source, source)
+ local hasMarkDoc
+ if source.bindDocs then
+ hasMarkDoc = bindDocs(source)
+ end
+ if source.ref and not hasMarkDoc then
+ for _, ref in ipairs(source.ref) do
+ if ref.type == 'setlocal' then
+ nodeMgr.setNode(source, m.compileNode(ref.value))
+ end
+ end
+ end
+ local hasMarkParam
+ if source.dummy and not hasMarkDoc then
+ hasMarkParam = true
+ nodeMgr.setNode(source, m.compileNode(source.method.node))
+ end
+ if source.value then
+ if not hasMarkDoc or guide.isLiteral(source.value) then
+ nodeMgr.setNode(source, m.compileNode(source.value))
+ end
+ end
+ -- function x.y(self, ...) --> function x:y(...)
+ if source[1] == 'self'
+ and not hasMarkDoc
+ and source.parent.type == 'funcargs'
+ and source.parent[1] == source then
+ local setfield = source.parent.parent.parent
+ if setfield.type == 'setfield' then
+ hasMarkParam = true
+ nodeMgr.setNode(source, m.compileNode(setfield.node))
+ end
+ end
+ if source.parent.type == 'funcargs' and not hasMarkDoc and not hasMarkParam then
+ local func = source.parent.parent
+ local funcNode = m.compileNode(func)
+ local hasDocArg
+ for n in nodeMgr.eachNode(funcNode) do
+ if n.type == 'doc.type.function' then
+ for index, arg in ipairs(n.args) do
+ if func.args[index] == source then
+ nodeMgr.setNode(source, m.compileNode(arg))
+ hasDocArg = true
+ end
+ end
+ end
+ end
+ if not hasDocArg then
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'any'))
+ end
+ end
+ -- for x in ... do
+ if source.parent.type == 'in' then
+ m.compileNode(source.parent)
+ end
+
+ -- for x = ... do
+ if source.parent.type == 'loop' then
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ end
+ end)
+ : case 'setlocal'
+ : case 'getlocal'
+ : call(function (source)
+ nodeMgr.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)
+ m.compileByParentNode(source.node, guide.getKeyName(source), function (src)
+ nodeMgr.setNode(source, m.compileNode(src))
+ end)
+ end)
+ : case 'tablefield'
+ : case 'tableindex'
+ : call(function (source)
+ local hasMarkDoc
+ if source.bindDocs then
+ hasMarkDoc = bindDocs(source)
+ end
+
+ if source.value then
+ if not hasMarkDoc or guide.isLiteral(source.value) then
+ nodeMgr.setNode(source, m.compileNode(source.value))
+ end
+ end
+
+ if not hasMarkDoc then
+ m.compileByParentNode(source.parent, guide.getKeyName(source), function (src)
+ nodeMgr.setNode(source, m.compileNode(src))
+ end)
+ end
+ end)
+ : case 'field'
+ : case 'method'
+ : call(function (source)
+ nodeMgr.setNode(source, m.compileNode(source.parent))
+ end)
+ : case 'tableexp'
+ : call(function (source)
+ nodeMgr.setNode(source, m.compileNode(source.value))
+ end)
+ : case 'function.return'
+ : call(function (source)
+ local func = source.parent
+ local index = source.index
+ local hasMarkDoc
+ if func.bindDocs then
+ local sign = getObjectSign(func)
+ for _, doc in ipairs(func.bindDocs) do
+ if doc.type == 'doc.return' then
+ for _, rtn in ipairs(doc.returns) do
+ if rtn.returnIndex == index then
+ hasMarkDoc = true
+ local hasGeneric
+ if sign then
+ guide.eachSourceType(rtn, 'doc.generic.name', function (src)
+ hasGeneric = true
+ end)
+ end
+ local rtnNode = m.compileNode(rtn)
+ if hasGeneric then
+ nodeMgr.setNode(source, genericMgr(rtnNode, sign))
+ else
+ nodeMgr.setNode(source, rtnNode)
+ end
+ end
+ end
+ end
+ end
+ end
+ if func.returns and not hasMarkDoc then
+ for _, rtn in ipairs(func.returns) do
+ selectNode(source, rtn, index)
+ end
+ end
+ end)
+ : case 'main'
+ : call(function (source)
+ if source.returns then
+ for _, rtn in ipairs(source.returns) do
+ nodeMgr.setNode(source, m.compileNode(rtn[1]))
+ end
+ end
+ end)
+ : case 'select'
+ : call(function (source)
+ local vararg = source.vararg
+ if vararg.type == 'call' then
+ local node = getReturn(vararg.node, source.sindex, vararg.args)
+ nodeMgr.setNode(source, node)
+ end
+ if vararg.type == 'varargs' then
+ nodeMgr.setNode(source, m.compileNode(vararg))
+ end
+ end)
+ : case 'varargs'
+ : call(function (source)
+ nodeMgr.setNode(source, m.compileNode(source.node))
+ end)
+ : case 'call'
+ : call(function (source)
+ local node = getReturn(source.node, 1, source.args)
+ nodeMgr.setNode(source, node)
+ end)
+ : case 'in'
+ : call(function (source)
+ if not source._iterator then
+ -- for k, v in pairs(t) do
+ --> for k, v in iterator, status, initValue do
+ --> local k, v = iterator(status, initValue)
+ source._iterator = {}
+ source._iterArgs = {{}, {}}
+ -- iterator
+ selectNode(source._iterator, source.exps, 1)
+ -- status
+ selectNode(source._iterArgs[1], source.exps, 2)
+ -- initValue
+ selectNode(source._iterArgs[2], source.exps, 3)
+ end
+ for i, loc in ipairs(source.keys) do
+ local node = getReturn(source._iterator, i, source._iterArgs)
+ nodeMgr.setNode(loc, node)
+ end
+ end)
+ : case 'doc.type'
+ : call(function (source)
+ for _, typeUnit in ipairs(source.types) do
+ nodeMgr.setNode(source, m.compileNode(typeUnit))
+ end
+ end)
+ : case 'doc.type.integer'
+ : call(function (source)
+ nodeMgr.setNode(source, source)
+ end)
+ : case 'doc.type.string'
+ : call(function (source)
+ nodeMgr.setNode(source, source)
+ end)
+ : case 'doc.generic.name'
+ : call(function (source)
+ nodeMgr.setNode(source, source)
+ end)
+ : case 'doc.type.name'
+ : call(function (source)
+ if source.signs then
+ nodeMgr.setNode(source, source)
+ local global = globalMgr.getGlobal('type', source[1])
+ for _, set in ipairs(global:getSets()) do
+ if set.type == 'doc.class' then
+ if set.extends then
+ for _, ext in ipairs(set.extends) do
+ if ext.type == 'doc.type.table' then
+ if ext._generic then
+ local resolved = ext._generic:resolve(source.signs)
+ nodeMgr.setNode(source, resolved)
+ end
+ end
+ end
+ end
+ end
+ if set.type == 'doc.alias' then
+ if set.extends._generic then
+ local resolved = set.extends._generic:resolve(source.signs)
+ nodeMgr.setNode(source, resolved)
+ end
+ end
+ end
+ end
+ end)
+ : case 'doc.class.name'
+ : call(function (source)
+ nodeMgr.setNode(source, m.compileNode(source.parent))
+ end)
+ : case 'doc.field'
+ : call(function (source)
+ nodeMgr.setNode(source, m.compileNode(source.extends))
+ end)
+ : case 'doc.type.field'
+ : call(function (source)
+ nodeMgr.setNode(source, m.compileNode(source.extends))
+ end)
+ : case 'doc.param'
+ : call(function (source)
+ nodeMgr.setNode(source, m.compileNode(source.extends))
+ end)
+ : case 'doc.vararg'
+ : call(function (source)
+ nodeMgr.setNode(source, m.compileNode(source.vararg))
+ end)
+ : case '...'
+ : call(function (source)
+ local func = source.parent.parent
+ if func.type ~= 'function' then
+ return
+ end
+ if not func.bindDocs then
+ return
+ end
+ for _, doc in ipairs(func.bindDocs) do
+ if doc.type == 'doc.vararg' then
+ nodeMgr.setNode(source, m.compileNode(doc))
+ end
+ if doc.type == 'doc.param' and doc.param[1] == '...' then
+ nodeMgr.setNode(source, m.compileNode(doc))
+ end
+ end
+ end)
+ : case 'doc.overload'
+ : call(function (source)
+ nodeMgr.setNode(source, m.compileNode(source.overload))
+ end)
+ : case 'doc.see.name'
+ : call(function (source)
+ local type = globalMgr.getGlobal('type', source[1])
+ if type then
+ nodeMgr.setNode(source, m.compileNode(type))
+ end
+ end)
+ : case 'doc.type.arg'
+ : call(function (source)
+ if source.extends then
+ nodeMgr.setNode(source, m.compileNode(source.extends))
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'any'))
+ end
+ end)
+ : case 'generic'
+ : call(function (source)
+ nodeMgr.setNode(source, source)
+ end)
+ : case 'unary'
+ : call(function (source)
+ local valueMgr = require 'vm.value'
+ if source.op.type == 'not' then
+ local result = valueMgr.test(source[1])
+ if result == nil then
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'boolean'))
+ return
+ else
+ nodeMgr.setNode(source, {
+ type = 'boolean',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = not result,
+ })
+ return
+ end
+ end
+ if source.op.type == '#' then
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ return
+ end
+ if source.op.type == '-' then
+ local v = valueMgr.getNumber(source[1])
+ if v == nil then
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'number'))
+ return
+ else
+ nodeMgr.setNode(source, {
+ type = 'number',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = -v,
+ })
+ return
+ end
+ end
+ if source.op.type == '~' then
+ local v = valueMgr.getInteger(source[1])
+ if v == nil then
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ return
+ else
+ nodeMgr.setNode(source, {
+ type = 'integer',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = ~v,
+ })
+ return
+ end
+ end
+ end)
+ : case 'binary'
+ : call(function (source)
+ local valueMgr = require 'vm.value'
+ if source.op.type == 'and' then
+ local r1 = valueMgr.test(source[1])
+ if r1 == true then
+ nodeMgr.setNode(source, m.compileNode(source[2]))
+ return
+ end
+ if r1 == false then
+ nodeMgr.setNode(source, m.compileNode(source[1]))
+ return
+ end
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'boolean'))
+ return
+ end
+ if source.op.type == 'or' then
+ local r1 = valueMgr.test(source[1])
+ if r1 == true then
+ nodeMgr.setNode(source, m.compileNode(source[1]))
+ return
+ end
+ if r1 == false then
+ nodeMgr.setNode(source, m.compileNode(source[2]))
+ return
+ end
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'boolean'))
+ return
+ end
+ if source.op.type == '==' then
+ local result = valueMgr.equal(source[1], source[2])
+ if result == nil then
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'boolean'))
+ return
+ else
+ nodeMgr.setNode(source, {
+ type = 'boolean',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = result,
+ })
+ return
+ end
+ end
+ if source.op.type == '~=' then
+ local result = valueMgr.equal(source[1], source[2])
+ if result == nil then
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'boolean'))
+ return
+ else
+ nodeMgr.setNode(source, {
+ type = 'boolean',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = not result,
+ })
+ return
+ end
+ end
+ if source.op.type == '<<' then
+ local a = valueMgr.getInteger(source[1])
+ local b = valueMgr.getInteger(source[2])
+ if a and b then
+ nodeMgr.setNode(source, {
+ type = 'integer',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = a << b,
+ })
+ return
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ return
+ end
+ end
+ if source.op.type == '>>' then
+ local a = valueMgr.getInteger(source[1])
+ local b = valueMgr.getInteger(source[2])
+ if a and b then
+ nodeMgr.setNode(source, {
+ type = 'integer',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = a >> b,
+ })
+ return
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ return
+ end
+ end
+ if source.op.type == '&' then
+ local a = valueMgr.getInteger(source[1])
+ local b = valueMgr.getInteger(source[2])
+ if a and b then
+ nodeMgr.setNode(source, {
+ type = 'integer',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = a & b,
+ })
+ return
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ return
+ end
+ end
+ if source.op.type == '|' then
+ local a = valueMgr.getInteger(source[1])
+ local b = valueMgr.getInteger(source[2])
+ if a and b then
+ nodeMgr.setNode(source, {
+ type = 'integer',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = a | b,
+ })
+ return
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ return
+ end
+ end
+ if source.op.type == '~' then
+ local a = valueMgr.getInteger(source[1])
+ local b = valueMgr.getInteger(source[2])
+ if a and b then
+ nodeMgr.setNode(source, {
+ type = 'integer',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = a ~ b,
+ })
+ return
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ return
+ end
+ end
+ if source.op.type == '+' then
+ local a = valueMgr.getNumber(source[1])
+ local b = valueMgr.getNumber(source[2])
+ if a and b then
+ local result = a + b
+ nodeMgr.setNode(source, {
+ type = math.type(result) == 'integer' and 'integer' or 'number',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = result,
+ })
+ return
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'number'))
+ return
+ end
+ end
+ if source.op.type == '-' then
+ local a = valueMgr.getNumber(source[1])
+ local b = valueMgr.getNumber(source[2])
+ if a and b then
+ local result = a - b
+ nodeMgr.setNode(source, {
+ type = math.type(result) == 'integer' and 'integer' or 'number',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = result,
+ })
+ return
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'number'))
+ return
+ end
+ end
+ if source.op.type == '*' then
+ local a = valueMgr.getNumber(source[1])
+ local b = valueMgr.getNumber(source[2])
+ if a and b then
+ local result = a * b
+ nodeMgr.setNode(source, {
+ type = math.type(result) == 'integer' and 'integer' or 'number',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = result,
+ })
+ return
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'number'))
+ return
+ end
+ end
+ if source.op.type == '/' then
+ local a = valueMgr.getNumber(source[1])
+ local b = valueMgr.getNumber(source[2])
+ if a and b then
+ nodeMgr.setNode(source, {
+ type = 'number',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = a / b,
+ })
+ return
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'number'))
+ return
+ end
+ end
+ if source.op.type == '%' then
+ local a = valueMgr.getNumber(source[1])
+ local b = valueMgr.getNumber(source[2])
+ if a and b then
+ local result = a % b
+ nodeMgr.setNode(source, {
+ type = math.type(result) == 'integer' and 'integer' or 'number',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = result,
+ })
+ return
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'number'))
+ return
+ end
+ end
+ if source.op.type == '^' then
+ local a = valueMgr.getNumber(source[1])
+ local b = valueMgr.getNumber(source[2])
+ if a and b then
+ nodeMgr.setNode(source, {
+ type = 'number',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = a ^ b,
+ })
+ return
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'number'))
+ return
+ end
+ end
+ if source.op.type == '//' then
+ local a = valueMgr.getNumber(source[1])
+ local b = valueMgr.getNumber(source[2])
+ if a and b then
+ local result = a // b
+ nodeMgr.setNode(source, {
+ type = math.type(result) == 'integer' and 'integer' or 'number',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = result,
+ })
+ return
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'number'))
+ return
+ end
+ end
+ if source.op.type == '..' then
+ local a = valueMgr.getString(source[1])
+ or valueMgr.getNumber(source[1])
+ local b = valueMgr.getString(source[2])
+ or valueMgr.getNumber(source[2])
+ if a and b then
+ if type(a) == 'number' or type(b) == 'number' then
+ local uri = guide.getUri(source)
+ local version = config.get(uri, 'Lua.runtime.version')
+ if math.tointeger(a) and math.type(a) == 'float' then
+ if version == 'Lua 5.3' or version == 'Lua 5.4' then
+ a = ('%.1f'):format(a)
+ else
+ a = ('%.0f'):format(a)
+ end
+ end
+ if math.tointeger(b) and math.type(b) == 'float' then
+ if version == 'Lua 5.3' or version == 'Lua 5.4' then
+ b = ('%.1f'):format(b)
+ else
+ b = ('%.0f'):format(b)
+ end
+ end
+ end
+ nodeMgr.setNode(source, {
+ type = 'string',
+ start = source.start,
+ finish = source.finish,
+ parent = source,
+ [1] = a .. b,
+ })
+ return
+ else
+ nodeMgr.setNode(source, globalMgr.getGlobal('type', 'string'))
+ return
+ end
+ end
+ end)
+
+---@param source parser.object
+local function compileByNode(source)
+ compilerSwitch(source.type, source)
+end
+
+---@param source vm.node
+local function compileByGlobal(source)
+ if source.type == 'global' then
+ nodeMgr.setNode(source, source)
+ if source.cate == 'variable' then
+ local hasMarkDoc
+ for _, set in ipairs(source:getSets()) do
+ if set.bindDocs then
+ if bindDocs(set) then
+ nodeMgr.setNode(source, m.compileNode(set))
+ hasMarkDoc = true
+ end
+ end
+ end
+ for _, set in ipairs(source:getSets()) do
+ if set.value then
+ if not hasMarkDoc or guide.isLiteral(set.value) then
+ nodeMgr.setNode(source, m.compileNode(set.value))
+ end
+ end
+ end
+ end
+ if source.cate == 'type' then
+ for _, set in ipairs(source:getSets()) do
+ if set.type == 'doc.class' then
+ if set.extends then
+ for _, ext in ipairs(set.extends) do
+ if ext.type == 'doc.type.table' then
+ if not ext._generic then
+ nodeMgr.setNode(source, m.compileNode(ext))
+ end
+ end
+ end
+ end
+ end
+ if set.type == 'doc.alias' then
+ if not set.extends._generic then
+ nodeMgr.setNode(source, m.compileNode(set.extends))
+ end
+ end
+ end
+ end
+ return
+ end
+ if source._globalNode then
+ nodeMgr.setNode(source, m.compileNode(source._globalNode))
+ return
+ end
+end
+
+---@param source parser.object
+---@return vm.node
+function m.compileNode(source)
+ if not source then
+ return false
+ end
+ if nodeMgr.nodeCache[source] ~= nil then
+ return nodeMgr.nodeCache[source]
+ end
+ nodeMgr.nodeCache[source] = false
+ compileByGlobal(source)
+ compileByNode(source)
+
+ --localMgr.subscribeLocal(source, source._node)
+
+ return nodeMgr.nodeCache[source]
+end
+
+return m