From a692d7b413e4aff34a2a714cc6aec9ad0b495b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=80=E8=90=8C=E5=B0=8F=E6=B1=90?= Date: Thu, 31 Jan 2019 16:20:08 +0800 Subject: =?UTF-8?q?=E6=95=B4=E7=90=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/core/env.lua | 139 ----- server/src/core/init.lua | 1 - server/src/core/local.lua | 45 -- server/src/core/value.lua | 214 ------- server/src/core/vm.lua | 1457 -------------------------------------------- server/src/core/vm.txt | 52 -- server/src/vm/dots.lua | 6 + server/src/vm/env.lua | 139 +++++ server/src/vm/function.lua | 36 ++ server/src/vm/global.lua | 20 + server/src/vm/init.lua | 1 + server/src/vm/library.lua | 63 ++ server/src/vm/local.lua | 62 ++ server/src/vm/source.lua | 18 + server/src/vm/value.lua | 232 +++++++ server/src/vm/vm.lua | 1360 +++++++++++++++++++++++++++++++++++++++++ server/src/vm/vm.txt | 52 ++ 17 files changed, 1989 insertions(+), 1908 deletions(-) delete mode 100644 server/src/core/env.lua delete mode 100644 server/src/core/local.lua delete mode 100644 server/src/core/value.lua delete mode 100644 server/src/core/vm.lua delete mode 100644 server/src/core/vm.txt create mode 100644 server/src/vm/dots.lua create mode 100644 server/src/vm/env.lua create mode 100644 server/src/vm/function.lua create mode 100644 server/src/vm/global.lua create mode 100644 server/src/vm/init.lua create mode 100644 server/src/vm/library.lua create mode 100644 server/src/vm/local.lua create mode 100644 server/src/vm/source.lua create mode 100644 server/src/vm/value.lua create mode 100644 server/src/vm/vm.lua create mode 100644 server/src/vm/vm.txt (limited to 'server/src') diff --git a/server/src/core/env.lua b/server/src/core/env.lua deleted file mode 100644 index ada26145..00000000 --- a/server/src/core/env.lua +++ /dev/null @@ -1,139 +0,0 @@ -local setmetatable = setmetatable -local pairs = pairs -local type = type -local table_sort = table.sort - -return function (root) - local env = {root} - local is_table = {} - for key, value in pairs(root) do - if type(value) == 'table' then - is_table[key] = true - end - end - root._next = nil - root._cut = {} - - local mt = { _env = env } - function mt:push() - env[#env+1] = { _next = env[#env], _cut = {} } - end - function mt:pop() - env[#env] = nil - end - function mt:cut(key) - env[#env]._cut[key] = true - end - function mt:__index(key) - local origin = env[#env] - if is_table[key] then - return setmetatable({}, { - __index = function (_, ckey) - local o = origin - while o do - local t = o[key] - if t and t[ckey] ~= nil then - return t[ckey] - end - o = not o._cut[key] and o._next - end - end, - __newindex = function (_, ckey, value) - local o = origin - if not o[key] then - o[key] = {} - end - o[key][ckey] = value - end, - __pairs = function () - local o = origin - local tbl = {} - while o do - local t = o[key] - if t then - for k, v in pairs(t) do - if tbl[k] == nil then - tbl[k] = v - end - end - end - o = not o._cut[key] and o._next - end - return next, tbl - end, - }) - else - local o = origin - while o do - if o[key] ~= nil then - return o[key] - end - o = not o._cut[key] and o._next - end - end - end - function mt:__newindex(key, value) - local o = env[#env] - if is_table[key] then - if type(o[key]) ~= 'table' then - o[key] = {} - end - if type(value) == 'table' then - for k, v in pairs(value) do - o[key][k] = v - end - else - error(('[env.%s] should be table, got [%s]'):format(key, value)) - end - else - o[key] = value - end - end - function mt:__pairs() - local keys = {} - local cuted = {} - local result = {} - local o = env[#env] - while true do - for key in pairs(o._cut) do - cuted[key] = true - end - for key, value in pairs(o) do - if key == '_cut' or key == '_next' then - goto CONTINUE - end - if cuted[key] then - goto CONTINUE - end - if result[key] == nil then - keys[#keys+1] = key - if is_table[key] then - result[key] = {} - else - result[key] = value - end - end - if is_table[key] then - for k, v in pairs(value) do - if result[key][k] == nil then - result[key][k] = v - end - end - end - ::CONTINUE:: - end - o = o._next - if not o then - break - end - end - table_sort(keys) - local i = 0 - return function () - i = i + 1 - local k = keys[i] - return k, result[k] - end - end - return setmetatable(mt, mt) -end diff --git a/server/src/core/init.lua b/server/src/core/init.lua index 68898814..148871a1 100644 --- a/server/src/core/init.lua +++ b/server/src/core/init.lua @@ -11,7 +11,6 @@ local api = { signature = require 'core.signature', documentSymbol = require 'core.document_symbol', global = require 'core.global', - vm = require 'core.vm', } return api diff --git a/server/src/core/local.lua b/server/src/core/local.lua deleted file mode 100644 index 7deac275..00000000 --- a/server/src/core/local.lua +++ /dev/null @@ -1,45 +0,0 @@ -local function getDefaultSource() - return { - start = 0, - finish = 0, - uri = '', - } -end - -local mt = {} -mt.__index = mt -mt.type = 'local' - -function mt:setValue(value) - if self.value then - self.value:mergeValue(value) - else - self.value = value - end -end - -function mt:getValue() - return self.value -end - -function mt:addInfo(tp, source) - self[#self+1] = { - type = tp, - source = source, - } -end - -function mt:eachInfo(callback) - for _, info in ipairs(self) do - callback(info) - end -end - -return function (name, source, value) - local self = setmetatable({ - name = name, - source = source or getDefaultSource(), - value = value, - }, mt) - return self -end diff --git a/server/src/core/value.lua b/server/src/core/value.lua deleted file mode 100644 index fdd9d902..00000000 --- a/server/src/core/value.lua +++ /dev/null @@ -1,214 +0,0 @@ -local function getDefaultSource() - return { - start = 0, - finish = 0, - uri = '', - } -end - -local mt = {} -mt.__index = mt -mt.type = 'value' - -function mt:setValue(value) - self._value = value -end - -function mt:getValue() - return self._value -end - -function mt:setType(tp, rate) - if type(tp) == 'table' then - for _, ctp in ipairs(tp) do - self:inference(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 - local current = self._type[tp] or 0.0 - self._type[tp] = current + (1 - current) * rate -end - -function mt:getType() - if not self._type then - return 'nil' - end - local mRate = 0.0 - local mType - for tp, rate in pairs(self._type) do - if rate > mRate then - mRate = rate - mType = tp - end - end - return mType or 'any' -end - -function mt:rawSet(index, value) - if not self._child then - self._child = {} - end - if self._child[index] then - self._child[index]:mergeValue(value) - else - self._child[index] = value - end -end - -function mt:rawGet(index) - if not self._child then - return nil - end - return self._child[index] -end - -function mt:setChild(index, value) - self:rawSet(index, value) -end - -function mt:getChild(index, mark) - local value = self:rawGet(index) - if value then - return value - end - local method = self:getMetaMethod('__index') - if not method then - return nil - end - if not mark then - mark = {} - end - if mark[method] then - return nil - end - mark[method] = true - return method:getChild(index, mark) -end - -function mt:setMetaTable(metatable) - self._meta = metatable -end - -function mt:getMetaTable() - return self._meta -end - -function mt:getMetaMethod(name) - local meta = self:getMetaTable() - if not meta then - return nil - end - return meta:rawGet(name) -end - -function mt:rawEach(callback, foundIndex) - if not self._child then - return nil - end - for index, value in pairs(self._child) do - if foundIndex then - if foundIndex[index] then - goto CONTINUE - end - foundIndex[index] = true - end - local res = callback(index, value) - if res ~= nil then - return res - end - ::CONTINUE:: - end - return nil -end - -function mt:eachChild(callback, mark, foundIndex) - if not foundIndex then - foundIndex = {} - end - local res = self:rawEach(callback, foundIndex) - if res ~= nil then - return res - end - local method = self:getMetaMethod('__index') - if not method then - return nil - end - if not mark then - mark = {} - end - if mark[method] then - return nil - end - mark[method] = true - return method:eachChild(callback, mark, foundIndex) -end - -function mt:mergeValue(value) - if value._type then - for tp, rate in pairs(value._type) do - self:setType(tp, rate) - end - end - if value._child then - if not self._child then - self._child = {} - end - for k, v in pairs(value._child) do - self._child[k] = v - end - end - if value._meta then - self._meta = value._meta - end - for _, info in ipairs(value) do - self[#self+1] = info - end -end - -function mt:addInfo(tp, source) - if source and not source.start then - error('Miss start: ' .. table.dump(source)) - end - self[#self] = { - type = tp, - source = source or getDefaultSource(), - } -end - -function mt:eachInfo(callback) - for _, info in ipairs(self) do - local res = callback(info) - if res ~= nil then - return res - end - end - return nil -end - -return function (tp, source, v) - if tp == '...' then - error('Value type cant be ...') - end - local self = setmetatable({ - source = source or getDefaultSource(), - _value = v, - _type = {}, - }, 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 - return self -end diff --git a/server/src/core/vm.lua b/server/src/core/vm.lua deleted file mode 100644 index 66fdd2c6..00000000 --- a/server/src/core/vm.lua +++ /dev/null @@ -1,1457 +0,0 @@ -local env = require 'core.env' -local library = require 'core.library' -local createValue = require 'core.value' - -local LibraryValue = {} -local LibraryChild = {} - -local mt = {} -mt.__index = mt - -function mt:getDefaultSource() - return { - start = 0, - finish = 0, - uri = self.chunk.func.uri, - } -end - -function mt:createDummyVar(source, value) - if source and source.bind then - return source.bind - end - local loc = { - type = 'local', - key = '', - source = source or self:getDefaultSource(), - } - - if source then - source.bind = loc - self.results.sources[#self.results.sources+1] = source - source.isLocal = true - end - - self:setValue(loc, value, source) - return loc -end - -function mt:createLocal(key, source, value) - if source and source.bind then - self.scope.locals[key] = source.bind - return source.bind - end - local loc = { - type = 'local', - key = key, - source = source or self:getDefaultSource(), - close = self.scope.block.finish, - } - - if source then - source.bind = loc - self.results.sources[#self.results.sources+1] = source - source.isLocal = true - source.uri = self.chunk.func.uri - end - - local shadow = self.scope.locals[key] - if shadow then - if source then - shadow.close = source.start - 1 - end - local group - if shadow.shadow then - group = shadow.shadow - else - group = { shadow } - shadow.shadow = group - end - group[#group+1] = loc - loc.shadow = group - end - - self.scope.locals[key] = loc - self.results.locals[#self.results.locals+1] = loc - - if source then - self:addInfo(loc, 'local', source, value) - if value then - value:addInfo('local', source) - end - end - self:setValue(loc, value, source) - return loc -end - -function mt:createField(value, index, source) - if source and source.bind then - return source.bind - end - local field = value:createField(index, source) - if source then - source.bind = field - self.results.sources[#self.results.sources+1] = source - end - if value.GLOBAL then - field.GLOBAL = true - end - if self.lsp and self:isGlobal(field) then - self.lsp.global:markSet(self.chunk.func.uri) - end - field.parentValue = value - - return field -end - -function mt:getField(value, index, source) - local field = value:getField(index, source) - if not field then - return nil - end - if source then - source.bind = field - self.results.sources[#self.results.sources+1] = source - end - if value.GLOBAL then - field.GLOBAL = true - end - if self.lsp and self:isGlobal(field) then - self.lsp.global:markGet(self.chunk.func.uri) - end - - return field -end - -function mt:createArg(key, source, value) - local loc = self:createLocal(key, source, value) - if source then - source.isArg = true - end - return loc -end - -function mt:scopePush(block) - if not block.start then - error('Scope push without start!') - end - self.scope:push() - self.scope.block = block -end - -function mt:scopePop() - self.scope:pop() -end - -function mt:addInfo(var, type, source, value) - if not source then - error('Miss source') - end - if not source.start then - error('Miss start: ' .. table.dump(source)) - end - if var.type ~= 'local' and var.type ~= 'field' and var.type ~= 'label' then - error('Must be local, field or label: ' .. table.dump(var)) - end - local info = { - type = type, - source = source or self:getDefaultSource(), - value = value, - } - if not self.results.infos[var] then - self.results.infos[var] = {} - end - self.results.infos[var][#self.results.infos[var]+1] = info -end - -function mt:eachInfo(var, callback) - if not self.results.infos[var] then - return nil - end - for _, info in ipairs(self.results.infos[var]) do - local res = callback(info) - if res ~= nil then - return res - end - end - return nil -end - -function mt:createDots(index, source) - local dots = { - type = 'dots', - source = source or self:getDefaultSource(), - func = self:getCurrentFunction(), - index = index, - } - self.chunk.dots = dots - return dots -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 - if obj.type == 'pair' then - local value = self:getExp(obj[2]) - local key = obj[1] - key.uri = self.chunk.func.uri - if key.index then - local index = self:getIndex(key) - local field = self:createField(tbl, index, key) - if value.type == 'list' then - self:setValue(field, value[1], key) - else - self:setValue(field, value, key) - end - else - if key.type == 'name' then - local field = self:createField(tbl, key[1], key) - key.isIndex = true - if value.type == 'list' then - self:setValue(field, value[1], key) - else - self:setValue(field, value, key) - end - end - end - else - local value = self:getExp(obj) - if value.type == 'list' then - if index == #source then - for i, v in ipairs(value) do - local field = self:createField(tbl, n + i) - self:setValue(field, v) - end - else - n = n + 1 - local field = self:createField(tbl, n) - self:setValue(field, value[1]) - end - else - n = n + 1 - local field = self:createField(tbl, n) - self:setValue(field, value) - end - -- 处理写了一半的 key = value,把name类的数组元素视为哈希键 - if obj.type == 'name' then - obj.isIndex = true - end - end - end - return tbl -end - -function mt:setValue(var, value, source) - if value and value.type == 'list' then - error('Cant set value list') - end - value = value or self:createValue('any', source) - if source and source.start then - self:addInfo(var, 'set', source, value) - value:addInfo('set', source) - end - if var.GLOBAL then - value.GLOBAL = true - end - var.value = value - return value -end - -function mt:getValue(var, source) - if not var.value then - var.value = self:createValue('any', source or self:getDefaultSource()) - if var.GLOBAL then - var.value.GLOBAL = true - end - end - return var.value -end - -function mt:isGlobal(field) - return field.GLOBAL == true -end - -function mt:callLeftFuncions() - for _, func in ipairs(self.results.funcs) do - if func.built and not func.runed then - self:runFunction(func) - end - end -end - -function mt:runFunction(func) - func.runed = (func.runed or 0) + 1 - if func.runed > 1 then - --return - end - self:scopePush(func.built) - self.chunk:push() - self.chunk:cut 'dots' - self.chunk:cut 'labels' - self.chunk:cut 'locals' - self.chunk.func = func - - self.scope:cut 'locals' - for name, loc in pairs(func.upvalues) do - self.scope.locals[name] = loc - end - - local index = 0 - if func.object then - local var = self:createArg('self', func.colon, self:getValue(func.object, func.colon)) - var.hide = true - var.link = func.object - if func.argValues[1] then - self:setValue(var, func.argValues[1]) - end - index = 1 - func.args[index] = var - end - - local stop - self:forList(func.built.arg, function (arg) - if stop then - return - end - index = index + 1 - if arg.type == 'name' then - local var = self:createArg(arg[1], arg) - self:setValue(var, func.argValues[index] or self:createValue('nil')) - func.args[index] = var - elseif arg.type == '...' then - local dots = self:createDots(index, arg) - for i = index, #func.argValues do - dots[#dots+1] = func.argValues[i] - end - func.hasDots = true - stop = true - end - end) - - self:doActions(func.built) - - self.chunk:pop() - self:scopePop() -end - -function mt:buildFunction(exp, object, colon) - if exp and exp.func then - return exp.func - end - - local func = self:createValue('function', exp) - func.args = {} - func.argValues = {} - - if not exp then - return func - end - - func.built = exp - func.upvalues = {} - func.object = object - func.colon = colon - func.uri = exp.uri - exp.func = func - for name, loc in pairs(self.scope.locals) do - func.upvalues[name] = loc - end - - self.results.funcs[#self.results.funcs+1] = func - - return func -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:countList(list) - if not list then - return 0 - end - if list.type == 'list' then - return #list - end - return 1 -end - -function mt:setFunctionArg(func, values) - if not func.argValues then - func.argValues = {} - end - for i = 1, #values do - func.argValues[i] = values[i] - end -end - -function mt:getFunctionArg(func, i) - if not func.argValues then - func.argValues = {} - end - return func.argValues[i] -end - -function mt:callSetMetaTable(func, values, source) - if not values[1] then - values[1] = self:createValue('any') - end - if not values[2] then - values[2] = self:createValue('any') - end - self:setFunctionReturn(func, 1, values[1]) - values[1]:setMetaTable(values[2], source) -end - -function mt:getRequire(strValue, destVM) - -- 取出对方的主函数 - local main = destVM.results.main - -- 获取主函数返回值,注意不能修改对方的环境 - local mainValue - if main.returns then - mainValue = self:selectList(main.returns, 1) - else - mainValue = self:createValue('boolean', nil, true) - mainValue.uri = destVM.uri - end - - return mainValue -end - -function mt:getLoadFile(strValue, destVM) - -- 取出对方的主函数 - local main = destVM.results.main - -- loadfile 的返回值就是对方的主函数 - local mainValue = main - - return mainValue -end - -function mt:tryRequireOne(strValue, mode) - if not self.lsp or not self.lsp.workspace then - return nil - end - local str = strValue:getValue() - if type(str) == 'string' then - -- 支持 require 'xxx' 的转到定义 - local strSource = strValue.source - strSource.bind = strValue - self.results.sources[#self.results.sources+1] = strSource - strValue.isRequire = true - - local uri - if mode == 'require' then - uri = self.lsp.workspace:searchPath(self.chunk.func.uri, str) - elseif mode == 'loadfile' then - uri = self.lsp.workspace:loadPath(self.chunk.func.uri, str) - elseif mode == 'dofile' then - uri = self.lsp.workspace:loadPath(self.chunk.func.uri, str) - end - if not uri then - return nil - end - - strValue.uri = uri - -- 如果取不到VM(不编译),则做个标记,之后再取一次 - local destVM = self.lsp:getVM(uri) - self.lsp:compileChain(self.chunk.func.uri, uri) - if destVM then - if mode == 'require' then - return self:getRequire(strValue, destVM) - elseif mode == 'loadfile' then - return self:getLoadFile(strValue, destVM) - elseif mode == 'dofile' then - return self:getRequire(strValue, destVM) - end - end - end - return nil -end - -function mt:callRequire(func, values) - if not values[1] then - values[1] = self:createValue('any') - end - local str = values[1]:getValue() - if type(str) ~= 'string' then - return - end - local lib = library.library[str] - if lib then - local value = self:getLibValue(lib, 'library') - self:setFunctionReturn(func, 1, value) - return - else - local requireValue = self:tryRequireOne(values[1], 'require') - if not requireValue then - requireValue = self:createValue('boolean') - requireValue.isRequire = true - end - self:setFunctionReturn(func, 1, requireValue) - end -end - -function mt:callLoadFile(func, values) - if not values[1] then - values[1] = self:createValue('any') - end - local str = values[1]:getValue() - if type(str) ~= 'string' then - return - end - local requireValue = self:tryRequireOne(values[1], 'loadfile') - if not requireValue then - requireValue = self:createValue('any') - requireValue.isRequire = true - end - self:setFunctionReturn(func, 1, requireValue) -end - -function mt:callDoFile(func, values) - if not values[1] then - values[1] = self:createValue('any') - end - local str = values[1]:getValue() - if type(str) ~= 'string' then - return - end - local requireValue = self:tryRequireOne(values[1], 'dofile') - if not requireValue then - requireValue = self:createValue('any') - requireValue.isRequire = true - end - self:setFunctionReturn(func, 1, requireValue) -end - -function mt:call(func, values, source) - func:inference('function', 0.9) - local lib = func.lib - if lib then - if lib.args then - for i, arg in ipairs(lib.args) do - local value = values[i] - if value and arg.type ~= '...' then - value:inference(arg.type, 1.0) - end - end - end - if lib.returns then - for i, rtn in ipairs(lib.returns) do - if rtn.type == '...' then - self:getFunctionReturns(func, i):inference('any', 0.0) - else - self:getFunctionReturns(func, i):inference(rtn.type or 'any', 1.0) - 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) - end - end - else - if not func.built then - self:setFunctionReturn(func, 1, self:createValue('any', source)) - end - end - - if not source.hasRuned and func.built then - source.hasRuned = true - self:setFunctionArg(func, values) - self:runFunction(func) - end - - return self:getFunctionReturns(func) -end - -function mt:getCurrentFunction() - return self.chunk.func -end - -function mt:setFunctionReturn(func, index, value) - func.hasReturn = true - if not func.returns then - func.returns = { - type = 'list', - } - end - if value then - if value.type == 'list' then - for i, v in ipairs(value) do - func.returns[index+i-1] = v - end - else - func.returns[index] = value - end - else - func.returns[index] = self:createValue('any', func.source) - end -end - -function mt:getFunctionReturns(func, i) - if func.maxReturns and i and func.maxReturns < i then - return self:createValue('nil') - end - if not func.returns then - func.returns = { - type = 'list', - } - end - if i then - if not func.returns[i] then - for n = #func.returns+1, i do - func.returns[n] = self:createValue('any') - end - end - return func.returns[i] - else - return func.returns - end -end - -function mt:createValue(tp, source, v) - local value = createValue(tp, source, v) - local lib = library.object[tp] - if lib then - self:getLibChild(value, lib, 'object') - end - return value -end - -function mt:getLibChild(value, lib, parentType) - if lib.child then - if LibraryChild[lib] then - value:setChild(LibraryChild[lib]) - return - end - -- 要先声明缓存,以免死循环 - LibraryChild[lib] = value:getChild() - for fName, fLib in pairs(lib.child) do - local fField = self:createField(value, fName) - local fValue = self:getLibValue(fLib, parentType) - self:setValue(fField, fValue) - end - end -end - -function mt:getLibValue(lib, parentType, v) - if LibraryValue[lib] then - return LibraryValue[lib] - end - local tp = lib.type - local value - if tp == 'table' then - value = self:createValue('table') - elseif tp == 'function' then - value = self:createValue('function') - if lib.returns then - local dots - for i, rtn in ipairs(lib.returns) do - self:setFunctionReturn(value, i, self:getLibValue(rtn, parentType)) - if rtn.type == '...' then - dots = true - end - end - if not dots then - value.maxReturns = #lib.returns - end - else - value.maxReturns = 0 - end - if lib.args then - local values = {} - for i, arg in ipairs(lib.args) do - values[i] = self:getLibValue(arg, parentType) or self:createValue('any') - end - -- TODO 确定参数类型 - end - elseif tp == 'string' then - value = self:createValue('string', nil, v or lib.value) - elseif tp == 'boolean' then - value = self:createValue('boolean', nil, v or lib.value) - elseif tp == 'number' then - value = self:createValue('number', nil, v or lib.value) - elseif tp == 'integer' then - value = self:createValue('integer', nil, v or lib.value) - elseif tp == 'nil' then - value = self:createValue('nil') - elseif tp == '...' then - value = self:createValue('any') - else - value = self:createValue(tp or 'any') - end - LibraryValue[lib] = value - value.lib = lib - value.parentType = parentType - - self:getLibChild(value, lib, parentType) - - return value -end - -function mt:getName(name, source) - if source and source.bind then - return source.bind - end - local loc = self.scope.locals[name] - if loc then - source.bind = loc - self.results.sources[#self.results.sources+1] = source - return loc - end - source.uri = self.chunk.func.uri - local ENV = self.scope.locals._ENV - local ENVValue = self:getValue(ENV, source) - local global = self:getField(ENVValue, name, source) - if global then - global.parent = ENV - return global - else - global = self:createField(ENVValue, name, source) - global.parent = ENV - return global - end -end - -function mt:setName(name, source, value) - source.uri = self.chunk.func.uri - local loc = self.scope.locals[name] - if loc then - source.bind = loc - self.results.sources[#self.results.sources+1] = source - self:setValue(loc, value, source) - return - end - local ENV = self.scope.locals._ENV - local ENVValue = self:getValue(ENV, source) - local global = self:getField(ENVValue, name, source) - if global then - global.parent = ENV - self:setValue(global, value, source) - else - global = self:createField(ENVValue, name, source) - global.parent = ENV - self:setValue(global, value, source) - end -end - -function mt:getIndex(obj) - local tp = obj.type - obj.uri = self.chunk.func.uri - if tp == 'name' then - local var = self:getName(obj[1], obj) - local value = self:getValue(var, obj) - self:addInfo(var, 'get', obj) - value:addInfo('get', obj) - return value - elseif (tp == 'string' or tp == 'number' or tp == 'boolean') then - return obj[1] - else - return self:getExp(obj) - end -end - --- expect表示遇到 ... 时,期待的返回数量 -function mt:unpackDots(res, expect) - local dots = self:getDots(1) - local func = dots.func - local start = dots.index - if expect then - local finish = start + expect - 1 - for i = start, finish do - res[#res+1] = self:getFunctionArg(func, i) - end - else - if not func.argValues then - return - end - for i = start, #func.argValues do - res[#res+1] = func.argValues[i] - end - end -end - -function mt:unpackList(list, expect) - local res = { - type = 'list', - } - if not list then - return res - end - if list.type == 'list' or list.type == 'call' then - for i, exp in ipairs(list) do - if exp.type == '...' then - self:unpackDots(res, expect) - break - end - local value = self:getExp(exp) - if value.type == 'list' then - if i == #list then - for _, v in ipairs(value) do - res[#res+1] = v - end - else - res[#res+1] = value[1] - end - else - res[#res+1] = value - end - end - elseif list.type == '...' then - self:unpackDots(res, expect) - else - local value = self:getExp(list) - if value.type == 'list' then - for i, v in ipairs(value) do - res[i] = v - end - else - res[1] = value - end - end - for _, v in ipairs(res) do - if v.type == 'list' then - error('Unpack list') - end - end - return res -end - -function mt:getSimple(simple, mode) - local value = self:getExp(simple[1]) - local field - local parentName - local tp = simple[1].type - simple[1].uri = self.chunk.func.uri - if tp == 'name' then - field = self:getName(simple[1][1], simple[1]) - parentName = field.key - elseif tp == 'string' or tp == 'number' or tp == 'nil' or tp == 'boolean' then - local v = self:createValue(tp, simple[1], simple[1][1]) - field = self:createDummyVar(simple[1], v) - parentName = '*' .. tp - else - local v = self:createValue('any', simple[1]) - field = self:createDummyVar(simple[1], v) - parentName = '?' - end - local object - local colon - local lastField = field - for i = 2, #simple do - local obj = simple[i] - local tp = obj.type - obj.uri = self.chunk.func.uri - obj.isSuffix = true - value = self:selectList(value, 1) - - if tp == 'call' then - value:inference('function', 0.9) - local args = self:unpackList(obj) - if object then - table.insert(args, 1, self:getValue(object, obj)) - end - local func = value - -- 函数的返回值一定是list - value = self:call(func, args, obj) - if i < #simple then - value = value[1] or self:createValue('any', obj) - end - self.results.calls[#self.results.calls+1] = { - args = obj, - lastObj = simple[i-1], - nextObj = simple[i+1], - func = func, - } - parentName = parentName .. '(...)' - elseif tp == 'index' then - value:inference('table', 0.8) - value:inference('string', 0.2) - local child = obj[1] - obj.indexName = parentName - local index = self:getIndex(child) - if mode == 'value' or i < #simple then - field = self:getField(value, index, obj) or self:createField(value, index, obj) - value = self:getValue(field, obj) - self:addInfo(field, 'get', obj) - value:addInfo('get', obj) - else - field = self:createField(value, index, obj) - value = self:getValue(field, obj) - end - field.parent = lastField - if obj[1].type == 'string' then - parentName = ('%s[%q]'):format(parentName, index) - elseif obj[1].type == 'number' or obj[1].type == 'boolean' then - parentName = ('%s[%s]'):format(parentName, index) - else - parentName = ('%s[?]'):format(parentName) - end - elseif tp == 'name' then - value:inference('table', 0.8) - value:inference('string', 0.2) - if mode == 'value' or i < #simple then - field = self:getField(value, obj[1], obj) or self:createField(value, obj[1], obj) - value = self:getValue(field, obj) - self:addInfo(field, 'get', obj) - value:addInfo('get', obj) - else - field = self:createField(value, obj[1], obj) - value = self:getValue(field, obj) - end - field.parent = lastField - lastField = field - obj.object = object - obj.parentName = parentName - parentName = parentName .. '.' .. field.key - elseif tp == ':' then - value:inference('table', 0.8) - value:inference('string', 0.2) - object = field - simple[i-1].colon = obj - colon = colon - elseif tp == '.' then - value:inference('table', 0.8) - value:inference('string', 0.2) - simple[i-1].dot = obj - end - end - if mode == 'value' then - return value, object - elseif mode == 'field' then - return field, object, colon - end - error('Unknow simple mode: ' .. mode) -end - -function mt:isTrue(v) - if v:getType() == 'nil' then - return false - end - if v:getType() == 'boolean' and not v:getValue() then - return false - end - return true -end - -function mt:selectList(list, n) - if list.type ~= 'list' then - return list - end - return list[n] or self:createValue('nil') -end - -function mt:getBinary(exp) - local v1 = self:getExp(exp[1]) - local v2 = self:getExp(exp[2]) - v1 = self:selectList(v1, 1) - v2 = self:selectList(v2, 1) - 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:inference('number', 0.9) - v2:inference('number', 0.9) - v1:inference('string', 0.1) - v2:inference('string', 0.1) - return self:createValue('boolean') - elseif op == '~=' - or op == '==' - then - return self:createValue('boolean') - elseif op == '|' - or op == '~' - or op == '&' - or op == '<<' - or op == '>>' - then - v1:inference('integer', 0.9) - v2:inference('integer', 0.9) - v1:inference('number', 0.9) - v2:inference('number', 0.9) - v1:inference('string', 0.1) - v2:inference('string', 0.1) - if math.type(v1:getValue()) == 'integer' and math.type(v2:getValue()) == 'integer' then - if op == '|' then - return self:createValue('integer', exp, v1:getValue() | v2:getValue()) - elseif op == '~' then - return self:createValue('integer', exp, v1:getValue() ~ v2:getValue()) - elseif op == '&' then - return self:createValue('integer', exp, v1:getValue() &v2:getValue()) - elseif op == '<<' then - return self:createValue('integer', exp, v1:getValue() << v2:getValue()) - elseif op == '>>' then - return self:createValue('integer', exp, v1:getValue() >> v2:getValue()) - end - end - return self:createValue('integer') - elseif op == '..' then - v1:inference('string', 0.9) - v2:inference('string', 0.9) - v1:inference('number', 0.1) - v2:inference('number', 0.1) - if type(v1:getValue()) == 'string' and type(v2:getValue()) == 'string' then - return self:createValue('string', nil, v1:getValue() .. v2:getValue()) - end - return self:createValue('string') - elseif op == '+' - or op == '-' - or op == '*' - or op == '/' - or op == '^' - or op == '%' - or op == '//' - then - v1:inference('number', 0.9) - v2:inference('number', 0.9) - if type(v1:getValue()) == 'number' and type(v2:getValue()) == 'number' then - if op == '+' then - return self:createValue('number', exp, v1:getValue() + v2:getValue()) - elseif op == '-' then - return self:createValue('number', exp, v1:getValue() - v2:getValue()) - elseif op == '*' then - return self:createValue('number', exp, v1:getValue() * v2:getValue()) - elseif op == '/' then - if v2:getValue() ~= 0 then - return self:createValue('number', exp, v1:getValue() / v2:getValue()) - end - elseif op == '^' then - return self:createValue('number', exp, v1:getValue() ^ v2:getValue()) - elseif op == '%' then - if v2:getValue() ~= 0 then - return self:createValue('number', exp, v1:getValue() % v2:getValue()) - end - elseif op == '//' then - if v2:getValue() ~= 0 then - return self:createValue('number', exp, v1:getValue() // v2:getValue()) - end - end - end - return self:createValue('number') - end - return nil -end - -function mt:getUnary(exp) - local v1 = self:getExp(exp[1]) - v1 = self:selectList(v1, 1) - local op = exp.op - -- TODO 搜索元方法 - if op == 'not' then - return self:createValue('boolean') - elseif op == '#' then - v1:inference('table', 0.9) - v1:inference('string', 0.9) - if type(v1:getValue()) == 'string' then - return self:createValue('integer', exp, #v1:getValue()) - end - return self:createValue('integer') - elseif op == '-' then - v1:inference('number', 0.9) - if type(v1:getValue()) == 'number' then - return self:createValue('number', exp, -v1:getValue()) - end - return self:createValue('number') - elseif op == '~' then - v1:inference('integer', 0.9) - if math.type(v1:getValue()) == 'integer' then - return self:createValue('integer', exp, ~v1:getValue()) - end - return self:createValue('integer') - end - return nil -end - -function mt:getDots() - if not self.chunk.dots then - self:createDots(1) - end - return self.chunk.dots -end - -function mt:getExp(exp) - exp.uri = self.chunk.func.uri - local tp = exp.type - if tp == 'nil' then - return self:createValue('nil', exp) - elseif tp == 'string' then - self.results.strings[#self.results.strings+1] = exp - 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 var = self:getName(exp[1], exp) - local value = self:getValue(var, exp) - self:addInfo(var, 'get', exp) - value:addInfo('get', exp) - return value - elseif tp == 'simple' then - return self:getSimple(exp, 'value') - 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 - local value = { type = 'list' } - self:unpackDots(value) - return value - end - error('Unkown exp type: ' .. tostring(tp)) -end - -function mt:doDo(action) - self:scopePush(action) - self:doActions(action) - self:scopePop() -end - -function mt:doReturn(action) - self:getCurrentFunction().hasReturn = true - for i, exp in ipairs(action) do - local value = self:getExp(exp) - if value.type == 'list' then - if i == #action then - if #value == 0 then - value[1] = self:createValue('any', exp) - end - for x, v in ipairs(value) do - v:addInfo('return', exp) - self:setFunctionReturn(self:getCurrentFunction(), i + x - 1, v) - end - break - else - local v = value[1] or self:createValue('nil', exp) - v:addInfo('return', exp) - self:setFunctionReturn(self:getCurrentFunction(), i, v) - end - else - value:addInfo('return', exp) - self:setFunctionReturn(self:getCurrentFunction(), i, value) - end - end -end - -function mt:createLabel(source) - if source.bind then - return source.bind - end - local name = source[1] - if not self.chunk.labels[name] then - local label = { - type = 'label', - key = name, - } - self.chunk.labels[name] = label - self.results.labels[#self.results.labels+1] = label - end - source.bind = self.chunk.labels[name] - self.results.sources[#self.results.sources+1] = source - return self.chunk.labels[name] -end - -function mt:doSet(action) - if not action[2] then - return - end - local n = self:countList(action[1]) - -- 要先计算值 - local values = self:unpackList(action[2], n) - self:forList(action[1], function (key) - local value = table.remove(values, 1) - if key.type == 'name' then - self:setName(key[1], key, value) - elseif key.type == 'simple' then - local field = self:getSimple(key, 'field') - self:setValue(field, value, key[#key]) - end - end) -end - -function mt:doLocal(action) - local n = self:countList(action[1]) - local values - if action[2] then - values = self:unpackList(action[2], n) - end - self:forList(action[1], function (key) - local value - if values then - value = table.remove(values, 1) - end - key.uri = self.chunk.func.uri - self:createLocal(key[1], key, value) - end) -end - -function mt:doIf(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) - - local min = self:unpackList(action.min)[1] - 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 args = self:unpackList(action.exp) - - self:scopePush(action) - local func = table.remove(args, 1) or self:createValue('any') - local values = self:call(func, args, action) - self:forList(action.arg, function (arg) - local value = table.remove(values, 1) - self:createLocal(arg[1], arg, value) - end) - - self:doActions(action) - - self:scopePop() -end - -function mt:doWhile(action) - - self:getExp(action.filter) - - self:scopePush(action) - self:doActions(action) - self:scopePop() -end - -function mt:doRepeat(action) - self:scopePush(action) - self:doActions(action) - self:getExp(action.filter) - self:scopePop() -end - -function mt:doFunction(action) - local name = action.name - local var, object, colon - local source - if name then - if name.type == 'simple' then - var, object, colon = self:getSimple(name, 'field') - source = name[#name] - else - var = self:getName(name[1], name) - source = name - end - end - local func = self:buildFunction(action, object, colon) - if var then - self:setValue(var, func, source) - end -end - -function mt:doLocalFunction(action) - local name = action.name - if name then - if name.type == 'simple' then - local var, object = self:getSimple(name, 'field') - local func = self:buildFunction(action, object) - self:setValue(var, func, name[#name]) - else - local loc = self:createLocal(name[1], name) - local func = self:buildFunction(action) - func:addInfo('local', name) - self:setValue(loc, func, name[#name]) - end - end -end - -function mt:doAction(action) - if not action then - -- Skip - return - end - action.uri = self.chunk.func.uri - local tp = action.type - if tp == 'do' then - self:doDo(action) - elseif tp == 'break' then - elseif tp == 'return' then - self:doReturn(action) - elseif tp == 'label' then - local label = self:createLabel(action) - self:addInfo(label, 'set', action) - elseif tp == 'goto' then - local label = self:createLabel(action) - self:addInfo(label, 'goto', action) - elseif tp == 'set' then - self:doSet(action) - elseif tp == 'local' then - self:doLocal(action) - elseif tp == 'simple' then - -- call - self:getSimple(action, 'value') - 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) - end -end - -function mt:doActions(actions) - for _, action in ipairs(actions) do - self:doAction(action) - if coroutine.isyieldable() then - if self.lsp:isNeedCompile(self.uri) then - coroutine.yield() - else - coroutine.yield('stop') - end - end - end -end - -function mt:getGlobalValue() - if self.lsp and self.lsp.globalValue then - return self.lsp.globalValue - end - local globalValue = self:createValue('table') - globalValue.GLOBAL = true - for name, lib in pairs(library.global) do - local field = self:createField(globalValue, name) - local value = self:getLibValue(lib, 'global') - self:setValue(field, value) - end - if self.lsp then - self.lsp.globalValue = globalValue - end - return globalValue -end - -function mt:createEnvironment() - self.scope.block = { start = 0, finish = math.maxinteger } - -- 整个文件是一个函数 - self.chunk.func = self:buildFunction() - self.chunk.func.uri = self.uri - self.results.main = self.chunk.func - -- 隐藏的上值`_ENV` - local evnField = self:createLocal('_ENV') - evnField.hide = true - -- 隐藏的参数`...` - self:createDots(1) - -- 全局变量`_G` - local globalValue = self:getGlobalValue() - -- 使用_G初始化_ENV - self:setValue(evnField, globalValue) - self.env = globalValue -end - -local function compile(ast, lsp, uri) - local vm = setmetatable({ - scope = env { - locals = {}, - }, - chunk = env { - labels = {}, - }, - results = { - locals = {}, - labels = {}, - funcs = {}, - calls = {}, - strings= {}, - infos = {}, - sources= {}, - main = nil, - }, - lsp = lsp, - uri = uri or '', - }, mt) - - -- 创建初始环境 - vm:createEnvironment() - - -- 执行代码 - vm:doActions(ast) - - -- 检查所有没有调用过的函数,调用一遍 - vm:callLeftFuncions() - - vm.scope = nil - vm.chunk = nil - - return vm -end - -return function (ast, lsp, uri) - if not ast then - return nil - end - local suc, res = xpcall(compile, log.error, ast, lsp, uri) - if not suc then - return nil - end - return res -end diff --git a/server/src/core/vm.txt b/server/src/core/vm.txt deleted file mode 100644 index 458a7c81..00000000 --- a/server/src/core/vm.txt +++ /dev/null @@ -1,52 +0,0 @@ -source -- ast生成的对象,是唯一对象 -{ - start: integer, -- source的开始位置,所有的活动source都应该由这个值,0表示是个马甲source - finish: integer, -- source的结束位置 - bindLocal: local, -- 绑定的local - bindField: field, -- 绑定的field - bindValue: value, -- 绑定的value - block: block, -- 所在的代码块 - uri: string, -- source所在的文件 - - isIndex: boolean, -- 是否作为构造表时的显性key - isSuffix: boolean, -- 是否作为simple的后缀 - parent: source, -- 作为simple后缀时的父source -} - -block -- ast生成的对象,是唯一对象 -{ - start: integer, -- block开始的位置,0表示主文件 - finish: integer, -- block结束的位置 -} - -local -{ - name: string, -- 局部变量一定有一个名字 - source: source, -- 声明该局部变量的source - value: value, -- 当前的value - - setValue(value), -- 设置value - getValue(value), -- 读取value - addInfo(tp, source), - eachInfo(function(info)), -} - -value -{ - source: source, -- 创建该值使用的source - - setType(type, rate), - getType(), - setChild(index, value), - getChild(index), - eachChild(function (value)), - rawSet(index, value), - rawGet(index), - setMetaTable(value), - getMetaMethod(name), - eachChild(function (value)), - rawEach(function (value)), - mergeValue(value), - addInfo(tp, source), - eachInfo(function (info)), -} diff --git a/server/src/vm/dots.lua b/server/src/vm/dots.lua new file mode 100644 index 00000000..1736de5c --- /dev/null +++ b/server/src/vm/dots.lua @@ -0,0 +1,6 @@ +local mt = {} +mt.__index = mt +mt.type = 'dots' + +return function () +end diff --git a/server/src/vm/env.lua b/server/src/vm/env.lua new file mode 100644 index 00000000..ada26145 --- /dev/null +++ b/server/src/vm/env.lua @@ -0,0 +1,139 @@ +local setmetatable = setmetatable +local pairs = pairs +local type = type +local table_sort = table.sort + +return function (root) + local env = {root} + local is_table = {} + for key, value in pairs(root) do + if type(value) == 'table' then + is_table[key] = true + end + end + root._next = nil + root._cut = {} + + local mt = { _env = env } + function mt:push() + env[#env+1] = { _next = env[#env], _cut = {} } + end + function mt:pop() + env[#env] = nil + end + function mt:cut(key) + env[#env]._cut[key] = true + end + function mt:__index(key) + local origin = env[#env] + if is_table[key] then + return setmetatable({}, { + __index = function (_, ckey) + local o = origin + while o do + local t = o[key] + if t and t[ckey] ~= nil then + return t[ckey] + end + o = not o._cut[key] and o._next + end + end, + __newindex = function (_, ckey, value) + local o = origin + if not o[key] then + o[key] = {} + end + o[key][ckey] = value + end, + __pairs = function () + local o = origin + local tbl = {} + while o do + local t = o[key] + if t then + for k, v in pairs(t) do + if tbl[k] == nil then + tbl[k] = v + end + end + end + o = not o._cut[key] and o._next + end + return next, tbl + end, + }) + else + local o = origin + while o do + if o[key] ~= nil then + return o[key] + end + o = not o._cut[key] and o._next + end + end + end + function mt:__newindex(key, value) + local o = env[#env] + if is_table[key] then + if type(o[key]) ~= 'table' then + o[key] = {} + end + if type(value) == 'table' then + for k, v in pairs(value) do + o[key][k] = v + end + else + error(('[env.%s] should be table, got [%s]'):format(key, value)) + end + else + o[key] = value + end + end + function mt:__pairs() + local keys = {} + local cuted = {} + local result = {} + local o = env[#env] + while true do + for key in pairs(o._cut) do + cuted[key] = true + end + for key, value in pairs(o) do + if key == '_cut' or key == '_next' then + goto CONTINUE + end + if cuted[key] then + goto CONTINUE + end + if result[key] == nil then + keys[#keys+1] = key + if is_table[key] then + result[key] = {} + else + result[key] = value + end + end + if is_table[key] then + for k, v in pairs(value) do + if result[key][k] == nil then + result[key][k] = v + end + end + end + ::CONTINUE:: + end + o = o._next + if not o then + break + end + end + table_sort(keys) + local i = 0 + return function () + i = i + 1 + local k = keys[i] + return k, result[k] + end + end + return setmetatable(mt, mt) +end diff --git a/server/src/vm/function.lua b/server/src/vm/function.lua new file mode 100644 index 00000000..25465398 --- /dev/null +++ b/server/src/vm/function.lua @@ -0,0 +1,36 @@ +local env = require 'vm.env' +local createDots = require 'vm.dots' + +local mt = {} +mt.__index = mt +mt.type = 'function' +mt._returnDots = false + +function mt:getUri() + return self.source.uri +end + +function mt:saveLocal(name, loc) + self.locals[name] = loc +end + +function mt:setReturn(index, value) + self.returns[index] = value +end + +function mt:returnDots(index, value) + self.returns[index] = value + self._returnDots = true +end + +return function (source) + if not source then + error('Function must has a source') + end + local self = setmetatable({ + source = source, + locals = {}, + returns = {}, + }, mt) + return self +end diff --git a/server/src/vm/global.lua b/server/src/vm/global.lua new file mode 100644 index 00000000..4e368641 --- /dev/null +++ b/server/src/vm/global.lua @@ -0,0 +1,20 @@ +local createValue = require 'vm.value' +local library = require 'core.library' +local libraryBuilder = require 'vm.library' + +return function (lsp) + local global = lsp and lsp.globalValue + if not global then + global = createValue('table') + end + for name, lib in pairs(library.global) do + if not global:rawGet(name) then + local value = libraryBuilder.value(lib) + global:rawSet(name, value) + end + end + if lsp then + lsp.globalValue = global + end + return global +end diff --git a/server/src/vm/init.lua b/server/src/vm/init.lua new file mode 100644 index 00000000..87576ba5 --- /dev/null +++ b/server/src/vm/init.lua @@ -0,0 +1 @@ +return require 'vm.vm' diff --git a/server/src/vm/library.lua b/server/src/vm/library.lua new file mode 100644 index 00000000..a6b9629e --- /dev/null +++ b/server/src/vm/library.lua @@ -0,0 +1,63 @@ +local createValue = require 'vm.value' +local createFunction = require 'vm.function' + +local buildLibValue +local buildLibChild + +local function getDefaultSource() + return { + start = 0, + finish = 0, + uri = '', + } +end + +function buildLibValue(lib) + local tp = lib.type + local value + if tp == 'table' then + value = createValue('table') + elseif tp == 'function' then + value = createValue('function') + local func = createFunction(getDefaultSource()) + value:setFunction(func) + if lib.returns then + for i, rtn in ipairs(lib.returns) do + if rtn.type == '...' then + func:returnDots(i, buildLibValue(rtn)) + else + func:setReturn(i, buildLibValue(rtn)) + end + end + end + elseif tp == 'string' then + value = createValue('string') + elseif tp == 'boolean' then + value = createValue('boolean') + elseif tp == 'number' then + value = createValue('number') + elseif tp == 'integer' then + value = createValue('integer') + elseif tp == 'nil' then + value = createValue('nil') + elseif tp == '...' then + value = createValue('any') + else + value = createValue(tp or 'any') + end + value:setLib(lib) + + if lib.child then + for fName, fLib in pairs(lib.child) do + local fValue = buildLibValue(fLib) + value:rawSet(fName, fValue) + end + end + + return value +end + +return { + value = buildLibValue, + child = buildLibChild, +} diff --git a/server/src/vm/local.lua b/server/src/vm/local.lua new file mode 100644 index 00000000..f1c33ecf --- /dev/null +++ b/server/src/vm/local.lua @@ -0,0 +1,62 @@ +local function getDefaultSource() + return { + start = 0, + finish = 0, + uri = '', + } +end + +local mt = {} +mt.__index = mt +mt.type = 'local' + +function mt:setValue(value) + if self.value then + self.value:mergeValue(value) + else + self.value = value + end +end + +function mt:getValue() + return self.value +end + +function mt:addInfo(tp, source) + self[#self+1] = { + type = tp, + source = source, + } +end + +function mt:eachInfo(callback) + for _, info in ipairs(self) do + callback(info) + end +end + +function mt:setFlag(name, v) + if not self._flag then + self._flag = {} + end + self._flag[name] = v +end + +function mt:getFlag(name) + if not self._flag then + return nil + end + return self._flag[name] +end + +return function (name, source, value) + if not value then + error('Local must has a value') + end + local self = setmetatable({ + name = name, + source = source or getDefaultSource(), + value = value, + }, mt) + return self +end diff --git a/server/src/vm/source.lua b/server/src/vm/source.lua new file mode 100644 index 00000000..5ec602c6 --- /dev/null +++ b/server/src/vm/source.lua @@ -0,0 +1,18 @@ +local mt = {} +mt.__index = mt + +function mt:bindLocal(loc) + if loc then + self._bindLocal = loc + else + return self._bindLocal + end +end + +function mt:setUri(uri) + self._uri = uri +end + +return function (source) + return setmetatable(source, mt) +end diff --git a/server/src/vm/value.lua b/server/src/vm/value.lua new file mode 100644 index 00000000..21dd4307 --- /dev/null +++ b/server/src/vm/value.lua @@ -0,0 +1,232 @@ +local function getDefaultSource() + return { + start = 0, + finish = 0, + uri = '', + } +end + +local mt = {} +mt.__index = mt +mt.type = 'value' + +function mt:setValue(value) + self._value = value +end + +function mt:getValue() + return self._value +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 + local current = self._type[tp] or 0.0 + self._type[tp] = current + (1 - current) * rate +end + +function mt:getType() + if not self._type then + return 'nil' + end + local mRate = 0.0 + local mType + for tp, rate in pairs(self._type) do + if rate > mRate then + mRate = rate + mType = tp + end + end + return mType or 'any' +end + +function mt:rawSet(index, value) + if not self._child then + self._child = {} + end + if self._child[index] then + self._child[index]:mergeValue(value) + else + self._child[index] = value + end +end + +function mt:rawGet(index) + if not self._child then + return nil + end + return self._child[index] +end + +function mt:setChild(index, value) + self:rawSet(index, value) +end + +function mt:getChild(index, mark) + local value = self:rawGet(index) + if value then + return value + end + local method = self:getMetaMethod('__index') + if not method then + return nil + end + if not mark then + mark = {} + end + if mark[method] then + return nil + end + mark[method] = true + return method:getChild(index, mark) +end + +function mt:setMetaTable(metatable) + self._meta = metatable +end + +function mt:getMetaTable() + return self._meta +end + +function mt:getMetaMethod(name) + local meta = self:getMetaTable() + if not meta then + return nil + end + return meta:rawGet(name) +end + +function mt:rawEach(callback, foundIndex) + if not self._child then + return nil + end + for index, value in pairs(self._child) do + if foundIndex then + if foundIndex[index] then + goto CONTINUE + end + foundIndex[index] = true + end + local res = callback(index, value) + if res ~= nil then + return res + end + ::CONTINUE:: + end + return nil +end + +function mt:eachChild(callback, mark, foundIndex) + if not foundIndex then + foundIndex = {} + end + local res = self:rawEach(callback, foundIndex) + if res ~= nil then + return res + end + local method = self:getMetaMethod('__index') + if not method then + return nil + end + if not mark then + mark = {} + end + if mark[method] then + return nil + end + mark[method] = true + return method:eachChild(callback, mark, foundIndex) +end + +function mt:mergeValue(value) + if value._type then + for tp, rate in pairs(value._type) do + self:setType(tp, rate) + end + end + if value._child then + if not self._child then + self._child = {} + end + for k, v in pairs(value._child) do + self._child[k] = v + end + end + for _, info in ipairs(value) do + self[#self+1] = info + end + if value._meta then + self._meta = value._meta + end + if value._func then + self._func = value._func + end + if value._lib then + self._lib = value._lib + end +end + +function mt:addInfo(tp, source) + if source and not source.start then + error('Miss start: ' .. table.dump(source)) + end + self[#self] = { + type = tp, + source = source or getDefaultSource(), + } +end + +function mt:eachInfo(callback) + for _, info in ipairs(self) do + local res = callback(info) + if res ~= nil then + return res + end + end + return nil +end + +function mt:setFunction(func) + self._func = func + self:setType('function', 1.0) +end + +function mt:setLib(lib) + self._lib = lib +end + +function mt:getLib() + return self._lib +end + +return function (tp, source) + if tp == '...' then + error('Value type cant be ...') + end + local self = setmetatable({ + source = source or getDefaultSource(), + _type = {}, + }, 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 + return self +end diff --git a/server/src/vm/vm.lua b/server/src/vm/vm.lua new file mode 100644 index 00000000..48d7e038 --- /dev/null +++ b/server/src/vm/vm.lua @@ -0,0 +1,1360 @@ +local library = require 'core.library' +local createValue = require 'vm.value' +local createLocal = require 'vm.local' +local createFunction = require 'vm.function' +local instantSource = require 'vm.source' +local buildGlobal = require 'vm.global' + +local mt = {} +mt.__index = mt + +function mt:getDefaultSource() + return { + start = 0, + finish = 0, + uri = self:getUri(), + } +end + +function mt:createDummyVar(source, value) + if source and source.bind then + return source.bind + end + local loc = { + type = 'local', + key = '', + source = source or self:getDefaultSource(), + } + + if source then + source.bind = loc + self.results.sources[#self.results.sources+1] = source + source.isLocal = true + end + + self:setValue(loc, value, source) + return loc +end + +function mt:createField(value, index, source) + if source and source.bind then + return source.bind + end + local field = value:createField(index, source) + if source then + source.bind = field + self.results.sources[#self.results.sources+1] = source + end + if value.GLOBAL then + field.GLOBAL = true + end + if self.lsp and self:isGlobal(field) then + self.lsp.global:markSet(self.chunk.func.uri) + end + field.parentValue = value + + return field +end + +function mt:getField(value, index, source) + local field = value:getField(index, source) + if not field then + return nil + end + if source then + source.bind = field + self.results.sources[#self.results.sources+1] = source + end + if value.GLOBAL then + field.GLOBAL = true + end + if self.lsp and self:isGlobal(field) then + self.lsp.global:markGet(self.chunk.func.uri) + end + + return field +end + +function mt:createArg(key, source, value) + local loc = self:createLocal(key, source, value) + if source then + source.isArg = true + end + return loc +end + +function mt:scopePush(block) + if not block.start then + error('Scope push without start!') + end + self.scope:push() + self.scope.block = block +end + +function mt:scopePop() + self.scope:pop() +end + +function mt:addInfo(var, type, source, value) + if not source then + error('Miss source') + end + if not source.start then + error('Miss start: ' .. table.dump(source)) + end + if var.type ~= 'local' and var.type ~= 'field' and var.type ~= 'label' then + error('Must be local, field or label: ' .. table.dump(var)) + end + local info = { + type = type, + source = source or self:getDefaultSource(), + value = value, + } + if not self.results.infos[var] then + self.results.infos[var] = {} + end + self.results.infos[var][#self.results.infos[var]+1] = info +end + +function mt:eachInfo(var, callback) + if not self.results.infos[var] then + return nil + end + for _, info in ipairs(self.results.infos[var]) do + local res = callback(info) + if res ~= nil then + return res + end + end + return nil +end + +function mt:createDots(index, source) + local dots = { + type = 'dots', + source = source or self:getDefaultSource(), + func = self:getCurrentFunction(), + index = index, + } + self.chunk.dots = dots + return dots +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 + if obj.type == 'pair' then + local value = self:getExp(obj[2]) + local key = obj[1] + key.uri = self.chunk.func.uri + if key.index then + local index = self:getIndex(key) + local field = self:createField(tbl, index, key) + if value.type == 'list' then + self:setValue(field, value[1], key) + else + self:setValue(field, value, key) + end + else + if key.type == 'name' then + local field = self:createField(tbl, key[1], key) + key.isIndex = true + if value.type == 'list' then + self:setValue(field, value[1], key) + else + self:setValue(field, value, key) + end + end + end + else + local value = self:getExp(obj) + if value.type == 'list' then + if index == #source then + for i, v in ipairs(value) do + local field = self:createField(tbl, n + i) + self:setValue(field, v) + end + else + n = n + 1 + local field = self:createField(tbl, n) + self:setValue(field, value[1]) + end + else + n = n + 1 + local field = self:createField(tbl, n) + self:setValue(field, value) + end + -- 处理写了一半的 key = value,把name类的数组元素视为哈希键 + if obj.type == 'name' then + obj.isIndex = true + end + end + end + return tbl +end + +function mt:setValue(var, value, source) + if value and value.type == 'list' then + error('Cant set value list') + end + value = value or self:createValue('any', source) + if source and source.start then + self:addInfo(var, 'set', source, value) + value:addInfo('set', source) + end + if var.GLOBAL then + value.GLOBAL = true + end + var.value = value + return value +end + +function mt:getValue(var, source) + if not var.value then + var.value = self:createValue('any', source or self:getDefaultSource()) + if var.GLOBAL then + var.value.GLOBAL = true + end + end + return var.value +end + +function mt:isGlobal(field) + return field.GLOBAL == true +end + +function mt:callLeftFuncions() + for _, func in ipairs(self.results.funcs) do + if func.built and not func.runed then + self:runFunction(func) + end + end +end + +function mt:runFunction(func) + func.runed = (func.runed or 0) + 1 + if func.runed > 1 then + --return + end + self:scopePush(func.built) + self.chunk:push() + self.chunk:cut 'dots' + self.chunk:cut 'labels' + self.chunk:cut 'locals' + self.chunk.func = func + + self.scope:cut 'locals' + for name, loc in pairs(func.upvalues) do + self.scope.locals[name] = loc + end + + local index = 0 + if func.object then + local var = self:createArg('self', func.colon, self:getValue(func.object, func.colon)) + var.hide = true + var.link = func.object + if func.argValues[1] then + self:setValue(var, func.argValues[1]) + end + index = 1 + func.args[index] = var + end + + local stop + self:forList(func.built.arg, function (arg) + if stop then + return + end + index = index + 1 + if arg.type == 'name' then + local var = self:createArg(arg[1], arg) + self:setValue(var, func.argValues[index] or self:createValue('nil')) + func.args[index] = var + elseif arg.type == '...' then + local dots = self:createDots(index, arg) + for i = index, #func.argValues do + dots[#dots+1] = func.argValues[i] + end + func.hasDots = true + stop = true + end + end) + + self:doActions(func.built) + + self.chunk:pop() + self:scopePop() +end + +function mt:buildFunction(exp, object, colon) + if exp and exp.func then + return exp.func + end + + local func = self:createValue('function', exp) + func.args = {} + func.argValues = {} + + if not exp then + return func + end + + func.built = exp + func.upvalues = {} + func.object = object + func.colon = colon + func.uri = exp.uri + exp.func = func + for name, loc in pairs(self.scope.locals) do + func.upvalues[name] = loc + end + + self.results.funcs[#self.results.funcs+1] = func + + return func +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:countList(list) + if not list then + return 0 + end + if list.type == 'list' then + return #list + end + return 1 +end + +function mt:setFunctionArg(func, values) + if not func.argValues then + func.argValues = {} + end + for i = 1, #values do + func.argValues[i] = values[i] + end +end + +function mt:getFunctionArg(func, i) + if not func.argValues then + func.argValues = {} + end + return func.argValues[i] +end + +function mt:callSetMetaTable(func, values, source) + if not values[1] then + values[1] = self:createValue('any') + end + if not values[2] then + values[2] = self:createValue('any') + end + self:setFunctionReturn(func, 1, values[1]) + values[1]:setMetaTable(values[2], source) +end + +function mt:getRequire(strValue, destVM) + -- 取出对方的主函数 + local main = destVM.results.main + -- 获取主函数返回值,注意不能修改对方的环境 + local mainValue + if main.returns then + mainValue = self:selectList(main.returns, 1) + else + mainValue = self:createValue('boolean', nil, true) + mainValue.uri = destVM.uri + end + + return mainValue +end + +function mt:getLoadFile(strValue, destVM) + -- 取出对方的主函数 + local main = destVM.results.main + -- loadfile 的返回值就是对方的主函数 + local mainValue = main + + return mainValue +end + +function mt:tryRequireOne(strValue, mode) + if not self.lsp or not self.lsp.workspace then + return nil + end + local str = strValue:getValue() + if type(str) == 'string' then + -- 支持 require 'xxx' 的转到定义 + local strSource = strValue.source + strSource.bind = strValue + self.results.sources[#self.results.sources+1] = strSource + strValue.isRequire = true + + local uri + if mode == 'require' then + uri = self.lsp.workspace:searchPath(self.chunk.func.uri, str) + elseif mode == 'loadfile' then + uri = self.lsp.workspace:loadPath(self.chunk.func.uri, str) + elseif mode == 'dofile' then + uri = self.lsp.workspace:loadPath(self.chunk.func.uri, str) + end + if not uri then + return nil + end + + strValue.uri = uri + -- 如果取不到VM(不编译),则做个标记,之后再取一次 + local destVM = self.lsp:getVM(uri) + self.lsp:compileChain(self.chunk.func.uri, uri) + if destVM then + if mode == 'require' then + return self:getRequire(strValue, destVM) + elseif mode == 'loadfile' then + return self:getLoadFile(strValue, destVM) + elseif mode == 'dofile' then + return self:getRequire(strValue, destVM) + end + end + end + return nil +end + +function mt:callRequire(func, values) + if not values[1] then + values[1] = self:createValue('any') + end + local str = values[1]:getValue() + if type(str) ~= 'string' then + return + end + local lib = library.library[str] + if lib then + local value = self:getLibValue(lib, 'library') + self:setFunctionReturn(func, 1, value) + return + else + local requireValue = self:tryRequireOne(values[1], 'require') + if not requireValue then + requireValue = self:createValue('boolean') + requireValue.isRequire = true + end + self:setFunctionReturn(func, 1, requireValue) + end +end + +function mt:callLoadFile(func, values) + if not values[1] then + values[1] = self:createValue('any') + end + local str = values[1]:getValue() + if type(str) ~= 'string' then + return + end + local requireValue = self:tryRequireOne(values[1], 'loadfile') + if not requireValue then + requireValue = self:createValue('any') + requireValue.isRequire = true + end + self:setFunctionReturn(func, 1, requireValue) +end + +function mt:callDoFile(func, values) + if not values[1] then + values[1] = self:createValue('any') + end + local str = values[1]:getValue() + if type(str) ~= 'string' then + return + end + local requireValue = self:tryRequireOne(values[1], 'dofile') + if not requireValue then + requireValue = self:createValue('any') + requireValue.isRequire = true + end + self:setFunctionReturn(func, 1, requireValue) +end + +function mt:call(func, values, source) + func:inference('function', 0.9) + local lib = func.lib + if lib then + if lib.args then + for i, arg in ipairs(lib.args) do + local value = values[i] + if value and arg.type ~= '...' then + value:inference(arg.type, 1.0) + end + end + end + if lib.returns then + for i, rtn in ipairs(lib.returns) do + if rtn.type == '...' then + self:getFunctionReturns(func, i):inference('any', 0.0) + else + self:getFunctionReturns(func, i):inference(rtn.type or 'any', 1.0) + 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) + end + end + else + if not func.built then + self:setFunctionReturn(func, 1, self:createValue('any', source)) + end + end + + if not source.hasRuned and func.built then + source.hasRuned = true + self:setFunctionArg(func, values) + self:runFunction(func) + end + + return self:getFunctionReturns(func) +end + +function mt:getCurrentFunction() + return self.chunk.func +end + +function mt:setFunctionReturn(func, index, value) + func.hasReturn = true + if not func.returns then + func.returns = { + type = 'list', + } + end + if value then + if value.type == 'list' then + for i, v in ipairs(value) do + func.returns[index+i-1] = v + end + else + func.returns[index] = value + end + else + func.returns[index] = self:createValue('any', func.source) + end +end + +function mt:getFunctionReturns(func, i) + if func.maxReturns and i and func.maxReturns < i then + return self:createValue('nil') + end + if not func.returns then + func.returns = { + type = 'list', + } + end + if i then + if not func.returns[i] then + for n = #func.returns+1, i do + func.returns[n] = self:createValue('any') + end + end + return func.returns[i] + else + return func.returns + end +end + +function mt:createValue(tp, source, v) + local value = createValue(tp, source, v) + local lib = library.object[tp] + if lib then + self:getLibChild(value, lib, 'object') + end + return value +end + +function mt:getName(name, source) + if source and source.bind then + return source.bind + end + local loc = self.scope.locals[name] + if loc then + source.bind = loc + self.results.sources[#self.results.sources+1] = source + return loc + end + source.uri = self.chunk.func.uri + local ENV = self.scope.locals._ENV + local ENVValue = self:getValue(ENV, source) + local global = self:getField(ENVValue, name, source) + if global then + global.parent = ENV + return global + else + global = self:createField(ENVValue, name, source) + global.parent = ENV + return global + end +end + +function mt:setName(name, source, value) + source.uri = self.chunk.func.uri + local loc = self.scope.locals[name] + if loc then + source.bind = loc + self.results.sources[#self.results.sources+1] = source + self:setValue(loc, value, source) + return + end + local ENV = self.scope.locals._ENV + local ENVValue = self:getValue(ENV, source) + local global = self:getField(ENVValue, name, source) + if global then + global.parent = ENV + self:setValue(global, value, source) + else + global = self:createField(ENVValue, name, source) + global.parent = ENV + self:setValue(global, value, source) + end +end + +function mt:getIndex(obj) + local tp = obj.type + obj.uri = self.chunk.func.uri + if tp == 'name' then + local var = self:getName(obj[1], obj) + local value = self:getValue(var, obj) + self:addInfo(var, 'get', obj) + value:addInfo('get', obj) + return value + elseif (tp == 'string' or tp == 'number' or tp == 'boolean') then + return obj[1] + else + return self:getExp(obj) + end +end + +-- expect表示遇到 ... 时,期待的返回数量 +function mt:unpackDots(res, expect) + local dots = self:getDots(1) + local func = dots.func + local start = dots.index + if expect then + local finish = start + expect - 1 + for i = start, finish do + res[#res+1] = self:getFunctionArg(func, i) + end + else + if not func.argValues then + return + end + for i = start, #func.argValues do + res[#res+1] = func.argValues[i] + end + end +end + +function mt:unpackList(list, expect) + local res = { + type = 'list', + } + if not list then + return res + end + if list.type == 'list' or list.type == 'call' then + for i, exp in ipairs(list) do + if exp.type == '...' then + self:unpackDots(res, expect) + break + end + local value = self:getExp(exp) + if value.type == 'list' then + if i == #list then + for _, v in ipairs(value) do + res[#res+1] = v + end + else + res[#res+1] = value[1] + end + else + res[#res+1] = value + end + end + elseif list.type == '...' then + self:unpackDots(res, expect) + else + local value = self:getExp(list) + if value.type == 'list' then + for i, v in ipairs(value) do + res[i] = v + end + else + res[1] = value + end + end + for _, v in ipairs(res) do + if v.type == 'list' then + error('Unpack list') + end + end + return res +end + +function mt:getSimple(simple, mode) + local value = self:getExp(simple[1]) + local field + local parentName + local tp = simple[1].type + simple[1].uri = self.chunk.func.uri + if tp == 'name' then + field = self:getName(simple[1][1], simple[1]) + parentName = field.key + elseif tp == 'string' or tp == 'number' or tp == 'nil' or tp == 'boolean' then + local v = self:createValue(tp, simple[1], simple[1][1]) + field = self:createDummyVar(simple[1], v) + parentName = '*' .. tp + else + local v = self:createValue('any', simple[1]) + field = self:createDummyVar(simple[1], v) + parentName = '?' + end + local object + local colon + local lastField = field + for i = 2, #simple do + local obj = simple[i] + local tp = obj.type + obj.uri = self.chunk.func.uri + obj.isSuffix = true + value = self:selectList(value, 1) + + if tp == 'call' then + value:inference('function', 0.9) + local args = self:unpackList(obj) + if object then + table.insert(args, 1, self:getValue(object, obj)) + end + local func = value + -- 函数的返回值一定是list + value = self:call(func, args, obj) + if i < #simple then + value = value[1] or self:createValue('any', obj) + end + self.results.calls[#self.results.calls+1] = { + args = obj, + lastObj = simple[i-1], + nextObj = simple[i+1], + func = func, + } + parentName = parentName .. '(...)' + elseif tp == 'index' then + value:inference('table', 0.8) + value:inference('string', 0.2) + local child = obj[1] + obj.indexName = parentName + local index = self:getIndex(child) + if mode == 'value' or i < #simple then + field = self:getField(value, index, obj) or self:createField(value, index, obj) + value = self:getValue(field, obj) + self:addInfo(field, 'get', obj) + value:addInfo('get', obj) + else + field = self:createField(value, index, obj) + value = self:getValue(field, obj) + end + field.parent = lastField + if obj[1].type == 'string' then + parentName = ('%s[%q]'):format(parentName, index) + elseif obj[1].type == 'number' or obj[1].type == 'boolean' then + parentName = ('%s[%s]'):format(parentName, index) + else + parentName = ('%s[?]'):format(parentName) + end + elseif tp == 'name' then + value:inference('table', 0.8) + value:inference('string', 0.2) + if mode == 'value' or i < #simple then + field = self:getField(value, obj[1], obj) or self:createField(value, obj[1], obj) + value = self:getValue(field, obj) + self:addInfo(field, 'get', obj) + value:addInfo('get', obj) + else + field = self:createField(value, obj[1], obj) + value = self:getValue(field, obj) + end + field.parent = lastField + lastField = field + obj.object = object + obj.parentName = parentName + parentName = parentName .. '.' .. field.key + elseif tp == ':' then + value:inference('table', 0.8) + value:inference('string', 0.2) + object = field + simple[i-1].colon = obj + colon = colon + elseif tp == '.' then + value:inference('table', 0.8) + value:inference('string', 0.2) + simple[i-1].dot = obj + end + end + if mode == 'value' then + return value, object + elseif mode == 'field' then + return field, object, colon + end + error('Unknow simple mode: ' .. mode) +end + +function mt:isTrue(v) + if v:getType() == 'nil' then + return false + end + if v:getType() == 'boolean' and not v:getValue() then + return false + end + return true +end + +function mt:selectList(list, n) + if list.type ~= 'list' then + return list + end + return list[n] or self:createValue('nil') +end + +function mt:getBinary(exp) + local v1 = self:getExp(exp[1]) + local v2 = self:getExp(exp[2]) + v1 = self:selectList(v1, 1) + v2 = self:selectList(v2, 1) + 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:inference('number', 0.9) + v2:inference('number', 0.9) + v1:inference('string', 0.1) + v2:inference('string', 0.1) + return self:createValue('boolean') + elseif op == '~=' + or op == '==' + then + return self:createValue('boolean') + elseif op == '|' + or op == '~' + or op == '&' + or op == '<<' + or op == '>>' + then + v1:inference('integer', 0.9) + v2:inference('integer', 0.9) + v1:inference('number', 0.9) + v2:inference('number', 0.9) + v1:inference('string', 0.1) + v2:inference('string', 0.1) + if math.type(v1:getValue()) == 'integer' and math.type(v2:getValue()) == 'integer' then + if op == '|' then + return self:createValue('integer', exp, v1:getValue() | v2:getValue()) + elseif op == '~' then + return self:createValue('integer', exp, v1:getValue() ~ v2:getValue()) + elseif op == '&' then + return self:createValue('integer', exp, v1:getValue() &v2:getValue()) + elseif op == '<<' then + return self:createValue('integer', exp, v1:getValue() << v2:getValue()) + elseif op == '>>' then + return self:createValue('integer', exp, v1:getValue() >> v2:getValue()) + end + end + return self:createValue('integer') + elseif op == '..' then + v1:inference('string', 0.9) + v2:inference('string', 0.9) + v1:inference('number', 0.1) + v2:inference('number', 0.1) + if type(v1:getValue()) == 'string' and type(v2:getValue()) == 'string' then + return self:createValue('string', nil, v1:getValue() .. v2:getValue()) + end + return self:createValue('string') + elseif op == '+' + or op == '-' + or op == '*' + or op == '/' + or op == '^' + or op == '%' + or op == '//' + then + v1:inference('number', 0.9) + v2:inference('number', 0.9) + if type(v1:getValue()) == 'number' and type(v2:getValue()) == 'number' then + if op == '+' then + return self:createValue('number', exp, v1:getValue() + v2:getValue()) + elseif op == '-' then + return self:createValue('number', exp, v1:getValue() - v2:getValue()) + elseif op == '*' then + return self:createValue('number', exp, v1:getValue() * v2:getValue()) + elseif op == '/' then + if v2:getValue() ~= 0 then + return self:createValue('number', exp, v1:getValue() / v2:getValue()) + end + elseif op == '^' then + return self:createValue('number', exp, v1:getValue() ^ v2:getValue()) + elseif op == '%' then + if v2:getValue() ~= 0 then + return self:createValue('number', exp, v1:getValue() % v2:getValue()) + end + elseif op == '//' then + if v2:getValue() ~= 0 then + return self:createValue('number', exp, v1:getValue() // v2:getValue()) + end + end + end + return self:createValue('number') + end + return nil +end + +function mt:getUnary(exp) + local v1 = self:getExp(exp[1]) + v1 = self:selectList(v1, 1) + local op = exp.op + -- TODO 搜索元方法 + if op == 'not' then + return self:createValue('boolean') + elseif op == '#' then + v1:inference('table', 0.9) + v1:inference('string', 0.9) + if type(v1:getValue()) == 'string' then + return self:createValue('integer', exp, #v1:getValue()) + end + return self:createValue('integer') + elseif op == '-' then + v1:inference('number', 0.9) + if type(v1:getValue()) == 'number' then + return self:createValue('number', exp, -v1:getValue()) + end + return self:createValue('number') + elseif op == '~' then + v1:inference('integer', 0.9) + if math.type(v1:getValue()) == 'integer' then + return self:createValue('integer', exp, ~v1:getValue()) + end + return self:createValue('integer') + end + return nil +end + +function mt:getDots() + if not self.chunk.dots then + self:createDots(1) + end + return self.chunk.dots +end + +function mt:getExp(exp) + exp.uri = self.chunk.func.uri + local tp = exp.type + if tp == 'nil' then + return self:createValue('nil', exp) + elseif tp == 'string' then + self.results.strings[#self.results.strings+1] = exp + 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 var = self:getName(exp[1], exp) + local value = self:getValue(var, exp) + self:addInfo(var, 'get', exp) + value:addInfo('get', exp) + return value + elseif tp == 'simple' then + return self:getSimple(exp, 'value') + 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 + local value = { type = 'list' } + self:unpackDots(value) + return value + end + error('Unkown exp type: ' .. tostring(tp)) +end + +function mt:doDo(action) + self:scopePush(action) + self:doActions(action) + self:scopePop() +end + +function mt:doReturn(action) + self:getCurrentFunction().hasReturn = true + for i, exp in ipairs(action) do + local value = self:getExp(exp) + if value.type == 'list' then + if i == #action then + if #value == 0 then + value[1] = self:createValue('any', exp) + end + for x, v in ipairs(value) do + v:addInfo('return', exp) + self:setFunctionReturn(self:getCurrentFunction(), i + x - 1, v) + end + break + else + local v = value[1] or self:createValue('nil', exp) + v:addInfo('return', exp) + self:setFunctionReturn(self:getCurrentFunction(), i, v) + end + else + value:addInfo('return', exp) + self:setFunctionReturn(self:getCurrentFunction(), i, value) + end + end +end + +function mt:createLabel(source) + if source.bind then + return source.bind + end + local name = source[1] + if not self.chunk.labels[name] then + local label = { + type = 'label', + key = name, + } + self.chunk.labels[name] = label + self.results.labels[#self.results.labels+1] = label + end + source.bind = self.chunk.labels[name] + self.results.sources[#self.results.sources+1] = source + return self.chunk.labels[name] +end + +function mt:doSet(action) + if not action[2] then + return + end + local n = self:countList(action[1]) + -- 要先计算值 + local values = self:unpackList(action[2], n) + self:forList(action[1], function (key) + local value = table.remove(values, 1) + if key.type == 'name' then + self:setName(key[1], key, value) + elseif key.type == 'simple' then + local field = self:getSimple(key, 'field') + self:setValue(field, value, key[#key]) + end + end) +end + +function mt:doLocal(action) + local n = self:countList(action[1]) + local values + if action[2] then + values = self:unpackList(action[2], n) + end + self:forList(action[1], function (key) + local value + if values then + value = table.remove(values, 1) + end + key.uri = self.chunk.func.uri + self:createLocal(key[1], key, value) + end) +end + +function mt:doIf(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) + + local min = self:unpackList(action.min)[1] + 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 args = self:unpackList(action.exp) + + self:scopePush(action) + local func = table.remove(args, 1) or self:createValue('any') + local values = self:call(func, args, action) + self:forList(action.arg, function (arg) + local value = table.remove(values, 1) + self:createLocal(arg[1], arg, value) + end) + + self:doActions(action) + + self:scopePop() +end + +function mt:doWhile(action) + + self:getExp(action.filter) + + self:scopePush(action) + self:doActions(action) + self:scopePop() +end + +function mt:doRepeat(action) + self:scopePush(action) + self:doActions(action) + self:getExp(action.filter) + self:scopePop() +end + +function mt:doFunction(action) + local name = action.name + local var, object, colon + local source + if name then + if name.type == 'simple' then + var, object, colon = self:getSimple(name, 'field') + source = name[#name] + else + var = self:getName(name[1], name) + source = name + end + end + local func = self:buildFunction(action, object, colon) + if var then + self:setValue(var, func, source) + end +end + +function mt:doLocalFunction(action) + local name = action.name + if name then + if name.type == 'simple' then + local var, object = self:getSimple(name, 'field') + local func = self:buildFunction(action, object) + self:setValue(var, func, name[#name]) + else + local loc = self:createLocal(name[1], name) + local func = self:buildFunction(action) + func:addInfo('local', name) + self:setValue(loc, func, name[#name]) + end + end +end + +function mt:doAction(action) + if not action then + -- Skip + return + end + action.uri = self.chunk.func.uri + local tp = action.type + if tp == 'do' then + self:doDo(action) + elseif tp == 'break' then + elseif tp == 'return' then + self:doReturn(action) + elseif tp == 'label' then + local label = self:createLabel(action) + self:addInfo(label, 'set', action) + elseif tp == 'goto' then + local label = self:createLabel(action) + self:addInfo(label, 'goto', action) + elseif tp == 'set' then + self:doSet(action) + elseif tp == 'local' then + self:doLocal(action) + elseif tp == 'simple' then + -- call + self:getSimple(action, 'value') + 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) + end +end + +function mt:doActions(actions) + for _, action in ipairs(actions) do + self:doAction(action) + if coroutine.isyieldable() then + if self.lsp:isNeedCompile(self.uri) then + coroutine.yield() + else + coroutine.yield('stop') + end + end + end +end + +function mt:createFunction(source) + local value = createValue('function', source) + local func = createFunction(source) + value:setFunction(func) + if source:getUri() == self.uri then + self.funcs[#self.funcs+1] = func + end + return value +end + +function mt:setCurrentFunction(func) + self.currentFunction = func +end + +function mt:saveLocal(name, loc) + self.currentFunction:saveLocal(name, loc) +end + +function mt:getUri() + return self.currentFunction:getUri() +end + +function mt:instantSource(source) + if instantSource(source) then + self.sources[#self.sources+1] = source + end +end + +function mt:bindLocal(source, loc) + if not source then + return + end + self:instantSource(source) + if loc then + source:bindLocal(loc) + source:setUri(self:getUri()) + else + return source:bindLocal() + end +end + +function mt:createLocal(key, source, value) + local loc = self:bindLocal(source) + if loc then + self:saveLocal(key, loc) + return loc + end + + if not value then + value = createValue('nil', self:getDefaultSource()) + end + + loc = createLocal(key, source, value) + self:saveLocal(key, loc) + self:bindLocal(source, loc) + return loc +end + +function mt:createEnvironment(ast) + -- 整个文件是一个函数 + self.main = self:createFunction(ast) + self:setCurrentFunction(self.main) + -- 全局变量`_G` + local global = buildGlobal(self.lsp) + -- 隐藏的上值`_ENV` + local env = self:createLocal('_ENV', nil, global) + env:setFlag('hide', true) + self.env = env +end + +local function compile(ast, lsp, uri) + local vm = setmetatable({ + strings= {}, + sources= {}, + funcs = {}, + main = nil, + env = nil, + lsp = lsp, + uri = uri or '', + }, mt) + + -- 创建初始环境 + ast.uri = vm.uri + vm:createEnvironment(ast) + + -- 检查所有没有调用过的函数,调用一遍 + vm:callLeftFuncions() + + vm.scope = nil + vm.chunk = nil + + return vm +end + +return function (ast, lsp, uri) + if not ast then + return nil + end + local suc, res = xpcall(compile, log.error, ast, lsp, uri) + if not suc then + return nil + end + return res +end diff --git a/server/src/vm/vm.txt b/server/src/vm/vm.txt new file mode 100644 index 00000000..458a7c81 --- /dev/null +++ b/server/src/vm/vm.txt @@ -0,0 +1,52 @@ +source -- ast生成的对象,是唯一对象 +{ + start: integer, -- source的开始位置,所有的活动source都应该由这个值,0表示是个马甲source + finish: integer, -- source的结束位置 + bindLocal: local, -- 绑定的local + bindField: field, -- 绑定的field + bindValue: value, -- 绑定的value + block: block, -- 所在的代码块 + uri: string, -- source所在的文件 + + isIndex: boolean, -- 是否作为构造表时的显性key + isSuffix: boolean, -- 是否作为simple的后缀 + parent: source, -- 作为simple后缀时的父source +} + +block -- ast生成的对象,是唯一对象 +{ + start: integer, -- block开始的位置,0表示主文件 + finish: integer, -- block结束的位置 +} + +local +{ + name: string, -- 局部变量一定有一个名字 + source: source, -- 声明该局部变量的source + value: value, -- 当前的value + + setValue(value), -- 设置value + getValue(value), -- 读取value + addInfo(tp, source), + eachInfo(function(info)), +} + +value +{ + source: source, -- 创建该值使用的source + + setType(type, rate), + getType(), + setChild(index, value), + getChild(index), + eachChild(function (value)), + rawSet(index, value), + rawGet(index), + setMetaTable(value), + getMetaMethod(name), + eachChild(function (value)), + rawEach(function (value)), + mergeValue(value), + addInfo(tp, source), + eachInfo(function (info)), +} -- cgit v1.2.3