diff options
Diffstat (limited to 'script/vm/compiler.lua')
-rw-r--r-- | script/vm/compiler.lua | 1342 |
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 |