summaryrefslogtreecommitdiff
path: root/server/src/vm
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2019-01-31 16:20:08 +0800
committer最萌小汐 <sumneko@hotmail.com>2019-01-31 16:20:08 +0800
commita692d7b413e4aff34a2a714cc6aec9ad0b495b33 (patch)
tree67b7897759009cef7b82963a529c580878ffd91c /server/src/vm
parent8d29cb3148ff2a6a05b879171c79b65cc21cf3d9 (diff)
downloadlua-language-server-a692d7b413e4aff34a2a714cc6aec9ad0b495b33.zip
整理代码
Diffstat (limited to 'server/src/vm')
-rw-r--r--server/src/vm/dots.lua6
-rw-r--r--server/src/vm/env.lua139
-rw-r--r--server/src/vm/function.lua36
-rw-r--r--server/src/vm/global.lua20
-rw-r--r--server/src/vm/init.lua1
-rw-r--r--server/src/vm/library.lua63
-rw-r--r--server/src/vm/local.lua62
-rw-r--r--server/src/vm/source.lua18
-rw-r--r--server/src/vm/value.lua232
-rw-r--r--server/src/vm/vm.lua1360
-rw-r--r--server/src/vm/vm.txt52
11 files changed, 1989 insertions, 0 deletions
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)),
+}