summaryrefslogtreecommitdiff
path: root/script/vm/vm.lua
diff options
context:
space:
mode:
Diffstat (limited to 'script/vm/vm.lua')
-rw-r--r--script/vm/vm.lua1334
1 files changed, 1334 insertions, 0 deletions
diff --git a/script/vm/vm.lua b/script/vm/vm.lua
new file mode 100644
index 00000000..36ad78c9
--- /dev/null
+++ b/script/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