diff options
author | 最萌小汐 <sumneko@hotmail.com> | 2019-09-18 18:02:17 +0800 |
---|---|---|
committer | 最萌小汐 <sumneko@hotmail.com> | 2019-09-18 18:02:17 +0800 |
commit | 28e9ccebae5e1cdb6dbd405a8d0f7e7c1132f0cf (patch) | |
tree | f9d5d4ff9b74ebb84bbb174b31ed762d4c0b9749 /server-beta/src/parser/compile.lua | |
parent | 7373fbc062e235806607e0ab908f6deed0c7e3db (diff) | |
download | lua-language-server-28e9ccebae5e1cdb6dbd405a8d0f7e7c1132f0cf.zip |
新的解析器
Diffstat (limited to 'server-beta/src/parser/compile.lua')
-rw-r--r-- | server-beta/src/parser/compile.lua | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/server-beta/src/parser/compile.lua b/server-beta/src/parser/compile.lua new file mode 100644 index 00000000..f6efb6e1 --- /dev/null +++ b/server-beta/src/parser/compile.lua @@ -0,0 +1,574 @@ +local guide = require 'parser.guide' +local type = type + +_ENV = nil + +local pushError, Root, Compile, CompileBlock, Cache, Block, GoToTag + +local vmMap = { + ['nil'] = function (obj) + Root[#Root+1] = obj + return #Root + end, + ['boolean'] = function (obj) + Root[#Root+1] = obj + return #Root + end, + ['string'] = function (obj) + Root[#Root+1] = obj + return #Root + end, + ['number'] = function (obj) + Root[#Root+1] = obj + return #Root + end, + ['...'] = function (obj) + Root[#Root+1] = obj + return #Root + end, + ['getname'] = function (obj) + Root[#Root+1] = obj + local id = #Root + local loc = guide.getLocal(Root, obj, obj[1], obj.start) + if loc then + obj.type = 'getlocal' + obj.loc = Cache[loc] + if not loc.ref then + loc.ref = {} + end + loc.ref[#loc.ref+1] = id + else + obj.type = 'getglobal' + end + return id + end, + ['getfield'] = function (obj) + local node = obj.node + Root[#Root+1] = obj + local id = #Root + obj.node = Compile(node, id) + return id + end, + ['call'] = function (obj) + Root[#Root+1] = obj + local id = #Root + local node = obj.node + local args = obj.args + if node then + obj.node = Compile(node, id) + end + if args then + obj.args = Compile(args, id) + end + return id + end, + ['callargs'] = function (obj) + Root[#Root+1] = obj + local id = #Root + for i = 1, #obj do + local arg = obj[i] + obj[i] = Compile(arg, id) + end + return id + end, + ['binary'] = function (obj) + local e1 = obj[1] + local e2 = obj[2] + Root[#Root+1] = obj + local id = #Root + obj[1] = Compile(e1, id) + obj[2] = Compile(e2, id) + return id + end, + ['unary'] = function (obj) + local e = obj[1] + Root[#Root+1] = obj + local id = #Root + obj[1] = Compile(e, id) + return id + end, + ['varargs'] = function (obj) + local func = guide.getParentFunction(Root, obj) + if func then + local index = guide.getFunctionVarArgs(Root, func) + if not index then + pushError { + type = 'UNEXPECT_DOTS', + start = obj.start, + finish = obj.finish, + } + end + end + Root[#Root+1] = obj + return #Root + end, + ['paren'] = function (obj) + local exp = obj.exp + Root[#Root+1] = obj + local id = #Root + obj.exp = Compile(exp, id) + return id + end, + ['getindex'] = function (obj) + Root[#Root+1] = obj + local id = #Root + local node = obj.node + obj.node = Compile(node, id) + local index = obj.index + if index then + obj.index = Compile(index, id) + end + return id + end, + ['getmethod'] = function (obj) + Root[#Root+1] = obj + local id = #Root + local node = obj.node + local method = obj.method + obj.node = Compile(node, id) + if method then + obj.method = Compile(method, id) + end + return id + end, + ['setmethod'] = function (obj) + Root[#Root+1] = obj + local id = #Root + local node = obj.node + local method = obj.method + local value = obj.value + obj.node = Compile(node, id) + if method then + obj.method = Compile(method, id) + end + obj.value = Compile(value, id) + return id + end, + ['method'] = function (obj) + Root[#Root+1] = obj + return #Root + end, + ['function'] = function (obj) + local lastBlock = Block + Block = obj + Root[#Root+1] = obj + local id = #Root + local args = obj.args + if args then + obj.args = Compile(args, id) + end + for i = 1, #obj do + local act = obj[i] + obj[i] = Compile(act, id) + end + Block = lastBlock + return id + end, + ['funcargs'] = function (obj) + Root[#Root+1] = obj + local id = #Root + for i = 1, #obj do + local arg = obj[i] + obj[i] = Compile(arg, id) + end + return id + end, + ['table'] = function (obj) + Root[#Root+1] = obj + local id = #Root + for i = 1, #obj do + local v = obj[i] + obj[i] = Compile(v, id) + end + return id + end, + ['tablefield'] = function (obj) + Root[#Root+1] = obj + local id = #Root + local value = obj.value + if value then + obj.value = Compile(value, id) + end + return id + end, + ['tableindex'] = function (obj) + Root[#Root+1] = obj + local id = #Root + local index = obj.index + local value = obj.value + obj.index = Compile(index, id) + obj.value = Compile(value, id) + return id + end, + ['index'] = function (obj) + Root[#Root+1] = obj + local id = #Root + local index = obj.index + obj.index = Compile(index, id) + return id + end, + ['select'] = function (obj) + Root[#Root+1] = obj + local id = #Root + local vararg = obj.vararg + if not Cache[vararg] then + obj.vararg = Compile(vararg, id) + Cache[vararg] = obj.vararg + else + obj.vararg = Cache[vararg] + if not vararg.extParent then + vararg.extParent = {} + end + vararg.extParent[#vararg.extParent+1] = id + end + return id + end, + ['setname'] = function (obj) + Root[#Root+1] = obj + local id = #Root + local value = obj.value + if value then + obj.value = Compile(value, id) + end + local loc = guide.getLocal(Root, obj, obj[1], obj.start) + if loc then + obj.type = 'setlocal' + obj.loc = Cache[loc] + if not loc.ref then + loc.ref = {} + end + loc.ref[#loc.ref+1] = id + else + obj.type = 'setglobal' + end + return id + end, + ['local'] = function (obj) + Root[#Root+1] = obj + local id = #Root + local attrs = obj.attrs + if attrs then + for i = 1, #attrs do + local attr = attrs[i] + attrs[i] = Compile(attr, id) + end + end + if Block then + if not Block.locals then + Block.locals = {} + end + Block.locals[#Block.locals+1] = id + end + local value = obj.value + if value then + obj.value = Compile(value, id) + end + Cache[obj] = id + return id + end, + ['localattr'] = function (obj) + Root[#Root+1] = obj + return #Root + end, + ['setfield'] = function (obj) + Root[#Root+1] = obj + local id = #Root + local node = obj.node + local field = obj.field + local value = obj.value + obj.node = Compile(node, id) + obj.field = Compile(field, id) + obj.value = Compile(value, id) + return id + end, + ['do'] = function (obj) + local lastBlock = Block + Block = obj + Root[#Root+1] = obj + local id = #Root + CompileBlock(obj, id) + Block = lastBlock + return id + end, + ['return'] = function (obj) + Root[#Root+1] = obj + local id = #Root + for i = 1, #obj do + local act = obj[i] + obj[i] = Compile(act, id) + end + if Block and Block[#Block] ~= obj then + pushError { + type = 'ACTION_AFTER_RETURN', + start = obj.start, + finish = obj.finish, + } + end + return id + end, + ['label'] = function (obj) + Root[#Root+1] = obj + local id = #Root + local block = guide.getBlock(Root, obj) + if block then + if not block.labels then + block.labels = {} + end + local name = obj[1] + local label = guide.getLabel(Root, block, name) + if label then + pushError { + type = 'REDEFINED_LABEL', + start = obj.start, + finish = obj.finish, + relative = { + { + label.start, + label.finish, + } + } + } + end + block.labels[name] = id + end + return id + end, + ['goto'] = function (obj) + Root[#Root+1] = obj + local id = #Root + GoToTag[#GoToTag+1] = obj + return id + end, + ['if'] = function (obj) + Root[#Root+1] = obj + local id = #Root + for i = 1, #obj do + local block = obj[i] + obj[i] = Compile(block, id) + end + return id + end, + ['ifblock'] = function (obj) + local lastBlock = Block + Block = obj + Root[#Root+1] = obj + local id = #Root + local filter = obj.filter + obj.filter = Compile(filter, id) + CompileBlock(obj, id) + Block = lastBlock + return id + end, + ['elseifblock'] = function (obj) + local lastBlock = Block + Block = obj + Root[#Root+1] = obj + local id = #Root + local filter = obj.filter + if filter then + obj.filter = Compile(filter, id) + end + CompileBlock(obj, id) + Block = lastBlock + return id + end, + ['elseblock'] = function (obj) + local lastBlock = Block + Block = obj + Root[#Root+1] = obj + local id = #Root + CompileBlock(obj, id) + Block = lastBlock + return id + end, + ['loop'] = function (obj) + local lastBlock = Block + Block = obj + Root[#Root+1] = obj + local id = #Root + local loc = obj.loc + local max = obj.max + local step = obj.step + if loc then + obj.loc = Compile(loc, id) + end + if max then + obj.max = Compile(max, id) + end + if step then + obj.step = Compile(step, id) + end + CompileBlock(obj, id) + Block = lastBlock + return id + end, + ['in'] = function (obj) + local lastBlock = Block + Block = obj + Root[#Root+1] = obj + local id = #Root + local keys = obj.keys + for i = 1, #keys do + local loc = keys[i] + keys[i] = Compile(loc, id) + end + CompileBlock(obj, id) + Block = lastBlock + return id + end, + ['while'] = function (obj) + local lastBlock = Block + Block = obj + Root[#Root+1] = obj + local id = #Root + local filter = obj.filter + obj.filter = Compile(filter, id) + CompileBlock(obj, id) + Block = lastBlock + return id + end, + ['repeat'] = function (obj) + local lastBlock = Block + Block = obj + Root[#Root+1] = obj + local id = #Root + CompileBlock(obj, id) + local filter = obj.filter + obj.filter = Compile(filter, id) + Block = lastBlock + return id + end, + ['break'] = function (obj) + if not guide.getBreakBlock(Root, obj) then + pushError { + type = 'BREAK_OUTSIDE', + start = obj.start, + finish = obj.finish, + } + end + Root[#Root+1] = obj + return #Root + end, + ['main'] = function (obj) + Block = obj + Root[#Root+1] = obj + local id = #Root + CompileBlock(obj, id) + Block = nil + return id + end, +} + +function CompileBlock(obj, parent) + for i = 1, #obj do + local act = obj[i] + local f = vmMap[act.type] + if f then + act.parent = parent + obj[i] = f(act) + end + end +end + +function Compile(obj, parent) + if not obj then + return nil + end + local f = vmMap[obj.type] + if not f then + return nil + end + obj.parent = parent + return f(obj) +end + +local function compileGoTo(obj) + local name = obj[1] + local label = guide.getLabel(Root, obj, name) + if not label then + pushError { + type = 'NO_VISIBLE_LABEL', + start = obj.start, + finish = obj.finish, + info = { + label = name, + } + } + return + end + -- 如果有局部变量在 goto 与 label 之间声明, + -- 并在 label 之后使用,则算作语法错误 + + -- 如果 label 在 goto 之前声明,那么不会有中间声明的局部变量 + if obj.start > label.start then + return + end + + local block = guide.getBlock(Root, obj) + local locals = block and block.locals + if not locals then + return + end + + for i = 1, #locals do + local loc = Root[locals[i]] + -- 检查局部变量声明位置为 goto 与 label 之间 + if loc.start < obj.start or loc.finish > label.finish then + goto CONTINUE + end + -- 检查局部变量的使用位置在 label 之后 + local refs = loc.ref + if not refs then + goto CONTINUE + end + for j = 1, #refs do + local ref = Root[refs[j]] + if ref.finish > label.finish then + pushError { + type = 'JUMP_LOCAL_SCOPE', + start = obj.start, + finish = obj.finish, + info = { + loc = loc[1], + }, + relative = { + { + start = label.start, + finish = label.finish, + }, + { + start = loc.start, + finish = loc.finish, + } + }, + } + return + end + end + ::CONTINUE:: + end +end + +local function PostCompile() + for i = 1, #GoToTag do + compileGoTo(GoToTag[i]) + end +end + +return function (self, lua, mode, version) + local state, errs = self:parse(lua, mode, version) + if not state then + return errs + end + pushError = state.pushError + Root = state.root + Cache = {} + GoToTag = {} + if type(state.ast) == 'table' then + Compile(state.ast) + end + PostCompile() + state.ast = nil + Cache = nil + return state, errs +end |