summaryrefslogtreecommitdiff
path: root/script/src/vm
diff options
context:
space:
mode:
Diffstat (limited to 'script/src/vm')
-rw-r--r--script/src/vm/chain.lua65
-rw-r--r--script/src/vm/emmy.lua372
-rw-r--r--script/src/vm/function.lua551
-rw-r--r--script/src/vm/global.lua25
-rw-r--r--script/src/vm/init.lua1
-rw-r--r--script/src/vm/ipairs.lua51
-rw-r--r--script/src/vm/label.lua75
-rw-r--r--script/src/vm/library.lua112
-rw-r--r--script/src/vm/list.lua30
-rw-r--r--script/src/vm/local.lua191
-rw-r--r--script/src/vm/manager.lua17
-rw-r--r--script/src/vm/module.lua56
-rw-r--r--script/src/vm/multi.lua83
-rw-r--r--script/src/vm/pcall.lua50
-rw-r--r--script/src/vm/raw.lua30
-rw-r--r--script/src/vm/source.lua183
-rw-r--r--script/src/vm/special.lua130
-rw-r--r--script/src/vm/value.lua634
-rw-r--r--script/src/vm/vm.lua1334
19 files changed, 3990 insertions, 0 deletions
diff --git a/script/src/vm/chain.lua b/script/src/vm/chain.lua
new file mode 100644
index 00000000..6e7c6ac7
--- /dev/null
+++ b/script/src/vm/chain.lua
@@ -0,0 +1,65 @@
+local valueMgr = require 'vm.value'
+local sourceMgr = require 'vm.source'
+
+local mt = {}
+mt.__index = mt
+mt.type = 'chain'
+
+mt.min = 100
+mt.max = 100
+mt.count = 0
+
+function mt:clearCache()
+ if self.count <= self.max then
+ return
+ end
+ local clock = os.clock()
+ local n = 0
+ for uri, value in pairs(self.cache) do
+ local ok = value:eachInfo(function ()
+ return true
+ end)
+ if ok then
+ n = n + 1
+ else
+ value:getSource():kill()
+ self.cache[uri] = nil
+ end
+ end
+ self.count = n
+ self.max = self.count * 1.1 + 10
+ if self.max < self.min then
+ self.max = self.min
+ end
+ local passed = os.clock() - clock
+ if passed > 0.1 then
+ log.warn(('chain:clearCache takes: [%.3f]sec, self.count: %d'):format(passed, self.count))
+ end
+end
+
+function mt:get(uri)
+ if not self.cache[uri] then
+ self.count = self.count + 1
+ self:clearCache()
+ self.cache[uri] = valueMgr.create('any', sourceMgr.dummy())
+ self.cache[uri]:markGlobal()
+ self.cache[uri].uri = uri
+ end
+ return self.cache[uri]
+end
+
+function mt:remove()
+ if self.removed then
+ return
+ end
+ self.removed = true
+ for _, value in pairs(self.cache) do
+ value:getSource():kill()
+ end
+end
+
+return function ()
+ return setmetatable({
+ cache = {},
+ }, mt)
+end
diff --git a/script/src/vm/emmy.lua b/script/src/vm/emmy.lua
new file mode 100644
index 00000000..9342a851
--- /dev/null
+++ b/script/src/vm/emmy.lua
@@ -0,0 +1,372 @@
+local functionMgr = require 'vm.function'
+local library = require 'vm.library'
+local mt = require 'vm.manager'
+
+function mt:clearEmmy()
+ self._emmy = nil
+ self._emmyParams = nil
+ self._emmyReturns = nil
+ self._emmyGeneric = nil
+ self._emmyComment = nil
+ self._emmyOverLoads = nil
+end
+
+function mt:doEmmy(action)
+ local tp = action.type
+ if tp == 'emmyClass' then
+ self:doEmmyClass(action)
+ elseif tp == 'emmyType' then
+ self:doEmmyType(action)
+ elseif tp == 'emmyAlias' then
+ self:doEmmyAlias(action)
+ elseif tp == 'emmyParam' then
+ self:doEmmyParam(action)
+ elseif tp == 'emmyReturn' then
+ self:doEmmyReturn(action)
+ elseif tp == 'emmyField' then
+ self:doEmmyField(action)
+ elseif tp == 'emmyGeneric' then
+ self:doEmmyGeneric(action)
+ elseif tp == 'emmyVararg' then
+ self:doEmmyVararg(action)
+ elseif tp == 'emmyLanguage' then
+ elseif tp == 'emmyArrayType' then
+ self:doEmmyArrayType(action)
+ elseif tp == 'emmyTableType' then
+ self:doEmmyTableType(action)
+ elseif tp == 'emmyFunctionType' then
+ self:doEmmyFunctionType(action)
+ elseif tp == 'emmySee' then
+ self:doEmmySee(action)
+ elseif tp == 'emmyOverLoad' then
+ self:doEmmyOverLoad(action)
+ elseif tp == 'emmyIncomplete' then
+ self:doEmmyIncomplete(action)
+ elseif tp == 'emmyComment' then
+ self:doEmmyComment(action)
+ end
+end
+
+function mt:getEmmy()
+ local emmy = self._emmy
+ self._emmy = nil
+ return emmy
+end
+
+function mt:addEmmyParam(param)
+ if not self._emmyParams then
+ self._emmyParams = {}
+ end
+ self._emmyParams[#self._emmyParams+1] = param
+end
+
+function mt:addEmmyReturn(rtn)
+ if not self._emmyReturns then
+ self._emmyReturns = {}
+ end
+ self._emmyReturns[#self._emmyReturns+1] = rtn
+end
+
+function mt:addEmmyOverLoad(funcObj)
+ if not self._emmyOverLoads then
+ self._emmyOverLoads = {}
+ end
+ self._emmyOverLoads[#self._emmyOverLoads+1] = funcObj
+end
+
+function mt:getEmmyParams()
+ local params = self._emmyParams
+ self._emmyParams = nil
+ return params
+end
+
+function mt:getEmmyReturns()
+ local returns = self._emmyReturns
+ self._emmyReturns = nil
+ return returns
+end
+
+function mt:getEmmyOverLoads()
+ local overLoads = self._emmyOverLoads
+ self._emmyOverLoads = nil
+ return overLoads
+end
+
+function mt:getEmmyGeneric()
+ local generic = self._emmyGeneric
+ self._emmyGeneric = nil
+ return generic
+end
+
+---@return string
+function mt:getEmmyComment()
+ local comment = self._emmyComment
+ self._emmyComment = nil
+ return comment
+end
+
+function mt:doEmmyClass(action)
+ ---@type emmyMgr
+ local emmyMgr = self.emmyMgr
+ self:instantSource(action)
+ self:instantSource(action[1])
+ local class = emmyMgr:addClass(action)
+ action:set('emmy class', class:getName())
+ action[1]:set('emmy class', class:getName())
+ local extends = action[2]
+ if extends then
+ self:instantSource(extends)
+ extends:set('emmy class', extends[1])
+ end
+ self._emmy = class
+ action:set('emmy.class', class)
+ if self.lsp then
+ self.lsp.global:markSet(self:getUri())
+ end
+end
+
+function mt:buildEmmyType(action)
+ ---@type emmyMgr
+ local emmyMgr = self.emmyMgr
+ self:instantSource(action)
+ for _, obj in ipairs(action) do
+ self:instantSource(obj)
+ obj:set('emmy class', obj[1])
+ end
+ local type = emmyMgr:addType(action)
+ return type
+end
+
+function mt:doEmmyType(action)
+ local type = self:buildEmmyType(action)
+ self._emmy = type
+ if self.lsp then
+ self.lsp.global:markGet(self:getUri())
+ end
+ return type
+end
+
+function mt:doEmmyAlias(action)
+ ---@type emmyMgr
+ local emmyMgr = self.emmyMgr
+ self:instantSource(action)
+ self:instantSource(action[1])
+ local type = self:buildEmmyAnyType(action[2])
+ local alias = emmyMgr:addAlias(action, type)
+ action:set('emmy.alias', alias)
+ action[1]:set('emmy class', alias:getName())
+ self._emmy = type
+ if self.lsp then
+ self.lsp.global:markSet(self:getUri())
+ end
+end
+
+function mt:getGenericByType(type)
+ local generics = self._emmyGeneric
+ if not generics then
+ return
+ end
+ if #type > 1 then
+ return
+ end
+ local name = type[1][1]
+ for _, generic in ipairs(generics) do
+ if generic:getName() == name then
+ return generic
+ end
+ end
+ return nil
+end
+
+function mt:doEmmyParam(action)
+ ---@type emmyMgr
+ local emmyMgr = self.emmyMgr
+ self:instantSource(action)
+ self:instantSource(action[1])
+ local type = self:getGenericByType(action[2]) or self:buildEmmyAnyType(action[2])
+ local param = emmyMgr:addParam(action, type)
+ action:set('emmy.param', param)
+ self:addEmmyParam(param)
+ if self.lsp then
+ self.lsp.global:markGet(self:getUri())
+ end
+end
+
+function mt:doEmmyReturn(action)
+ ---@type emmyMgr
+ local emmyMgr = self.emmyMgr
+ self:instantSource(action)
+ local type = action[1] and (self:getGenericByType(action[1]) or self:buildEmmyAnyType(action[1]))
+ local name = action[2]
+ local rtn = emmyMgr:addReturn(action, type, name)
+ action:set('emmy.return', rtn)
+ self:addEmmyReturn(rtn)
+ if self.lsp then
+ self.lsp.global:markGet(self:getUri())
+ end
+end
+
+function mt:doEmmyField(action)
+ ---@type emmyMgr
+ local emmyMgr = self.emmyMgr
+ self:instantSource(action)
+ self:instantSource(action[2])
+ local type = self:buildEmmyAnyType(action[3])
+ local value = self:createValue('nil', action[2])
+ local field = emmyMgr:addField(action, type, value)
+ value:setEmmy(type)
+ action:set('emmy.field', field)
+
+ local class = self._emmy
+ if not self._emmy or self._emmy.type ~= 'emmy.class' then
+ return
+ end
+ class:addField(field)
+ action:set('target class', class)
+end
+
+function mt:doEmmyGeneric(action)
+ ---@type emmyMgr
+ local emmyMgr = self.emmyMgr
+ self:instantSource(action)
+
+ local defs = {}
+ for i, obj in ipairs(action) do
+ defs[i] = {}
+ defs[i].name = self:instantSource(obj[1])
+ if obj[2] then
+ defs[i].type = self:buildEmmyAnyType(obj[2])
+ end
+ end
+
+ local generic = emmyMgr:addGeneric(defs)
+ self._emmyGeneric = generic
+end
+
+function mt:doEmmyVararg(action)
+ ---@type emmyMgr
+ local emmyMgr = self.emmyMgr
+ self:instantSource(action)
+ local type = self:getGenericByType(action[1]) or self:buildEmmyAnyType(action[1])
+ local param = emmyMgr:addParam(action, type)
+ action:set('emmy.param', param)
+ self:addEmmyParam(param)
+ if self.lsp then
+ self.lsp.global:markGet(self:getUri())
+ end
+end
+
+function mt:buildEmmyArrayType(action)
+ ---@type emmyMgr
+ local emmyMgr = self.emmyMgr
+ self:instantSource(action)
+ for _, obj in ipairs(action) do
+ self:instantSource(obj)
+ action:set('emmy class', obj[1])
+ end
+ local type = emmyMgr:addArrayType(action)
+ return type
+end
+
+function mt:doEmmyArrayType(action)
+ local type = self:buildEmmyArrayType(action)
+ self._emmy = type
+ if self.lsp then
+ self.lsp.global:markGet(self:getUri())
+ end
+ return type
+end
+
+function mt:buildEmmyTableType(action)
+ ---@type emmyMgr
+ local emmyMgr = self.emmyMgr
+ self:instantSource(action)
+ local keyType = self:buildEmmyAnyType(action[1])
+ local valueType = self:buildEmmyAnyType(action[2])
+ local type = emmyMgr:addTableType(action, keyType, valueType)
+ return type
+end
+
+function mt:doEmmyTableType(action)
+ local type = self:buildEmmyTableType(action)
+ self._emmy = type
+ if self.lsp then
+ self.lsp.global:markGet(self:getUri())
+ end
+ return type
+end
+
+function mt:buildEmmyFunctionType(source)
+ ---@type emmyMgr
+ local emmyMgr = self.emmyMgr
+ self:instantSource(source)
+ local funcObj = emmyMgr:addFunctionType(source)
+ ---@type emmyFunction
+ local func = functionMgr.create(source)
+ local args = source.args
+ if args then
+ for i = 1, #args // 2 do
+ local nameSource = args[i*2-1]
+ local typeSource = args[i*2]
+ local paramType = self:buildEmmyAnyType(typeSource)
+ funcObj:addParam(nameSource[1], paramType)
+ local value = self:createValue(paramType:getType(), typeSource)
+ value:setEmmy(paramType)
+ self:instantSource(nameSource)
+ local arg = func:addArg(nameSource[1], nameSource, value)
+ arg:set('emmy arg', true)
+ end
+ end
+ local returns = source.returns
+ if returns then
+ for i = 1, #returns do
+ local returnSource = returns[i]
+ local returnType = self:buildEmmyAnyType(returnSource)
+ funcObj:addReturn(returnType)
+ local value = self:createValue(returnType:getType(), returnSource)
+ value:setEmmy(returnType)
+ func:setReturn(i, value)
+ end
+ end
+ funcObj:bindFunction(func)
+ return funcObj
+end
+
+function mt:doEmmyFunctionType(action)
+ local funcObj = self:buildEmmyFunctionType(action)
+ self._emmy = funcObj
+ return funcObj
+end
+
+function mt:buildEmmyAnyType(source)
+ if source.type == 'emmyType' then
+ return self:buildEmmyType(source)
+ elseif source.type == 'emmyArrayType' then
+ return self:buildEmmyArrayType(source)
+ elseif source.type == 'emmyTableType' then
+ return self:buildEmmyTableType(source)
+ elseif source.type == 'emmyFunctionType' then
+ return self:buildEmmyFunctionType(source)
+ else
+ error('Unknown emmy type: ' .. table.dump(source))
+ end
+end
+
+function mt:doEmmyIncomplete(action)
+ self:instantSource(action)
+end
+
+function mt:doEmmyComment(action)
+ self._emmyComment = action[1]
+end
+
+function mt:doEmmySee(action)
+ self:instantSource(action)
+ self:instantSource(action[2])
+ action[2]:set('emmy see', action)
+end
+
+function mt:doEmmyOverLoad(action)
+ local funcObj = self:buildEmmyFunctionType(action)
+ self:addEmmyOverLoad(funcObj)
+end
diff --git a/script/src/vm/function.lua b/script/src/vm/function.lua
new file mode 100644
index 00000000..1ba01363
--- /dev/null
+++ b/script/src/vm/function.lua
@@ -0,0 +1,551 @@
+local createMulti = require 'vm.multi'
+local valueMgr = require 'vm.value'
+local localMgr = require 'vm.local'
+local sourceMgr = require 'vm.source'
+local listMgr = require 'vm.list'
+
+local Watch = setmetatable({}, {__mode = 'kv'})
+
+---@class emmyFunction
+local mt = {}
+mt.__index = mt
+mt.type = 'function'
+mt._runed = 0
+mt._top = 0
+
+function mt:getSource()
+ return listMgr.get(self.source)
+end
+
+function mt:getUri()
+ local source = self:getSource()
+ return source and source.uri or ''
+end
+
+function mt:push(source, ischunk)
+ if self._removed then
+ return
+ end
+ self._top = self._top + 1
+ self.locals[self._top] = {}
+ self.finishs[self._top] = source and source.finish or math.maxinteger
+end
+
+function mt:markChunk()
+ self.chunk[self._top] = true
+end
+
+function mt:pop()
+ if self._removed then
+ return
+ end
+ local closed = self.finishs[self._top]
+ local closedLocals = self.locals[self._top]
+ self.locals[self._top] = nil
+ self.chunk[self._top] = nil
+ for _, loc in pairs(closedLocals) do
+ loc:close(closed)
+ end
+ self._top = self._top - 1
+end
+
+function mt:saveLocal(name, loc)
+ if self._removed then
+ return
+ end
+ if loc.type ~= 'local' then
+ error('saveLocal必须是local')
+ end
+ if not loc:getSource() then
+ return
+ end
+ local old = self:loadLocal(name)
+ if old then
+ loc:shadow(old)
+ end
+ self.locals[self._top][name] = loc
+end
+
+function mt:saveUpvalue(name, loc)
+ if self._removed then
+ return
+ end
+ if loc.type ~= 'local' then
+ error('saveLocal必须是local')
+ end
+ self.upvalues[name] = loc
+end
+
+function mt:loadLocal(name)
+ for i = self._top, 1, -1 do
+ local locals = self.locals[i]
+ local loc = locals[name]
+ if loc then
+ return loc
+ end
+ if self.chunk[i] then
+ break
+ end
+ end
+ local uv = self.upvalues[name]
+ if uv then
+ return uv
+ end
+ return nil
+end
+
+function mt:eachLocal(callback)
+ local mark = {}
+ for i = self._top, 1, -1 do
+ local locals = self.locals[i]
+ for name, loc in pairs(locals) do
+ if not mark[name] then
+ mark[name] = true
+ local res = callback(name, loc)
+ if res ~= nil then
+ return res
+ end
+ end
+ end
+ if self.chunk[i] then
+ break
+ end
+ end
+ for name, loc in pairs(self.upvalues) do
+ if not mark[name] then
+ mark[name] = true
+ local res = callback(name, loc)
+ if res ~= nil then
+ return res
+ end
+ end
+ end
+ return nil
+end
+
+function mt:saveLabel(label)
+ if self._removed then
+ return
+ end
+ if not self._label then
+ self._label = {}
+ end
+ self._label[#self._label+1] = label
+end
+
+function mt:loadLabel(name)
+ if not self._label then
+ return nil
+ end
+ for _, label in ipairs(self._label) do
+ if label:getName() == name then
+ return label
+ end
+ end
+ return nil
+end
+
+function mt:rawSetReturn(index, value)
+ if self._removed then
+ return
+ end
+ self:set('hasReturn', true)
+ if not self.returns then
+ self.returns = createMulti()
+ end
+ if value then
+ self.returns:set(index, value)
+ if self._global then
+ value:markGlobal()
+ end
+ end
+end
+
+function mt:setReturn(index, value)
+ local emmy = self._emmyReturns and self._emmyReturns[index]
+ if emmy then
+ if emmy:bindType() or emmy:bindGeneric() then
+ return
+ end
+ end
+ return self:rawSetReturn(index, value)
+end
+
+function mt:mergeReturn(index, value)
+ if self._removed then
+ return
+ end
+ local emmy = self._emmyReturns and self._emmyReturns[index]
+ if emmy then
+ if emmy:bindType() or emmy:bindGeneric() then
+ return
+ end
+ end
+ self:set('hasReturn', true)
+ if not self.returns then
+ self.returns = createMulti()
+ end
+ if value then
+ if self.returns[index] then
+ self.returns[index]:mergeValue(value)
+ self.returns[index] = value
+ else
+ self.returns:set(index, value)
+ end
+ end
+ if self._global then
+ value:markGlobal()
+ end
+end
+
+function mt:getReturn(index)
+ if self._removed then
+ return nil
+ end
+ if self.maxReturns and index and self.maxReturns < index then
+ return nil
+ end
+ if not self.returns then
+ self.returns = createMulti()
+ end
+ if index then
+ return self.returns:get(index)
+ else
+ return self.returns
+ end
+end
+
+function mt:returnDots(index)
+ if not self.returns then
+ self.returns = createMulti()
+ end
+ --self.returns[index] = createMulti()
+end
+
+function mt:loadDots()
+ if not self._dots then
+ self._dots = createMulti()
+ end
+ self._dotsLoad = true
+ return self._dots
+end
+
+function mt:setObject(value, source)
+ self._objectValue = value
+ self._objectSource = source
+end
+
+function mt:getObject()
+ return self._objectSource, self._objectValue
+end
+
+function mt:hasRuned()
+ return self._runed > 0
+end
+
+function mt:needSkip()
+ return self._runed > 3
+end
+
+---@param vm VM
+function mt:run(vm)
+ if self._removed then
+ return
+ end
+ if not self:getSource() then
+ return
+ end
+
+ self._runed = self._runed + 1
+
+ -- 第一次运行函数时,创建函数的参数
+ if self._runed == 1 then
+ -- 如果是面向对象形式的函数,创建隐藏的参数self
+ if self._objectSource then
+ local loc = localMgr.create('self', vm:instantSource(self._objectSource), self._objectValue)
+ loc:set('hide', true)
+ loc:set('start', self:getSource().start)
+ loc:close(self:getSource().finish)
+ self:saveUpvalue('self', loc)
+ self.args[#self.args+1] = loc
+ end
+
+ -- 显性声明的参数
+ self:createArgs(vm)
+ end
+
+ if self:needSkip() then
+ return
+ end
+
+ -- 向局部变量中填充参数
+ for i, loc in ipairs(self.args) do
+ loc:setValue(self.argValues[i])
+ local emmyParam = self:findEmmyParamByName(loc:getName())
+ if emmyParam then
+ local typeObj = emmyParam:bindType()
+ if typeObj then
+ loc:getValue():setEmmy(typeObj)
+ end
+ local genericObj = emmyParam:bindGeneric()
+ if genericObj then
+ genericObj:setValue(loc:getValue())
+ end
+ end
+ end
+ if self._dots then
+ local emmyParam = self:findEmmyParamByName('...')
+ self._dots = createMulti()
+ for i = #self.args + 1, #self.argValues do
+ local value = self.argValues[i]
+ self._dots:push(value)
+ if emmyParam then
+ local typeObj = emmyParam:bindType()
+ if typeObj then
+ value:setEmmy(typeObj)
+ end
+ local genericObj = emmyParam:bindGeneric()
+ if genericObj then
+ genericObj:setValue(value)
+ end
+ end
+ end
+ if emmyParam then
+ local typeObj = emmyParam:bindType()
+ if typeObj then
+ self._dots:setEmmy(typeObj)
+ end
+ local genericObj = emmyParam:bindGeneric()
+ if genericObj then
+ local value = self._dots:first()
+ if value then
+ genericObj:setValue(value)
+ end
+ end
+ end
+ end
+
+ -- 填充返回值
+ if self._emmyReturns then
+ for i, rtn in ipairs(self._emmyReturns) do
+ local value = vm:createValue('nil', rtn:getSource())
+ local typeObj = rtn:bindType()
+ if typeObj then
+ value:setEmmy(typeObj)
+ end
+ local genericObj = rtn:bindGeneric()
+ if genericObj then
+ local destValue = genericObj:getValue()
+ if destValue then
+ value:mergeType(destValue)
+ end
+ end
+ self:rawSetReturn(i, value)
+ end
+ end
+end
+
+function mt:eachEmmyReturn(callback)
+ if not self._emmyReturns then
+ return
+ end
+ for _, rtn in ipairs(self._emmyReturns) do
+ callback(rtn)
+ end
+end
+
+function mt:setArgs(values)
+ for i = 1, #self.argValues do
+ self.argValues[i] = nil
+ end
+ for i = 1, #values do
+ self.argValues[i] = values[i]
+ end
+end
+
+function mt:findEmmyParamByName(name)
+ local params = self._emmyParams
+ if not params then
+ return nil
+ end
+ for i = #params, 1, -1 do
+ local param = params[i]
+ if param:getName() == name then
+ return param
+ end
+ end
+ return nil
+end
+
+function mt:findEmmyParamByIndex(index)
+ local arg = self.args[index]
+ if not arg then
+ return nil
+ end
+ local name = arg:getName()
+ return self:findEmmyParamByName(name)
+end
+
+function mt:addArg(name, source, value, close)
+ local loc = localMgr.create(name, source, value)
+ loc:close(close)
+ self:saveUpvalue(name, loc)
+ self.args[#self.args+1] = loc
+ return loc
+end
+
+function mt:createArg(vm, arg, close)
+ vm:instantSource(arg)
+ arg:set('arg', self)
+ if arg.type == 'name' then
+ vm:instantSource(arg)
+ local value = valueMgr.create('nil', arg)
+ self:addArg(arg[1], arg, value, close)
+ elseif arg.type == '...' then
+ self._dots = createMulti()
+ self._dotsSource = arg
+ end
+end
+
+function mt:createLibArg(arg, source)
+ if arg.type == '...' then
+ self._dots = createMulti()
+ else
+ local name = arg.name or '_'
+ local loc = localMgr.create(name, source, valueMgr.create('any', source))
+ self:saveUpvalue(name, loc)
+ self.args[#self.args+1] = loc
+ end
+end
+
+function mt:hasDots()
+ return self._dots ~= nil
+end
+
+function mt:createArgs(vm)
+ if not self:getSource() then
+ return
+ end
+ local args = self:getSource().arg
+ if not args then
+ return
+ end
+ local close = self:getSource().finish
+ if args.type == 'list' then
+ for _, arg in ipairs(args) do
+ self:createArg(vm, arg, close)
+ end
+ else
+ self:createArg(vm, args, close)
+ end
+end
+
+function mt:set(name, v)
+ if not self._flag then
+ self._flag = {}
+ end
+ self._flag[name] = v
+end
+
+function mt:get(name)
+ if not self._flag then
+ return nil
+ end
+ return self._flag[name]
+end
+
+function mt:getSource()
+ if self._removed then
+ return nil
+ end
+ return listMgr.get(self.source)
+end
+
+function mt:kill()
+ if self._removed then
+ return
+ end
+ self._removed = true
+ listMgr.clear(self.id)
+end
+
+function mt:markGlobal()
+ if self._global then
+ return
+ end
+ self._global = true
+ if self.returns then
+ self.returns:eachValue(function (_, v)
+ v:markGlobal()
+ end)
+ end
+end
+
+function mt:setEmmy(params, returns, overLoads)
+ if params then
+ self._emmyParams = params
+ for _, param in ipairs(params) do
+ param:getSource():set('emmy function', self)
+ param:getSource()[1]:set('emmy function', self)
+ end
+ end
+ if returns then
+ self._emmyReturns = returns
+ for _, rtn in ipairs(returns) do
+ rtn:getSource():set('emmy function', self)
+ end
+ end
+ if overLoads then
+ self._emmyOverLoads = overLoads
+ for _, ol in ipairs(overLoads) do
+ ol:getSource():set('emmy function', self)
+ end
+ end
+end
+
+---@param comment string
+function mt:setComment(comment)
+ self._comment = comment
+end
+
+---@return string
+function mt:getComment()
+ return self._comment
+end
+
+function mt:getEmmyParams()
+ return self._emmyParams
+end
+
+function mt:getEmmyOverLoads()
+ return self._emmyOverLoads
+end
+
+local function create(source)
+ if not source then
+ error('Function need source')
+ end
+ local id = source.id
+ if not id then
+ error('Not instanted source')
+ end
+ local self = setmetatable({
+ source = id,
+ locals = {},
+ upvalues = {},
+ chunk = {},
+ finishs = {},
+ args = {},
+ argValues = {},
+ }, mt)
+
+ local id = listMgr.add(self)
+ self.id = id
+ Watch[self] = id
+ return self
+end
+
+return {
+ create = create,
+ watch = Watch,
+}
diff --git a/script/src/vm/global.lua b/script/src/vm/global.lua
new file mode 100644
index 00000000..af30ffdd
--- /dev/null
+++ b/script/src/vm/global.lua
@@ -0,0 +1,25 @@
+local library = require 'core.library'
+local libraryBuilder = require 'vm.library'
+local sourceMgr = require 'vm.source'
+
+return function (lsp)
+ local global = lsp and lsp.globalValue
+ if not global then
+ libraryBuilder.clear()
+ local t = {}
+ for name, lib in pairs(library.global) do
+ t[name] = libraryBuilder.value(lib)
+ end
+
+ global = t._G
+ global:markGlobal()
+ global:set('ENV', true)
+ for k, v in pairs(t) do
+ global:setChild(k, v, sourceMgr.dummy())
+ end
+ end
+ if lsp then
+ lsp.globalValue = global
+ end
+ return global
+end
diff --git a/script/src/vm/init.lua b/script/src/vm/init.lua
new file mode 100644
index 00000000..87576ba5
--- /dev/null
+++ b/script/src/vm/init.lua
@@ -0,0 +1 @@
+return require 'vm.vm'
diff --git a/script/src/vm/ipairs.lua b/script/src/vm/ipairs.lua
new file mode 100644
index 00000000..cb8356da
--- /dev/null
+++ b/script/src/vm/ipairs.lua
@@ -0,0 +1,51 @@
+local mt = require 'vm.manager'
+local library = require 'vm.library'
+
+---@param func emmyFunction
+function mt:callIpairs(func, values, source)
+ local tbl = values[1]
+ func:setReturn(1, library.special['@ipairs'])
+ func:setReturn(2, tbl)
+end
+
+---@param func emmyFunction
+function mt:callAtIpairs(func, values, source)
+ local tbl = values[1]
+ if tbl then
+ local emmy = tbl:getEmmy()
+ if emmy then
+ if emmy.type == 'emmy.arrayType' then
+ local value = self:createValue(emmy:getName(), source)
+ func:setReturn(2, value)
+ end
+ end
+ end
+end
+
+---@param func emmyFunction
+function mt:callPairs(func, values, source)
+ local tbl = values[1]
+ func:setReturn(1, library.special['next'])
+ func:setReturn(2, tbl)
+end
+
+---@param func emmyFunction
+function mt:callNext(func, values, source)
+ local tbl = values[1]
+ if tbl then
+ local emmy = tbl:getEmmy()
+ if emmy then
+ if emmy.type == 'emmy.arrayType' then
+ local key = self:createValue('integer', source)
+ local value = self:createValue(emmy:getName(), source)
+ func:setReturn(1, key)
+ func:setReturn(2, value)
+ elseif emmy.type == 'emmy.tableType' then
+ local key = self:createValue(emmy:getKeyType():getType(), source)
+ local value = self:createValue(emmy:getValueType():getType(), source)
+ func:setReturn(1, key)
+ func:setReturn(2, value)
+ end
+ end
+ end
+end
diff --git a/script/src/vm/label.lua b/script/src/vm/label.lua
new file mode 100644
index 00000000..c0e0dfb8
--- /dev/null
+++ b/script/src/vm/label.lua
@@ -0,0 +1,75 @@
+local listMgr = require 'vm.list'
+
+local Sort = 0
+
+local mt = {}
+mt.__index = mt
+mt.type = 'label'
+
+function mt:getName()
+ return self.name
+end
+
+function mt:addInfo(tp, source)
+ if not source then
+ error('No source')
+ end
+ local id = source.id
+ if not id then
+ error('Not instanted source')
+ end
+ if self._info[id] then
+ return
+ end
+ Sort = Sort + 1
+ local info = {
+ type = tp,
+ source = id,
+ _sort = Sort,
+ }
+
+ self._info[id] = info
+end
+
+function mt:eachInfo(callback)
+ local list = {}
+ for srcId, info in pairs(self._info) do
+ local src = listMgr.get(srcId)
+ if src then
+ list[#list+1] = info
+ else
+ self._info[srcId] = nil
+ end
+ end
+ table.sort(list, function (a, b)
+ return a._sort < b._sort
+ end)
+ for i = 1, #list do
+ local info = list[i]
+ local res = callback(info, listMgr.get(info.source))
+ if res ~= nil then
+ return res
+ end
+ end
+ return nil
+end
+
+function mt:getSource()
+ return listMgr.get(self.source)
+end
+
+return function (name, source)
+ if not source then
+ error('No source')
+ end
+ local id = source.id
+ if not id then
+ error('Not instanted source')
+ end
+ local self = setmetatable({
+ name = name,
+ source = id,
+ _info = {},
+ }, mt)
+ return self
+end
diff --git a/script/src/vm/library.lua b/script/src/vm/library.lua
new file mode 100644
index 00000000..018d69f3
--- /dev/null
+++ b/script/src/vm/library.lua
@@ -0,0 +1,112 @@
+local sourceMgr = require 'vm.source'
+
+local valueMgr
+local functionMgr
+
+local CHILD_CACHE = {}
+local VALUE_CACHE = {}
+local Special = {}
+
+local buildLibValue
+local buildLibChild
+
+function buildLibValue(lib)
+ if VALUE_CACHE[lib] then
+ return VALUE_CACHE[lib]
+ end
+ if not valueMgr then
+ valueMgr = require 'vm.value'
+ functionMgr = require 'vm.function'
+ end
+ local tp = lib.type
+ local value
+ if tp == 'table' then
+ value = valueMgr.create('table', sourceMgr.dummy())
+ elseif tp == 'function' then
+ local dummySource = sourceMgr.dummy()
+ value = valueMgr.create('function', dummySource)
+ local func = functionMgr.create(dummySource)
+ value:setFunction(func)
+ if lib.args then
+ for _, arg in ipairs(lib.args) do
+ func:createLibArg(arg, sourceMgr.dummy())
+ end
+ end
+ if lib.returns then
+ for i, rtn in ipairs(lib.returns) do
+ if rtn.type == '...' then
+ func:returnDots(i)
+ else
+ func:setReturn(i, buildLibValue(rtn))
+ end
+ end
+ if lib.special == 'pairs' then
+ func:setReturn(1, Special['next'])
+ end
+ if lib.special == 'ipairs' then
+ func:setReturn(1, Special['@ipairs'])
+ end
+ end
+ elseif tp == 'string' then
+ value = valueMgr.create('string', sourceMgr.dummy())
+ elseif tp == 'boolean' then
+ value = valueMgr.create('boolean', sourceMgr.dummy())
+ elseif tp == 'number' then
+ value = valueMgr.create('number', sourceMgr.dummy())
+ elseif tp == 'integer' then
+ value = valueMgr.create('integer', sourceMgr.dummy())
+ elseif tp == 'nil' then
+ value = valueMgr.create('nil', sourceMgr.dummy())
+ else
+ value = valueMgr.create(tp or 'any', sourceMgr.dummy())
+ end
+ value:setLib(lib)
+ VALUE_CACHE[lib] = value
+
+ if lib.child then
+ for fName, fLib in pairs(lib.child) do
+ local fValue = buildLibValue(fLib)
+ value:rawSet(fName, fValue)
+ value:addInfo('set child', sourceMgr.dummy(), fName, fValue)
+ end
+ end
+
+ if lib.special == 'next' then
+ Special['next'] = value
+ end
+ if lib.special == '@ipairs' then
+ Special['@ipairs'] = value
+ return nil
+ end
+
+ return value
+end
+
+function buildLibChild(lib)
+ if not valueMgr then
+ valueMgr = require 'vm.value'
+ functionMgr = require 'vm.function'
+ end
+ if CHILD_CACHE[lib] then
+ return CHILD_CACHE[lib]
+ end
+ local child = {}
+ for fName, fLib in pairs(lib.child) do
+ local fValue = buildLibValue(fLib)
+ child[fName] = fValue
+ end
+ CHILD_CACHE[lib] = child
+ return child
+end
+
+local function clearCache()
+ CHILD_CACHE = {}
+ VALUE_CACHE = {}
+end
+
+return {
+ value = buildLibValue,
+ child = buildLibChild,
+ clear = clearCache,
+ special = Special,
+}
diff --git a/script/src/vm/list.lua b/script/src/vm/list.lua
new file mode 100644
index 00000000..234f241f
--- /dev/null
+++ b/script/src/vm/list.lua
@@ -0,0 +1,30 @@
+local Id = 0
+local Version = 0
+local List = {}
+
+local function get(id)
+ return List[id]
+end
+
+local function add(obj)
+ Id = Id + 1
+ List[Id] = obj
+ return Id
+end
+
+local function clear(id)
+ List[id] = nil
+ Version = Version + 1
+end
+
+local function getVersion()
+ return Version
+end
+
+return {
+ get = get,
+ add = add,
+ clear = clear,
+ list = List,
+ getVersion = getVersion,
+}
diff --git a/script/src/vm/local.lua b/script/src/vm/local.lua
new file mode 100644
index 00000000..7e8af0f1
--- /dev/null
+++ b/script/src/vm/local.lua
@@ -0,0 +1,191 @@
+local listMgr = require 'vm.list'
+
+local Sort = 0
+local Watch = setmetatable({}, {__mode = 'kv'})
+
+---@class Local
+local mt = {}
+mt.__index = mt
+mt.type = 'local'
+mt._close = math.maxinteger
+
+function mt:setValue(value)
+ if not value then
+ return
+ end
+ if self.value then
+ --self.value:mergeValue(value)
+ self.value:mergeType(value)
+ self.value = value
+ else
+ self.value = value
+ end
+ if self._emmy then
+ self.value:setEmmy(self._emmy)
+ end
+ return value
+end
+
+function mt:getValue()
+ return self.value
+end
+
+function mt:addInfo(tp, source)
+ if not source then
+ error('No source')
+ end
+ local id = source.id
+ if not id then
+ error('Not instanted source')
+ end
+ if self._info[id] then
+ return
+ end
+ Sort = Sort + 1
+ local info = {
+ type = tp,
+ source = id,
+ _sort = Sort,
+ }
+
+ self._info[id] = info
+end
+
+function mt:eachInfo(callback)
+ local list = {}
+ for srcId, info in pairs(self._info) do
+ local src = listMgr.get(srcId)
+ if src then
+ list[#list+1] = info
+ else
+ self._info[srcId] = nil
+ end
+ end
+ table.sort(list, function (a, b)
+ return a._sort < b._sort
+ end)
+ for i = 1, #list do
+ local info = list[i]
+ local res = callback(info, listMgr.get(info.source))
+ if res ~= nil then
+ return res
+ end
+ end
+ return nil
+end
+
+function mt:set(name, v)
+ if not self._flag then
+ self._flag = {}
+ end
+ self._flag[name] = v
+end
+
+function mt:get(name)
+ if not self._flag then
+ return nil
+ end
+ return self._flag[name]
+end
+
+function mt:getName()
+ return self.name
+end
+
+function mt:shadow(old)
+ if not old then
+ if not self._shadow then
+ return nil
+ end
+ for i = #self._shadow, 1, -1 do
+ local loc = self._shadow[i]
+ if not loc:getSource() then
+ table.remove(self._shadow, i)
+ end
+ end
+ return self._shadow
+ end
+ local group = old._shadow
+ if not group then
+ group = {}
+ group[#group+1] = old
+ end
+ group[#group+1] = self
+ self._shadow = group
+
+ if not self:getSource() then
+ log.error('local no source')
+ return
+ end
+
+ old:close(self:getSource().start - 1)
+end
+
+function mt:close(pos)
+ if pos then
+ if pos <= 0 then
+ pos = math.maxinteger
+ end
+ self._close = pos
+ else
+ return self._close
+ end
+end
+
+function mt:getSource()
+ return listMgr.get(self.source)
+end
+
+local EMMY_TYPE = {
+ ['emmy.class'] = true,
+ ['emmy.type'] = true,
+ ['emmy.arrayType'] = true,
+ ['emmy.tableType'] = true,
+ ['emmy.functionType'] = true,
+}
+
+function mt:setEmmy(emmy)
+ if not emmy then
+ return
+ end
+ if self.value and EMMY_TYPE[emmy.type] then
+ self.value:setEmmy(emmy)
+ end
+end
+
+---@param comment string
+function mt:setComment(comment)
+ self._comment = comment
+end
+
+---@return string
+function mt:getComment()
+ return self._comment
+end
+
+local function create(name, source, value, tags)
+ if not value then
+ error('Local must has a value')
+ end
+ if not source then
+ error('No source')
+ end
+ local id = source.id
+ if not id then
+ error('Not instanted source')
+ end
+ local self = setmetatable({
+ name = name,
+ source = id,
+ value = value,
+ tags = tags,
+ _info = {},
+ }, mt)
+ Watch[self] = true
+ return self
+end
+
+return {
+ create = create,
+ watch = Watch,
+}
diff --git a/script/src/vm/manager.lua b/script/src/vm/manager.lua
new file mode 100644
index 00000000..b9762d2e
--- /dev/null
+++ b/script/src/vm/manager.lua
@@ -0,0 +1,17 @@
+---@class VM
+local mt = {}
+mt.__index = mt
+mt.type = 'vm'
+mt._version = -1
+
+---@param version integer
+function mt:setVersion(version)
+ self._version = version
+end
+
+---@return integer
+function mt:getVersion()
+ return self._version
+end
+
+return mt
diff --git a/script/src/vm/module.lua b/script/src/vm/module.lua
new file mode 100644
index 00000000..60191bf3
--- /dev/null
+++ b/script/src/vm/module.lua
@@ -0,0 +1,56 @@
+local mt = require 'vm.manager'
+local createMulti = require 'vm.multi'
+
+--[[
+function module(name, ...)
+ local env = {}
+ for _, opt in ipairs {...} do
+ opt(env)
+ end
+ @ENV = env
+end
+--]]
+function mt:callModuel(func, values)
+ local envLoc = self:loadLocal('@ENV')
+ if not envLoc then
+ return
+ end
+ local source = self:getDefaultSource()
+ local newEnvValue = self:createValue('table', source)
+ local args = createMulti()
+
+ args:push(newEnvValue)
+
+ for i = 2, #values do
+ local value = values[i]
+ -- opt(env)
+ self:call(value, args, source)
+ end
+
+ -- @ENV = env
+ envLoc:setValue(newEnvValue)
+end
+
+--[[
+function package.seeall(env)
+ setmetatable(env, { __index = @ENV })
+end
+--]]
+function mt:callSeeAll(func, values)
+ local newEnv = values[1]
+ if not newEnv then
+ return
+ end
+ local envLoc = self:loadLocal('@ENV')
+ if not envLoc then
+ return
+ end
+ local oldEnv = envLoc:getValue()
+ if not oldEnv then
+ return
+ end
+ local source = self:getDefaultSource()
+ local meta = self:createValue('table', source)
+ meta:setChild('__index', oldEnv, source)
+ newEnv:setMetaTable(meta)
+end
diff --git a/script/src/vm/multi.lua b/script/src/vm/multi.lua
new file mode 100644
index 00000000..4b27b8cf
--- /dev/null
+++ b/script/src/vm/multi.lua
@@ -0,0 +1,83 @@
+local mt = {}
+mt.__index = mt
+mt.type = 'multi'
+mt.len = 0
+
+function mt:push(value, isLast)
+ if value and value.type == 'list' then
+ if isLast then
+ for _, v in ipairs(value) do
+ self.len = self.len + 1
+ self[self.len] = v
+ end
+ else
+ self.len = self.len + 1
+ self[self.len] = value[1]
+ end
+ else
+ self.len = self.len + 1
+ self[self.len] = value
+ end
+end
+
+function mt:get(index)
+ return self[index]
+end
+
+function mt:set(index, value)
+ if index > self.len then
+ self.len = index
+ end
+ self[index] = value
+end
+
+function mt:first()
+ local value = self[1]
+ if not value then
+ return nil
+ end
+ if value.type == 'multi' then
+ return value:first()
+ else
+ return value
+ end
+end
+
+function mt:eachValue(callback)
+ local i = 0
+ for n, value in ipairs(self) do
+ if value.type == 'multi' then
+ if n == self.len then
+ value:eachValue(function (_, nvalue)
+ i = i + 1
+ callback(i, nvalue)
+ end)
+ else
+ i = i + 1
+ value:first()
+ end
+ else
+ i = i + 1
+ callback(i, value)
+ end
+ end
+end
+
+function mt:merge(other)
+ other:eachValue(function (_, value)
+ self:push(value)
+ end)
+end
+
+function mt:setEmmy(emmy)
+ self._emmy = emmy
+end
+
+function mt:getEmmy()
+ return self._emmy
+end
+
+return function ()
+ local self = setmetatable({}, mt)
+ return self
+end
diff --git a/script/src/vm/pcall.lua b/script/src/vm/pcall.lua
new file mode 100644
index 00000000..e5d1e26f
--- /dev/null
+++ b/script/src/vm/pcall.lua
@@ -0,0 +1,50 @@
+local mt = require 'vm.manager'
+local multi = require 'vm.multi'
+
+function mt:callPcall(func, values, source)
+ local funcValue = values:first()
+ if not funcValue then
+ return
+ end
+ local realFunc = funcValue:getFunction()
+ if not realFunc then
+ return
+ end
+ local argList = multi()
+ values:eachValue(function (i, v)
+ if i >= 2 then
+ argList:push(v)
+ end
+ end)
+ self:call(funcValue, argList, source)
+ if realFunc ~= func then
+ func:setReturn(1, self:createValue('boolean', source))
+ realFunc:getReturn():eachValue(function (i, v)
+ func:setReturn(i + 1, v)
+ end)
+ end
+end
+
+function mt:callXpcall(func, values, source)
+ local funcValue = values:first()
+ if not funcValue then
+ return
+ end
+ local realFunc = funcValue:getFunction()
+ if not realFunc then
+ return
+ end
+ local argList = multi()
+ values:eachValue(function (i, v)
+ if i >= 3 then
+ argList:push(v)
+ end
+ end)
+ self:call(funcValue, argList, source)
+ if realFunc ~= func then
+ func:setReturn(1, self:createValue('boolean', source))
+ realFunc:getReturn():eachValue(function (i, v)
+ func:setReturn(i + 1, v)
+ end)
+ end
+end
diff --git a/script/src/vm/raw.lua b/script/src/vm/raw.lua
new file mode 100644
index 00000000..f8c35734
--- /dev/null
+++ b/script/src/vm/raw.lua
@@ -0,0 +1,30 @@
+local mt = require 'vm.manager'
+
+function mt:callRawSet(func, values, source)
+ local tbl = values[1]
+ local index = values[2]
+ local value = values[3]
+ if not tbl or not index or not value then
+ return
+ end
+ if index:getLiteral() then
+ index = index:getLiteral()
+ end
+ tbl:addInfo('set child', source, index)
+ tbl:rawSet(index, value, source)
+ func:setReturn(1, tbl)
+end
+
+function mt:callRawGet(func, values, source)
+ local tbl = values[1]
+ local index = values[2]
+ if not tbl or not index then
+ return
+ end
+ if index:getLiteral() then
+ index = index:getLiteral()
+ end
+ tbl:addInfo('get child', source, index)
+ local value = tbl:rawGet(index)
+ func:setReturn(1, value)
+end
diff --git a/script/src/vm/source.lua b/script/src/vm/source.lua
new file mode 100644
index 00000000..7a10a38e
--- /dev/null
+++ b/script/src/vm/source.lua
@@ -0,0 +1,183 @@
+local listMgr = require 'vm.list'
+
+---@class source
+local mt = {}
+mt.__index = mt
+mt.type = 'source'
+mt.uri = ''
+mt.start = 0
+mt.finish = 0
+mt.id = 0
+
+local Watch = setmetatable({}, {__mode = 'k'})
+
+function mt:bindLocal(loc, action)
+ if loc then
+ self._bindLocal = loc
+ self._bindValue = loc:getValue()
+ self._action = action
+ loc:addInfo(action, self)
+ else
+ if not self._bindLocal then
+ return nil
+ end
+ if not self._bindLocal:getSource() then
+ self._bindLocal = nil
+ return nil
+ end
+ return self._bindLocal
+ end
+end
+
+function mt:bindLabel(label, action)
+ if label then
+ self._bindLabel = label
+ self._action = action
+ label:addInfo(action, self)
+ else
+ return self._bindLabel
+ end
+end
+
+function mt:bindFunction(func)
+ if func then
+ self._bindFunction = func
+ else
+ return self._bindFunction
+ end
+end
+
+function mt:bindValue(value, action)
+ if value then
+ self._bindValue = value
+ self._action = action
+ value:addInfo(action, self)
+ else
+ return self._bindValue
+ end
+end
+
+function mt:bindCall(args)
+ if args then
+ self._bindCallArgs = args
+ else
+ return self._bindCallArgs
+ end
+end
+
+function mt:bindMetatable(meta)
+ if meta then
+ self._bindMetatable = meta
+ else
+ return self._bindMetatable
+ end
+end
+
+function mt:action()
+ return self._action
+end
+
+function mt:setUri(uri)
+ self.uri = uri
+end
+
+function mt:getUri()
+ return self.uri
+end
+
+function mt:set(name, v)
+ if not self._flag then
+ self._flag = {}
+ end
+ self._flag[name] = v
+end
+
+function mt:get(name)
+ if not self._flag then
+ return nil
+ end
+ return self._flag[name]
+end
+
+function mt:getName()
+ return self[1]
+end
+
+function mt:kill()
+ self._dead = true
+ listMgr.clear(self.id)
+end
+
+function mt:isDead()
+ return self._dead
+end
+
+function mt:findValue()
+ local value = self:bindValue()
+ if not value then
+ return nil
+ end
+ if not value:isGlobal() then
+ return value
+ end
+ if self.type ~= 'name' then
+ return value
+ end
+ local parent = self:get 'parent'
+ if not parent then
+ return value
+ end
+ local name = self[1]
+ if type(name) ~= 'string' then
+ return value
+ end
+ return parent:getChild(name) or value
+end
+
+function mt:findCallFunction()
+ local simple = self:get 'simple'
+ if not simple then
+ return nil
+ end
+ local source
+ for i = 1, #simple do
+ if simple[i] == self then
+ source = simple[i-1]
+ end
+ end
+ if not source then
+ return nil
+ end
+ local value = source:bindValue()
+ if value and value:getFunction() then
+ return value
+ end
+ value = source:findValue()
+ if value and value:getFunction() then
+ return value
+ end
+ return nil
+end
+
+local function instant(source)
+ if source.id then
+ return false
+ end
+ local id = listMgr.add(source)
+ source.id = id
+ Watch[source] = id
+ setmetatable(source, mt)
+ return true
+end
+
+local function dummy()
+ local src = {}
+ instant(src)
+ return src
+end
+
+return {
+ instant = instant,
+ watch = Watch,
+ dummy = dummy,
+}
diff --git a/script/src/vm/special.lua b/script/src/vm/special.lua
new file mode 100644
index 00000000..e93c4445
--- /dev/null
+++ b/script/src/vm/special.lua
@@ -0,0 +1,130 @@
+local mt = require 'vm.manager'
+local multi = require 'vm.multi'
+local library = require 'core.library'
+local libraryBuilder = require 'vm.library'
+local plugin = require 'plugin'
+
+---@param func emmyFunction
+---@param values table
+function mt:callEmmySpecial(func, values, source)
+ local emmyParams = func:getEmmyParams()
+ for index, param in ipairs(emmyParams) do
+ local option = param:getOption()
+ if option and type(option.special) == 'string' then
+ self:checkEmmyParam(func, values, index, option.special, source)
+ end
+ end
+end
+
+---@param func emmyFunction
+---@param values table
+---@param index integer
+---@param special string
+function mt:checkEmmyParam(func, values, index, special, source)
+ if special == 'dofile:1' then
+ self:callEmmyDoFile(func, values, index)
+ elseif special == 'loadfile:1' then
+ self:callEmmyLoadFile(func, values, index)
+ elseif special == 'pcall:1' then
+ self:callEmmyPCall(func, values, index, source)
+ elseif special == 'require:1' then
+ self:callEmmyRequire(func, values, index)
+ end
+end
+
+---@param func emmyFunction
+---@param values table
+---@param index integer
+function mt:callEmmyDoFile(func, values, index)
+ if not values[index] then
+ values[index] = self:createValue('any', self:getDefaultSource())
+ end
+ local str = values[index]:getLiteral()
+ if type(str) ~= 'string' then
+ return
+ end
+ local requireValue = self:tryRequireOne(str, values[index], 'dofile')
+ if not requireValue then
+ requireValue = self:createValue('any', self:getDefaultSource())
+ requireValue.isRequire = true
+ end
+ func:setReturn(1, requireValue)
+end
+
+---@param func emmyFunction
+---@param values table
+---@param index integer
+function mt:callEmmyLoadFile(func, values, index)
+ if not values[index] then
+ values[index] = self:createValue('any', self:getDefaultSource())
+ end
+ local str = values[index]:getLiteral()
+ if type(str) ~= 'string' then
+ return
+ end
+ local requireValue = self:tryRequireOne(str, values[index], 'loadfile')
+ if not requireValue then
+ requireValue = self:createValue('any', self:getDefaultSource())
+ requireValue:set('cross file', true)
+ end
+ func:setReturn(1, requireValue)
+end
+
+---@param func emmyFunction
+---@param values table
+---@param index integer
+---@param source source
+function mt:callEmmyPCall(func, values, index, source)
+ local funcValue = values[index]
+ if not funcValue then
+ return
+ end
+ local realFunc = funcValue:getFunction()
+ if not realFunc then
+ return
+ end
+ local argList = multi()
+ values:eachValue(function (i, v)
+ if i > index then
+ argList:push(v)
+ end
+ end)
+ self:call(funcValue, argList, source)
+ if realFunc ~= func then
+ func:setReturn(1, self:createValue('boolean', source))
+ realFunc:getReturn():eachValue(function (i, v)
+ func:setReturn(i + 1, v)
+ end)
+ end
+end
+
+---@param func emmyFunction
+---@param values table
+---@param index integer
+function mt:callEmmyRequire(func, values, index)
+ if not values[index] then
+ values[index] = self:createValue('any', self:getDefaultSource())
+ end
+ local strValue = values[index]
+ local strSource = strValue:getSource()
+ if not strSource then
+ return nil
+ end
+ local str = strValue:getLiteral()
+ local raw = self.text:sub(strSource.start, strSource.finish)
+ str = plugin.call('OnRequirePath', str, raw) or str
+ local lib = library.library[str]
+ if lib then
+ local value = libraryBuilder.value(lib)
+ value:markGlobal()
+ func:setReturn(1, value)
+ return
+ else
+ local requireValue = self:tryRequireOne(str, strValue, 'require')
+ if not requireValue then
+ requireValue = self:createValue('any', self:getDefaultSource())
+ requireValue:set('cross file', true)
+ end
+ func:setReturn(1, requireValue)
+ end
+end
diff --git a/script/src/vm/value.lua b/script/src/vm/value.lua
new file mode 100644
index 00000000..5de0d8e8
--- /dev/null
+++ b/script/src/vm/value.lua
@@ -0,0 +1,634 @@
+local libraryBuilder = require 'vm.library'
+local library = require 'core.library'
+local listMgr = require 'vm.list'
+local config = require 'config'
+
+local Sort = 0
+local Watch = setmetatable({}, {__mode = 'kv'})
+local TypeLevel = {
+ ['table'] = 1.0,
+ ['function'] = 0.9,
+ ['string'] = 0.8,
+ ['integer'] = 0.7,
+ ['number'] = 0.6,
+}
+
+---@class value
+local mt = {}
+mt.__index = mt
+mt.type = 'value'
+mt.uri = ''
+mt._global = false
+
+local function create (tp, source, literal)
+ if tp == '...' then
+ error('Value type cant be ...')
+ end
+ if not source then
+ error('No source')
+ end
+ local id = source.id
+ if not id then
+ error('Not instanted source')
+ end
+ local self = setmetatable({
+ source = id,
+ _type = {},
+ _literal = literal,
+ _info = {},
+ }, mt)
+ if type(tp) == 'table' then
+ for i = 1, #tp do
+ self:setType(tp[i], 1.0 / #tp)
+ end
+ else
+ self:setType(tp, 1.0)
+ end
+ Watch[self] = true
+ return self
+end
+
+function mt:setType(tp, rate)
+ if type(tp) == 'table' then
+ for _, ctp in ipairs(tp) do
+ self:setType(ctp, rate)
+ end
+ return
+ end
+ if tp == '...' then
+ error('Value type cant be ...')
+ end
+ if not tp then
+ tp = 'nil'
+ end
+ if tp == 'any' or tp == 'nil' then
+ rate = 0.0
+ end
+ if tp == 'integer' then
+ local version = config.config.runtime.version
+ if version ~= 'Lua 5.3' and version ~= 'Lua 5.4' then
+ tp = 'number'
+ end
+ end
+ local current = self._type[tp] or 0.0
+ if rate > current then
+ self._type[tp] = rate
+ end
+end
+
+function mt:getType()
+ if self:getEmmy() then
+ return self:getEmmy():getType(), 1.0
+ end
+ if not self._type then
+ return 'nil', 0.0
+ end
+ local mRate = 0.0
+ local mType
+ for tp, rate in pairs(self._type) do
+ if rate > mRate then
+ mRate = rate
+ mType = tp
+ elseif rate == mRate then
+ local level1 = TypeLevel[tp] or 0.0
+ local level2 = TypeLevel[mType] or 0.0
+ if level1 > level2 then
+ mRate = rate
+ mType = tp
+ end
+ end
+ end
+ return mType or 'any', mRate
+end
+
+function mt:rawSet(index, value, source)
+ if index == nil then
+ return
+ end
+ if not self._child then
+ self._child = {}
+ end
+ if self._child[index] then
+ if self._global then
+ self._child[index]:mergeValue(value)
+ else
+ self._child[index]:mergeType(value)
+ self._child[index]:mergeInfo(value)
+ end
+ self._child[index] = value
+ else
+ self._child[index] = value
+ end
+ self:addInfo('set child', source, index, self._child[index])
+ if self._global then
+ self._child[index]:markGlobal()
+ end
+end
+
+function mt:rawGet(index)
+ if not self._child then
+ return nil
+ end
+ self:flushChild()
+ local child = self._child[index]
+ if not child then
+ return nil
+ end
+ return child
+end
+
+function mt:setChild(index, value, source)
+ if index == nil then
+ return
+ end
+ self:setType('table', 0.5)
+ self:rawSet(index, value, source)
+ return value
+end
+
+function mt:getLibChild(index)
+ local tp = self:getType()
+ local lib = library.object[tp]
+ if lib then
+ local childs = libraryBuilder.child(lib)
+ return childs[index]
+ end
+ return nil
+end
+
+function mt:eachLibChild(callback)
+ local tp = self:getType()
+ local lib = library.object[tp]
+ if lib then
+ local childs = libraryBuilder.child(lib)
+ for k, v in pairs(childs) do
+ callback(k, v)
+ end
+ end
+end
+
+function mt:getChild(index, source)
+ self:setType('table', 0.5)
+ local parent = self
+ local value
+ -- 最多检查3层 __index
+ for _ = 1, 3 do
+ value = parent:rawGet(index)
+ if value then
+ break
+ end
+ local method = parent:getMetaMethod('__index')
+ if not method then
+ value = parent:getLibChild(index)
+ break
+ end
+ parent = method
+ end
+ if not value and source then
+ local emmy = self:getEmmy()
+ if emmy then
+ if emmy.type == 'emmy.arrayType' then
+ if type(index) == 'number' then
+ value = create(emmy:getName(), source)
+ end
+ elseif emmy.type == 'emmy.tableType' then
+ value = create(emmy:getValueType():getType(), source)
+ end
+ end
+ if not value then
+ value = create('any', source)
+ end
+ self:setChild(index, value)
+ value.uri = self.uri
+ end
+ return value
+end
+
+function mt:setMetaTable(metatable)
+ local source = metatable:getSource()
+ if not source then
+ return
+ end
+ source:bindMetatable(metatable)
+ self._meta = metatable.source
+end
+
+function mt:getMetaTable()
+ if not self._meta then
+ return nil
+ end
+ local metaSource = listMgr.get(self._meta)
+ if not metaSource then
+ self._meta = nil
+ return nil
+ end
+ return metaSource:bindMetatable()
+end
+
+function mt:getMetaMethod(name)
+ local meta = self:getMetaTable()
+ if not meta then
+ return nil
+ end
+ return meta:rawGet(name)
+end
+
+function mt:flushChild()
+ if not self._child then
+ return nil
+ end
+ -- 非全局值不会出现dead child
+ if not self._global then
+ return
+ end
+ local listVersion = listMgr.getVersion()
+ if self._flushVersion == listVersion then
+ return
+ end
+ self._flushVersion = listVersion
+ local alived = {}
+ local infos = self._info
+ local count = 0
+ for srcId, info in pairs(infos) do
+ local src = listMgr.get(srcId)
+ if src then
+ if info.type == 'set child' or info.type == 'get child' then
+ if info[1] then
+ alived[info[1]] = true
+ end
+ end
+ count = count + 1
+ else
+ infos[srcId] = nil
+ end
+ end
+ infos._count = count
+ infos._limit = count * 1.1 + 10
+ infos._version = listMgr.getVersion()
+ for index in pairs(self._child) do
+ if not alived[index] then
+ self._child[index] = nil
+ end
+ end
+end
+
+function mt:rawEach(callback, mark)
+ if not self._child then
+ return nil
+ end
+ self:flushChild()
+ for index, value in pairs(self._child) do
+ if mark then
+ if mark[index] then
+ goto CONTINUE
+ end
+ mark[index] = true
+ end
+ local res = callback(index, value)
+ if res ~= nil then
+ return res
+ end
+ ::CONTINUE::
+ end
+ return nil
+end
+
+function mt:eachChild(callback)
+ local mark = {}
+ local parent = self
+ -- 最多检查3层 __index
+ for _ = 1, 3 do
+ local res = parent:rawEach(callback, mark)
+ if res ~= nil then
+ return res
+ end
+ local method = parent:getMetaMethod('__index')
+ if not method then
+ return parent:eachLibChild(callback)
+ end
+ parent = method
+ end
+end
+
+function mt:mergeType(value)
+ if self == value then
+ return
+ end
+ if not value then
+ return
+ end
+ if self._emmy and not value._emmy then
+ value._emmy = self._emmy
+ return
+ elseif not self._emmy and value._emmy then
+ self._emmy = value._emmy
+ return
+ end
+ if value._type then
+ for tp, rate in pairs(value._type) do
+ self:setType(tp, rate)
+ end
+ end
+ value._type = self._type
+end
+
+function mt:mergeInfo(value)
+ if self == value then
+ return
+ end
+ if not value then
+ return
+ end
+ local infos = self._info
+ for srcId, info in pairs(value._info) do
+ local src = listMgr.get(srcId)
+ if src and not infos[srcId] then
+ infos[srcId] = info
+ infos._count = (infos._count or 0) + 1
+ end
+ end
+ value._info = infos
+end
+
+function mt:mergeValue(value)
+ if self == value then
+ return
+ end
+ if not value then
+ return
+ end
+ local list = {self, value}
+ local pos = 1
+ while true do
+ local a, b = list[pos], list[pos+1]
+ if not a then
+ break
+ end
+ pos = pos + 2
+ list[a] = true
+ list[b] = true
+ a:mergeType(b)
+ a:mergeInfo(b)
+
+ a:flushChild()
+ b:flushChild()
+ local global = a._global or b._global
+ if b._child then
+ if not a._child then
+ a._child = {}
+ end
+ for k, bc in pairs(b._child) do
+ local ac = a._child[k]
+ if ac and ac ~= bc and global then
+ if list[ac] and list[bc] then
+ else
+ list[#list+1] = ac
+ list[#list+1] = bc
+ end
+ end
+ a._child[k] = bc
+ end
+ end
+ b._child = a._child
+ if global then
+ a:markGlobal()
+ b:markGlobal()
+ end
+
+ if b._meta then
+ a._meta = b._meta
+ end
+ if b._func then
+ a._func = b._func
+ end
+ if b._lib then
+ a._lib = b._lib
+ end
+ if b.uri then
+ a.uri = b.uri
+ end
+ end
+end
+
+function mt:addInfo(tp, source, ...)
+ if not source then
+ return
+ end
+ if not source.start then
+ error('Miss start: ' .. table.dump(source))
+ end
+ local id = source.id
+ if not id then
+ error('Not instanted source')
+ end
+ if not tp then
+ error('Miss info type')
+ end
+
+ local infos = self._info
+ if infos[id] then
+ return
+ end
+ Sort = Sort + 1
+ local info = {
+ type = tp,
+ source = id,
+ _sort = Sort,
+ ...
+ }
+ infos[id] = info
+ infos._count = (infos._count or 0) + 1
+ local version = listMgr.getVersion()
+ -- 只有全局值需要压缩info
+ if self._global and infos._count > (infos._limit or 10) and infos._version ~= version then
+ local count = 0
+ for srcId in pairs(infos) do
+ local src = listMgr.get(srcId)
+ if src then
+ count = count + 1
+ else
+ infos[srcId] = nil
+ end
+ end
+ infos._count = count
+ infos._limit = count * 1.1 + 10
+ infos._version = version
+ end
+end
+
+function mt:eachInfo(callback)
+ local clock = os.clock()
+ local infos = self._info
+ local list = {}
+ for srcId, info in pairs(infos) do
+ local src = listMgr.get(srcId)
+ if src then
+ list[#list+1] = info
+ else
+ infos[srcId] = nil
+ end
+ end
+ infos._count = #list
+ infos._limit = infos._count * 1.1 + 10
+ infos._version = listMgr.getVersion()
+ --local clock2 = os.clock()
+ --table.sort(list, function (a, b)
+ -- return a._sort < b._sort
+ --end)
+ local passed = os.clock() - clock
+ if passed > 0.1 then
+ log.warn(('eachInfo takes: [%.3f]sec, #list: %d'):format(passed, #list))
+ end
+ for i = 1, #list do
+ local info = list[i]
+ local res = callback(info, listMgr.get(info.source))
+ if res ~= nil then
+ return res
+ end
+ end
+ return nil
+end
+
+function mt:setFunction(func)
+ self._func = func.id
+ if self._global then
+ func:markGlobal()
+ end
+end
+
+function mt:getFunction()
+ local id = self._func
+ local func = listMgr.get(id)
+ if not func then
+ return nil
+ end
+ if func._removed then
+ return nil
+ end
+ if not func:getSource() then
+ func = nil
+ listMgr.clear(id)
+ end
+ return func
+end
+
+function mt:setLib(lib)
+ self._lib = lib
+end
+
+function mt:getLib()
+ return self._lib
+end
+
+function mt:getLiteral()
+ return self._literal
+end
+
+function mt:set(name, v)
+ if not self._flag then
+ self._flag = {}
+ end
+ self._flag[name] = v
+end
+
+function mt:get(name)
+ if not self._flag then
+ return nil
+ end
+ return self._flag[name]
+end
+
+function mt:getSource()
+ return listMgr.get(self.source)
+end
+
+function mt:markGlobal()
+ if self._global then
+ return
+ end
+ self._global = true
+ self:rawEach(function (index, value)
+ value:markGlobal()
+ end)
+ local func = self:getFunction()
+ if func then
+ func:markGlobal()
+ end
+end
+
+function mt:isGlobal()
+ return self._global
+end
+
+function mt:setEmmy(emmy)
+ if not emmy then
+ return
+ end
+ if emmy.type == 'emmy.class' then
+ ---@type EmmyClass
+ local emmyClass = emmy
+ emmyClass:setValue(self)
+ emmyClass:eachChild(function (obj)
+ local value = obj:getValue()
+ if value then
+ value:mergeValue(self)
+ end
+ end)
+ emmyClass:eachField(function (field)
+ local name = field:getName()
+ local value = field:bindValue()
+ self:setChild(name, value, field:getSource())
+ end)
+ elseif emmy.type == 'emmy.type' then
+ ---@type EmmyType
+ local emmyType = emmy
+ emmyType:setValue(self)
+ emmyType:eachClass(function (class)
+ if class then
+ self:mergeValue(class:getValue())
+ end
+ end)
+ elseif emmy.type == 'emmy.arrayType' then
+ ---@type EmmyArrayType
+ local emmyArrayType = emmy
+ emmyArrayType:setValue(self)
+ elseif emmy.type == 'emmy.tableType' then
+ ---@type EmmyTableType
+ local emmyTableType = emmy
+ emmyTableType:setValue(self)
+ elseif emmy.type == 'emmy.functionType' then
+ ---@type EmmyFunctionType
+ local emmyFuncType = emmy
+ emmyFuncType:setValue(self)
+ self:setFunction(emmyFuncType:bindFunction())
+ else
+ return
+ end
+ self._emmy = emmy
+ self:markGlobal()
+end
+
+function mt:getEmmy()
+ if not self._emmy then
+ return nil
+ end
+ local source = self._emmy.source
+ if not listMgr.get(source) then
+ self._emmy = nil
+ return nil
+ end
+ return self._emmy
+end
+
+function mt:setComment(comment)
+ self._comment = comment
+end
+
+function mt:getComment(comment)
+ return self._comment
+end
+
+return {
+ create = create,
+ watch = Watch,
+}
diff --git a/script/src/vm/vm.lua b/script/src/vm/vm.lua
new file mode 100644
index 00000000..36ad78c9
--- /dev/null
+++ b/script/src/vm/vm.lua
@@ -0,0 +1,1334 @@
+local library = require 'core.library'
+local valueMgr = require 'vm.value'
+local localMgr = require 'vm.local'
+local createLabel = require 'vm.label'
+local functionMgr = require 'vm.function'
+local sourceMgr = require 'vm.source'
+local buildGlobal = require 'vm.global'
+local createMulti = require 'vm.multi'
+local libraryBuilder = require 'vm.library'
+local emmyMgr = require 'emmy.manager'
+local config = require 'config'
+local mt = require 'vm.manager'
+local plugin = require 'plugin'
+
+require 'vm.module'
+require 'vm.raw'
+require 'vm.pcall'
+require 'vm.ipairs'
+require 'vm.emmy'
+require 'vm.special'
+
+-- TODO source测试
+--rawset(_G, 'CachedSource', setmetatable({}, { __mode = 'kv' }))
+
+function mt:getDefaultSource()
+ return self:instantSource {
+ start = 0,
+ finish = 0,
+ }
+end
+
+function mt:scopePush(source)
+ self.currentFunction:push(source)
+end
+
+function mt:scopePop()
+ self.currentFunction:pop()
+end
+
+function mt:buildTable(source)
+ local tbl = self:createValue('table', source)
+ if not source then
+ return tbl
+ end
+ local n = 0
+ for index, obj in ipairs(source) do
+ local emmy = self:getEmmy()
+ if obj.type == 'pair' then
+ local value = self:getFirstInMulti(self:getExp(obj[2]))
+ if value then
+ local key = obj[1]
+ self:instantSource(obj)
+ self:instantSource(key)
+ key:bindValue(value, 'set')
+ value:setEmmy(emmy)
+ if key.type == 'index' then
+ local index = self:getIndex(key)
+ key:set('parent', tbl)
+ tbl:setChild(index, value, key)
+ else
+ if key.type == 'name' then
+ key:set('parent', tbl)
+ key:set('table index', true)
+ tbl:setChild(key[1], value, key)
+ end
+ end
+ end
+ elseif obj.type:sub(1, 4) == 'emmy' then
+ self:doEmmy(obj)
+ else
+ local value = self:getExp(obj)
+ if value.type == 'multi' then
+ if index == #source then
+ value:eachValue(function (_, v)
+ n = n + 1
+ tbl:setChild(n, v, obj)
+ end)
+ else
+ n = n + 1
+ local v = self:getFirstInMulti(value)
+ tbl:setChild(n, v, obj)
+ end
+ else
+ n = n + 1
+ tbl:setChild(n, value, obj)
+ end
+ -- 处理写了一半的 key = value,把name类的数组元素视为哈希键
+ if obj.type == 'name' then
+ obj:set('table index', true)
+ end
+ end
+ end
+ return tbl
+end
+
+function mt:runFunction(func)
+ func:run(self)
+
+ if not func:getSource() then
+ return
+ end
+
+ if func:needSkip() then
+ return
+ end
+
+ -- 暂时使用这种方式激活参数的source
+ for _, arg in ipairs(func.args) do
+ if arg:getSource() ~= func:getObject() then
+ self:bindLocal(arg:getSource(), arg, 'local')
+ end
+ end
+
+ local originFunction = self:getCurrentFunction()
+ self:setCurrentFunction(func)
+ func:push(func:getSource())
+ func:markChunk()
+
+ self:doActions(func:getSource())
+
+ func:pop()
+ self:setCurrentFunction(originFunction)
+end
+
+function mt:buildFunction(exp)
+ if exp and exp:bindFunction() then
+ return exp:bindFunction()
+ end
+
+ local value = self:createFunction(exp)
+
+ if not exp then
+ return value
+ end
+
+ exp:bindFunction(value)
+ local func = value:getFunction()
+
+ self:eachLocal(function (name, loc)
+ func:saveUpvalue(name, loc)
+ end)
+
+ return value
+end
+
+function mt:forList(list, callback)
+ if not list then
+ return
+ end
+ if list.type == 'list' then
+ for i = 1, #list do
+ callback(list[i])
+ end
+ else
+ callback(list)
+ end
+end
+
+function mt:callSetMetaTable(func, values, source)
+ if not values[1] then
+ values[1] = self:createValue('any', self:getDefaultSource())
+ end
+ if not values[2] then
+ values[2] = self:createValue('any', self:getDefaultSource())
+ end
+ func:setReturn(1, values[1])
+ values[1]:setMetaTable(values[2])
+end
+
+function mt:tryRequireOne(str, strValue, mode)
+ if not self.lsp or not self.lsp.workspace then
+ return nil
+ end
+ local strSource = strValue:getSource()
+ if not strSource then
+ return nil
+ end
+ if type(str) == 'string' then
+ -- 支持 require 'xxx' 的转到定义
+ self:instantSource(strSource)
+ local uri
+ if mode == 'require' then
+ uri = self.lsp.workspace:searchPath(self:getUri(), str)
+ elseif mode == 'loadfile' then
+ uri = self.lsp.workspace:loadPath(self:getUri(), str)
+ elseif mode == 'dofile' then
+ uri = self.lsp.workspace:loadPath(self:getUri(), str)
+ end
+ if not uri then
+ return nil
+ end
+
+ strSource:set('target uri', uri)
+ self.lsp:compileChain(self:getUri(), uri)
+ return self.lsp.chain:get(uri)
+ end
+ return nil
+end
+
+function mt:callRequire(func, values)
+ if not values[1] then
+ values[1] = self:createValue('any', self:getDefaultSource())
+ end
+ local strValue = values[1]
+ local strSource = strValue:getSource()
+ if not strSource then
+ return nil
+ end
+ local str = strValue:getLiteral()
+ local raw = self.text:sub(strSource.start, strSource.finish)
+ str = plugin.call('OnRequirePath', str, raw) or str
+ local lib = library.library[str]
+ if lib then
+ local value = libraryBuilder.value(lib)
+ value:markGlobal()
+ func:setReturn(1, value)
+ return
+ else
+ local requireValue = self:tryRequireOne(str, values[1], 'require')
+ if not requireValue then
+ requireValue = self:createValue('any', self:getDefaultSource())
+ requireValue:set('cross file', true)
+ end
+ func:setReturn(1, requireValue)
+ end
+end
+
+function mt:callLoadFile(func, values)
+ if not values[1] then
+ values[1] = self:createValue('any', self:getDefaultSource())
+ end
+ local strValue = values[1]
+ local requireValue = self:tryRequireOne(strValue:getLiteral(), values[1], 'loadfile')
+ if not requireValue then
+ requireValue = self:createValue('any', self:getDefaultSource())
+ requireValue:set('cross file', true)
+ end
+ func:setReturn(1, requireValue)
+end
+
+function mt:callDoFile(func, values)
+ if not values[1] then
+ values[1] = self:createValue('any', self:getDefaultSource())
+ end
+ local strValue = values[1]
+ local requireValue = self:tryRequireOne(strValue:getLiteral(), values[1], 'dofile')
+ if not requireValue then
+ requireValue = self:createValue('any', self:getDefaultSource())
+ requireValue.isRequire = true
+ end
+ func:setReturn(1, requireValue)
+end
+
+function mt:callLibrary(func, values, source, lib)
+ if lib.args then
+ for i, arg in ipairs(lib.args) do
+ local value = values[i]
+ if value and arg.type ~= '...' then
+ value:setType(arg.type, 0.6)
+ end
+ end
+ end
+ if lib.returns then
+ for i, rtn in ipairs(lib.returns) do
+ if rtn.type == '...' then
+ --func:getReturn(i):setType('any', 0.0)
+ else
+ if rtn.type == 'boolean' or rtn.type == 'number' or rtn.type == 'integer' or rtn.type == 'string' then
+ func:setReturn(i, self:createValue(rtn.type, self:getDefaultSource()))
+ end
+ local value = func:getReturn(i)
+ if value then
+ value:setType(rtn.type or 'any', 0.6)
+ end
+ end
+ end
+ end
+ if lib.special then
+ if lib.special == 'setmetatable' then
+ self:callSetMetaTable(func, values, source)
+ elseif lib.special == 'require' then
+ self:callRequire(func, values)
+ elseif lib.special == 'loadfile' then
+ self:callLoadFile(func, values)
+ elseif lib.special == 'dofile' then
+ self:callDoFile(func, values)
+ elseif lib.special == 'module' then
+ self:callModuel(func, values)
+ elseif lib.special == 'seeall' then
+ self:callSeeAll(func, values)
+ elseif lib.special == 'rawset' then
+ self:callRawSet(func, values, source)
+ elseif lib.special == 'rawget' then
+ self:callRawGet(func, values, source)
+ elseif lib.special == 'pcall' then
+ self:callPcall(func, values, source)
+ elseif lib.special == 'xpcall' then
+ self:callXpcall(func, values, source)
+ elseif lib.special == 'ipairs' then
+ self:callIpairs(func, values, source)
+ elseif lib.special == '@ipairs' then
+ self:callAtIpairs(func, values, source)
+ elseif lib.special == 'pairs' then
+ self:callPairs(func, values, source)
+ elseif lib.special == 'next' then
+ self:callNext(func, values, source)
+ end
+ else
+ -- 如果lib的参数中有function,则立即执行function
+ if lib.args then
+ local args
+ for i = 1, #lib.args do
+ local value = values[i]
+ if value and value:getFunction() then
+ if not args then
+ args = createMulti()
+ end
+ self:call(value, args, source)
+ end
+ end
+ end
+ end
+end
+
+function mt:call(value, values, source)
+ local lib = value:getLib()
+ ---@type emmyFunction
+ local func = value:getFunction()
+ value:setType('function', 0.5)
+ if not func then
+ return
+ end
+ self:instantSource(source)
+ if lib then
+ self:callLibrary(func, values, source, lib)
+ else
+ if func:getSource() then
+ if not source:get 'called' then
+ source:set('called', true)
+ func:setArgs(values)
+ self:runFunction(func)
+ end
+ else
+ func:mergeReturn(1, self:createValue('any', source))
+ end
+ if func:getEmmyParams() then
+ self:callEmmySpecial(func, values, source)
+ end
+ end
+
+ return func:getReturn()
+end
+
+function mt:createValue(tp, source, literal)
+ local value = valueMgr.create(tp, source, literal)
+ value.uri = self:getUri()
+ return value
+end
+
+function mt:getName(name, source)
+ if source then
+ self:instantSource(source)
+ if source:bindLocal() then
+ local loc = source:bindLocal()
+ return loc:getValue()
+ end
+ end
+ local loc = self:loadLocal(name)
+ if loc then
+ source:bindLocal(loc, 'get')
+ return loc:getValue()
+ end
+ local global = source:bindValue()
+ if global then
+ return global
+ end
+ local ENV
+ if self.envType == '_ENV' then
+ ENV = self:loadLocal('_ENV')
+ else
+ ENV = self:loadLocal('@ENV')
+ end
+ local ENVValue = ENV:getValue()
+ ENVValue:addInfo('get child', source, name)
+ global = ENVValue:getChild(name, source)
+ source:bindValue(global, 'get')
+ source:set('global', true)
+ source:set('parent', ENVValue)
+ if not global:getLib() then
+ if self.lsp then
+ self.lsp.global:markGet(self:getUri())
+ end
+ end
+ return global
+end
+
+function mt:setName(name, source, value)
+ self:instantSource(source)
+ local loc = self:loadLocal(name)
+ if loc then
+ loc:setValue(value)
+ source:bindLocal(loc, 'set')
+ return
+ end
+ local global = source:bindValue()
+ if global then
+ return global
+ end
+ local ENV
+ if self.envType == '_ENV' then
+ ENV = self:loadLocal('_ENV')
+ else
+ ENV = self:loadLocal('@ENV')
+ end
+ local ENVValue = ENV:getValue()
+ source:bindValue(value, 'set')
+ ENVValue:setChild(name, value, source)
+ source:set('global', true)
+ source:set('parent', ENVValue)
+ if self.lsp then
+ self.lsp.global:markSet(self:getUri())
+ end
+end
+
+function mt:getIndex(source)
+ local child = source[1]
+ if child.type == 'name' then
+ local value = self:getName(child[1], child)
+ child:set('in index', source)
+ return value
+ elseif child.type == 'string' or child.type == 'number' or child.type == 'boolean' then
+ self:instantSource(child)
+ child:set('in index', source)
+ return child[1]
+ else
+ local index = self:getExp(child)
+ return self:getFirstInMulti(index)
+ end
+end
+
+function mt:unpackList(list)
+ local values = createMulti()
+ local exps = createMulti()
+ if not list then
+ return values
+ end
+ if list.type == 'list' or list.type == 'call' or list.type == 'return' then
+ for i, exp in ipairs(list) do
+ self:instantSource(exp)
+ exps:push(exp)
+ if exp.type == '...' then
+ values:merge(self:loadDots())
+ break
+ end
+ local value = self:getExp(exp)
+ if value.type == 'multi' then
+ if i == #list then
+ value:eachValue(function (_, v)
+ values:push(v)
+ end)
+ else
+ values:push(self:getFirstInMulti(value))
+ end
+ else
+ values:push(value)
+ end
+ end
+ elseif list.type == '...' then
+ self:instantSource(list)
+ exps:push(list)
+ values:merge(self:loadDots())
+ else
+ self:instantSource(list)
+ exps:push(list)
+ local value = self:getExp(list)
+ if value.type == 'multi' then
+ value:eachValue(function (_, v)
+ values:push(v)
+ end)
+ else
+ values:push(value)
+ end
+ end
+ return values, exps
+end
+
+function mt:getFirstInMulti(multi)
+ if not multi then
+ return multi
+ end
+ if multi.type == 'multi' then
+ return self:getFirstInMulti(multi[1])
+ else
+ return multi
+ end
+end
+
+function mt:getSimple(simple, max)
+ self:instantSource(simple)
+ local first = simple[1]
+ self:instantSource(first)
+ local value = self:getExp(first)
+ value = self:getFirstInMulti(value) or valueMgr.create('nil', self:getDefaultSource())
+ first:bindValue(value, 'get')
+ if not max then
+ max = #simple
+ elseif max < 0 then
+ max = #simple + 1 + max
+ end
+ local object
+ for i = 2, max do
+ local source = simple[i]
+ self:instantSource(source)
+ source:set('simple', simple)
+ value = self:getFirstInMulti(value) or valueMgr.create('nil', self:getDefaultSource())
+
+ if source.type == 'call' then
+ local values, args = self:unpackList(source)
+ local func = value
+ if object then
+ table.insert(values, 1, object)
+ table.insert(args, 1, simple[i-3])
+ source:set('has object', true)
+ end
+ object = nil
+ source:bindCall(args)
+ value = self:call(func, values, source) or valueMgr.create('any', self:getDefaultSource())
+ elseif source.type == 'index' then
+ local child = source[1]
+ local index = self:getIndex(source)
+ child:set('parent', value)
+ value:addInfo('get child', source, index)
+ value = value:getChild(index, source)
+ source:bindValue(value, 'get')
+ elseif source.type == 'name' then
+ source:set('parent', value)
+ source:set('object', object)
+ value:addInfo('get child', source, source[1])
+ value = value:getChild(source[1], source)
+ source:bindValue(value, 'get')
+ elseif source.type == ':' then
+ object = value
+ source:set('parent', value)
+ source:set('object', object)
+ elseif source.type == '.' then
+ source:set('parent', value)
+ end
+ end
+ return value
+end
+
+function mt:isTrue(v)
+ if v:getType() == 'nil' then
+ return false
+ end
+ if v:getType() == 'boolean' and not v:getLiteral() then
+ return false
+ end
+ return true
+end
+
+function mt:getBinary(exp)
+ self:instantSource(exp)
+ local v1 = self:getExp(exp[1])
+ local v2 = self:getExp(exp[2])
+ v1 = self:getFirstInMulti(v1) or valueMgr.create('nil', exp[1])
+ v2 = self:getFirstInMulti(v2) or valueMgr.create('nil', exp[2])
+ local op = exp.op
+ -- TODO 搜索元方法
+ if op == 'or' then
+ if self:isTrue(v1) then
+ return v1
+ else
+ return v2
+ end
+ elseif op == 'and' then
+ if self:isTrue(v1) then
+ return v2
+ else
+ return v1
+ end
+ elseif op == '<='
+ or op == '>='
+ or op == '<'
+ or op == '>'
+ then
+ v1:setType('number', 0.5)
+ v2:setType('number', 0.5)
+ v1:setType('string', 0.1)
+ v2:setType('string', 0.1)
+ return self:createValue('boolean', exp)
+ elseif op == '~='
+ or op == '=='
+ then
+ return self:createValue('boolean', exp)
+ elseif op == '|'
+ or op == '~'
+ or op == '&'
+ or op == '<<'
+ or op == '>>'
+ then
+ v1:setType('integer', 0.5)
+ v2:setType('integer', 0.5)
+ v1:setType('number', 0.5)
+ v2:setType('number', 0.5)
+ v1:setType('string', 0.1)
+ v2:setType('string', 0.1)
+ if math.type(v1:getLiteral()) == 'integer' and math.type(v2:getLiteral()) == 'integer' then
+ if op == '|' then
+ return self:createValue('integer', exp, v1:getLiteral() | v2:getLiteral())
+ elseif op == '~' then
+ return self:createValue('integer', exp, v1:getLiteral() ~ v2:getLiteral())
+ elseif op == '&' then
+ return self:createValue('integer', exp, v1:getLiteral() &v2:getLiteral())
+ elseif op == '<<' then
+ return self:createValue('integer', exp, v1:getLiteral() << v2:getLiteral())
+ elseif op == '>>' then
+ return self:createValue('integer', exp, v1:getLiteral() >> v2:getLiteral())
+ end
+ end
+ return self:createValue('integer', exp)
+ elseif op == '..' then
+ v1:setType('string', 0.5)
+ v2:setType('string', 0.5)
+ v1:setType('number', 0.1)
+ v2:setType('number', 0.1)
+ if type(v1:getLiteral()) == 'string' and type(v2:getLiteral()) == 'string' then
+ return self:createValue('string', exp, v1:getLiteral() .. v2:getLiteral())
+ end
+ return self:createValue('string', exp)
+ elseif op == '+'
+ or op == '-'
+ or op == '*'
+ or op == '/'
+ or op == '^'
+ or op == '%'
+ or op == '//'
+ then
+ v1:setType('number', 0.5)
+ v2:setType('number', 0.5)
+ if type(v1:getLiteral()) == 'number' and type(v2:getLiteral()) == 'number' then
+ if op == '+' then
+ return self:createValue('number', exp, v1:getLiteral() + v2:getLiteral())
+ elseif op == '-' then
+ return self:createValue('number', exp, v1:getLiteral() - v2:getLiteral())
+ elseif op == '*' then
+ return self:createValue('number', exp, v1:getLiteral() * v2:getLiteral())
+ elseif op == '/' then
+ if v2:getLiteral() ~= 0 then
+ return self:createValue('number', exp, v1:getLiteral() / v2:getLiteral())
+ end
+ elseif op == '^' then
+ return self:createValue('number', exp, v1:getLiteral() ^ v2:getLiteral())
+ elseif op == '%' then
+ if v2:getLiteral() ~= 0 then
+ return self:createValue('number', exp, v1:getLiteral() % v2:getLiteral())
+ end
+ elseif op == '//' then
+ if v2:getLiteral() ~= 0 then
+ return self:createValue('number', exp, v1:getLiteral() // v2:getLiteral())
+ end
+ end
+ end
+ return self:createValue('number', exp)
+ end
+ return nil
+end
+
+function mt:getUnary(exp)
+ self:instantSource(exp)
+ local v1 = self:getExp(exp[1])
+ v1 = self:getFirstInMulti(v1) or self:createValue('nil', exp[1])
+ local op = exp.op
+ -- TODO 搜索元方法
+ if op == 'not' then
+ return self:createValue('boolean', exp)
+ elseif op == '#' then
+ v1:setType('table', 0.5)
+ v1:setType('string', 0.5)
+ if type(v1:getLiteral()) == 'string' then
+ return self:createValue('integer', exp, #v1:getLiteral())
+ end
+ return self:createValue('integer', exp)
+ elseif op == '-' then
+ v1:setType('number', 0.5)
+ if type(v1:getLiteral()) == 'number' then
+ return self:createValue('number', exp, -v1:getLiteral())
+ end
+ return self:createValue('number', exp)
+ elseif op == '~' then
+ v1:setType('integer', 0.5)
+ if math.type(v1:getLiteral()) == 'integer' then
+ return self:createValue('integer', exp, ~v1:getLiteral())
+ end
+ return self:createValue('integer', exp)
+ end
+ return nil
+end
+
+function mt:getExp(exp)
+ self:instantSource(exp)
+ local tp = exp.type
+ if tp == 'nil' then
+ return self:createValue('nil', exp)
+ elseif tp == 'string' then
+ return self:createValue('string', exp, exp[1])
+ elseif tp == 'boolean' then
+ return self:createValue('boolean', exp, exp[1])
+ elseif tp == 'number' then
+ return self:createValue('number', exp, exp[1])
+ elseif tp == 'name' then
+ local value = self:getName(exp[1], exp)
+ return value
+ elseif tp == 'simple' then
+ return self:getSimple(exp)
+ elseif tp == 'index' then
+ return self:getIndex(exp)
+ elseif tp == 'binary' then
+ return self:getBinary(exp)
+ elseif tp == 'unary' then
+ return self:getUnary(exp)
+ elseif tp == 'function' then
+ return self:buildFunction(exp)
+ elseif tp == 'table' then
+ return self:buildTable(exp)
+ elseif tp == '...' then
+ return self:loadDots()
+ elseif tp == 'list' then
+ return self:getMultiByList(exp)
+ end
+ error('Unkown exp type: ' .. tostring(tp))
+end
+
+function mt:getMultiByList(list)
+ local multi = createMulti()
+ for i, exp in ipairs(list) do
+ multi:push(self:getExp(exp), i == #list)
+ end
+ return multi
+end
+
+function mt:doDo(action)
+ self:instantSource(action)
+ self:scopePush(action)
+ self:doActions(action)
+ self:scopePop()
+end
+
+function mt:doReturn(action)
+ if #action == 0 then
+ return
+ end
+ self:instantSource(action)
+ local values = self:unpackList(action)
+ local func = self:getCurrentFunction()
+ values:eachValue(function (n, value)
+ value.uri = self:getUri()
+ func:mergeReturn(n, value)
+ local source = action[n] or value:getSource()
+ if not source or source.start == 0 then
+ source = self:getDefaultSource()
+ end
+ value:addInfo('return', source)
+ end)
+end
+
+function mt:doLabel(source)
+ local name = source[1]
+ local label = self:loadLabel(name)
+ if label then
+ self:bindLabel(source, label, 'set')
+ else
+ label = self:createLabel(name, source, 'set')
+ end
+end
+
+function mt:createLabel(name, source, action)
+ local label = self:bindLabel(source)
+ if label then
+ self:saveLabel(label)
+ return label
+ end
+
+ label = createLabel(name, source)
+ self:saveLabel(label)
+ self:bindLabel(source, label, action)
+ return label
+end
+
+function mt:doGoTo(source)
+ local name = source[1]
+ local label = self:loadLabel(name)
+ if label then
+ self:bindLabel(source, label, 'get')
+ else
+ label = self:createLabel(name, source, 'get')
+ end
+end
+
+function mt:setOne(var, value, emmy, comment)
+ if not value then
+ value = valueMgr.create('nil', self:getDefaultSource())
+ end
+ value:setEmmy(emmy)
+ value:setComment(comment)
+ self:instantSource(var)
+ if var.type == 'name' then
+ self:setName(var[1], var, value)
+ elseif var.type == 'simple' then
+ local parent = self:getSimple(var, -2)
+ parent = self:getFirstInMulti(parent)
+ local key = var[#var]
+ self:instantSource(key)
+ key:set('simple', var)
+ if key.type == 'index' then
+ local index = self:getIndex(key)
+ key[1]:set('parent', parent)
+ parent:setChild(index, value, key[1])
+ elseif key.type == 'name' then
+ local index = key[1]
+ key:set('parent', parent)
+ parent:setChild(index, value, key)
+ end
+ key:bindValue(value, 'set')
+ end
+end
+
+function mt:doSet(action)
+ local emmy = self:getEmmy()
+ local comment = self:getEmmyComment()
+ if not action[2] then
+ return
+ end
+ self:instantSource(action)
+ -- 要先计算值
+ local vars = action[1]
+ local exps = action[2]
+ local value = self:getExp(exps)
+ local values = {}
+ if value.type == 'multi' then
+ if not emmy then
+ emmy = value:getEmmy()
+ end
+ value:eachValue(function (i, v)
+ values[i] = v
+ end)
+ else
+ values[1] = value
+ end
+ local i = 0
+ self:forList(vars, function (var)
+ i = i + 1
+ self:setOne(var, values[i], emmy, comment)
+ end)
+end
+
+function mt:doLocal(action)
+ local emmy = self:getEmmy()
+ local comment = self:getEmmyComment()
+ self:instantSource(action)
+ local vars = action[1]
+ local exps = action[2]
+ local values
+ if exps then
+ local value = self:getExp(exps)
+ values = {}
+ if value.type == 'multi' then
+ if not emmy then
+ emmy = value:getEmmy()
+ end
+ value:eachValue(function (i, v)
+ values[i] = v
+ end)
+ else
+ values[1] = value
+ end
+ end
+ local i = 0
+ self:forList(vars, function (key)
+ i = i + 1
+ local value
+ if values then
+ value = values[i]
+ end
+ self:createLocal(key[1], key, value, emmy, comment)
+ end)
+end
+
+function mt:doIf(action)
+ self:instantSource(action)
+ for _, block in ipairs(action) do
+ if block.filter then
+ self:getExp(block.filter)
+ end
+
+ self:scopePush(block)
+ self:doActions(block)
+ self:scopePop()
+ end
+end
+
+function mt:doLoop(action)
+ self:instantSource(action)
+ local min = self:getFirstInMulti(self:getExp(action.min))
+ self:getExp(action.max)
+ if action.step then
+ self:getExp(action.step)
+ end
+
+ self:scopePush(action)
+ self:createLocal(action.arg[1], action.arg, min)
+ self:doActions(action)
+ self:scopePop()
+end
+
+function mt:doIn(action)
+ local emmyParams = self:getEmmyParams()
+ self:instantSource(action)
+ local args = self:unpackList(action.exp)
+
+ self:scopePush(action)
+ local func = table.remove(args, 1) or valueMgr.create('any', self:getDefaultSource())
+ local values = self:call(func, args, action) or createMulti()
+ self:forList(action.arg, function (arg)
+ self:instantSource(arg)
+ local value = table.remove(values, 1) or self:createValue('nil', arg)
+ if emmyParams then
+ for i = #emmyParams, 1, -1 do
+ local emmyParam = emmyParams[i]
+ if emmyParam and emmyParam:getName() == arg[1] then
+ value:setEmmy(emmyParam:bindType())
+ end
+ end
+ end
+ self:createLocal(arg[1], arg, value)
+ end)
+
+ self:doActions(action)
+
+ self:scopePop()
+end
+
+function mt:doWhile(action)
+ self:instantSource(action)
+ self:getExp(action.filter)
+
+ self:scopePush(action)
+ self:doActions(action)
+ self:scopePop()
+end
+
+function mt:doRepeat(action)
+ self:instantSource(action)
+ self:scopePush(action)
+ self:doActions(action)
+ self:getExp(action.filter)
+ self:scopePop()
+end
+
+function mt:doFunction(action)
+ self:instantSource(action)
+ local name = action.name
+ if name then
+ self:instantSource(name)
+ if name.type == 'simple' then
+ local parent = self:getSimple(name, -2)
+ if name[#name-1].type == ':' then
+ local value = self:buildFunction(action)
+ local source = name[#name]
+ self:instantSource(source)
+ source:set('simple', name)
+ source:set('parent', parent)
+ source:set('object', parent)
+ if source.type == 'index' then
+ local index = self:getIndex(source)
+ parent:setChild(index, value, source[1])
+ elseif source.type == 'name' then
+ local index = source[1]
+ parent:setChild(index, value, source)
+ end
+ source:bindValue(value, 'set')
+
+ local func = value:getFunction()
+ if func then
+ if #name == 3 then
+ -- function x:b()
+ local loc = self:loadLocal(name[1][1])
+ if loc then
+ func:setObject(parent, loc:getSource())
+ else
+ func:setObject(parent, name[#name-2])
+ end
+ else
+ func:setObject(parent, name[#name-2])
+ end
+ end
+ else
+ local value = self:buildFunction(action)
+ local source = name[#name]
+ self:instantSource(source)
+ source:set('simple', name)
+ source:set('parent', parent)
+ if source.type == 'index' then
+ local index = self:getIndex(source)
+ parent:setChild(index, value, source[1])
+ elseif source.type == 'name' then
+ local index = source[1]
+ parent:setChild(index, value, source)
+ end
+ source:bindValue(value, 'set')
+ end
+ else
+ local value = self:buildFunction(action)
+ self:setName(name[1], name, value)
+ end
+ else
+ self:buildFunction(action)
+ end
+end
+
+function mt:doLocalFunction(action)
+ self:instantSource(action)
+ local name = action.name
+ if name then
+ self:instantSource(name)
+ if name.type == 'simple' then
+ self:doFunction(action)
+ else
+ local loc = self:createLocal(name[1], name)
+ local func = self:buildFunction(action)
+ func:addInfo('local', name)
+ loc:setValue(func)
+ name:bindValue(func, 'local')
+ end
+ end
+end
+
+function mt:doAction(action)
+ if not action then
+ -- Skip
+ return
+ end
+ if coroutine.isyieldable() then
+ if self.lsp:isNeedCompile(self.uri) then
+ coroutine.yield()
+ if self._removed then
+ coroutine.yield('stop')
+ return
+ end
+ else
+ self:remove()
+ coroutine.yield('stop')
+ return
+ end
+ end
+ local tp = action.type
+ if tp:sub(1, 4) == 'emmy' then
+ self:doEmmy(action)
+ return
+ end
+ if tp == 'do' then
+ self:doDo(action)
+ elseif tp == 'break' then
+ elseif tp == 'return' then
+ self:doReturn(action)
+ elseif tp == 'label' then
+ self:doLabel(action)
+ elseif tp == 'goto' then
+ self:doGoTo(action)
+ elseif tp == 'set' then
+ self:doSet(action)
+ elseif tp == 'local' then
+ self:doLocal(action)
+ elseif tp == 'simple' then
+ -- call
+ self:getSimple(action)
+ action:set('as action', true)
+ elseif tp == 'if' then
+ self:doIf(action)
+ elseif tp == 'loop' then
+ self:doLoop(action)
+ elseif tp == 'in' then
+ self:doIn(action)
+ elseif tp == 'while' then
+ self:doWhile(action)
+ elseif tp == 'repeat' then
+ self:doRepeat(action)
+ elseif tp == 'function' then
+ self:doFunction(action)
+ elseif tp == 'localfunction' then
+ self:doLocalFunction(action)
+ else
+ self:getExp(action)
+ action:set('as action', true)
+ end
+ self:clearEmmy()
+end
+
+function mt:doActions(actions)
+ for _, action in ipairs(actions) do
+ self:doAction(action)
+ end
+end
+
+function mt:createFunction(source)
+ local value = self:createValue('function', source)
+ local func = functionMgr.create(source)
+ func:setEmmy(self:getEmmyParams(), self:getEmmyReturns(), self:getEmmyOverLoads())
+ func:setComment(self:getEmmyComment())
+ value:setFunction(func)
+ value:setType('function', 1.0)
+ if source:getUri() == self.uri then
+ self.funcs[#self.funcs+1] = func
+ end
+ return value
+end
+
+function mt:callLeftFuncions()
+ for _, func in ipairs(self.funcs) do
+ if not func:hasRuned() then
+ self:runFunction(func)
+ end
+ end
+end
+
+function mt:setCurrentFunction(func)
+ self.currentFunction = func
+end
+
+function mt:getCurrentFunction()
+ return self.currentFunction
+end
+
+function mt:saveLocal(name, loc)
+ self.currentFunction:saveLocal(name, loc)
+end
+
+function mt:saveUpvalue(name, loc)
+ self.currentFunction:saveUpvalue(name, loc)
+end
+
+function mt:loadLocal(name)
+ return self.currentFunction:loadLocal(name)
+end
+
+function mt:eachLocal(callback)
+ return self.currentFunction:eachLocal(callback)
+end
+
+function mt:saveLabel(label)
+ self.currentFunction:saveLabel(label)
+end
+
+function mt:loadLabel(name)
+ return self.currentFunction:loadLabel(name)
+end
+
+function mt:loadDots()
+ return self.currentFunction:loadDots()
+end
+
+function mt:getUri()
+ return self.currentFunction and self.currentFunction:getUri() or self.uri
+end
+
+function mt:instantSource(source)
+ if self:isRemoved() then
+ error('dead vm')
+ return nil
+ end
+ if sourceMgr.instant(source) then
+ source:setUri(self:getUri())
+ self.sources[#self.sources+1] = source
+ --CachedSource[source] = true
+ end
+ return source
+end
+
+function mt:bindLocal(source, loc, action)
+ if not source then
+ return
+ end
+ self:instantSource(source)
+ if loc then
+ source:bindLocal(loc, action)
+ else
+ return source:bindLocal()
+ end
+end
+
+function mt:bindLabel(source, label, action)
+ self:instantSource(source)
+ if label then
+ source:bindLabel(label, action)
+ else
+ return source:bindLabel()
+ end
+end
+
+function mt:createLocal(key, source, value, emmy, comment)
+ local loc = self:bindLocal(source)
+ if not value then
+ value = self:createValue('nil', source)
+ end
+ if loc then
+ loc:setValue(value)
+ loc:setEmmy(emmy)
+ self:saveLocal(key, loc)
+ return loc
+ end
+
+ loc = localMgr.create(key, source, value, source.tags)
+ loc:setEmmy(emmy)
+ loc:setComment(comment)
+ self:saveLocal(key, loc)
+ self:bindLocal(source, loc, 'local')
+ loc:close(self:getCurrentFunction():getSource().finish)
+ value:addInfo('local', source)
+ return loc
+end
+
+function mt:createUpvalue(key, source, value)
+ local loc = self:bindLocal(source)
+ if not value then
+ value = self:createValue('nil', source)
+ end
+ if loc then
+ loc:setValue(value)
+ self:saveUpvalue(key, loc)
+ return loc
+ end
+
+ loc = localMgr.create(key, source, value)
+ self:saveUpvalue(key, loc)
+ self:bindLocal(source, loc, 'local')
+ value:addInfo('local', source)
+ return loc
+end
+
+function mt:createEnvironment(ast)
+ -- 整个文件是一个函数
+ self.main = self:createFunction(ast)
+ self:setCurrentFunction(self.main:getFunction())
+ if self.lsp then
+ self.main:getFunction():mergeReturn(1, self.lsp.chain:get(self.uri))
+ end
+ -- 全局变量`_G`
+ local global = buildGlobal(self.lsp)
+ local env
+ if self.envType == '_ENV' then
+ -- 隐藏的上值`_ENV`
+ env = self:createUpvalue('_ENV', self:getDefaultSource(), global)
+ else
+ -- 为了实现方便,fenv也使用隐藏上值来实现
+ -- 使用了非法标识符保证用户无法访问
+ env = self:createUpvalue('@ENV', self:getDefaultSource(), global)
+ end
+ env:set('hide', true)
+ self.env = env
+end
+
+function mt:eachSource(callback)
+ if self._removed then
+ return
+ end
+ local sources = self.sources
+ for i = 1, #sources do
+ local res = callback(sources[i])
+ if res ~= nil then
+ return res
+ end
+ end
+end
+
+function mt:isRemoved()
+ return self._removed == true
+end
+
+function mt:remove()
+ if self._removed then
+ return
+ end
+ self._removed = true
+ for _, source in ipairs(self.sources) do
+ source:kill()
+ end
+ self.sources = nil
+ for _, func in ipairs(self.funcs) do
+ func:kill()
+ end
+ self.funcs = nil
+end
+
+local function compile(vm, ast, lsp, uri)
+ -- 创建初始环境
+ ast.uri = vm.uri
+ -- 根据运行版本决定环境实现方式
+ if config.config.runtime.version == 'Lua 5.1' or config.config.runtime.version == 'LuaJIT' then
+ vm.envType = 'fenv'
+ else
+ vm.envType = '_ENV'
+ end
+ vm:instantSource(ast)
+ vm:createEnvironment(ast)
+
+ -- 检查所有没有调用过的函数,调用一遍
+ vm:callLeftFuncions()
+
+ return vm
+end
+
+return function (ast, lsp, uri, text)
+ if not ast then
+ return nil, 'Ast failed'
+ end
+ local vm = setmetatable({
+ funcs = {},
+ sources = {},
+ main = nil,
+ env = nil,
+ emmy = nil,
+ ---@type emmyMgr
+ emmyMgr = lsp and lsp.emmy or emmyMgr(),
+ lsp = lsp,
+ uri = uri or '',
+ text = text or '',
+ }, mt)
+ local suc, res = xpcall(compile, log.error, vm, ast, lsp, uri)
+ if not suc then
+ vm:remove()
+ return nil, res
+ end
+ return res
+end