summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/src/matcher/init.lua1
-rw-r--r--server/src/matcher/vm.lua176
-rw-r--r--server/test/main.lua1
-rw-r--r--server/test/vm/init.lua21
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'
+]]