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.lua1341
1 files changed, 0 insertions, 1341 deletions
diff --git a/script/vm/vm.lua b/script/vm/vm.lua
deleted file mode 100644
index 02cb574c..00000000
--- a/script/vm/vm.lua
+++ /dev/null
@@ -1,1341 +0,0 @@
-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 then
- return nil
- end
- local ws = self.lsp:findWorkspaceFor(self:getUri())
- if not ws 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 = ws:searchPath(self:getUri(), str)
- elseif mode == 'loadfile' then
- uri = ws:loadPath(self:getUri(), str)
- elseif mode == 'dofile' then
- uri = ws: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)
- if not parent then
- return
- end
- 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