diff options
-rw-r--r-- | server/src/matcher/init.lua | 1 | ||||
-rw-r--r-- | server/src/matcher/vm.lua | 176 | ||||
-rw-r--r-- | server/test/main.lua | 1 | ||||
-rw-r--r-- | server/test/vm/init.lua | 21 |
4 files changed, 177 insertions, 22 deletions
diff --git a/server/src/matcher/init.lua b/server/src/matcher/init.lua index eda4ac7b..c3422e34 100644 --- a/server/src/matcher/init.lua +++ b/server/src/matcher/init.lua @@ -9,6 +9,7 @@ local api = { typeInference = require 'matcher.type_inference', findResult = require 'matcher.find_result', findLib = require 'matcher.find_lib', + vm = require 'matcher.vm', } return api diff --git a/server/src/matcher/vm.lua b/server/src/matcher/vm.lua index 66e23c91..bd053e89 100644 --- a/server/src/matcher/vm.lua +++ b/server/src/matcher/vm.lua @@ -4,6 +4,7 @@ local library = require 'matcher.library' local DefaultSource = { start = 0, finish = 0 } local mt = {} +mt.__index = mt function mt:createLocal(key, source) local loc = { @@ -12,7 +13,7 @@ function mt:createLocal(key, source) source = source or DefaultSource, } self.results.locals[#self.results.locals+1] = loc - self.locals[key] = loc + self.scope.locals[key] = loc return loc end @@ -33,24 +34,33 @@ function mt:getValue(var) return var.value end -function mt:addField(value, field) +function mt:addField(value, name, field) if not value.child then value.child = {} end - value.child[#value.child+1] = field + value.child[name] = field end function mt:createField(parent, name, source) local field = { - type = 'field', - key = name, + type = 'field', + key = name, source = source or DefaultSource, } self.results.fields[#self.results.fields+1] = field local value = self:getValue(parent) or self:setValue(parent, self:createTable(source)) - self:addField(value, field) + self:addField(value, name, field) + + return field +end + +function mt:getField(parent, name, source) + local value = self:getValue(parent) + or self:setValue(parent, self:createTable(source)) + local field = (value.child and value.child[name]) + or self:createField(parent, name, source) return field end @@ -63,60 +73,176 @@ function mt:createFunction(source) return func end +function mt:setFunctionReturn(index, value) + local func = self.func.func + if not func.returns then + func.returns = {} + end + func.returns[index] = value +end + function mt:createString(str, source) - local string = { + return { type = 'string', source = source or DefaultSource, value = str, } - return string end function mt:createBoolean(bool, source) - local boolean = { + return { type = 'boolean', source = source or DefaultSource, value = bool or false, } - return boolean end function mt:createNumber(num, source) - local number = { + return { type = 'number', source = source or DefaultSource, value = num or 0.0, } - return number end function mt:createInteger(int, source) - local integer = { + return { type = 'integer', source = source or DefaultSource, value = int or 0, } - return integer +end + +function mt:createNil(source) + return { + type = 'nil', + source = source or DefaultSource, + } end function mt:setLib(obj, lib) obj.lib = lib - if lib.type == 'table' then + local tp = lib.type + if not tp then + return + end + if tp == 'table' then self:setValue(obj, self:createTable()) - elseif lib.type == 'function' then + elseif tp == 'function' then self:setValue(obj, self:createFunction()) -- TODO - elseif lib.type == 'string' then + elseif tp == 'string' then self:setValue(obj, self:createString(lib.value)) - elseif lib.type == 'boolean' then + elseif tp == 'boolean' then self:setValue(obj, self:createBoolean(lib.value)) - elseif lib.type == 'number' then + elseif tp == 'number' then self:setValue(obj, self:createNumber(lib.value)) - elseif lib.type == 'integer' then + elseif tp == 'integer' then self:setValue(obj, self:createInteger(lib.value)) end end +function mt:getName(name, source) + local var = self.scope.locals[name] + or self:getField(self.scope.locals._ENV, name, source) + return var +end + +function mt:getBinary(exp) + local v1 = self:getExp(exp[1]) + local v2 = self:getExp(exp[2]) + local op = exp.op + -- TODO 搜索元方法 + if op == 'or' + or op == 'and' + or op == '<=' + or op == '>=' + or op == '<' + or op == '>' + or op == '~=' + or op == '==' + then + return self:createBoolean() + elseif op == '|' + or op == '~' + or op == '&' + or op == '<<' + or op == '>>' + or op == '//' + then + return self:createInteger() + elseif op == '..' then + return self:createString() + elseif op == '+' + or op == '-' + or op == '*' + or op == '/' + or op == '^' + or op == '%' + then + return self:craeteNumber() + end + return nil +end + +function mt:getUnary(exp) + local v1 = self:getExp(exp[1]) + local op = exp.op + -- TODO 搜索元方法 + if op == 'not' then + return self:createBoolean() + elseif op == '#' then + return self:createInteger() + elseif op == '-' then + return self:createNumber() + elseif op == '~' then + return self:createInteger() + end + return nil +end + +function mt:getExp(exp) + local tp = exp.type + if tp == 'nil' then + return self:createNil(exp) + elseif tp == 'string' then + return self:createString(exp[1], exp) + elseif tp == 'boolean' then + return self:createBoolean(exp[1], exp) + elseif tp == 'number' then + return self:createNumber(exp[1], exp) + elseif tp == 'name' then + local var = self:getVar(exp[1], exp) + return self:getValue(var) + elseif tp == 'binary' then + return self:getBinary(exp) + elseif tp == 'unary' then + return self:getUnary(exp) + elseif tp == '...' then + end + return nil +end + +function mt:doDo(action) + self.scope:push() + self:doActions(action) + self.scope:pop() +end + +function mt:doReturn(action) + for i, exp in ipairs(action) do + local value = self:getExp(exp) + self:setFunctionReturn(i, value) + end +end + function mt:doAction(action) + local tp = action.type + if tp == 'do' then + self:doDo(action) + elseif tp == 'break' then + elseif tp == 'return' then + self:doReturn(action) + end end function mt:doActions(actions) @@ -126,6 +252,8 @@ function mt:doActions(actions) end function mt:createEnvironment() + -- 整个文件是一个函数 + self.func.func = self:createFunction() -- 所有脚本都有个隐藏的上值`_ENV` local parent = self:createLocal('_ENV') -- 设置全局变量 @@ -145,8 +273,12 @@ end local function compile(ast) local vm = setmetatable({ - locals = env {}, - label = env {}, + scope = env { + locals = {}, + }, + func = env { + labels = {}, + }, results = { locals = {}, fields = {}, diff --git a/server/test/main.lua b/server/test/main.lua index 3167089e..5b372421 100644 --- a/server/test/main.lua +++ b/server/test/main.lua @@ -27,6 +27,7 @@ local function main() test 'type_inference' test 'definition' test 'find_lib' + test 'vm' print('测试完成') end diff --git a/server/test/vm/init.lua b/server/test/vm/init.lua new file mode 100644 index 00000000..db096252 --- /dev/null +++ b/server/test/vm/init.lua @@ -0,0 +1,21 @@ +local matcher = require 'matcher' +local parser = require 'parser' + +rawset(_G, 'TEST', true) + +function TEST(script) + local ast = parser:ast(script) + assert(ast) + local results = matcher.vm(ast) + assert(results) +end + +TEST [[ +do + x = 1 +end +]] + +TEST [[ +return nil, 1, true, 'xx' +]] |