diff options
Diffstat (limited to 'server-beta/src')
93 files changed, 0 insertions, 13397 deletions
diff --git a/server-beta/src/await.lua b/server-beta/src/await.lua deleted file mode 100644 index 5a960e96..00000000 --- a/server-beta/src/await.lua +++ /dev/null @@ -1,100 +0,0 @@ -local timer = require 'timer' - ----@class await -local m = {} -m.type = 'await' - -m.coTracker = setmetatable({}, { __mode = 'k' }) -m.delayQueue = {} -m.delayQueueIndex = 1 - ---- 设置错误处理器 ----@param errHandle function {comment = '当有错误发生时,会以错误堆栈为参数调用该函数'} -function m.setErrorHandle(errHandle) - m.errorHandle = errHandle -end - -function m.checkResult(co, ...) - local suc, err = ... - if not suc and m.errorHandle then - m.errorHandle(debug.traceback(co, err)) - end - return ... -end - ---- 创建一个任务 -function m.create(callback, ...) - local co = coroutine.create(callback) - m.coTracker[co] = true - return m.checkResult(co, coroutine.resume(co, ...)) -end - ---- 休眠一段时间 ----@param time number -function m.sleep(time, getVersion) - if not coroutine.isyieldable() then - if m.errorHandle then - m.errorHandle(debug.traceback('Cannot yield')) - end - return - end - local version = getVersion and getVersion() - local co = coroutine.running() - timer.wait(time, function () - if version == (getVersion and getVersion()) then - return m.checkResult(co, coroutine.resume(co)) - else - coroutine.close(co) - end - end) - return coroutine.yield(getVersion) -end - ---- 等待直到唤醒 ----@param callback function -function m.wait(callback, ...) - if not coroutine.isyieldable() then - return - end - local co = coroutine.running() - callback(function (...) - return m.checkResult(co, coroutine.resume(co, ...)) - end) - return coroutine.yield(...) -end - ---- 延迟 -function m.delay(getVersion) - if not coroutine.isyieldable() then - return - end - local co = coroutine.running() - local version = getVersion and getVersion() - m.delayQueue[#m.delayQueue+1] = function () - if version == (getVersion and getVersion()) then - return m.checkResult(co, coroutine.resume(co)) - else - coroutine.close(co) - end - end - return coroutine.yield() -end - ---- 步进 -function m.step() - local waker = m.delayQueue[m.delayQueueIndex] - if waker then - m.delayQueue[m.delayQueueIndex] = false - m.delayQueueIndex = m.delayQueueIndex + 1 - waker() - return true - else - for i = 1, #m.delayQueue do - m.delayQueue[i] = nil - end - m.delayQueueIndex = 1 - return false - end -end - -return m diff --git a/server-beta/src/brave/brave.lua b/server-beta/src/brave/brave.lua deleted file mode 100644 index 08909074..00000000 --- a/server-beta/src/brave/brave.lua +++ /dev/null @@ -1,70 +0,0 @@ -local thread = require 'bee.thread' - ----@class pub_brave -local m = {} -m.type = 'brave' -m.ability = {} -m.queue = {} - ---- 注册成为勇者 -function m.register(id) - m.taskpad = thread.channel('taskpad' .. id) - m.waiter = thread.channel('waiter' .. id) - m.id = id - - if #m.queue > 0 then - for _, info in ipairs(m.queue) do - m.waiter:push(info.name, info.params) - end - end - m.queue = nil - - m.start() -end - ---- 注册能力 -function m.on(name, callback) - m.ability[name] = callback -end - ---- 报告 -function m.push(name, params) - if m.waiter then - m.waiter:push(name, params) - else - m.queue[#m.queue+1] = { - name = name, - params = params, - } - end -end - ---- 开始找工作 -function m.start() - m.push('mem', collectgarbage 'count') - while true do - local suc, name, id, params = m.taskpad:pop() - if not suc then - -- 找不到工作的勇者,只好睡觉 - thread.sleep(0.001) - goto CONTINUE - end - local ability = m.ability[name] - -- TODO - if not ability then - m.waiter:push(id) - log.error('Brave can not handle this work: ' .. name) - goto CONTINUE - end - local ok, res = xpcall(ability, log.error, params) - if ok then - m.waiter:push(id, res) - else - m.waiter:push(id) - end - m.push('mem', collectgarbage 'count') - ::CONTINUE:: - end -end - -return m diff --git a/server-beta/src/brave/init.lua b/server-beta/src/brave/init.lua deleted file mode 100644 index 24c2e412..00000000 --- a/server-beta/src/brave/init.lua +++ /dev/null @@ -1,4 +0,0 @@ -local brave = require 'brave.brave' -require 'brave.work' - -return brave diff --git a/server-beta/src/brave/log.lua b/server-beta/src/brave/log.lua deleted file mode 100644 index cd27cd55..00000000 --- a/server-beta/src/brave/log.lua +++ /dev/null @@ -1,52 +0,0 @@ -local brave = require 'brave' - -local tablePack = table.pack -local tostring = tostring -local tableConcat = table.concat -local debugTraceBack = debug.traceback -local debugGetInfo = debug.getinfo - -_ENV = nil - -local function pushLog(level, ...) - local t = tablePack(...) - for i = 1, t.n do - t[i] = tostring(t[i]) - end - local str = tableConcat(t, '\t', 1, t.n) - if level == 'error' then - str = str .. '\n' .. debugTraceBack(nil, 3) - end - local info = debugGetInfo(3, 'Sl') - brave.push('log', { - level = level, - msg = str, - src = info.source, - line = info.currentline, - }) - return str -end - -local m = {} - -function m.info(...) - pushLog('info', ...) -end - -function m.debug(...) - pushLog('debug', ...) -end - -function m.trace(...) - pushLog('trace', ...) -end - -function m.warn(...) - pushLog('warn', ...) -end - -function m.error(...) - pushLog('error', ...) -end - -return m diff --git a/server-beta/src/brave/work.lua b/server-beta/src/brave/work.lua deleted file mode 100644 index dba27808..00000000 --- a/server-beta/src/brave/work.lua +++ /dev/null @@ -1,55 +0,0 @@ -local brave = require 'brave.brave' -local jsonrpc = require 'jsonrpc' -local parser = require 'parser' -local fs = require 'bee.filesystem' -local furi = require 'file-uri' -local util = require 'utility' - -brave.on('loadProto', function () - while true do - local proto = jsonrpc.decode(io.read, log.error) - if proto then - brave.push('proto', proto) - end - end -end) - -brave.on('compile', function (text) - local state, err = parser:compile(text, 'lua', 'Lua 5.4') - if not state then - log.error(err) - return - end - local lines = parser:lines(text) - return { - root = state.root, - value = state.value, - errs = state.errs, - lines = lines, - } -end) - -brave.on('listDirectory', function (uri) - local path = fs.path(furi.decode(uri)) - local uris = {} - for child in path:list_directory() do - local childUri = furi.encode(child:string()) - uris[#uris+1] = childUri - end - return uris -end) - -brave.on('isDirectory', function (uri) - local path = fs.path(furi.decode(uri)) - return fs.is_directory(path) -end) - -brave.on('loadFile', function (uri) - local filename = furi.decode(uri) - return util.loadFile(filename) -end) - -brave.on('saveFile', function (params) - local filename = furi.decode(params.uri) - return util.saveFile(filename, params.text) -end) diff --git a/server-beta/src/config.lua b/server-beta/src/config.lua deleted file mode 100644 index 758402b0..00000000 --- a/server-beta/src/config.lua +++ /dev/null @@ -1,193 +0,0 @@ -local util = require 'utility' -local DiagnosticDefaultSeverity = require 'define.DiagnosticDefaultSeverity' - -local m = {} -m.version = 0 - -local function Boolean(v) - if type(v) == 'boolean' then - return true, v - end - return false -end - -local function Integer(v) - if type(v) == 'number' then - return true, math.floor(v) - end - return false -end - -local function String(v) - return true, tostring(v) -end - -local function Str2Hash(sep) - return function (v) - if type(v) == 'string' then - local t = {} - for s in v:gmatch('[^'..sep..']+') do - t[s] = true - end - return true, t - end - if type(v) == 'table' then - local t = {} - for _, s in ipairs(v) do - if type(s) == 'string' then - t[s] = true - end - end - return true, t - end - return false - end -end - -local function Array(checker) - return function (tbl) - if type(tbl) ~= 'table' then - return false - end - local t = {} - for _, v in ipairs(tbl) do - local ok, result = checker(v) - if ok then - t[#t+1] = result - end - end - return true, t - end -end - -local function Hash(keyChecker, valueChecker) - return function (tbl) - if type(tbl) ~= 'table' then - return false - end - local t = {} - for k, v in pairs(tbl) do - local ok1, key = keyChecker(k) - local ok2, value = valueChecker(v) - if ok1 and ok2 then - t[key] = value - end - end - if not next(t) then - return false - end - return true, t - end -end - -local function Or(...) - local checkers = {...} - return function (obj) - for _, checker in ipairs(checkers) do - local suc, res = checker(obj) - if suc then - return true, res - end - end - return false - end -end - -local ConfigTemplate = { - runtime = { - version = {'Lua 5.3', String}, - library = {{}, Str2Hash ';'}, - path = {{ - "?.lua", - "?/init.lua", - "?/?.lua" - }, Array(String)}, - }, - diagnostics = { - enable = {true, Boolean}, - globals = {{}, Str2Hash ';'}, - disable = {{}, Str2Hash ';'}, - severity = { - util.deepCopy(DiagnosticDefaultSeverity), - Hash(String, String), - }, - }, - workspace = { - ignoreDir = {{}, Str2Hash ';'}, - ignoreSubmodules= {true, Boolean}, - useGitIgnore = {true, Boolean}, - maxPreload = {300, Integer}, - preloadFileSize = {100, Integer}, - library = {{}, Hash( - String, - Or(Boolean, Array(String)) - )} - }, - completion = { - enable = {true, Boolean}, - callSnippet = {'Both', String}, - keywordSnippet = {'Both', String}, - }, - plugin = { - enable = {false, Boolean}, - path = {'.vscode/lua-plugin/*.lua', String}, - }, -} - -local OtherTemplate = { - associations = {{}, Hash(String, String)}, - exclude = {{}, Hash(String, Boolean)}, -} - -local function init() - if m.config then - return - end - - m.config = {} - for c, t in pairs(ConfigTemplate) do - m.config[c] = {} - for k, info in pairs(t) do - m.config[c][k] = info[1] - end - end - - m.other = {} - for k, info in pairs(OtherTemplate) do - m.other[k] = info[1] - end -end - -function m.setConfig(config, other) - m.version = m.version + 1 - xpcall(function () - for c, t in pairs(config) do - for k, v in pairs(t) do - local region = ConfigTemplate[c] - if region then - local info = region[k] - local suc, v = info[2](v) - if suc then - m.config[c][k] = v - else - m.config[c][k] = info[1] - end - end - end - end - for k, v in pairs(other) do - local info = OtherTemplate[k] - local suc, v = info[2](v) - if suc then - m.other[k] = v - else - m.other[k] = info[1] - end - end - log.debug('Config update: ', util.dump(m.config), util.dump(m.other)) - end, log.error) -end - -init() - -return m diff --git a/server-beta/src/core/definition.lua b/server-beta/src/core/definition.lua deleted file mode 100644 index 865fc7cb..00000000 --- a/server-beta/src/core/definition.lua +++ /dev/null @@ -1,105 +0,0 @@ -local guide = require 'parser.guide' -local workspace = require 'workspace' -local files = require 'files' -local vm = require 'vm' - -local function findDef(source, callback) - if source.type ~= 'local' - and source.type ~= 'getlocal' - and source.type ~= 'setlocal' - and source.type ~= 'setglobal' - and source.type ~= 'getglobal' - and source.type ~= 'field' - and source.type ~= 'method' - and source.type ~= 'string' - and source.type ~= 'number' - and source.type ~= 'boolean' - and source.type ~= 'goto' then - return - end - vm.eachDef(source, function (info) - if info.mode == 'declare' - or info.mode == 'set' - or info.mode == 'return' then - local src = info.source - local root = guide.getRoot(src) - local uri = root.uri - if src.type == 'setfield' - or src.type == 'getfield' - or src.type == 'tablefield' then - callback(src.field, uri) - elseif src.type == 'setindex' - or src.type == 'getindex' - or src.type == 'tableindex' then - callback(src.index, uri) - elseif src.type == 'getmethod' - or src.type == 'setmethod' then - callback(src.method, uri) - else - callback(src, uri) - end - end - end) -end - -local function checkRequire(source, offset, callback) - if source.type ~= 'call' then - return - end - local func = source.node - local pathSource = source.args and source.args[1] - if not pathSource then - return - end - if not guide.isContain(pathSource, offset) then - return - end - local literal = guide.getLiteral(pathSource) - if type(literal) ~= 'string' then - return - end - local name = func.special - if name == 'require' then - local result = workspace.findUrisByRequirePath(literal, true) - for _, uri in ipairs(result) do - callback(uri) - end - elseif name == 'dofile' - or name == 'loadfile' then - local result = workspace.findUrisByFilePath(literal, true) - for _, uri in ipairs(result) do - callback(uri) - end - end -end - -return function (uri, offset) - local ast = files.getAst(uri) - if not ast then - return nil - end - local results = {} - guide.eachSourceContain(ast.ast, offset, function (source) - checkRequire(source, offset, function (uri) - results[#results+1] = { - uri = files.getOriginUri(uri), - source = source, - target = { - start = 0, - finish = 0, - } - } - end) - findDef(source, function (target, uri) - results[#results+1] = { - target = target, - uri = files.getOriginUri(uri), - source = source, - } - end) - end) - if #results == 0 then - return nil - end - return results -end diff --git a/server-beta/src/core/diagnostics/ambiguity-1.lua b/server-beta/src/core/diagnostics/ambiguity-1.lua deleted file mode 100644 index 37815fb5..00000000 --- a/server-beta/src/core/diagnostics/ambiguity-1.lua +++ /dev/null @@ -1,69 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local lang = require 'language' - -local opMap = { - ['+'] = true, - ['-'] = true, - ['*'] = true, - ['/'] = true, - ['//'] = true, - ['^'] = true, - ['<<'] = true, - ['>>'] = true, - ['&'] = true, - ['|'] = true, - ['~'] = true, - ['..'] = true, -} - -local literalMap = { - ['number'] = true, - ['boolean'] = true, - ['string'] = true, - ['table'] = true, -} - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - local text = files.getText(uri) - guide.eachSourceType(ast.ast, 'binary', function (source) - if source.op.type ~= 'or' then - return - end - local first = source[1] - local second = source[2] - -- a + (b or 0) --> (a + b) or 0 - do - if opMap[first.op and first.op.type] - and first.type ~= 'unary' - and not second.op - and literalMap[second.type] - and not literalMap[first[2].type] - then - callback { - start = source.start, - finish = source.finish, - message = lang.script('DIAG_AMBIGUITY_1', text:sub(first.start, first.finish)) - } - end - end - -- (a or 0) + c --> a or (0 + c) - do - if opMap[second.op and second.op.type] - and second.type ~= 'unary' - and not first.op - and literalMap[second[1].type] - then - callback { - start = source.start, - finish = source.finish, - message = lang.script('DIAG_AMBIGUITY_1', text:sub(second.start, second.finish)) - } - end - end - end) -end diff --git a/server-beta/src/core/diagnostics/duplicate-index.lua b/server-beta/src/core/diagnostics/duplicate-index.lua deleted file mode 100644 index 76b1c958..00000000 --- a/server-beta/src/core/diagnostics/duplicate-index.lua +++ /dev/null @@ -1,62 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local lang = require 'language' -local define = require 'proto.define' - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - - guide.eachSourceType(ast.ast, 'table', function (source) - local mark = {} - for _, obj in ipairs(source) do - if obj.type == 'tablefield' - or obj.type == 'tableindex' then - local name = guide.getKeyName(obj) - if name then - if not mark[name] then - mark[name] = {} - end - mark[name][#mark[name]+1] = obj.field or obj.index - end - end - end - - for name, defs in pairs(mark) do - local sname = name:match '^.|(.+)$' - if #defs > 1 and sname then - local related = {} - for i = 1, #defs do - local def = defs[i] - related[i] = { - start = def.start, - finish = def.finish, - uri = uri, - } - end - for i = 1, #defs - 1 do - local def = defs[i] - callback { - start = def.start, - finish = def.finish, - related = related, - message = lang.script('DIAG_DUPLICATE_INDEX', sname), - level = define.DiagnosticSeverity.Hint, - tags = { define.DiagnosticTag.Unnecessary }, - } - end - for i = #defs, #defs do - local def = defs[i] - callback { - start = def.start, - finish = def.finish, - related = related, - message = lang.script('DIAG_DUPLICATE_INDEX', sname), - } - end - end - end - end) -end diff --git a/server-beta/src/core/diagnostics/emmy-lua.lua b/server-beta/src/core/diagnostics/emmy-lua.lua deleted file mode 100644 index b3d19c21..00000000 --- a/server-beta/src/core/diagnostics/emmy-lua.lua +++ /dev/null @@ -1,3 +0,0 @@ -return function () - -end diff --git a/server-beta/src/core/diagnostics/empty-block.lua b/server-beta/src/core/diagnostics/empty-block.lua deleted file mode 100644 index 2024f4e3..00000000 --- a/server-beta/src/core/diagnostics/empty-block.lua +++ /dev/null @@ -1,49 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local lang = require 'language' -local define = require 'proto.define' - --- 检查空代码块 --- 但是排除忙等待(repeat/while) -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - - guide.eachSourceType(ast.ast, 'if', function (source) - for _, block in ipairs(source) do - if #block > 0 then - return - end - end - callback { - start = source.start, - finish = source.finish, - tags = { define.DiagnosticTag.Unnecessary }, - message = lang.script.DIAG_EMPTY_BLOCK, - } - end) - guide.eachSourceType(ast.ast, 'loop', function (source) - if #source > 0 then - return - end - callback { - start = source.start, - finish = source.finish, - tags = { define.DiagnosticTag.Unnecessary }, - message = lang.script.DIAG_EMPTY_BLOCK, - } - end) - guide.eachSourceType(ast.ast, 'in', function (source) - if #source > 0 then - return - end - callback { - start = source.start, - finish = source.finish, - tags = { define.DiagnosticTag.Unnecessary }, - message = lang.script.DIAG_EMPTY_BLOCK, - } - end) -end diff --git a/server-beta/src/core/diagnostics/global-in-nil-env.lua b/server-beta/src/core/diagnostics/global-in-nil-env.lua deleted file mode 100644 index 9a0d4f35..00000000 --- a/server-beta/src/core/diagnostics/global-in-nil-env.lua +++ /dev/null @@ -1,66 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local lang = require 'language' - --- TODO: 检查路径是否可达 -local function mayRun(path) - return true -end - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - local root = guide.getRoot(ast.ast) - local env = guide.getENV(root) - - local nilDefs = {} - if not env.ref then - return - end - for _, ref in ipairs(env.ref) do - if ref.type == 'setlocal' then - if ref.value and ref.value.type == 'nil' then - nilDefs[#nilDefs+1] = ref - end - end - end - - if #nilDefs == 0 then - return - end - - local function check(source) - local node = source.node - if node.tag == '_ENV' then - local ok - for _, nilDef in ipairs(nilDefs) do - local mode, pathA = guide.getPath(nilDef, source) - if mode == 'before' - and mayRun(pathA) then - ok = nilDef - break - end - end - if ok then - callback { - start = source.start, - finish = source.finish, - uri = uri, - message = lang.script.DIAG_GLOBAL_IN_NIL_ENV, - related = { - { - start = ok.start, - finish = ok.finish, - uri = uri, - } - } - } - end - end - end - - guide.eachSourceType(ast.ast, 'getglobal', check) - guide.eachSourceType(ast.ast, 'setglobal', check) -end diff --git a/server-beta/src/core/diagnostics/init.lua b/server-beta/src/core/diagnostics/init.lua deleted file mode 100644 index 0d523f26..00000000 --- a/server-beta/src/core/diagnostics/init.lua +++ /dev/null @@ -1,41 +0,0 @@ -local files = require 'files' -local define = require 'proto.define' -local config = require 'config' -local await = require 'await' - -local function check(uri, name, level, results) - if config.config.diagnostics.disable[name] then - return - end - level = config.config.diagnostics.severity[name] or level - local severity = define.DiagnosticSeverity[level] - local clock = os.clock() - require('core.diagnostics.' .. name)(uri, function (result) - result.level = severity or result.level - result.code = name - results[#results+1] = result - end, name) - local passed = os.clock() - clock - if passed >= 0.5 then - log.warn(('Diagnostics [%s] @ [%s] takes [%.3f] sec!'):format(name, uri, passed)) - await.delay() - end -end - -return function (uri) - local ast = files.getAst(uri) - if not ast then - return nil - end - local results = {} - - for name, level in pairs(define.DiagnosticDefaultSeverity) do - check(uri, name, level, results) - end - - if #results == 0 then - return nil - end - - return results -end diff --git a/server-beta/src/core/diagnostics/lowercase-global.lua b/server-beta/src/core/diagnostics/lowercase-global.lua deleted file mode 100644 index bc48e1e6..00000000 --- a/server-beta/src/core/diagnostics/lowercase-global.lua +++ /dev/null @@ -1,39 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local lang = require 'language' -local config = require 'config' -local library = require 'library' - --- 不允许定义首字母小写的全局变量(很可能是拼错或者漏删) -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - - local definedGlobal = {} - for name in pairs(config.config.diagnostics.globals) do - definedGlobal[name] = true - end - for name in pairs(library.global) do - definedGlobal[name] = true - end - - guide.eachSourceType(ast.ast, 'setglobal', function (source) - local name = guide.getName(source) - if definedGlobal[name] then - return - end - local first = name:match '%w' - if not first then - return - end - if first:match '%l' then - callback { - start = source.start, - finish = source.finish, - message = lang.script.DIAG_LOWERCASE_GLOBAL, - } - end - end) -end diff --git a/server-beta/src/core/diagnostics/newfield-call.lua b/server-beta/src/core/diagnostics/newfield-call.lua deleted file mode 100644 index 75681cbc..00000000 --- a/server-beta/src/core/diagnostics/newfield-call.lua +++ /dev/null @@ -1,37 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local lang = require 'language' - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - - local lines = files.getLines(uri) - local text = files.getText(uri) - - guide.eachSourceType(ast.ast, 'table', function (source) - for i = 1, #source do - local field = source[i] - if field.type == 'call' then - local func = field.node - local args = field.args - if args then - local funcLine = guide.positionOf(lines, func.finish) - local argsLine = guide.positionOf(lines, args.start) - if argsLine > funcLine then - callback { - start = field.start, - finish = field.finish, - message = lang.script('DIAG_PREFIELD_CALL' - , text:sub(func.start, func.finish) - , text:sub(args.start, args.finish) - ) - } - end - end - end - end - end) -end diff --git a/server-beta/src/core/diagnostics/newline-call.lua b/server-beta/src/core/diagnostics/newline-call.lua deleted file mode 100644 index cb318380..00000000 --- a/server-beta/src/core/diagnostics/newline-call.lua +++ /dev/null @@ -1,38 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local lang = require 'language' - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - local lines = files.getLines(uri) - - guide.eachSourceType(ast.ast, 'call', function (source) - local node = source.node - local args = source.args - if not args then - return - end - - -- 必须有其他人在继续使用当前对象 - if not source.next then - return - end - - local nodeRow = guide.positionOf(lines, node.finish) - local argRow = guide.positionOf(lines, args.start) - if nodeRow == argRow then - return - end - - if #args == 1 then - callback { - start = args.start, - finish = args.finish, - message = lang.script.DIAG_PREVIOUS_CALL, - } - end - end) -end diff --git a/server-beta/src/core/diagnostics/redefined-local.lua b/server-beta/src/core/diagnostics/redefined-local.lua deleted file mode 100644 index f6176794..00000000 --- a/server-beta/src/core/diagnostics/redefined-local.lua +++ /dev/null @@ -1,32 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local lang = require 'language' - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - guide.eachSourceType(ast.ast, 'local', function (source) - local name = source[1] - if name == '_' - or name == '_ENV' then - return - end - local exist = guide.getLocal(source, name, source.start-1) - if exist then - callback { - start = source.start, - finish = source.finish, - message = lang.script('DIAG_REDEFINED_LOCAL', name), - related = { - { - start = exist.start, - finish = exist.finish, - uri = uri, - } - }, - } - end - end) -end diff --git a/server-beta/src/core/diagnostics/redundant-parameter.lua b/server-beta/src/core/diagnostics/redundant-parameter.lua deleted file mode 100644 index ec14188e..00000000 --- a/server-beta/src/core/diagnostics/redundant-parameter.lua +++ /dev/null @@ -1,102 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local vm = require 'vm' -local lang = require 'language' -local define = require 'proto.define' -local await = require 'await' - -local function countLibraryArgs(source) - local func = vm.getLibrary(source) - if not func then - return nil - end - local result = 0 - if not func.args then - return result - end - if func.args[#func.args].type == '...' then - return math.maxinteger - end - result = result + #func.args - return result -end - -local function countCallArgs(source) - local result = 0 - if not source.args then - return 0 - end - if source.node and source.node.type == 'getmethod' then - result = result + 1 - end - result = result + #source.args - return result -end - -local function countFuncArgs(source) - local result = 0 - if not source.args then - return result - end - if source.args[#source.args].type == '...' then - return math.maxinteger - end - if source.parent and source.parent.type == 'setmethod' then - result = result + 1 - end - result = result + #source.args - return result -end - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - - guide.eachSourceType(ast.ast, 'call', function (source) - local callArgs = countCallArgs(source) - if callArgs == 0 then - return - end - - await.delay(function () - return files.globalVersion - end) - - local func = source.node - local funcArgs - vm.eachDef(func, function (info) - if info.mode == 'value' then - local src = info.source - if src.type == 'function' then - local args = countFuncArgs(src) - if not funcArgs or args > funcArgs then - funcArgs = args - end - end - end - end) - - funcArgs = funcArgs or countLibraryArgs(func) - if not funcArgs then - return - end - - local delta = callArgs - funcArgs - if delta <= 0 then - return - end - for i = #source.args - delta + 1, #source.args do - local arg = source.args[i] - if arg then - callback { - start = arg.start, - finish = arg.finish, - tags = { define.DiagnosticTag.Unnecessary }, - message = lang.script('DIAG_OVER_MAX_ARGS', funcArgs, callArgs) - } - end - end - end) -end diff --git a/server-beta/src/core/diagnostics/redundant-value.lua b/server-beta/src/core/diagnostics/redundant-value.lua deleted file mode 100644 index be483448..00000000 --- a/server-beta/src/core/diagnostics/redundant-value.lua +++ /dev/null @@ -1,24 +0,0 @@ -local files = require 'files' -local define = require 'proto.define' -local lang = require 'language' - -return function (uri, callback, code) - local ast = files.getAst(uri) - if not ast then - return - end - - local diags = ast.diags[code] - if not diags then - return - end - - for _, info in ipairs(diags) do - callback { - start = info.start, - finish = info.finish, - tags = { define.DiagnosticTag.Unnecessary }, - message = lang.script('DIAG_OVER_MAX_VALUES', info.max, info.passed) - } - end -end diff --git a/server-beta/src/core/diagnostics/trailing-space.lua b/server-beta/src/core/diagnostics/trailing-space.lua deleted file mode 100644 index e54a6e60..00000000 --- a/server-beta/src/core/diagnostics/trailing-space.lua +++ /dev/null @@ -1,55 +0,0 @@ -local files = require 'files' -local lang = require 'language' -local guide = require 'parser.guide' - -local function isInString(ast, offset) - local result = false - guide.eachSourceType(ast, 'string', function (source) - if offset >= source.start and offset <= source.finish then - result = true - end - end) - return result -end - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - local text = files.getText(uri) - local lines = files.getLines(uri) - for i = 1, #lines do - local start = lines[i].start - local range = lines[i].range - local lastChar = text:sub(range, range) - if lastChar ~= ' ' and lastChar ~= '\t' then - goto NEXT_LINE - end - if isInString(ast.ast, range) then - goto NEXT_LINE - end - local first = start - for n = range - 1, start, -1 do - local char = text:sub(n, n) - if char ~= ' ' and char ~= '\t' then - first = n + 1 - break - end - end - if first == start then - callback { - start = first, - finish = range, - message = lang.script.DIAG_LINE_ONLY_SPACE, - } - else - callback { - start = first, - finish = range, - message = lang.script.DIAG_LINE_POST_SPACE, - } - end - ::NEXT_LINE:: - end -end diff --git a/server-beta/src/core/diagnostics/undefined-env-child.lua b/server-beta/src/core/diagnostics/undefined-env-child.lua deleted file mode 100644 index df096cb8..00000000 --- a/server-beta/src/core/diagnostics/undefined-env-child.lua +++ /dev/null @@ -1,32 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local vm = require 'vm' -local lang = require 'language' - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - -- 再遍历一次 getglobal ,找出 _ENV 被重载的情况 - guide.eachSourceType(ast.ast, 'getglobal', function (source) - -- 单独验证自己是否在重载过的 _ENV 中有定义 - if source.node.tag == '_ENV' then - return - end - local setInENV = vm.eachRef(source, function (info) - if info.mode == 'set' then - return true - end - end) - if setInENV then - return - end - local key = source[1] - callback { - start = source.start, - finish = source.finish, - message = lang.script('DIAG_UNDEF_ENV_CHILD', key), - } - end) -end diff --git a/server-beta/src/core/diagnostics/undefined-global.lua b/server-beta/src/core/diagnostics/undefined-global.lua deleted file mode 100644 index ed81ced3..00000000 --- a/server-beta/src/core/diagnostics/undefined-global.lua +++ /dev/null @@ -1,63 +0,0 @@ -local files = require 'files' -local vm = require 'vm' -local lang = require 'language' -local library = require 'library' -local config = require 'config' - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - - local globalCache = {} - - -- 遍历全局变量,检查所有没有 mode['set'] 的全局变量 - local globals = vm.getGlobals(ast.ast) - for key, infos in pairs(globals) do - if infos.mode['set'] == true then - goto CONTINUE - end - if globalCache[key] then - goto CONTINUE - end - local skey = key and key:match '^s|(.+)$' - if not skey then - goto CONTINUE - end - if library.global[skey] then - goto CONTINUE - end - if config.config.diagnostics.globals[skey] then - goto CONTINUE - end - if globalCache[key] == nil then - local uris = files.findGlobals(key) - for i = 1, #uris do - local destAst = files.getAst(uris[i]) - local destGlobals = vm.getGlobals(destAst.ast) - if destGlobals[key] and destGlobals[key].mode['set'] then - globalCache[key] = true - goto CONTINUE - end - end - end - globalCache[key] = false - local message = lang.script('DIAG_UNDEF_GLOBAL', skey) - local otherVersion = library.other[skey] - local customVersion = library.custom[skey] - if otherVersion then - message = ('%s(%s)'):format(message, lang.script('DIAG_DEFINED_VERSION', table.concat(otherVersion, '/'), config.config.runtime.version)) - elseif customVersion then - message = ('%s(%s)'):format(message, lang.script('DIAG_DEFINED_CUSTOM', table.concat(customVersion, '/'))) - end - for _, info in ipairs(infos) do - callback { - start = info.source.start, - finish = info.source.finish, - message = message, - } - end - ::CONTINUE:: - end -end diff --git a/server-beta/src/core/diagnostics/unused-function.lua b/server-beta/src/core/diagnostics/unused-function.lua deleted file mode 100644 index 6c53cdf7..00000000 --- a/server-beta/src/core/diagnostics/unused-function.lua +++ /dev/null @@ -1,45 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local vm = require 'vm' -local define = require 'proto.define' -local lang = require 'language' -local await = require 'await' - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - -- 只检查局部函数与全局函数 - guide.eachSourceType(ast.ast, 'function', function (source) - local parent = source.parent - if not parent then - return - end - if parent.type ~= 'local' - and parent.type ~= 'setlocal' - and parent.type ~= 'setglobal' then - return - end - local hasSet - local hasGet = vm.eachRef(source, function (info) - if info.mode == 'get' then - return true - elseif info.mode == 'set' - or info.mode == 'declare' then - hasSet = true - end - end) - if not hasGet and hasSet then - callback { - start = source.start, - finish = source.finish, - tags = { define.DiagnosticTag.Unnecessary }, - message = lang.script.DIAG_UNUSED_FUNCTION, - } - end - await.delay(function () - return files.globalVersion - end) - end) -end diff --git a/server-beta/src/core/diagnostics/unused-label.lua b/server-beta/src/core/diagnostics/unused-label.lua deleted file mode 100644 index e6d998ba..00000000 --- a/server-beta/src/core/diagnostics/unused-label.lua +++ /dev/null @@ -1,22 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local define = require 'proto.define' -local lang = require 'language' - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - - guide.eachSourceType(ast.ast, 'label', function (source) - if not source.ref then - callback { - start = source.start, - finish = source.finish, - tags = { define.DiagnosticTag.Unnecessary }, - message = lang.script('DIAG_UNUSED_LABEL', source[1]), - } - end - end) -end diff --git a/server-beta/src/core/diagnostics/unused-local.lua b/server-beta/src/core/diagnostics/unused-local.lua deleted file mode 100644 index 22b2e16b..00000000 --- a/server-beta/src/core/diagnostics/unused-local.lua +++ /dev/null @@ -1,46 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local define = require 'proto.define' -local lang = require 'language' - -local function hasGet(loc) - if not loc.ref then - return false - end - for _, ref in ipairs(loc.ref) do - if ref.type == 'getlocal' then - if not ref.next then - return true - end - local nextType = ref.next.type - if nextType ~= 'setmethod' - and nextType ~= 'setfield' - and nextType ~= 'setindex' then - return true - end - end - end - return false -end - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - guide.eachSourceType(ast.ast, 'local', function (source) - local name = source[1] - if name == '_' - or name == '_ENV' then - return - end - if not hasGet(source) then - callback { - start = source.start, - finish = source.finish, - tags = { define.DiagnosticTag.Unnecessary }, - message = lang.script('DIAG_UNUSED_LOCAL', name), - } - end - end) -end diff --git a/server-beta/src/core/diagnostics/unused-vararg.lua b/server-beta/src/core/diagnostics/unused-vararg.lua deleted file mode 100644 index 74cc08e7..00000000 --- a/server-beta/src/core/diagnostics/unused-vararg.lua +++ /dev/null @@ -1,31 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local define = require 'proto.define' -local lang = require 'language' - -return function (uri, callback) - local ast = files.getAst(uri) - if not ast then - return - end - - guide.eachSourceType(ast.ast, 'function', function (source) - local args = source.args - if not args then - return - end - - for _, arg in ipairs(args) do - if arg.type == '...' then - if not arg.ref then - callback { - start = arg.start, - finish = arg.finish, - tags = { define.DiagnosticTag.Unnecessary }, - message = lang.script.DIAG_UNUSED_VARARG, - } - end - end - end - end) -end diff --git a/server-beta/src/core/highlight.lua b/server-beta/src/core/highlight.lua deleted file mode 100644 index 61e3f91a..00000000 --- a/server-beta/src/core/highlight.lua +++ /dev/null @@ -1,230 +0,0 @@ -local guide = require 'parser.guide' -local files = require 'files' -local vm = require 'vm' -local define = require 'proto.define' - -local function ofLocal(source, callback) - callback(source) - if source.ref then - for _, ref in ipairs(source.ref) do - callback(ref) - end - end -end - -local function ofField(source, uri, callback) - local parent = source.parent - if not parent then - return - end - local myKey = guide.getKeyName(source) - if parent.type == 'tableindex' - or parent.type == 'tablefield' then - local tbl = parent.parent - vm.eachField(tbl, function (info) - if info.key ~= myKey then - return - end - local destUri = guide.getRoot(info.source).uri - if destUri ~= uri then - return - end - callback(info.source) - end) - else - vm.eachField(parent.node, function (info) - if info.key ~= myKey then - return - end - local destUri = guide.getRoot(info.source).uri - if destUri ~= uri then - return - end - callback(info.source) - end) - end -end - -local function ofIndex(source, uri, callback) - local parent = source.parent - if not parent then - return - end - if parent.type == 'setindex' - or parent.type == 'getindex' - or parent.type == 'tableindex' then - ofField(source, uri, callback) - end -end - -local function ofLabel(source, callback) - vm.eachRef(source, function (info) - callback(info.source) - end) -end - -local function find(source, uri, callback) - if source.type == 'local' then - ofLocal(source, callback) - elseif source.type == 'getlocal' - or source.type == 'setlocal' then - ofLocal(source.node, callback) - elseif source.type == 'field' - or source.type == 'method' then - ofField(source, uri, callback) - elseif source.type == 'string' - or source.type == 'boolean' - or source.type == 'number' then - ofIndex(source, uri, callback) - callback(source) - elseif source.type == 'nil' then - callback(source) - elseif source.type == 'goto' - or source.type == 'label' then - ofLabel(source, callback) - end -end - -local function checkInIf(source, text, offset) - -- 检查 end - local endA = source.finish - #'end' + 1 - local endB = source.finish - if offset >= endA - and offset <= endB - and text:sub(endA, endB) == 'end' then - return true - end - -- 检查每个子模块 - for _, block in ipairs(source) do - for i = 1, #block.keyword, 2 do - local start = block.keyword[i] - local finish = block.keyword[i+1] - if offset >= start and offset <= finish then - return true - end - end - end - return false -end - -local function makeIf(source, text, callback) - -- end - local endA = source.finish - #'end' + 1 - local endB = source.finish - if text:sub(endA, endB) == 'end' then - callback(endA, endB) - end - -- 每个子模块 - for _, block in ipairs(source) do - for i = 1, #block.keyword, 2 do - local start = block.keyword[i] - local finish = block.keyword[i+1] - callback(start, finish) - end - end - return false -end - -local function findKeyword(source, text, offset, callback) - if source.type == 'do' - or source.type == 'function' - or source.type == 'loop' - or source.type == 'in' - or source.type == 'while' - or source.type == 'repeat' then - local ok - for i = 1, #source.keyword, 2 do - local start = source.keyword[i] - local finish = source.keyword[i+1] - if offset >= start and offset <= finish then - ok = true - break - end - end - if ok then - for i = 1, #source.keyword, 2 do - local start = source.keyword[i] - local finish = source.keyword[i+1] - callback(start, finish) - end - end - elseif source.type == 'if' then - local ok = checkInIf(source, text, offset) - if ok then - makeIf(source, text, callback) - end - end -end - -return function (uri, offset) - local ast = files.getAst(uri) - if not ast then - return nil - end - local text = files.getText(uri) - local results = {} - local mark = {} - guide.eachSourceContain(ast.ast, offset, function (source) - find(source, uri, function (target) - local kind - if target.type == 'getfield' then - target = target.field - kind = define.DocumentHighlightKind.Read - elseif target.type == 'setfield' - or target.type == 'tablefield' then - target = target.field - kind = define.DocumentHighlightKind.Write - elseif target.type == 'getmethod' then - target = target.method - kind = define.DocumentHighlightKind.Read - elseif target.type == 'setmethod' then - target = target.method - kind = define.DocumentHighlightKind.Write - elseif target.type == 'getindex' then - target = target.index - kind = define.DocumentHighlightKind.Read - elseif target.type == 'setindex' - or target.type == 'tableindex' then - target = target.index - kind = define.DocumentHighlightKind.Write - elseif target.type == 'getlocal' - or target.type == 'getglobal' - or target.type == 'goto' then - kind = define.DocumentHighlightKind.Read - elseif target.type == 'setlocal' - or target.type == 'local' - or target.type == 'setglobal' - or target.type == 'label' then - kind = define.DocumentHighlightKind.Write - elseif target.type == 'string' - or target.type == 'boolean' - or target.type == 'number' - or target.type == 'nil' then - kind = define.DocumentHighlightKind.Text - else - log.warn('Unknow target.type:', target.type) - return - end - if mark[target] then - return - end - mark[target] = true - results[#results+1] = { - start = target.start, - finish = target.finish, - kind = kind, - } - end) - findKeyword(source, text, offset, function (start, finish) - results[#results+1] = { - start = start, - finish = finish, - kind = define.DocumentHighlightKind.Write - } - end) - end) - if #results == 0 then - return nil - end - return results -end diff --git a/server-beta/src/core/hover/arg.lua b/server-beta/src/core/hover/arg.lua deleted file mode 100644 index be344488..00000000 --- a/server-beta/src/core/hover/arg.lua +++ /dev/null @@ -1,20 +0,0 @@ -local guide = require 'parser.guide' -local vm = require 'vm' - -local function asFunction(source) - if not source.args then - return '' - end - local args = {} - for i = 1, #source.args do - local arg = source.args[i] - args[i] = ('%s: %s'):format(guide.getName(arg), vm.getType(arg)) - end - return table.concat(args, ', ') -end - -return function (source) - if source.type == 'function' then - return asFunction(source) - end -end diff --git a/server-beta/src/core/hover/init.lua b/server-beta/src/core/hover/init.lua deleted file mode 100644 index b99c14b2..00000000 --- a/server-beta/src/core/hover/init.lua +++ /dev/null @@ -1,56 +0,0 @@ -local files = require 'files' -local guide = require 'parser.guide' -local vm = require 'vm' -local getLabel = require 'core.hover.label' - -local function getHoverAsFunction(source) - local values = vm.getValue(source) - local labels = {} - for _, value in ipairs(values) do - if value.type == 'function' then - labels[#labels+1] = getLabel(value.source) - end - end - - local label = table.concat(labels, '\n') - return { - label = label, - source = source, - } -end - -local function getHoverAsValue(source) - local label = getLabel(source) - return { - label = label, - source = source, - } -end - -local function getHover(source) - local isFunction = vm.hasType(source, 'function') - if isFunction then - return getHoverAsFunction(source) - else - return getHoverAsValue(source) - end -end - -return function (uri, offset) - local ast = files.getAst(uri) - if not ast then - return nil - end - local hover = guide.eachSourceContain(ast.ast, offset, function (source) - if source.type == 'local' - or source.type == 'setlocal' - or source.type == 'getlocal' - or source.type == 'setglobal' - or source.type == 'getglobal' - or source.type == 'field' - or source.type == 'method' then - return getHover(source) - end - end) - return hover -end diff --git a/server-beta/src/core/hover/label.lua b/server-beta/src/core/hover/label.lua deleted file mode 100644 index 72ce60f4..00000000 --- a/server-beta/src/core/hover/label.lua +++ /dev/null @@ -1,103 +0,0 @@ -local buildName = require 'core.hover.name' -local buildArg = require 'core.hover.arg' -local buildReturn = require 'core.hover.return' -local buildTable = require 'core.hover.table' -local vm = require 'vm' -local util = require 'utility' - -local function asFunction(source) - local name = buildName(source) - local arg = buildArg(source) - local rtn = buildReturn(source) - local lines = {} - lines[1] = ('function %s(%s)'):format(name, arg) - lines[2] = rtn - return table.concat(lines, '\n') -end - -local function asLocal(source) - local name = buildName(source) - local type = vm.getType(source) - local literal = vm.getLiteral(source) - if type == 'table' then - type = buildTable(source) - end - if literal == nil then - return ('local %s: %s'):format(name, type) - else - return ('local %s: %s = %s'):format(name, type, util.viewLiteral(literal)) - end -end - -local function asGlobal(source) - local name = buildName(source) - local type = vm.getType(source) - local literal = vm.getLiteral(source) - if type == 'table' then - type = buildTable(source) - end - if literal == nil then - return ('global %s: %s'):format(name, type) - else - return ('global %s: %s = %s'):format(name, type, util.viewLiteral(literal)) - end -end - -local function isGlobalField(source) - if source.type == 'field' - or source.type == 'method' then - source = source.parent - end - if source.type == 'setfield' - or source.type == 'getfield' - or source.type == 'setmethod' - or source.type == 'getmethod' - or source.type == 'tablefield' then - local node = source.node - if node.type == 'setglobal' - or node.type == 'getglobal' then - return true - end - return isGlobalField(node) - else - return false - end -end - -local function asField(source) - if isGlobalField(source) then - return asGlobal(source) - end - local name = buildName(source) - local type = vm.getType(source) - local literal = vm.getLiteral(source) - if type == 'table' then - type = buildTable(source) - end - if literal == nil then - return ('field %s: %s'):format(name, type) - else - return ('field %s: %s = %s'):format(name, type, util.viewLiteral(literal)) - end -end - -return function (source) - if source.type == 'function' then - return asFunction(source) - elseif source.type == 'local' - or source.type == 'getlocal' - or source.type == 'setlocal' then - return asLocal(source) - elseif source.type == 'setglobal' - or source.type == 'getglobal' then - return asGlobal(source) - elseif source.type == 'getfield' - or source.type == 'setfield' - or source.type == 'getmethod' - or source.type == 'setmethod' - or source.type == 'tablefield' - or source.type == 'field' - or source.type == 'method' then - return asField(source) - end -end diff --git a/server-beta/src/core/hover/name.lua b/server-beta/src/core/hover/name.lua deleted file mode 100644 index a22a8b5a..00000000 --- a/server-beta/src/core/hover/name.lua +++ /dev/null @@ -1,64 +0,0 @@ -local guide = require 'parser.guide' -local vm = require 'vm' - -local function asLocal(source) - return guide.getName(source) -end - -local function asMethod(source) - local class = vm.eachField(source.node, function (info) - if info.key == 's|type' or info.key == 's|__name' or info.key == 's|name' then - if info.value and info.value.type == 'string' then - return info.value[1] - end - end - end) - local node = class or guide.getName(source.node) or '?' - local method = guide.getName(source) - return ('%s:%s'):format(node, method) -end - -local function asField(source) - local class = vm.eachField(source.node, function (info) - if info.key == 's|type' or info.key == 's|__name' or info.key == 's|name' then - if info.value and info.value.type == 'string' then - return info.value[1] - end - end - end) - local node = class or guide.getName(source.node) or '?' - local method = guide.getName(source) - return ('%s.%s'):format(node, method) -end - -local function asGlobal(source) - return guide.getName(source) -end - -local function buildName(source) - if source.type == 'local' - or source.type == 'getlocal' - or source.type == 'setlocal' then - return asLocal(source) or '' - end - if source.type == 'setglobal' - or source.type == 'getglobal' then - return asGlobal(source) or '' - end - if source.type == 'setmethod' - or source.type == 'getmethod' then - return asMethod(source) or '' - end - if source.type == 'setfield' - or source.tyoe == 'getfield' - or source.type == 'tablefield' then - return asField(source) or '' - end - local parent = source.parent - if parent then - return buildName(parent) - end - return '' -end - -return buildName diff --git a/server-beta/src/core/hover/return.lua b/server-beta/src/core/hover/return.lua deleted file mode 100644 index c22626a6..00000000 --- a/server-beta/src/core/hover/return.lua +++ /dev/null @@ -1,34 +0,0 @@ -local guide = require 'parser.guide' -local vm = require 'vm' - -local function asFunction(source) - if not source.returns then - return nil - end - local returns = {} - for _, rtn in ipairs(source.returns) do - for i = 1, #rtn do - local values = vm.getValue(rtn[i]) - returns[#returns+1] = values - end - break - end - if #returns == 0 then - return nil - end - local lines = {} - for i = 1, #returns do - if i == 1 then - lines[i] = (' -> %s'):format(vm.viewType(returns[i])) - else - lines[i] = ('% 3d. %s'):format(i, returns[i]) - end - end - return table.concat(lines, '\n') -end - -return function (source) - if source.type == 'function' then - return asFunction(source) - end -end diff --git a/server-beta/src/core/hover/table.lua b/server-beta/src/core/hover/table.lua deleted file mode 100644 index 9ed86692..00000000 --- a/server-beta/src/core/hover/table.lua +++ /dev/null @@ -1,35 +0,0 @@ -local vm = require 'vm' - -local function checkClass(source) -end - -return function (source) - local fields = {} - local class - vm.eachField(source, function (info) - if info.key == 's|type' or info.key == 's|__name' or info.key == 's|name' then - if info.value and info.value.type == 'string' then - class = info.value[1] - end - end - local type = vm.getType(info.source) - fields[#fields+1] = ('%s'):format(type) - end) - local fieldsBuf - if #fields == 0 then - fieldsBuf = '{}' - else - local lines = {} - lines[#lines+1] = '{' - for _, field in ipairs(fields) do - lines[#lines+1] = ' ' .. field - end - lines[#lines+1] = '}' - fieldsBuf = table.concat(lines, '\n') - end - if class then - return ('%s %s'):format(class, fieldsBuf) - else - return fieldsBuf - end -end diff --git a/server-beta/src/core/reference.lua b/server-beta/src/core/reference.lua deleted file mode 100644 index 7e265e97..00000000 --- a/server-beta/src/core/reference.lua +++ /dev/null @@ -1,84 +0,0 @@ -local guide = require 'parser.guide' -local files = require 'files' -local vm = require 'vm' - -local function isFunction(source, offset) - if source.type ~= 'function' then - return false - end - -- 必须点在 `function` 这个单词上才能查找函数引用 - return offset >= source.start and offset < source.start + #'function' -end - -local function findRef(source, offset, callback) - if source.type ~= 'local' - and source.type ~= 'getlocal' - and source.type ~= 'setlocal' - and source.type ~= 'setglobal' - and source.type ~= 'getglobal' - and source.type ~= 'field' - and source.type ~= 'tablefield' - and source.type ~= 'method' - and source.type ~= 'string' - and source.type ~= 'number' - and source.type ~= 'boolean' - and source.type ~= 'goto' - and source.type ~= 'label' - and not isFunction(source, offset) then - return - end - vm.eachRef(source, function (info) - if info.mode == 'declare' - or info.mode == 'set' - or info.mode == 'get' - or info.mode == 'return' then - local src = info.source - local root = guide.getRoot(src) - local uri = root.uri - if src.type == 'setfield' - or src.type == 'getfield' - or src.type == 'tablefield' then - callback(src.field, uri) - elseif src.type == 'setindex' - or src.type == 'getindex' - or src.type == 'tableindex' then - callback(src.index, uri) - elseif src.type == 'getmethod' - or src.type == 'setmethod' then - callback(src.method, uri) - else - callback(src, uri) - end - end - if info.mode == 'value' then - local src = info.source - local root = guide.getRoot(src) - local uri = root.uri - if src.type == 'function' then - if src.parent.type == 'return' then - callback(src, uri) - end - end - end - end) -end - -return function (uri, offset) - local ast = files.getAst(uri) - if not ast then - return nil - end - local results = {} - guide.eachSourceContain(ast.ast, offset, function (source) - findRef(source, offset, function (target, uri) - results[#results+1] = { - target = target, - uri = files.getOriginUri(uri), - } - end) - end) - if #results == 0 then - return nil - end - return results -end diff --git a/server-beta/src/core/rename.lua b/server-beta/src/core/rename.lua deleted file mode 100644 index 3e4512da..00000000 --- a/server-beta/src/core/rename.lua +++ /dev/null @@ -1,374 +0,0 @@ -local files = require 'files' -local vm = require 'vm' -local guide = require 'parser.guide' -local proto = require 'proto' -local define = require 'proto.define' -local util = require 'utility' - -local Forcing - -local function askForcing(str) - if TEST then - return true - end - if Forcing == false then - return false - end - local version = files.globalVersion - -- TODO - local item = proto.awaitRequest('window/showMessageRequest', { - type = define.MessageType.Warning, - message = ('[%s]不是有效的标识符,是否强制替换?'):format(str), - actions = { - { - title = '强制替换', - }, - { - title = '取消', - }, - } - }) - if version ~= files.globalVersion then - Forcing = false - proto.notify('window/showMessage', { - type = define.MessageType.Warning, - message = '文件发生了变化,替换取消。' - }) - return false - end - if not item then - Forcing = false - return false - end - if item.title == '强制替换' then - Forcing = true - return true - else - Forcing = false - return false - end -end - -local function askForMultiChange(results, newname) - if TEST then - return true - end - local uris = {} - for _, result in ipairs(results) do - local uri = result.uri - if not uris[uri] then - uris[uri] = 0 - uris[#uris+1] = uri - end - uris[uri] = uris[uri] + 1 - end - if #uris <= 1 then - return true - end - - local version = files.globalVersion - -- TODO - local item = proto.awaitRequest('window/showMessageRequest', { - type = define.MessageType.Warning, - message = ('将修改 %d 个文件,共 %d 处。'):format( - #uris, - #results - ), - actions = { - { - title = '继续', - }, - { - title = '放弃', - }, - } - }) - if version ~= files.globalVersion then - proto.notify('window/showMessage', { - type = define.MessageType.Warning, - message = '文件发生了变化,替换取消。' - }) - return false - end - if item and item.title == '继续' then - local fileList = {} - for _, uri in ipairs(uris) do - fileList[#fileList+1] = ('%s (%d)'):format(uri, uris[uri]) - end - - log.debug(('Renamed [%s]\r\n%s'):format(newname, table.concat(fileList, '\r\n'))) - return true - end - return false -end - -local function trim(str) - return str:match '^%s*(%S+)%s*$' -end - -local function isValidName(str) - return str:match '^[%a_][%w_]*$' -end - -local function isValidGlobal(str) - for s in str:gmatch '[^%.]*' do - if not isValidName(trim(s)) then - return false - end - end - return true -end - -local function isValidFunctionName(str) - if isValidGlobal(str) then - return true - end - local pos = str:find(':', 1, true) - if not pos then - return false - end - return isValidGlobal(trim(str:sub(1, pos-1))) - and isValidName(trim(str:sub(pos+1))) -end - -local function isFunctionGlobalName(source) - local parent = source.parent - if parent.type ~= 'setglobal' then - return false - end - local value = parent.value - if not value.type ~= 'function' then - return false - end - return value.start <= parent.start -end - -local function renameLocal(source, newname, callback) - if isValidName(newname) then - callback(source, source.start, source.finish, newname) - return - end - if askForcing(newname) then - callback(source, source.start, source.finish, newname) - end -end - -local function renameField(source, newname, callback) - if isValidName(newname) then - callback(source, source.start, source.finish, newname) - return true - end - local parent = source.parent - if parent.type == 'setfield' - or parent.type == 'getfield' then - local dot = parent.dot - local newstr = '[' .. util.viewString(newname) .. ']' - callback(source, dot.start, source.finish, newstr) - elseif parent.type == 'tablefield' then - local newstr = '[' .. util.viewString(newname) .. ']' - callback(source, source.start, source.finish, newstr) - elseif parent.type == 'getmethod' then - if not askForcing(newname) then - return false - end - callback(source, source.start, source.finish, newname) - elseif parent.type == 'setmethod' then - local uri = guide.getRoot(source).uri - local text = files.getText(uri) - local func = parent.value - -- function mt:name () end --> mt['newname'] = function (self) end - local newstr = string.format('%s[%s] = function ' - , text:sub(parent.start, parent.node.finish) - , util.viewString(newname) - ) - callback(source, func.start, parent.finish, newstr) - local pl = text:find('(', parent.finish, true) - if pl then - if func.args then - callback(source, pl + 1, pl, 'self, ') - else - callback(source, pl + 1, pl, 'self') - end - end - end - return true -end - -local function renameGlobal(source, newname, callback) - if isValidGlobal(newname) then - callback(source, source.start, source.finish, newname) - return true - end - if isValidFunctionName(newname) then - if not isFunctionGlobalName(source) then - askForcing(newname) - end - callback(source, source.start, source.finish, newname) - return true - end - local newstr = '_ENV[' .. util.viewString(newname) .. ']' - -- function name () end --> _ENV['newname'] = function () end - if source.value and source.value.type == 'function' - and source.value.start < source.start then - callback(source, source.value.start, source.finish, newstr .. ' = function ') - return true - end - callback(source, source.start, source.finish, newstr) - return true -end - -local function ofLocal(source, newname, callback) - renameLocal(source, newname, callback) - if source.ref then - for _, ref in ipairs(source.ref) do - renameLocal(ref, newname, callback) - end - end -end - -local function ofField(source, newname, callback) - return vm.eachRef(source, function (info) - local src = info.source - if src.type == 'tablefield' - or src.type == 'getfield' - or src.type == 'setfield' then - src = src.field - elseif src.type == 'tableindex' - or src.type == 'getindex' - or src.type == 'setindex' then - src = src.index - elseif src.type == 'getmethod' - or src.type == 'setmethod' then - src = src.method - end - if src.type == 'string' then - local quo = src[2] - local text = util.viewString(newname, quo) - callback(src, src.start, src.finish, text) - return - elseif src.type == 'field' - or src.type == 'method' then - local suc = renameField(src, newname, callback) - if not suc then - return false - end - elseif src.type == 'setglobal' - or src.type == 'getglobal' then - local suc = renameGlobal(src, newname, callback) - if not suc then - return false - end - end - end) -end - -local function rename(source, newname, callback) - if source.type == 'label' - or source.type == 'goto' then - if not isValidName(newname) and not askForcing(newname)then - return false - end - vm.eachRef(source, function (info) - callback(info.source, info.source.start, info.source.finish, newname) - end) - elseif source.type == 'local' then - return ofLocal(source, newname, callback) - elseif source.type == 'setlocal' - or source.type == 'getlocal' then - return ofLocal(source.node, newname, callback) - elseif source.type == 'field' - or source.type == 'method' - or source.type == 'tablefield' - or source.type == 'string' - or source.type == 'setglobal' - or source.type == 'getglobal' then - return ofField(source, newname, callback) - end - return true -end - -local function prepareRename(source) - if source.type == 'label' - or source.type == 'goto' - or source.type == 'local' - or source.type == 'setlocal' - or source.type == 'getlocal' - or source.type == 'field' - or source.type == 'method' - or source.type == 'tablefield' - or source.type == 'setglobal' - or source.type == 'getglobal' then - return source, source[1] - elseif source.type == 'string' then - local parent = source.parent - if not parent then - return nil - end - if parent.type == 'setindex' - or parent.type == 'getindex' - or parent.type == 'tableindex' then - return source, source[1] - end - return nil - end - return nil -end - -local m = {} - -function m.rename(uri, pos, newname) - local ast = files.getAst(uri) - if not ast then - return nil - end - local results = {} - - guide.eachSourceContain(ast.ast, pos, function(source) - rename(source, newname, function (target, start, finish, text) - results[#results+1] = { - start = start, - finish = finish, - text = text, - uri = guide.getRoot(target).uri, - } - end) - end) - - if Forcing == false then - Forcing = nil - return nil - end - - if #results == 0 then - return nil - end - - if not askForMultiChange(results, newname) then - return nil - end - - return results -end - -function m.prepareRename(uri, pos) - local ast = files.getAst(uri) - if not ast then - return nil - end - - local result - guide.eachSourceContain(ast.ast, pos, function(source) - local res, text = prepareRename(source) - if res then - result = { - start = source.start, - finish = source.finish, - text = text, - } - end - end) - - return result -end - -return m diff --git a/server-beta/src/define/DiagnosticDefaultSeverity.lua b/server-beta/src/define/DiagnosticDefaultSeverity.lua deleted file mode 100644 index cc26cab2..00000000 --- a/server-beta/src/define/DiagnosticDefaultSeverity.lua +++ /dev/null @@ -1,21 +0,0 @@ -return { - ['unused-local'] = 'Hint', - ['unused-function'] = 'Hint', - ['undefined-global'] = 'Warning', - ['global-in-nil-env'] = 'Warning', - ['unused-label'] = 'Hint', - ['unused-vararg'] = 'Hint', - ['trailing-space'] = 'Hint', - ['redefined-local'] = 'Hint', - ['newline-call'] = 'Information', - ['redundant-parameter'] = 'Hint', - ['ambiguity-1'] = 'Warning', - ['lowercase-global'] = 'Information', - ['undefined-env-child'] = 'Information', - ['duplicate-index'] = 'Warning', - ['duplicate-method'] = 'Warning', - ['empty-block'] = 'Hint', - ['redundant-value'] = 'Hint', - ['emmy-lua'] = 'Warning', - ['set-const'] = 'Error', -} diff --git a/server-beta/src/define/DiagnosticSeverity.lua b/server-beta/src/define/DiagnosticSeverity.lua deleted file mode 100644 index 05bd3659..00000000 --- a/server-beta/src/define/DiagnosticSeverity.lua +++ /dev/null @@ -1,6 +0,0 @@ -return { - Error = 1, - Warning = 2, - Information = 3, - Hint = 4, -} diff --git a/server-beta/src/define/ErrorCodes.lua b/server-beta/src/define/ErrorCodes.lua deleted file mode 100644 index befb5630..00000000 --- a/server-beta/src/define/ErrorCodes.lua +++ /dev/null @@ -1,16 +0,0 @@ - -return { - -- Defined by JSON RPC - ParseError = -32700, - InvalidRequest = -32600, - MethodNotFound = -32601, - InvalidParams = -32602, - InternalError = -32603, - serverErrorStart = -32099, - serverErrorEnd = -32000, - ServerNotInitialized = -32002, - UnknownErrorCode = -32001, - - -- Defined by the protocol. - RequestCancelled = -32800, -} diff --git a/server-beta/src/doctor.lua b/server-beta/src/doctor.lua deleted file mode 100644 index 08ec69cf..00000000 --- a/server-beta/src/doctor.lua +++ /dev/null @@ -1,380 +0,0 @@ -local type = type -local next = next -local ipairs = ipairs -local rawget = rawget -local pcall = pcall -local getregistry = debug.getregistry -local getmetatable = debug.getmetatable -local getupvalue = debug.getupvalue -local getuservalue = debug.getuservalue -local getlocal = debug.getlocal -local getinfo = debug.getinfo -local maxinterger = math.maxinteger -local mathType = math.type -local tableConcat = table.concat -local _G = _G -local registry = getregistry() -local tableSort = table.sort - -_ENV = nil - -local m = {} - -local function getTostring(obj) - local mt = getmetatable(obj) - if not mt then - return nil - end - local toString = rawget(mt, '__tostring') - if not toString then - return nil - end - local suc, str = pcall(toString, obj) - if not suc then - return nil - end - if type(str) ~= 'string' then - return nil - end - return str -end - -local function formatName(obj) - local tp = type(obj) - if tp == 'nil' then - return 'nil:nil' - elseif tp == 'boolean' then - if obj == true then - return 'boolean:true' - else - return 'boolean:false' - end - elseif tp == 'number' then - if mathType(obj) == 'integer' then - return ('number:%d'):format(obj) - else - -- 如果浮点数可以完全表示为整数,那么就转换为整数 - local str = ('%.10f'):format(obj):gsub('%.?[0]+$', '') - if str:find('.', 1, true) then - -- 如果浮点数不能表示为整数,那么再加上它的精确表示法 - str = ('%s(%q)'):format(str, obj) - end - return 'number:' .. str - end - elseif tp == 'string' then - local str = ('%q'):format(obj) - if #str > 100 then - local new = ('%s...(len=%d)'):format(str:sub(1, 100), #str) - if #new < #str then - str = new - end - end - return 'string:' .. str - elseif tp == 'function' then - local info = getinfo(obj, 'S') - if info.what == 'c' then - return ('function:%p(C)'):format(obj) - elseif info.what == 'main' then - return ('function:%p(main)'):format(obj) - else - return ('function:%p(%s:%d-%d)'):format(obj, info.source, info.linedefined, info.lastlinedefined) - end - elseif tp == 'table' then - local id = getTostring(obj) - if not id then - if obj == _G then - id = '_G' - elseif obj == registry then - id = 'registry' - end - end - if id then - return ('table:%p(%s)'):format(obj, id) - else - return ('table:%p'):format(obj) - end - elseif tp == 'userdata' then - local id = getTostring(obj) - if id then - return ('userdata:%p(%s)'):format(obj, id) - else - return ('userdata:%p'):format(obj) - end - else - return ('%s:%p'):format(tp, obj) - end -end - ---- 内存快照 ----@return table -function m.snapshot() - local mark = {} - local find - - local function findTable(t, result) - result = result or {} - local mt = getmetatable(t) - local wk, wv - if mt then - local mode = rawget(mt, '__mode') - if type(mode) == 'string' then - if mode:find('k', 1, true) then - wk = true - end - if mode:find('v', 1, true) then - wv = true - end - end - end - for k, v in next, t do - if not wk then - local keyInfo = find(k) - if keyInfo then - result[#result+1] = { - type = 'key', - name = formatName(k), - info = keyInfo, - } - end - end - if not wv then - local valueInfo = find(v) - if valueInfo then - result[#result+1] = { - type = 'field', - name = formatName(k) .. '|' .. formatName(v), - info = valueInfo, - } - end - end - end - local MTInfo = find(getmetatable(t)) - if MTInfo then - result[#result+1] = { - type = 'metatable', - name = '', - info = MTInfo, - } - end - if #result == 0 then - return nil - end - return result - end - - local function findFunction(f, result, trd, stack) - result = result or {} - for i = 1, maxinterger do - local n, v = getupvalue(f, i) - if not n then - break - end - local valueInfo = find(v) - if valueInfo then - result[#result+1] = { - type = 'upvalue', - name = n, - info = valueInfo, - } - end - end - if trd then - for i = 1, maxinterger do - local n, l = getlocal(trd, stack, i) - if not n then - break - end - local valueInfo = find(l) - if valueInfo then - result[#result+1] = { - type = 'local', - name = n, - info = valueInfo, - } - end - end - end - if #result == 0 then - return nil - end - return result - end - - local function findUserData(u, result) - result = result or {} - for i = 1, maxinterger do - local v, b = getuservalue(u, i) - if not b then - break - end - local valueInfo = find(v) - if valueInfo then - result[#result+1] = { - type = 'uservalue', - name = formatName(i), - info = valueInfo, - } - end - end - local MTInfo = find(getmetatable(u)) - if MTInfo then - result[#result+1] = { - type = 'metatable', - name = '', - info = MTInfo, - } - end - if #result == 0 then - return nil - end - return result - end - - local function findThread(trd, result) - -- 不查找主线程,主线程一定是临时的(视为弱引用) - if trd == registry[1] then - return nil - end - result = result or {} - - for i = 1, maxinterger do - local info = getinfo(trd, i, 'Sf') - if not info then - break - end - local funcInfo = find(info.func, trd, i) - if funcInfo then - result[#result+1] = { - type = 'stack', - name = i .. '@' .. formatName(info.func), - info = funcInfo, - } - end - end - - if #result == 0 then - return nil - end - return result - end - - function find(obj, trd, stack) - if mark[obj] then - return mark[obj] - end - local tp = type(obj) - if tp == 'table' then - mark[obj] = {} - mark[obj] = findTable(obj, mark[obj]) - elseif tp == 'function' then - mark[obj] = {} - mark[obj] = findFunction(obj, mark[obj], trd, stack) - elseif tp == 'userdata' then - mark[obj] = {} - mark[obj] = findUserData(obj, mark[obj]) - elseif tp == 'thread' then - mark[obj] = {} - mark[obj] = findThread(obj, mark[obj]) - else - return nil - end - if mark[obj] then - mark[obj].object = obj - end - return mark[obj] - end - - return { - name = formatName(registry), - type = 'root', - info = find(registry), - } -end - ---- 寻找对象的引用 ----@return string -function m.catch(...) - local targets = {} - for _, target in ipairs {...} do - targets[target] = true - end - local report = m.snapshot() - local path = {} - local result = {} - local mark = {} - - local function push() - result[#result+1] = tableConcat(path, ' => ') - end - - local function search(t) - path[#path+1] = ('(%s)%s'):format(t.type, t.name) - local addTarget - if targets[t.info.object] then - targets[t.info.object] = nil - addTarget = t.info.object - push(t) - end - if not mark[t.info] then - mark[t.info] = true - for _, obj in ipairs(t.info) do - search(obj) - end - end - path[#path] = nil - if addTarget then - targets[addTarget] = true - end - end - - search(report) - - return result -end - ---- 生成一个报告 ----@return string -function m.report() - local snapshot = m.snapshot() - local cache = {} - local mark = {} - - local function scan(t) - local obj = t.info.object - local tp = type(obj) - if tp == 'table' - or tp == 'userdata' - or tp == 'function' - or tp == 'string' - or tp == 'thread' then - local point = ('%p'):format(obj) - if not cache[point] then - cache[point] = { - point = point, - count = 0, - name = formatName(obj), - } - end - cache[point].count = cache[point].count + 1 - end - if not mark[t.info] then - mark[t.info] = true - for _, child in ipairs(t.info) do - scan(child) - end - end - end - - scan(snapshot) - - local list = {} - for _, info in next, cache do - list[#list+1] = info - end - tableSort(list, function (a, b) - return a.name < b.name - end) - return list -end - -return m diff --git a/server-beta/src/file-uri.lua b/server-beta/src/file-uri.lua deleted file mode 100644 index 8acd4f64..00000000 --- a/server-beta/src/file-uri.lua +++ /dev/null @@ -1,108 +0,0 @@ -local platform = require 'bee.platform' - -local esc = { - [':'] = '%3A', - ['/'] = '%2F', - ['?'] = '%3F', - ['#'] = '%23', - ['['] = '%5B', - [']'] = '%5D', - ['@'] = '%40', - - ['!'] = '%21', -- sub-delims - ['$'] = '%24', - ['&'] = '%26', - ["'"] = '%27', - ['('] = '%28', - [')'] = '%29', - ['*'] = '%2A', - ['+'] = '%2B', - [','] = '%2C', - [';'] = '%3B', - ['='] = '%3D', - - [' '] = '%20', -} - -local escPatt = '[^%w%-%.%_%~%/]' - -local function normalize(str) - return str:gsub('%%(%x%x)', function (n) - return string.char(tonumber(n, 16)) - end) -end - -local m = {} - --- c:\my\files --> file:///c%3A/my/files --- /usr/home --> file:///usr/home --- \\server\share\some\path --> file://server/share/some/path - ---- path -> uri ----@param path string ----@return string uri -function m.encode(path) - local authority = '' - if platform.OS == 'Windows' then - path = path:gsub('\\', '/') - end - - if path:sub(1, 2) == '//' then - local idx = path:find('/', 3) - if idx then - authority = path:sub(3, idx) - path = path:sub(idx + 1) - if path == '' then - path = '/' - end - else - authority = path:sub(3) - path = '/' - end - end - - if path:sub(1, 1) ~= '/' then - path = '/' .. path - end - - -- lower-case windows drive letters in /C:/fff or C:/fff - if path:match '/%u:' then - path = path:lower() - end - - local uri = 'file://' - .. authority:gsub(escPatt, esc) - .. path:gsub(escPatt, esc) - return uri -end - --- file:///c%3A/my/files --> c:\my\files --- file:///usr/home --> /usr/home --- file://server/share/some/path --> \\server\share\some\path - ---- uri -> path ----@param uri string ----@return string path -function m.decode(uri) - local scheme, authority, path = uri:match('([^:]*):?/?/?([^/]*)(.*)') - if not scheme then - return '' - end - scheme = normalize(scheme) - authority = normalize(authority) - path = normalize(path) - local value - if scheme == 'file' and #authority > 0 and #path > 1 then - value = '//' .. authority .. path - elseif path:match '/%a:' then - value = path:sub(2, 2):lower() .. path:sub(3) - else - value = path - end - if platform.OS == 'Windows' then - value = value:gsub('/', '\\') - end - return value -end - -return m diff --git a/server-beta/src/files.lua b/server-beta/src/files.lua deleted file mode 100644 index ac27117c..00000000 --- a/server-beta/src/files.lua +++ /dev/null @@ -1,290 +0,0 @@ -local platform = require 'bee.platform' -local config = require 'config' -local glob = require 'glob' -local furi = require 'file-uri' -local parser = require 'parser' -local vm = require 'vm.vm' -local guide = require 'parser.guide' - -local m = {} - -m.openMap = {} -m.fileMap = {} -m.assocVersion = -1 -m.assocMatcher = nil -m.globalVersion = 0 - ---- 打开文件 ----@param uri string -function m.open(uri) - if platform.OS == 'Windows' then - uri = uri:lower() - end - m.openMap[uri] = true -end - ---- 关闭文件 ----@param uri string -function m.close(uri) - if platform.OS == 'Windows' then - uri = uri:lower() - end - m.openMap[uri] = nil -end - ---- 是否打开 ----@param uri string ----@return boolean -function m.isOpen(uri) - if platform.OS == 'Windows' then - uri = uri:lower() - end - return m.openMap[uri] == true -end - ---- 是否存在 ----@return boolean -function m.exists(uri) - if platform.OS == 'Windows' then - uri = uri:lower() - end - return m.fileMap[uri] ~= nil -end - ---- 设置文件文本 ----@param uri string ----@param text string -function m.setText(uri, text) - local originUri = uri - if platform.OS == 'Windows' then - uri = uri:lower() - end - if not m.fileMap[uri] then - m.fileMap[uri] = { - uri = originUri, - } - end - local file = m.fileMap[uri] - if file.text == text then - return - end - file.text = text - file.vm = nil - file.lines = nil - file.ast = nil - file.globals = nil - file.links = nil - m.globalVersion = m.globalVersion + 1 - vm.refreshCache() - - local diagnostic = require 'provider.diagnostic' - diagnostic.refresh(originUri) -end - ---- 监听编译完成 -function m.onCompiled(uri, callback) - if platform.OS == 'Windows' then - uri = uri:lower() - end - local file = m.fileMap[uri] - if not file then - return - end - if not file.onCompiledList then - file.onCompiledList = {} - end - file.onCompiledList[#file.onCompiledList+1] = callback -end - ---- 获取文件文本 ----@param uri string ----@return string text -function m.getText(uri) - if platform.OS == 'Windows' then - uri = uri:lower() - end - local file = m.fileMap[uri] - if not file then - return nil - end - return file.text -end - ---- 移除文件 ----@param uri string -function m.remove(uri) - if platform.OS == 'Windows' then - uri = uri:lower() - end - local file = m.fileMap[uri] - if not file then - return - end - m.fileMap[uri] = nil - - m.globalVersion = m.globalVersion + 1 - vm.refreshCache() - - local diagnostic = require 'service.diagnostic' - diagnostic.refresh(file.uri) - diagnostic.clear(file.uri) -end - ---- 移除所有文件 -function m.removeAll() - for uri in pairs(m.fileMap) do - m.fileMap[uri] = nil - end - m.globalVersion = m.globalVersion + 1 - vm.refreshCache() -end - ---- 遍历文件 -function m.eachFile() - return pairs(m.fileMap) -end - ---- 获取文件语法树 ----@param uri string ----@return table ast -function m.getAst(uri) - if platform.OS == 'Windows' then - uri = uri:lower() - end - local file = m.fileMap[uri] - if file.ast == nil then - local state, err = parser:compile(file.text, 'lua', config.config.runtime.version) - if state then - state.uri = file.uri - state.ast.uri = file.uri - file.ast = state - else - log.error(err) - file.ast = false - return nil - end - end - return file.ast -end - ---- 获取文件行信息 ----@param uri string ----@return table lines -function m.getLines(uri) - if platform.OS == 'Windows' then - uri = uri:lower() - end - local file = m.fileMap[uri] - if not file then - return nil - end - if not file.lines then - file.lines = parser:lines(file.text) - end - return file.lines -end - ---- 获取原始uri -function m.getOriginUri(uri) - if platform.OS == 'Windows' then - uri = uri:lower() - end - local file = m.fileMap[uri] - if not file then - return nil - end - return file.uri -end - ---- 寻找全局变量 -function m.findGlobals(name) - local uris = {} - for uri, file in pairs(m.fileMap) do - if not file.globals then - file.globals = {} - local ast = m.getAst(uri) - if ast then - local globals = vm.getGlobals(ast.ast) - for name in pairs(globals) do - file.globals[name] = true - end - end - end - if file.globals[name] then - uris[#uris+1] = file.uri - end - end - return uris -end - ---- 寻找link自己的其他文件 -function m.findLinkTo(uri) - if platform.OS == 'Windows' then - uri = uri:lower() - end - local result = {} - for _, file in pairs(m.fileMap) do - if file.links == nil then - local ast = m.getAst(file.uri) - if ast then - file.links = vm.getLinks(ast.ast) - else - file.links = false - end - end - if file.links then - for linkUri in pairs(file.links) do - if m.eq(uri, linkUri) then - result[#result+1] = file.uri - end - end - end - end - return result -end - ---- 判断文件名相等 -function m.eq(a, b) - if platform.OS == 'Windows' then - return a:lower() == b:lower() - else - return a == b - end -end - ---- 获取文件关联 -function m.getAssoc() - if m.assocVersion == config.version then - return m.assocMatcher - end - m.assocVersion = config.version - local patt = {} - for k, v in pairs(config.other.associations) do - if m.eq(v, 'lua') then - patt[#patt+1] = k - end - end - m.assocMatcher = glob.glob(patt) - if platform.OS == 'Windows' then - m.assocMatcher:setOption 'ignoreCase' - end - return m.assocMatcher -end - ---- 判断是否是Lua文件 ----@param uri string ----@return boolean -function m.isLua(uri) - local ext = uri:match '%.([^%.%/%\\]-)$' - if not ext then - return false - end - if m.eq(ext, 'lua') then - return true - end - local matcher = m.getAssoc() - local path = furi.decode(uri) - return matcher(path) -end - -return m diff --git a/server-beta/src/fs-utility.lua b/server-beta/src/fs-utility.lua deleted file mode 100644 index 14dcb08f..00000000 --- a/server-beta/src/fs-utility.lua +++ /dev/null @@ -1,314 +0,0 @@ -local fs = require 'bee.filesystem' -local platform = require 'bee.platform' - -local type = type -local ioOpen = io.open -local pcall = pcall -local pairs = pairs -local setmetatable = setmetatable -local next = next - -_ENV = nil - -local m = {} ---- 读取文件 ----@param path string -function m.loadFile(path) - if type(path) ~= 'string' then - path = path:string() - end - local f, e = ioOpen(path, 'rb') - if not f then - return nil, e - end - if f:read(3) ~= '\xEF\xBB\xBF' then - f:seek("set") - end - local buf = f:read 'a' - f:close() - return buf -end - ---- 写入文件 ----@param path string ----@param content string -function m.saveFile(path, content) - if type(path) ~= 'string' then - path = path:string() - end - local f, e = ioOpen(path, "wb") - - if f then - f:write(content) - f:close() - return true - else - return false, e - end -end - -local function buildOptional(optional) - optional = optional or {} - optional.add = optional.add or {} - optional.del = optional.del or {} - optional.mod = optional.mod or {} - optional.err = optional.err or {} - return optional -end - -local function fsAbsolute(path, optional) - if type(path) == 'string' then - local suc, res = pcall(fs.path, path) - if not suc then - optional.err[#optional.err+1] = res - return nil - end - path = res - end - local suc, res = pcall(fs.absolute, path) - if not suc then - optional.err[#optional.err+1] = res - return nil - end - return res -end - -local function fsIsDirectory(path, optional) - local suc, res = pcall(fs.is_directory, path) - if not suc then - optional.err[#optional.err+1] = res - return false - end - return res -end - -local function fsRemove(path, optional) - local suc, res = pcall(fs.remove, path) - if not suc then - optional.err[#optional.err+1] = res - end - optional.del[#optional.del+1] = path:string() -end - -local function fsExists(path, optional) - local suc, res = pcall(fs.exists, path) - if not suc then - optional.err[#optional.err+1] = res - return false - end - return res -end - -local function fsCopy(source, target, optional) - local suc, res = pcall(fs.copy_file, source, target, true) - if not suc then - optional.err[#optional.err+1] = res - return false - end - return true -end - -local function fsCreateDirectories(path, optional) - local suc, res = pcall(fs.create_directories, path) - if not suc then - optional.err[#optional.err+1] = res - return false - end - return true -end - -local function fileRemove(path, optional) - if optional.onRemove and optional.onRemove(path) == false then - return - end - if fsIsDirectory(path, optional) then - for child in path:list_directory() do - fileRemove(child, optional) - end - end - if fsRemove(path, optional) then - optional.del[#optional.del+1] = path:string() - end -end - -local function fileCopy(source, target, optional) - local isDir1 = fsIsDirectory(source, optional) - local isDir2 = fsIsDirectory(target, optional) - local isExists = fsExists(target, optional) - if isDir1 then - if isDir2 or fsCreateDirectories(target) then - for filePath in source:list_directory() do - local name = filePath:filename() - fileCopy(filePath, target / name, optional) - end - end - else - if isExists and not isDir2 then - local buf1, err1 = m.loadFile(source) - local buf2, err2 = m.loadFile(target) - if buf1 and buf2 then - if buf1 ~= buf2 then - if fsCopy(source, target, optional) then - optional.mod[#optional.mod+1] = target:string() - end - end - else - if not buf1 then - optional.err[#optional.err+1] = err1 - end - if not buf2 then - optional.err[#optional.err+1] = err2 - end - end - else - if fsCopy(source, target, optional) then - optional.add[#optional.add+1] = target:string() - end - end - end -end - -local function fileSync(source, target, optional) - local isDir1 = fsIsDirectory(source, optional) - local isDir2 = fsIsDirectory(target, optional) - local isExists = fsExists(target, optional) - if isDir1 then - if isDir2 then - local fileList = m.fileList() - for filePath in target:list_directory() do - fileList[filePath] = true - end - for filePath in source:list_directory() do - local name = filePath:filename() - local targetPath = target / name - fileSync(filePath, targetPath, optional) - fileList[targetPath] = nil - end - for path in pairs(fileList) do - fileRemove(path, optional) - end - else - if isExists then - fileRemove(target, optional) - end - if fsCreateDirectories(target) then - for filePath in source:list_directory() do - local name = filePath:filename() - fileCopy(filePath, target / name, optional) - end - end - end - else - if isDir2 then - fileRemove(target, optional) - end - if isExists then - local buf1, err1 = m.loadFile(source) - local buf2, err2 = m.loadFile(target) - if buf1 and buf2 then - if buf1 ~= buf2 then - if fsCopy(source, target, optional) then - optional.mod[#optional.mod+1] = target:string() - end - end - else - if not buf1 then - optional.err[#optional.err+1] = err1 - end - if not buf2 then - optional.err[#optional.err+1] = err2 - end - end - else - if fsCopy(source, target, optional) then - optional.add[#optional.add+1] = target:string() - end - end - end -end - ---- 文件列表 -function m.fileList(optional) - optional = optional or buildOptional(optional) - local os = platform.OS - local keyMap = {} - local fileList = {} - local function computeKey(path) - path = fsAbsolute(path, optional) - if not path then - return nil - end - local key - if os == 'Windows' then - key = path:string():lower() - else - key = path:string() - end - return key - end - return setmetatable({}, { - __index = function (_, path) - local key = computeKey(path) - return fileList[key] - end, - __newindex = function (_, path, value) - local key = computeKey(path) - if not key then - return - end - if value == nil then - keyMap[key] = nil - else - keyMap[key] = path - fileList[key] = value - end - end, - __pairs = function () - local key, path - return function () - key, path = next(keyMap, key) - return path, fileList[key] - end - end, - }) -end - ---- 删除文件(夹) -function m.fileRemove(path, optional) - optional = buildOptional(optional) - path = fsAbsolute(path, optional) - - fileRemove(path, optional) - - return optional -end - ---- 复制文件(夹) ----@param source string ----@param target string ----@return table -function m.fileCopy(source, target, optional) - optional = buildOptional(optional) - source = fsAbsolute(source, optional) - target = fsAbsolute(target, optional) - - fileCopy(source, target, optional) - - return optional -end - ---- 同步文件(夹) ----@param source string ----@param target string ----@return table -function m.fileSync(source, target, optional) - optional = buildOptional(optional) - source = fsAbsolute(source, optional) - target = fsAbsolute(target, optional) - - fileSync(source, target, optional) - - return optional -end - -return m diff --git a/server-beta/src/glob/gitignore.lua b/server-beta/src/glob/gitignore.lua deleted file mode 100644 index f98a2f31..00000000 --- a/server-beta/src/glob/gitignore.lua +++ /dev/null @@ -1,221 +0,0 @@ -local m = require 'lpeglabel' -local matcher = require 'glob.matcher' - -local function prop(name, pat) - return m.Cg(m.Cc(true), name) * pat -end - -local function object(type, pat) - return m.Ct( - m.Cg(m.Cc(type), 'type') * - m.Cg(pat, 'value') - ) -end - -local function expect(p, err) - return p + m.T(err) -end - -local parser = m.P { - 'Main', - ['Sp'] = m.S(' \t')^0, - ['Slash'] = m.S('/\\')^1, - ['Main'] = m.Ct(m.V'Sp' * m.P'{' * m.V'Pattern' * (',' * expect(m.V'Pattern', 'Miss exp after ","'))^0 * m.P'}') - + m.Ct(m.V'Pattern') - + m.T'Main Failed' - , - ['Pattern'] = m.Ct(m.V'Sp' * prop('neg', m.P'!') * expect(m.V'Unit', 'Miss exp after "!"')) - + m.Ct(m.V'Unit') - , - ['NeedRoot'] = prop('root', (m.P'.' * m.V'Slash' + m.V'Slash')), - ['Unit'] = m.V'Sp' * m.V'NeedRoot'^-1 * expect(m.V'Exp', 'Miss exp') * m.V'Sp', - ['Exp'] = m.V'Sp' * (m.V'FSymbol' + object('/', m.V'Slash') + m.V'Word')^0 * m.V'Sp', - ['Word'] = object('word', m.Ct((m.V'CSymbol' + m.V'Char' - m.V'FSymbol')^1)), - ['CSymbol'] = object('*', m.P'*') - + object('?', m.P'?') - + object('[]', m.V'Range') - , - ['Char'] = object('char', (1 - m.S',{}[]*?/\\')^1), - ['FSymbol'] = object('**', m.P'**'), - ['Range'] = m.P'[' * m.Ct(m.V'RangeUnit'^0) * m.P']'^-1, - ['RangeUnit'] = m.Ct(- m.P']' * m.C(m.P(1)) * (m.P'-' * - m.P']' * m.C(m.P(1)))^-1), -} - -local mt = {} -mt.__index = mt -mt.__name = 'gitignore' - -function mt:addPattern(pat) - if type(pat) ~= 'string' then - return - end - self.pattern[#self.pattern+1] = pat - if self.options.ignoreCase then - pat = pat:lower() - end - local states, err = parser:match(pat) - if not states then - self.errors[#self.errors+1] = { - pattern = pat, - message = err - } - return - end - for _, state in ipairs(states) do - self.matcher[#self.matcher+1] = matcher(state) - end -end - -function mt:setOption(op, val) - if val == nil then - val = true - end - self.options[op] = val -end - ----@param key string | "'type'" | "'list'" ----@param func function | "function (path) end" -function mt:setInterface(key, func) - if type(func) ~= 'function' then - return - end - self.interface[key] = func -end - -function mt:callInterface(name, ...) - local func = self.interface[name] - return func(...) -end - -function mt:hasInterface(name) - return self.interface[name] ~= nil -end - -function mt:checkDirectory(catch, path, matcher) - if not self:hasInterface 'type' then - return true - end - if not matcher:isNeedDirectory() then - return true - end - if #catch < #path then - -- if path is 'a/b/c' and catch is 'a/b' - -- then the catch must be a directory - return true - else - return self:callInterface('type', path) == 'directory' - end -end - -function mt:simpleMatch(path) - for i = #self.matcher, 1, -1 do - local matcher = self.matcher[i] - local catch = matcher(path) - if catch and self:checkDirectory(catch, path, matcher) then - if matcher:isNegative() then - return false - else - return true - end - end - end - return nil -end - -function mt:finishMatch(path) - local paths = {} - for filename in path:gmatch '[^/\\]+' do - paths[#paths+1] = filename - end - for i = 1, #paths do - local newPath = table.concat(paths, '/', 1, i) - local passed = self:simpleMatch(newPath) - if passed == true then - return true - elseif passed == false then - return false - end - end - return false -end - -function mt:scan(callback) - local files = {} - if type(callback) ~= 'function' then - callback = nil - end - local list = {} - local result = self:callInterface('list', '') - if type(result) ~= 'table' then - return files - end - for _, path in ipairs(result) do - list[#list+1] = path:match '([^/\\]+)[/\\]*$' - end - while #list > 0 do - local current = list[#list] - if not current then - break - end - list[#list] = nil - if not self:simpleMatch(current) then - local fileType = self:callInterface('type', current) - if fileType == 'file' then - if callback then - callback(current) - end - files[#files+1] = current - elseif fileType == 'directory' then - local result = self:callInterface('list', current) - if type(result) == 'table' then - for _, path in ipairs(result) do - local filename = path:match '([^/\\]+)[/\\]*$' - if filename then - list[#list+1] = current .. '/' .. filename - end - end - end - end - end - end - return files -end - -function mt:__call(path) - if self.options.ignoreCase then - path = path:lower() - end - return self:finishMatch(path) -end - -return function (pattern, options, interface) - local self = setmetatable({ - pattern = {}, - options = {}, - matcher = {}, - errors = {}, - interface = {}, - }, mt) - - if type(pattern) == 'table' then - for _, pat in ipairs(pattern) do - self:addPattern(pat) - end - else - self:addPattern(pattern) - end - - if type(options) == 'table' then - for op, val in pairs(options) do - self:setOption(op, val) - end - end - - if type(interface) == 'table' then - for key, func in pairs(interface) do - self:setInterface(key, func) - end - end - - return self -end diff --git a/server-beta/src/glob/glob.lua b/server-beta/src/glob/glob.lua deleted file mode 100644 index aa8923f3..00000000 --- a/server-beta/src/glob/glob.lua +++ /dev/null @@ -1,122 +0,0 @@ -local m = require 'lpeglabel' -local matcher = require 'glob.matcher' - -local function prop(name, pat) - return m.Cg(m.Cc(true), name) * pat -end - -local function object(type, pat) - return m.Ct( - m.Cg(m.Cc(type), 'type') * - m.Cg(pat, 'value') - ) -end - -local function expect(p, err) - return p + m.T(err) -end - -local parser = m.P { - 'Main', - ['Sp'] = m.S(' \t')^0, - ['Slash'] = m.S('/\\')^1, - ['Main'] = m.Ct(m.V'Sp' * m.P'{' * m.V'Pattern' * (',' * expect(m.V'Pattern', 'Miss exp after ","'))^0 * m.P'}') - + m.Ct(m.V'Pattern') - + m.T'Main Failed' - , - ['Pattern'] = m.Ct(m.V'Sp' * prop('neg', m.P'!') * expect(m.V'Unit', 'Miss exp after "!"')) - + m.Ct(m.V'Unit') - , - ['NeedRoot'] = prop('root', (m.P'.' * m.V'Slash' + m.V'Slash')), - ['Unit'] = m.V'Sp' * m.V'NeedRoot'^-1 * expect(m.V'Exp', 'Miss exp') * m.V'Sp', - ['Exp'] = m.V'Sp' * (m.V'FSymbol' + object('/', m.V'Slash') + m.V'Word')^0 * m.V'Sp', - ['Word'] = object('word', m.Ct((m.V'CSymbol' + m.V'Char' - m.V'FSymbol')^1)), - ['CSymbol'] = object('*', m.P'*') - + object('?', m.P'?') - + object('[]', m.V'Range') - , - ['Char'] = object('char', (1 - m.S',{}[]*?/\\')^1), - ['FSymbol'] = object('**', m.P'**'), - ['RangeWord'] = 1 - m.P']', - ['Range'] = m.P'[' * m.Ct(m.V'RangeUnit'^0) * m.P']'^-1, - ['RangeUnit'] = m.Ct(m.C(m.V'RangeWord') * m.P'-' * m.C(m.V'RangeWord')) - + m.V'RangeWord', -} - -local mt = {} -mt.__index = mt -mt.__name = 'glob' - -function mt:addPattern(pat) - if type(pat) ~= 'string' then - return - end - self.pattern[#self.pattern+1] = pat - if self.options.ignoreCase then - pat = pat:lower() - end - local states, err = parser:match(pat) - if not states then - self.errors[#self.errors+1] = { - pattern = pat, - message = err - } - return - end - for _, state in ipairs(states) do - if state.neg then - self.refused[#self.refused+1] = matcher(state) - else - self.passed[#self.passed+1] = matcher(state) - end - end -end - -function mt:setOption(op, val) - if val == nil then - val = true - end - self.options[op] = val -end - -function mt:__call(path) - if self.options.ignoreCase then - path = path:lower() - end - for _, refused in ipairs(self.refused) do - if refused(path) then - return false - end - end - for _, passed in ipairs(self.passed) do - if passed(path) then - return true - end - end - return false -end - -return function (pattern, options) - local self = setmetatable({ - pattern = {}, - options = {}, - passed = {}, - refused = {}, - errors = {}, - }, mt) - - if type(pattern) == 'table' then - for _, pat in ipairs(pattern) do - self:addPattern(pat) - end - else - self:addPattern(pattern) - end - - if type(options) == 'table' then - for op, val in pairs(options) do - self:setOption(op, val) - end - end - return self -end diff --git a/server-beta/src/glob/init.lua b/server-beta/src/glob/init.lua deleted file mode 100644 index 6578a0d4..00000000 --- a/server-beta/src/glob/init.lua +++ /dev/null @@ -1,4 +0,0 @@ -return { - glob = require 'glob.glob', - gitignore = require 'glob.gitignore', -} diff --git a/server-beta/src/glob/matcher.lua b/server-beta/src/glob/matcher.lua deleted file mode 100644 index f4c2b12c..00000000 --- a/server-beta/src/glob/matcher.lua +++ /dev/null @@ -1,151 +0,0 @@ -local m = require 'lpeglabel' - -local Slash = m.S('/\\')^1 -local Symbol = m.S',{}[]*?/\\' -local Char = 1 - Symbol -local Path = Char^1 * Slash -local NoWord = #(m.P(-1) + Symbol) -local function whatHappened() - return m.Cmt(m.P(1)^1, function (...) - print(...) - end) -end - -local mt = {} -mt.__index = mt -mt.__name = 'matcher' - -function mt:exp(state, index) - local exp = state[index] - if not exp then - return - end - if exp.type == 'word' then - return self:word(exp, state, index + 1) - elseif exp.type == 'char' then - return self:char(exp, state, index + 1) - elseif exp.type == '**' then - return self:anyPath(exp, state, index + 1) - elseif exp.type == '*' then - return self:anyChar(exp, state, index + 1) - elseif exp.type == '?' then - return self:oneChar(exp, state, index + 1) - elseif exp.type == '[]' then - return self:range(exp, state, index + 1) - elseif exp.type == '/' then - return self:slash(exp, state, index + 1) - end -end - -function mt:word(exp, state, index) - local current = self:exp(exp.value, 1) - local after = self:exp(state, index) - if after then - return current * Slash * after - else - return current - end -end - -function mt:char(exp, state, index) - local current = m.P(exp.value) - local after = self:exp(state, index) - if after then - return current * after * NoWord - else - return current * NoWord - end -end - -function mt:anyPath(_, state, index) - local after = self:exp(state, index) - if after then - return m.P { - 'Main', - Main = after - + Path * m.V'Main' - } - else - return Path^0 - end -end - -function mt:anyChar(_, state, index) - local after = self:exp(state, index) - if after then - return m.P { - 'Main', - Main = after - + Char * m.V'Main' - } - else - return Char^0 - end -end - -function mt:oneChar(_, state, index) - local after = self:exp(state, index) - if after then - return Char * after - else - return Char - end -end - -function mt:range(exp, state, index) - local after = self:exp(state, index) - local ranges = {} - local selects = {} - for _, range in ipairs(exp.value) do - if #range == 1 then - selects[#selects+1] = range[1] - elseif #range == 2 then - ranges[#ranges+1] = range[1] .. range[2] - end - end - local current = m.S(table.concat(selects)) + m.R(table.unpack(ranges)) - if after then - return current * after - else - return current - end -end - -function mt:slash(_, state, index) - local after = self:exp(state, index) - if after then - return after - else - self.needDirectory = true - return nil - end -end - -function mt:pattern(state) - if state.root then - return m.C(self:exp(state, 1)) - else - return m.C(self:anyPath(nil, state, 1)) - end -end - -function mt:isNeedDirectory() - return self.needDirectory == true -end - -function mt:isNegative() - return self.state.neg == true -end - -function mt:__call(path) - return self.matcher:match(path) -end - -return function (state, options) - local self = setmetatable({ - options = options, - state = state, - }, mt) - self.matcher = self:pattern(state) - return self -end diff --git a/server-beta/src/json/decode.lua b/server-beta/src/json/decode.lua deleted file mode 100644 index 36f8aa54..00000000 --- a/server-beta/src/json/decode.lua +++ /dev/null @@ -1,153 +0,0 @@ -local lpeg = require 'lpeglabel' -local tablePack = table.pack -local rawset = rawset -local tointeger = math.tointeger -local tonumber = tonumber -local setmetatable = setmetatable -local stringChar = string.char -local error = error - -_ENV = nil - -local SaveSort -local P = lpeg.P -local S = lpeg.S -local R = lpeg.R -local V = lpeg.V -local C = lpeg.C -local Ct = lpeg.Ct -local Cc = lpeg.Cc -local Cp = lpeg.Cp -local Cs = lpeg.Cs - -local EscMap = { - ['t'] = '\t', - ['r'] = '\r', - ['n'] = '\n', - ['"'] = '"', - ['\\'] = '\\', -} -local BoolMap = { - ['true'] = true, - ['false'] = false, -} - -local hashmt = { - __pairs = function (self) - local i = 1 - local function next() - i = i + 1 - local k = self[i] - if k == nil then - return - end - local v = self[k] - if v == nil then - return next() - end - return k, v - end - return next - end, - __newindex = function (self, k, v) - local i = 2 - while self[i] do - i = i + 1 - end - rawset(self, i, k) - rawset(self, k, v) - end, -} - ------------------------------------------------------------------------------ --- JSON4Lua: JSON encoding / decoding support for the Lua language. --- json Module. --- Author: Craig Mason-Jones --- Homepage: http://github.com/craigmj/json4lua/ --- Version: 1.0.0 --- This module is released under the MIT License (MIT). --- Please see LICENCE.txt for details. --- -local function Utf8(str) - local n = tonumber(str, 16) - -- math.floor(x/2^y) == lazy right shift - -- a % 2^b == bitwise_and(a, (2^b)-1) - -- 64 = 2^6 - -- 4096 = 2^12 (or 2^6 * 2^6) - local x - if n < 0x80 then - x = stringChar(n % 0x80) - elseif n < 0x800 then - -- [110x xxxx] [10xx xxxx] - x = stringChar(0xC0 + ((n // 64) % 0x20), 0x80 + (n % 0x40)) - else - -- [1110 xxxx] [10xx xxxx] [10xx xxxx] - x = stringChar(0xE0 + ((n // 4096) % 0x10), 0x80 + ((n // 64) % 0x40), 0x80 + (n % 0x40)) - end - return x -end - -local function HashTable(patt) - return C(patt) / function (_, ...) - local hash = tablePack(...) - local n = hash.n - hash.n = nil - if SaveSort then - local max = n // 2 - for i = 1, max do - local key, value = hash[2*i-1], hash[2*i] - hash[key] = value - hash[i+1] = key - end - hash[1] = nil - for i = max+2, max*2 do - hash[i] = nil - end - return setmetatable(hash, hashmt) - else - local max = n // 2 - for i = 1, max do - local a = 2*i-1 - local b = 2*i - local key, value = hash[a], hash[b] - hash[key] = value - hash[a] = nil - hash[b] = nil - end - return hash - end - end -end - -local Token = P -{ - V'Value' * Cp(), - Nl = P'\r\n' + S'\r\n', - Sp = S' \t' + '//' * (1-V'Nl')^0, - Spnl = (V'Sp' + V'Nl')^0, - Bool = C(P'true' + P'false') / BoolMap, - Int = C('0' + (P'-'^-1 * R'19' * R'09'^0)) / tointeger, - Float = C(P'-'^-1 * ('0' + R'19' * R'09'^0) * '.' * R'09'^0) / tonumber, - Null = P'null' * Cc(nil), - String = '"' * Cs(V'Char'^0) * '"', - Char = V'Esc' + V'Utf8' + (1 - P'"' - P'\t' - V'Nl'), - Esc = P'\\' * C(S'tnr"\\') / EscMap, - Utf8 = P'\\u' * C(P(4)) / Utf8, - Hash = V'Spnl' * '{' * V'Spnl' * HashTable((V'Object' + P',' * V'Spnl')^0) * V'Spnl' * P'}' * V'Spnl', - Array = V'Spnl' * '[' * V'Spnl' * Ct((V'Value' * V'Spnl' + P',' * V'Spnl')^0) * V'Spnl' * P']' * V'Spnl', - Object = V'Spnl' * V'Key' * V'Spnl' * V'Value' * V'Spnl', - Key = V'String' * V'Spnl' * ':', - Value = V'Hash' + V'Array' + V'Bool' + V'Null' + V'String' + V'Float' + V'Int', -} - -return function (str, save_sort_) - SaveSort = save_sort_ - local table, res, pos = Token:match(str) - if not table then - if not pos or pos <= #str then - pos = pos or 1 - error(('没匹配完[%s][%s]\n%s'):format(pos, res, str:sub(pos, pos+100))) - end - end - return table -end diff --git a/server-beta/src/json/encode.lua b/server-beta/src/json/encode.lua deleted file mode 100644 index 492c5a58..00000000 --- a/server-beta/src/json/encode.lua +++ /dev/null @@ -1,135 +0,0 @@ -local rep = string.rep -local gsub = string.gsub -local sort = table.sort -local find = string.find -local tostring = tostring -local getmetatable = debug.getmetatable -local type = type -local next = next -local pairs = pairs -local tableConcat = table.concat - -_ENV = nil - -local index -local lines -local n = -1 -local tabs = {} - -local esc_map = { - ['\\'] = '\\\\', - ['\r'] = '\\r', - ['\n'] = '\\n', - ['\t'] = '\\t', - ['"'] = '\\"', -} - -local function encode(data, key) - n = n + 1 - if not tabs[n] then - tabs[n] = rep(' ', n) - end - local tp = type(data) - if tp == 'table' then - if not data[1] and next(data) then - -- 认为这个是哈希表 - if key then - index=index+1;lines[index] = tabs[n] .. '"' .. gsub(key, '[\\\r\n\t"]', esc_map) .. '": {\r\n' - else - index=index+1;lines[index] = tabs[n] .. '{\r\n' - end - local meta = getmetatable(data) - local sep - if meta and meta.__pairs then - for k, v in meta.__pairs(data), data do - if encode(v, k) then - index=index+1;lines[index] = ',\r\n' - sep = true - end - end - else - local list = {} - local i = 0 - for k in next, data do - i=i+1;list[i] = k - end - sort(list) - for j = 1, i do - local k = list[j] - if encode(data[k], k) then - index=index+1;lines[index] = ',\r\n' - sep = true - end - end - end - if sep then - lines[index] = '\r\n' - end - index=index+1;lines[index] = tabs[n] .. '}' - else - -- 认为这个是数组 - if key then - index=index+1;lines[index] = tabs[n] .. '"' .. gsub(key, '[\\\r\n\t"]', esc_map) .. '": [\r\n' - else - index=index+1;lines[index] = tabs[n] .. '[\r\n' - end - local sep - for k, v in pairs(data) do - if encode(v) then - index=index+1;lines[index] = ',\r\n' - sep = true - end - end - if sep then - lines[index] = '\r\n' - end - index=index+1;lines[index] = tabs[n] .. ']' - end - elseif tp == 'number' then - data = tostring(data) - -- 判断 inf -inf -nan(ind) 1.#INF -1.#INF -1.#IND - if find(data, '%a') then - data = '0' - end - if key then - index=index+1;lines[index] = tabs[n] .. '"' .. gsub(key, '[\\\r\n\t"]', esc_map) .. '": ' .. data - else - index=index+1;lines[index] = tabs[n] .. data - end - elseif tp == 'boolean' then - if key then - index=index+1;lines[index] = tabs[n] .. '"' .. gsub(key, '[\\\r\n\t"]', esc_map) .. '": ' .. tostring(data) - else - index=index+1;lines[index] = tabs[n] .. tostring(data) - end - elseif tp == 'nil' then - if key then - index=index+1;lines[index] = tabs[n] .. '"' .. gsub(key, '[\\\r\n\t"]', esc_map) .. '": null' - else - index=index+1;lines[index] = tabs[n] .. 'null' - end - elseif tp == 'string' then - local str = gsub(data, '[\\\r\n\t"]', esc_map) - if key then - index=index+1;lines[index] = tabs[n] .. '"' .. gsub(key, '[\\\r\n\t"]', esc_map) .. '": "' .. str .. '"' - else - index=index+1;lines[index] = tabs[n] .. '"' .. str .. '"' - end - else - n = n - 1 - return false - end - n = n - 1 - return true -end - -local function json(t) - lines = {} - index = 0 - - encode(t) - - return tableConcat(lines) -end - -return json diff --git a/server-beta/src/json/init.lua b/server-beta/src/json/init.lua deleted file mode 100644 index c28e7aed..00000000 --- a/server-beta/src/json/init.lua +++ /dev/null @@ -1,6 +0,0 @@ -local api = { - decode = require 'json.decode', - encode = require 'json.encode', -} - -return api diff --git a/server-beta/src/jsonrpc.lua b/server-beta/src/jsonrpc.lua deleted file mode 100644 index 5c73f54d..00000000 --- a/server-beta/src/jsonrpc.lua +++ /dev/null @@ -1,41 +0,0 @@ -local json = require 'json' -local pcall = pcall - -_ENV = nil - ----@class jsonrpc -local m = {} -m.type = 'jsonrpc' - -function m.encode(pack) - pack.jsonrpc = '2.0' - local content = json.encode(pack) - local buf = ('Content-Length: %d\r\n\r\n%s'):format(#content, content) - return buf -end - -function m.decode(reader, errHandle) - -- 读取协议头 - local line = reader 'l' - -- 不支持修改文本编码 - if line:find('Content-Type', 1, true) then - return nil - end - local len = line:match('Content%-Length%: (%d+)') - if not len then - errHandle('Error header: ' .. line) - return nil - end - local content = reader(len + 2) - if not content then - return nil - end - local suc, res = pcall(json.decode, content) - if not suc then - errHandle('Proto parse error: ' .. res) - return nil - end - return res -end - -return m diff --git a/server-beta/src/language.lua b/server-beta/src/language.lua deleted file mode 100644 index d1a4b4cf..00000000 --- a/server-beta/src/language.lua +++ /dev/null @@ -1,137 +0,0 @@ -local fs = require 'bee.filesystem' -local lni = require 'lni' -local util = require 'utility' - -local function supportLanguage() - local list = {} - for path in (ROOT / 'locale'):list_directory() do - if fs.is_directory(path) then - list[#list+1] = path:filename():string():lower() - end - end - return list -end - -local function osLanguage() - return LANG:lower() -end - -local function getLanguage(id) - local support = supportLanguage() - -- 检查是否支持语言 - if support[id] then - return id - end - -- 根据语言的前2个字母来找近似语言 - for _, lang in ipairs(support) do - if lang:sub(1, 2) == id:sub(1, 2) then - return lang - end - end - -- 使用英文 - return 'enUS' -end - -local function loadFileByLanguage(name, language) - local path = ROOT / 'locale' / language / (name .. '.lni') - local buf = util.loadFile(path:string()) - if not buf then - return {} - end - local suc, tbl = xpcall(lni, log.error, buf, path:string()) - if not suc then - return {} - end - return tbl -end - -local function formatAsArray(str, ...) - local index = 0 - local args = {...} - return str:gsub('%{(.-)%}', function (pat) - local id, fmt - local pos = pat:find(':', 1, true) - if pos then - id = pat:sub(1, pos-1) - fmt = pat:sub(pos+1) - else - id = pat - fmt = 's' - end - id = tonumber(id) - if not id then - index = index + 1 - id = index - end - return ('%'..fmt):format(args[id]) - end) -end - -local function formatAsTable(str, ...) - local args = ... - return str:gsub('%{(.-)%}', function (pat) - local id, fmt - local pos = pat:find(':', 1, true) - if pos then - id = pat:sub(1, pos-1) - fmt = pat:sub(pos+1) - else - id = pat - fmt = 's' - end - if not id then - return - end - return ('%'..fmt):format(args[id]) - end) -end - -local function loadLang(name, language) - local tbl = loadFileByLanguage(name, 'en-US') - if language ~= 'en-US' then - local other = loadFileByLanguage(name, language) - for k, v in pairs(other) do - tbl[k] = v - end - end - return setmetatable(tbl, { - __index = function (self, key) - self[key] = key - return key - end, - __call = function (self, key, ...) - local str = self[key] - local suc, res - if type(...) == 'table' then - suc, res = pcall(formatAsTable, str, ...) - else - suc, res = pcall(formatAsArray, str, ...) - end - if suc then - return res - else - -- 这里不能使用翻译,以免死循环 - log.warn(('[%s][%s-%s] formated error: %s'):format( - language, name, key, str - )) - return str - end - end, - }) -end - -local function init() - local id = osLanguage() - local language = getLanguage(id) - log.info(('VSC language: %s'):format(id)) - log.info(('LS language: %s'):format(language)) - return setmetatable({ id = language }, { - __index = function (self, name) - local tbl = loadLang(name, language) - self[name] = tbl - return tbl - end, - }) -end - -return init() diff --git a/server-beta/src/library.lua b/server-beta/src/library.lua deleted file mode 100644 index d4dba7c9..00000000 --- a/server-beta/src/library.lua +++ /dev/null @@ -1,296 +0,0 @@ -local lni = require 'lni' -local fs = require 'bee.filesystem' -local config = require 'config' -local util = require 'utility' - -local m = {} - -local function mergeEnum(lib, locale) - if not lib or not locale then - return - end - local pack = {} - for _, enum in ipairs(lib) do - if enum.enum then - pack[enum.enum] = enum - end - if enum.code then - pack[enum.code] = enum - end - end - for _, enum in ipairs(locale) do - if pack[enum.enum] then - if enum.description then - pack[enum.enum].description = enum.description - end - end - if pack[enum.code] then - if enum.description then - pack[enum.code].description = enum.description - end - end - end -end - -local function mergeField(lib, locale) - if not lib or not locale then - return - end - local pack = {} - for _, field in ipairs(lib) do - if field.field then - pack[field.field] = field - end - end - for _, field in ipairs(locale) do - if pack[field.field] then - if field.description then - pack[field.field].description = field.description - end - end - end -end - -local function mergeLocale(libs, locale) - if not libs or not locale then - return - end - for name in pairs(locale) do - if libs[name] then - if locale[name].description then - libs[name].description = locale[name].description - end - mergeEnum(libs[name].enums, locale[name].enums) - mergeField(libs[name].fields, locale[name].fields) - end - end -end - -local function isMatchVersion(version) - if not version then - return true - end - local runtimeVersion = config.config.runtime.version - if type(version) == 'table' then - for i = 1, #version do - if version[i] == runtimeVersion then - return true - end - end - else - if version == runtimeVersion then - return true - end - end - return false -end - -local function insertGlobal(tbl, key, value) - if not isMatchVersion(value.version) then - return false - end - if not value.doc then - value.doc = key - end - tbl[key] = value - return true -end - -local function insertOther(tbl, key, value) - if not value.version then - return - end - if not tbl[key] then - tbl[key] = {} - end - if type(value.version) == 'string' then - tbl[key][#tbl[key]+1] = value.version - elseif type(value.version) == 'table' then - for _, version in ipairs(value.version) do - if type(version) == 'string' then - tbl[key][#tbl[key]+1] = version - end - end - end - table.sort(tbl[key]) -end - -local function insertCustom(tbl, key, value, libName) - if not tbl[key] then - tbl[key] = {} - end - tbl[key][#tbl[key]+1] = libName - table.sort(tbl[key]) -end - -local function isEnableGlobal(libName) - if config.config.runtime.library[libName] then - return true - end - if libName:sub(1, 1) == '@' then - return true - end - return false -end - -local function mergeSource(alllibs, name, lib, libName) - if not lib.source then - if isEnableGlobal(libName) then - local suc = insertGlobal(alllibs.global, name, lib) - if not suc then - insertOther(alllibs.other, name, lib) - end - else - insertCustom(alllibs.custom, name, lib, libName) - end - return - end - for _, source in ipairs(lib.source) do - local sourceName = source.name or name - if source.type == 'global' then - if isEnableGlobal(libName) then - local suc = insertGlobal(alllibs.global, sourceName, lib) - if not suc then - insertOther(alllibs.other, sourceName, lib) - end - else - insertCustom(alllibs.custom, sourceName, lib, libName) - end - elseif source.type == 'library' then - insertGlobal(alllibs.library, sourceName, lib) - elseif source.type == 'object' then - insertGlobal(alllibs.object, sourceName, lib) - end - end -end - -local function copy(t) - local new = {} - for k, v in pairs(t) do - new[k] = v - end - return new -end - -local function insertChild(tbl, name, key, value) - if not name or not key then - return - end - if not isMatchVersion(value.version) then - return - end - if not value.doc then - value.doc = ('%s.%s'):format(name, key) - end - if not tbl[name] then - tbl[name] = { - type = name, - name = name, - child = {}, - } - end - tbl[name].child[key] = copy(value) -end - -local function mergeParent(alllibs, name, lib, libName) - for _, parent in ipairs(lib.parent) do - if parent.type == 'global' then - if isEnableGlobal(libName) then - insertChild(alllibs.global, parent.name, name, lib) - end - elseif parent.type == 'library' then - insertChild(alllibs.library, parent.name, name, lib) - elseif parent.type == 'object' then - insertChild(alllibs.object, parent.name, name, lib) - end - end -end - -local function mergeLibs(alllibs, libs, libName) - if not libs then - return - end - for _, lib in pairs(libs) do - if lib.parent then - mergeParent(alllibs, lib.name, lib, libName) - else - mergeSource(alllibs, lib.name, lib, libName) - end - end -end - -local function loadLocale(language, relative) - local localePath = ROOT / 'locale' / language / relative - local localeBuf = util.loadFile(localePath:string()) - if localeBuf then - local locale = util.container() - xpcall(lni, log.error, localeBuf, localePath:string(), {locale}) - return locale - end - return nil -end - -local function fix(libs) - for name, lib in pairs(libs) do - lib.name = lib.name or name - lib.child = {} - end -end - -local function scan(path) - local result = {path} - local i = 0 - return function () - i = i + 1 - local current = result[i] - if not current then - return nil - end - if fs.is_directory(current) then - for path in current:list_directory() do - result[#result+1] = path - end - end - return current - end -end - -local function init() - local lang = require 'language' - local id = lang.id - m.global = util.container() - m.library = util.container() - m.object = util.container() - m.other = util.container() - m.custom = util.container() - - for libPath in (ROOT / 'libs'):list_directory() do - local libName = libPath:filename():string() - for path in scan(libPath) do - local libs - local buf = util.loadFile(path:string()) - if buf then - libs = util.container() - xpcall(lni, log.error, buf, path:string(), {libs}) - fix(libs) - end - local relative = fs.relative(path, ROOT) - - local locale = loadLocale('en-US', relative) - mergeLocale(libs, locale) - if id ~= 'en-US' then - locale = loadLocale(id, relative) - mergeLocale(libs, locale) - end - mergeLibs(m, libs, libName) - end - end -end - -function m.reload() - init() -end - -init() - -return m diff --git a/server-beta/src/log.lua b/server-beta/src/log.lua deleted file mode 100644 index 1a66685a..00000000 --- a/server-beta/src/log.lua +++ /dev/null @@ -1,140 +0,0 @@ -local fs = require 'bee.filesystem' - -local osTime = os.time -local osClock = os.clock -local osDate = os.date -local ioOpen = io.open -local tablePack = table.pack -local tableConcat = table.concat -local tostring = tostring -local debugTraceBack = debug.traceback -local mathModf = math.modf -local debugGetInfo = debug.getinfo -local ioStdErr = io.stderr - -_ENV = nil - -local m = {} - -m.file = nil -m.startTime = osTime() - osClock() -m.size = 0 -m.maxSize = 100 * 1024 * 1024 - -local function trimSrc(src) - src = src:sub(m.prefixLen + 3, -5) - src = src:gsub('^[/\\]+', '') - src = src:gsub('[\\/]+', '.') - return src -end - -local function init_log_file() - if not m.file then - m.file = ioOpen(m.path, 'w') - if not m.file then - return - end - m.file:write('') - m.file:close() - m.file = ioOpen(m.path, 'ab') - if not m.file then - return - end - m.file:setvbuf 'no' - end -end - -local function pushLog(level, ...) - if not m.path then - return - end - if m.size > m.maxSize then - return - end - local t = tablePack(...) - for i = 1, t.n do - t[i] = tostring(t[i]) - end - local str = tableConcat(t, '\t', 1, t.n) - if level == 'error' then - str = str .. '\n' .. debugTraceBack(nil, 3) - end - local info = debugGetInfo(3, 'Sl') - return m.raw(0, level, str, info.source, info.currentline) -end - -function m.info(...) - pushLog('info', ...) -end - -function m.debug(...) - pushLog('debug', ...) -end - -function m.trace(...) - pushLog('trace', ...) -end - -function m.warn(...) - pushLog('warn', ...) -end - -function m.error(...) - pushLog('error', ...) -end - -function m.raw(thd, level, msg, source, currentline) - if level == 'error' then - ioStdErr:write(msg .. '\n') - end - init_log_file() - if not m.file then - return - end - local sec, ms = mathModf(m.startTime + osClock()) - local timestr = osDate('%H:%M:%S', sec) - local agl = '' - if #level < 5 then - agl = (' '):rep(5 - #level) - end - local buf - if currentline == -1 then - buf = ('[%s.%03.f][%s]: %s[#%d]%s\n'):format(timestr, ms * 1000, level, agl, thd, msg) - else - buf = ('[%s.%03.f][%s]: %s[#%d:%s:%s]%s\n'):format(timestr, ms * 1000, level, agl, thd, trimSrc(source), currentline, msg) - end - m.file:write(buf) - m.size = m.size + #buf - if m.size > m.maxSize then - m.file:write('[REACH MAX SIZE]') - end - return -end - -function m.init(root, path) - local lastBuf - if m.file then - m.file:close() - m.file = nil - local file = ioOpen(m.path, 'rb') - if file then - lastBuf = file:read 'a' - file:close() - end - end - m.path = path:string() - m.prefixLen = #root:string() - m.size = 0 - if not fs.exists(path:parent_path()) then - fs.create_directories(path:parent_path()) - end - if lastBuf then - init_log_file() - if m.file then - m.file:write(lastBuf) - m.size = m.size + #lastBuf - end - end -end - -return m diff --git a/server-beta/src/parser/ast.lua b/server-beta/src/parser/ast.lua deleted file mode 100644 index dfd7656d..00000000 --- a/server-beta/src/parser/ast.lua +++ /dev/null @@ -1,1738 +0,0 @@ -local emmy = require 'parser.emmy' - -local tonumber = tonumber -local stringChar = string.char -local utf8Char = utf8.char -local tableUnpack = table.unpack -local mathType = math.type -local tableRemove = table.remove -local pairs = pairs -local tableSort = table.sort - -_ENV = nil - -local State -local PushError -local PushDiag - --- goto 单独处理 -local RESERVED = { - ['and'] = true, - ['break'] = true, - ['do'] = true, - ['else'] = true, - ['elseif'] = true, - ['end'] = true, - ['false'] = true, - ['for'] = true, - ['function'] = true, - ['if'] = true, - ['in'] = true, - ['local'] = true, - ['nil'] = true, - ['not'] = true, - ['or'] = true, - ['repeat'] = true, - ['return'] = true, - ['then'] = true, - ['true'] = true, - ['until'] = true, - ['while'] = true, -} - -local VersionOp = { - ['&'] = {'Lua 5.3', 'Lua 5.4'}, - ['~'] = {'Lua 5.3', 'Lua 5.4'}, - ['|'] = {'Lua 5.3', 'Lua 5.4'}, - ['<<'] = {'Lua 5.3', 'Lua 5.4'}, - ['>>'] = {'Lua 5.3', 'Lua 5.4'}, - ['//'] = {'Lua 5.3', 'Lua 5.4'}, -} - -local function checkOpVersion(op) - local versions = VersionOp[op.type] - if not versions then - return - end - for i = 1, #versions do - if versions[i] == State.version then - return - end - end - PushError { - type = 'UNSUPPORT_SYMBOL', - start = op.start, - finish = op.finish, - version = versions, - info = { - version = State.version, - } - } -end - -local function checkMissEnd(start) - if not State.MissEndErr then - return - end - local err = State.MissEndErr - State.MissEndErr = nil - local _, finish = State.lua:find('[%w_]+', start) - if not finish then - return - end - err.info.related = { - { - start = start, - finish = finish, - } - } - PushError { - type = 'MISS_END', - start = start, - finish = finish, - } -end - -local function getSelect(vararg, index) - return { - type = 'select', - start = vararg.start, - finish = vararg.finish, - vararg = vararg, - index = index, - } -end - -local function getValue(values, i) - if not values then - return nil, nil - end - local value = values[i] - if not value then - local last = values[#values] - if not last then - return nil, nil - end - if last.type == 'call' or last.type == 'varargs' then - return getSelect(last, i - #values + 1) - end - return nil, nil - end - if value.type == 'call' or value.type == 'varargs' then - value = getSelect(value, 1) - end - return value -end - -local function createLocal(key, effect, value, attrs) - if not key then - return nil - end - key.type = 'local' - key.effect = effect - key.value = value - key.attrs = attrs - if value then - key.range = value.finish - end - return key -end - -local function createCall(args, start, finish) - if args then - args.type = 'callargs' - args.start = start - args.finish = finish - end - return { - type = 'call', - start = start, - finish = finish, - args = args, - } -end - -local function packList(start, list, finish) - local lastFinish = start - local wantName = true - local count = 0 - for i = 1, #list do - local ast = list[i] - if ast.type == ',' then - if wantName or i == #list then - PushError { - type = 'UNEXPECT_SYMBOL', - start = ast.start, - finish = ast.finish, - info = { - symbol = ',', - } - } - end - wantName = true - else - if not wantName then - PushError { - type = 'MISS_SYMBOL', - start = lastFinish, - finish = ast.start - 1, - info = { - symbol = ',', - } - } - end - wantName = false - count = count + 1 - list[count] = list[i] - end - lastFinish = ast.finish + 1 - end - for i = count + 1, #list do - list[i] = nil - end - list.type = 'list' - list.start = start - list.finish = finish - 1 - return list -end - -local BinaryLevel = { - ['or'] = 1, - ['and'] = 2, - ['<='] = 3, - ['>='] = 3, - ['<'] = 3, - ['>'] = 3, - ['~='] = 3, - ['=='] = 3, - ['|'] = 4, - ['~'] = 5, - ['&'] = 6, - ['<<'] = 7, - ['>>'] = 7, - ['..'] = 8, - ['+'] = 9, - ['-'] = 9, - ['*'] = 10, - ['//'] = 10, - ['/'] = 10, - ['%'] = 10, - ['^'] = 11, -} - -local BinaryForward = { - [01] = true, - [02] = true, - [03] = true, - [04] = true, - [05] = true, - [06] = true, - [07] = true, - [08] = false, - [09] = true, - [10] = true, - [11] = false, -} - -local Defs = { - Nil = function (pos) - return { - type = 'nil', - start = pos, - finish = pos + 2, - } - end, - True = function (pos) - return { - type = 'boolean', - start = pos, - finish = pos + 3, - [1] = true, - } - end, - False = function (pos) - return { - type = 'boolean', - start = pos, - finish = pos + 4, - [1] = false, - } - end, - LongComment = function (beforeEq, afterEq, str, missPos) - if missPos then - local endSymbol = ']' .. ('='):rep(afterEq-beforeEq) .. ']' - local s, _, w = str:find('(%][%=]*%])[%c%s]*$') - if s then - PushError { - type = 'ERR_LCOMMENT_END', - start = missPos - #str + s - 1, - finish = missPos - #str + s + #w - 2, - info = { - symbol = endSymbol, - }, - fix = { - title = 'FIX_LCOMMENT_END', - { - start = missPos - #str + s - 1, - finish = missPos - #str + s + #w - 2, - text = endSymbol, - } - }, - } - end - PushError { - type = 'MISS_SYMBOL', - start = missPos, - finish = missPos, - info = { - symbol = endSymbol, - }, - fix = { - title = 'ADD_LCOMMENT_END', - { - start = missPos, - finish = missPos, - text = endSymbol, - } - }, - } - end - end, - CLongComment = function (start1, finish1, start2, finish2) - PushError { - type = 'ERR_C_LONG_COMMENT', - start = start1, - finish = finish2 - 1, - fix = { - title = 'FIX_C_LONG_COMMENT', - { - start = start1, - finish = finish1 - 1, - text = '--[[', - }, - { - start = start2, - finish = finish2 - 1, - text = '--]]' - }, - } - } - end, - CCommentPrefix = function (start, finish) - PushError { - type = 'ERR_COMMENT_PREFIX', - start = start, - finish = finish - 1, - fix = { - title = 'FIX_COMMENT_PREFIX', - { - start = start, - finish = finish - 1, - text = '--', - }, - } - } - end, - String = function (start, quote, str, finish) - return { - type = 'string', - start = start, - finish = finish - 1, - [1] = str, - [2] = quote, - } - end, - LongString = function (beforeEq, afterEq, str, missPos) - if missPos then - local endSymbol = ']' .. ('='):rep(afterEq-beforeEq) .. ']' - local s, _, w = str:find('(%][%=]*%])[%c%s]*$') - if s then - PushError { - type = 'ERR_LSTRING_END', - start = missPos - #str + s - 1, - finish = missPos - #str + s + #w - 2, - info = { - symbol = endSymbol, - }, - fix = { - title = 'FIX_LSTRING_END', - { - start = missPos - #str + s - 1, - finish = missPos - #str + s + #w - 2, - text = endSymbol, - } - }, - } - end - PushError { - type = 'MISS_SYMBOL', - start = missPos, - finish = missPos, - info = { - symbol = endSymbol, - }, - fix = { - title = 'ADD_LSTRING_END', - { - start = missPos, - finish = missPos, - text = endSymbol, - } - }, - } - end - return '[' .. ('='):rep(afterEq-beforeEq) .. '[', str - end, - Char10 = function (char) - char = tonumber(char) - if not char or char < 0 or char > 255 then - return '' - end - return stringChar(char) - end, - Char16 = function (pos, char) - if State.version == 'Lua 5.1' then - PushError { - type = 'ERR_ESC', - start = pos-1, - finish = pos, - version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'}, - info = { - version = State.version, - } - } - return char - end - return stringChar(tonumber(char, 16)) - end, - CharUtf8 = function (pos, char) - if State.version ~= 'Lua 5.3' - and State.version ~= 'Lua 5.4' - and State.version ~= 'LuaJIT' - then - PushError { - type = 'ERR_ESC', - start = pos-3, - finish = pos-2, - version = {'Lua 5.3', 'Lua 5.4', 'LuaJIT'}, - info = { - version = State.version, - } - } - return char - end - if #char == 0 then - PushError { - type = 'UTF8_SMALL', - start = pos-3, - finish = pos, - } - return '' - end - local v = tonumber(char, 16) - if not v then - for i = 1, #char do - if not tonumber(char:sub(i, i), 16) then - PushError { - type = 'MUST_X16', - start = pos + i - 1, - finish = pos + i - 1, - } - end - end - return '' - end - if State.version == 'Lua 5.4' then - if v < 0 or v > 0x7FFFFFFF then - PushError { - type = 'UTF8_MAX', - start = pos-3, - finish = pos+#char, - info = { - min = '00000000', - max = '7FFFFFFF', - } - } - end - else - if v < 0 or v > 0x10FFFF then - PushError { - type = 'UTF8_MAX', - start = pos-3, - finish = pos+#char, - version = v <= 0x7FFFFFFF and 'Lua 5.4' or nil, - info = { - min = '000000', - max = '10FFFF', - } - } - end - end - if v >= 0 and v <= 0x10FFFF then - return utf8Char(v) - end - return '' - end, - Number = function (start, number, finish) - local n = tonumber(number) - if n then - State.LastNumber = { - type = 'number', - start = start, - finish = finish - 1, - [1] = n, - } - return State.LastNumber - else - PushError { - type = 'MALFORMED_NUMBER', - start = start, - finish = finish - 1, - } - State.LastNumber = { - type = 'number', - start = start, - finish = finish - 1, - [1] = 0, - } - return State.LastNumber - end - end, - FFINumber = function (start, symbol) - local lastNumber = State.LastNumber - if mathType(lastNumber[1]) == 'float' then - PushError { - type = 'UNKNOWN_SYMBOL', - start = start, - finish = start + #symbol - 1, - info = { - symbol = symbol, - } - } - lastNumber[1] = 0 - return - end - if State.version ~= 'LuaJIT' then - PushError { - type = 'UNSUPPORT_SYMBOL', - start = start, - finish = start + #symbol - 1, - version = 'LuaJIT', - info = { - version = State.version, - } - } - lastNumber[1] = 0 - end - end, - ImaginaryNumber = function (start, symbol) - local lastNumber = State.LastNumber - if State.version ~= 'LuaJIT' then - PushError { - type = 'UNSUPPORT_SYMBOL', - start = start, - finish = start + #symbol - 1, - version = 'LuaJIT', - info = { - version = State.version, - } - } - end - lastNumber[1] = 0 - end, - Name = function (start, str, finish) - local isKeyWord - if RESERVED[str] then - isKeyWord = true - elseif str == 'goto' then - if State.version ~= 'Lua 5.1' and State.version ~= 'LuaJIT' then - isKeyWord = true - end - end - if isKeyWord then - PushError { - type = 'KEYWORD', - start = start, - finish = finish - 1, - } - end - return { - type = 'name', - start = start, - finish = finish - 1, - [1] = str, - } - end, - GetField = function (dot, field) - local obj = { - type = 'getfield', - field = field, - dot = dot, - start = dot.start, - finish = (field or dot).finish, - } - if field then - field.type = 'field' - field.parent = obj - end - return obj - end, - GetIndex = function (start, index, finish) - local obj = { - type = 'getindex', - start = start, - finish = finish - 1, - index = index, - } - if index then - index.parent = obj - end - return obj - end, - GetMethod = function (colon, method) - local obj = { - type = 'getmethod', - method = method, - colon = colon, - start = colon.start, - finish = (method or colon).finish, - } - if method then - method.type = 'method' - method.parent = obj - end - return obj - end, - Single = function (unit) - unit.type = 'getname' - return unit - end, - Simple = function (units) - local last = units[1] - for i = 2, #units do - local current = units[i] - current.node = last - current.start = last.start - last.next = current - last = units[i] - end - return last - end, - SimpleCall = function (call) - if call.type ~= 'call' and call.type ~= 'getmethod' then - PushError { - type = 'EXP_IN_ACTION', - start = call.start, - finish = call.finish, - } - end - return call - end, - BinaryOp = function (start, op) - return { - type = op, - start = start, - finish = start + #op - 1, - } - end, - UnaryOp = function (start, op) - return { - type = op, - start = start, - finish = start + #op - 1, - } - end, - Unary = function (first, ...) - if not ... then - return nil - end - local list = {first, ...} - local e = list[#list] - for i = #list - 1, 1, -1 do - local op = list[i] - checkOpVersion(op) - e = { - type = 'unary', - op = op, - start = op.start, - finish = e.finish, - [1] = e, - } - end - return e - end, - SubBinary = function (op, symb) - if symb then - return op, symb - end - PushError { - type = 'MISS_EXP', - start = op.start, - finish = op.finish, - } - end, - Binary = function (first, op, second, ...) - if not first then - return second - end - if not op then - return first - end - if not ... then - checkOpVersion(op) - return { - type = 'binary', - op = op, - start = first.start, - finish = second.finish, - [1] = first, - [2] = second, - } - end - local list = {first, op, second, ...} - local ops = {} - for i = 2, #list, 2 do - ops[#ops+1] = i - end - tableSort(ops, function (a, b) - local op1 = list[a] - local op2 = list[b] - local lv1 = BinaryLevel[op1.type] - local lv2 = BinaryLevel[op2.type] - if lv1 == lv2 then - local forward = BinaryForward[lv1] - if forward then - return op1.start > op2.start - else - return op1.start < op2.start - end - else - return lv1 < lv2 - end - end) - local final - for i = #ops, 1, -1 do - local n = ops[i] - local op = list[n] - local left = list[n-1] - local right = list[n+1] - local exp = { - type = 'binary', - op = op, - start = left.start, - finish = right and right.finish or op.finish, - [1] = left, - [2] = right, - } - local leftIndex, rightIndex - if list[left] then - leftIndex = list[left[1]] - else - leftIndex = n - 1 - end - if list[right] then - rightIndex = list[right[2]] - else - rightIndex = n + 1 - end - - list[leftIndex] = exp - list[rightIndex] = exp - list[left] = leftIndex - list[right] = rightIndex - list[exp] = n - final = exp - - checkOpVersion(op) - end - return final - end, - Paren = function (start, exp, finish) - if exp and exp.type == 'paren' then - exp.start = start - exp.finish = finish - 1 - return exp - end - return { - type = 'paren', - start = start, - finish = finish - 1, - exp = exp - } - end, - VarArgs = function (dots) - dots.type = 'varargs' - return dots - end, - PackLoopArgs = function (start, list, finish) - local list = packList(start, list, finish) - if #list == 0 then - PushError { - type = 'MISS_LOOP_MIN', - start = finish, - finish = finish, - } - elseif #list == 1 then - PushError { - type = 'MISS_LOOP_MAX', - start = finish, - finish = finish, - } - end - return list - end, - PackInNameList = function (start, list, finish) - local list = packList(start, list, finish) - if #list == 0 then - PushError { - type = 'MISS_NAME', - start = start, - finish = finish, - } - end - return list - end, - PackInExpList = function (start, list, finish) - local list = packList(start, list, finish) - if #list == 0 then - PushError { - type = 'MISS_EXP', - start = start, - finish = finish, - } - end - return list - end, - PackExpList = function (start, list, finish) - local list = packList(start, list, finish) - return list - end, - PackNameList = function (start, list, finish) - local list = packList(start, list, finish) - return list - end, - Call = function (start, args, finish) - return createCall(args, start, finish-1) - end, - COMMA = function (start) - return { - type = ',', - start = start, - finish = start, - } - end, - SEMICOLON = function (start) - return { - type = ';', - start = start, - finish = start, - } - end, - DOTS = function (start) - return { - type = '...', - start = start, - finish = start + 2, - } - end, - COLON = function (start) - return { - type = ':', - start = start, - finish = start, - } - end, - DOT = function (start) - return { - type = '.', - start = start, - finish = start, - } - end, - Function = function (functionStart, functionFinish, args, actions, endStart, endFinish) - actions.type = 'function' - actions.start = functionStart - actions.finish = endFinish - 1 - actions.args = args - actions.keyword= { - functionStart, functionFinish - 1, - endStart, endFinish - 1, - } - checkMissEnd(functionStart) - return actions - end, - NamedFunction = function (functionStart, functionFinish, name, args, actions, endStart, endFinish) - actions.type = 'function' - actions.start = functionStart - actions.finish = endFinish - 1 - actions.args = args - actions.keyword= { - functionStart, functionFinish - 1, - endStart, endFinish - 1, - } - checkMissEnd(functionStart) - if not name then - return - end - if name.type == 'getname' then - name.type = 'setname' - name.value = actions - elseif name.type == 'getfield' then - name.type = 'setfield' - name.value = actions - elseif name.type == 'getmethod' then - name.type = 'setmethod' - name.value = actions - end - name.range = actions.finish - name.vstart = functionStart - return name - end, - LocalFunction = function (start, functionStart, functionFinish, name, args, actions, endStart, endFinish) - actions.type = 'function' - actions.start = start - actions.finish = endFinish - 1 - actions.args = args - actions.keyword= { - functionStart, functionFinish - 1, - endStart, endFinish - 1, - } - checkMissEnd(start) - - if not name then - return - end - - if name.type ~= 'getname' then - PushError { - type = 'UNEXPECT_LFUNC_NAME', - start = name.start, - finish = name.finish, - } - return - end - - local loc = createLocal(name, name.start, actions) - loc.localfunction = true - loc.vstart = functionStart - - return loc - end, - Table = function (start, tbl, finish) - tbl.type = 'table' - tbl.start = start - tbl.finish = finish - 1 - local wantField = true - local lastStart = start + 1 - local fieldCount = 0 - for i = 1, #tbl do - local field = tbl[i] - if field.type == ',' or field.type == ';' then - if wantField then - PushError { - type = 'MISS_EXP', - start = lastStart, - finish = field.start - 1, - } - end - wantField = true - lastStart = field.finish + 1 - else - if not wantField then - PushError { - type = 'MISS_SEP_IN_TABLE', - start = lastStart, - finish = field.start - 1, - } - end - wantField = false - lastStart = field.finish + 1 - fieldCount = fieldCount + 1 - tbl[fieldCount] = field - end - end - for i = fieldCount + 1, #tbl do - tbl[i] = nil - end - return tbl - end, - NewField = function (start, field, value, finish) - local obj = { - type = 'tablefield', - start = start, - finish = finish-1, - field = field, - value = value, - } - if field then - field.type = 'field' - field.parent = obj - end - return obj - end, - NewIndex = function (start, index, value, finish) - local obj = { - type = 'tableindex', - start = start, - finish = finish-1, - index = index, - value = value, - } - if index then - index.parent = obj - end - return obj - end, - FuncArgs = function (start, args, finish) - args.type = 'funcargs' - args.start = start - args.finish = finish - 1 - local lastStart = start + 1 - local wantName = true - local argCount = 0 - for i = 1, #args do - local arg = args[i] - local argAst = arg - if argAst.type == ',' then - if wantName then - PushError { - type = 'MISS_NAME', - start = lastStart, - finish = argAst.start-1, - } - end - wantName = true - else - if not wantName then - PushError { - type = 'MISS_SYMBOL', - start = lastStart-1, - finish = argAst.start-1, - info = { - symbol = ',', - } - } - end - wantName = false - argCount = argCount + 1 - - if argAst.type == '...' then - args[argCount] = arg - if i < #args then - local a = args[i+1] - local b = args[#args] - PushError { - type = 'ARGS_AFTER_DOTS', - start = a.start, - finish = b.finish, - } - end - break - else - args[argCount] = createLocal(arg, arg.start) - end - end - lastStart = argAst.finish + 1 - end - for i = argCount + 1, #args do - args[i] = nil - end - if wantName and argCount > 0 then - PushError { - type = 'MISS_NAME', - start = lastStart, - finish = finish - 1, - } - end - return args - end, - Set = function (start, keys, values, finish) - for i = 1, #keys do - local key = keys[i] - if key.type == 'getname' then - key.type = 'setname' - key.value = getValue(values, i) - elseif key.type == 'getfield' then - key.type = 'setfield' - key.value = getValue(values, i) - elseif key.type == 'getindex' then - key.type = 'setindex' - key.value = getValue(values, i) - end - if key.value then - key.range = key.value.finish - end - end - if values then - for i = #keys+1, #values do - local value = values[i] - PushDiag('redundant-value', { - start = value.start, - finish = value.finish, - max = #keys, - passed = #values, - }) - end - end - return tableUnpack(keys) - end, - LocalAttr = function (attrs) - for i = 1, #attrs do - local attr = attrs[i] - local attrAst = attr - attrAst.type = 'localattr' - if State.version ~= 'Lua 5.4' then - PushError { - type = 'UNSUPPORT_SYMBOL', - start = attrAst.start, - finish = attrAst.finish, - version = 'Lua 5.4', - info = { - version = State.version, - } - } - elseif attrAst[1] ~= 'const' and attrAst[1] ~= 'close' then - PushError { - type = 'UNKNOWN_TAG', - start = attrAst.start, - finish = attrAst.finish, - info = { - tag = attrAst[1], - } - } - elseif i > 1 then - PushError { - type = 'MULTI_TAG', - start = attrAst.start, - finish = attrAst.finish, - info = { - tag = attrAst[1], - } - } - end - end - return attrs - end, - LocalName = function (name, attrs) - if not name then - return name - end - name.attrs = attrs - return name - end, - Local = function (start, keys, values, finish) - for i = 1, #keys do - local key = keys[i] - local attrs = key.attrs - key.attrs = nil - local value = getValue(values, i) - createLocal(key, finish, value, attrs) - end - if values then - for i = #keys+1, #values do - local value = values[i] - PushDiag('redundant-value', { - start = value.start, - finish = value.finish, - max = #keys, - passed = #values, - }) - end - end - return tableUnpack(keys) - end, - Do = function (start, actions, endA, endB) - actions.type = 'do' - actions.start = start - actions.finish = endB - 1 - actions.keyword= { - start, start + #'do' - 1, - endA , endB - 1, - } - checkMissEnd(start) - return actions - end, - Break = function (start, finish) - return { - type = 'break', - start = start, - finish = finish - 1, - } - end, - Return = function (start, exps, finish) - exps.type = 'return' - exps.start = start - exps.finish = finish - 1 - return exps - end, - Label = function (start, name, finish) - if State.version == 'Lua 5.1' then - PushError { - type = 'UNSUPPORT_SYMBOL', - start = start, - finish = finish - 1, - version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'}, - info = { - version = State.version, - } - } - return - end - if not name then - return nil - end - name.type = 'label' - return name - end, - GoTo = function (start, name, finish) - if State.version == 'Lua 5.1' then - PushError { - type = 'UNSUPPORT_SYMBOL', - start = start, - finish = finish - 1, - version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'}, - info = { - version = State.version, - } - } - return - end - if not name then - return nil - end - name.type = 'goto' - return name - end, - IfBlock = function (ifStart, ifFinish, exp, thenStart, thenFinish, actions, finish) - actions.type = 'ifblock' - actions.start = ifStart - actions.finish = finish - 1 - actions.filter = exp - actions.keyword= { - ifStart, ifFinish - 1, - thenStart, thenFinish - 1, - } - return actions - end, - ElseIfBlock = function (elseifStart, elseifFinish, exp, thenStart, thenFinish, actions, finish) - actions.type = 'elseifblock' - actions.start = elseifStart - actions.finish = finish - 1 - actions.filter = exp - actions.keyword= { - elseifStart, elseifFinish - 1, - thenStart, thenFinish - 1, - } - return actions - end, - ElseBlock = function (elseStart, elseFinish, actions, finish) - actions.type = 'elseblock' - actions.start = elseStart - actions.finish = finish - 1 - actions.keyword= { - elseStart, elseFinish - 1, - } - return actions - end, - If = function (start, blocks, endStart, endFinish) - blocks.type = 'if' - blocks.start = start - blocks.finish = endFinish - 1 - local hasElse - for i = 1, #blocks do - local block = blocks[i] - if i == 1 and block.type ~= 'ifblock' then - PushError { - type = 'MISS_SYMBOL', - start = block.start, - finish = block.start, - info = { - symbol = 'if', - } - } - end - if hasElse then - PushError { - type = 'BLOCK_AFTER_ELSE', - start = block.start, - finish = block.finish, - } - end - if block.type == 'elseblock' then - hasElse = true - end - end - checkMissEnd(start) - return blocks - end, - Loop = function (forA, forB, arg, steps, doA, doB, blockStart, block, endA, endB) - local loc = createLocal(arg, blockStart, steps[1]) - block.type = 'loop' - block.start = forA - block.finish = endB - 1 - block.loc = loc - block.max = steps[2] - block.step = steps[3] - block.keyword= { - forA, forB - 1, - doA , doB - 1, - endA, endB - 1, - } - checkMissEnd(forA) - return block - end, - In = function (forA, forB, keys, inA, inB, exp, doA, doB, blockStart, block, endA, endB) - local func = tableRemove(exp, 1) - block.type = 'in' - block.start = forA - block.finish = endB - 1 - block.keys = keys - block.keyword= { - forA, forB - 1, - inA , inB - 1, - doA , doB - 1, - endA, endB - 1, - } - - local values - if func then - local call = createCall(exp, func.finish + 1, exp.finish) - call.node = func - call.start = func.start - func.next = call - values = { call } - keys.range = call.finish - end - for i = 1, #keys do - local loc = keys[i] - if values then - createLocal(loc, blockStart, getValue(values, i)) - else - createLocal(loc, blockStart) - end - end - checkMissEnd(forA) - return block - end, - While = function (whileA, whileB, filter, doA, doB, block, endA, endB) - block.type = 'while' - block.start = whileA - block.finish = endB - 1 - block.filter = filter - block.keyword= { - whileA, whileB - 1, - doA , doB - 1, - endA , endB - 1, - } - checkMissEnd(whileA) - return block - end, - Repeat = function (repeatA, repeatB, block, untilA, untilB, filter, finish) - block.type = 'repeat' - block.start = repeatA - block.finish = finish - block.filter = filter - block.keyword= { - repeatA, repeatB - 1, - untilA , untilB - 1, - } - return block - end, - Lua = function (start, actions, finish) - actions.type = 'main' - actions.start = start - actions.finish = finish - 1 - return actions - end, - - -- 捕获错误 - UnknownSymbol = function (start, symbol) - PushError { - type = 'UNKNOWN_SYMBOL', - start = start, - finish = start + #symbol - 1, - info = { - symbol = symbol, - } - } - return - end, - UnknownAction = function (start, symbol) - PushError { - type = 'UNKNOWN_SYMBOL', - start = start, - finish = start + #symbol - 1, - info = { - symbol = symbol, - } - } - end, - DirtyName = function (pos) - PushError { - type = 'MISS_NAME', - start = pos, - finish = pos, - } - return nil - end, - DirtyExp = function (pos) - PushError { - type = 'MISS_EXP', - start = pos, - finish = pos, - } - return nil - end, - MissExp = function (pos) - PushError { - type = 'MISS_EXP', - start = pos, - finish = pos, - } - end, - MissExponent = function (start, finish) - PushError { - type = 'MISS_EXPONENT', - start = start, - finish = finish - 1, - } - end, - MissQuote1 = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = '"' - } - } - end, - MissQuote2 = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = "'" - } - } - end, - MissEscX = function (pos) - PushError { - type = 'MISS_ESC_X', - start = pos-2, - finish = pos+1, - } - end, - MissTL = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = '{', - } - } - end, - MissTR = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = '}', - } - } - end, - MissBR = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = ']', - } - } - end, - MissPL = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = '(', - } - } - end, - MissPR = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = ')', - } - } - end, - ErrEsc = function (pos) - PushError { - type = 'ERR_ESC', - start = pos-1, - finish = pos, - } - end, - MustX16 = function (pos, str) - PushError { - type = 'MUST_X16', - start = pos, - finish = pos + #str - 1, - } - end, - MissAssign = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = '=', - } - } - end, - MissTableSep = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = ',' - } - } - end, - MissField = function (pos) - PushError { - type = 'MISS_FIELD', - start = pos, - finish = pos, - } - end, - MissMethod = function (pos) - PushError { - type = 'MISS_METHOD', - start = pos, - finish = pos, - } - end, - MissLabel = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = '::', - } - } - end, - MissEnd = function (pos) - State.MissEndErr = PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = 'end', - } - } - return pos, pos - end, - MissDo = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = 'do', - } - } - return pos, pos - end, - MissComma = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = ',', - } - } - end, - MissIn = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = 'in', - } - } - return pos, pos - end, - MissUntil = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = 'until', - } - } - return pos, pos - end, - MissThen = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = 'then', - } - } - return pos, pos - end, - MissName = function (pos) - PushError { - type = 'MISS_NAME', - start = pos, - finish = pos, - } - end, - ExpInAction = function (start, exp, finish) - PushError { - type = 'EXP_IN_ACTION', - start = start, - finish = finish - 1, - } - return exp - end, - MissIf = function (start, block) - PushError { - type = 'MISS_SYMBOL', - start = start, - finish = start, - info = { - symbol = 'if', - } - } - return block - end, - MissGT = function (start) - PushError { - type = 'MISS_SYMBOL', - start = start, - finish = start, - info = { - symbol = '>' - } - } - end, - ErrAssign = function (start, finish) - PushError { - type = 'ERR_ASSIGN_AS_EQ', - start = start, - finish = finish - 1, - fix = { - title = 'FIX_ASSIGN_AS_EQ', - { - start = start, - finish = finish - 1, - text = '=', - } - } - } - end, - ErrEQ = function (start, finish) - PushError { - type = 'ERR_EQ_AS_ASSIGN', - start = start, - finish = finish - 1, - fix = { - title = 'FIX_EQ_AS_ASSIGN', - { - start = start, - finish = finish - 1, - text = '==', - } - } - } - return '==' - end, - ErrUEQ = function (start, finish) - PushError { - type = 'ERR_UEQ', - start = start, - finish = finish - 1, - fix = { - title = 'FIX_UEQ', - { - start = start, - finish = finish - 1, - text = '~=', - } - } - } - return '==' - end, - ErrThen = function (start, finish) - PushError { - type = 'ERR_THEN_AS_DO', - start = start, - finish = finish - 1, - fix = { - title = 'FIX_THEN_AS_DO', - { - start = start, - finish = finish - 1, - text = 'then', - } - } - } - return start, finish - end, - ErrDo = function (start, finish) - PushError { - type = 'ERR_DO_AS_THEN', - start = start, - finish = finish - 1, - fix = { - title = 'FIX_DO_AS_THEN', - { - start = start, - finish = finish - 1, - text = 'do', - } - } - } - return start, finish - end, -} - ---for k, v in pairs(emmy.ast) do --- Defs[k] = v ---end - -local function init(state) - State = state - PushError = state.pushError - PushDiag = state.pushDiag - emmy.init(State) -end - -local function close() - State = nil - PushError = nil - PushDiag = nil -end - -return { - defs = Defs, - init = init, - close = close, -} diff --git a/server-beta/src/parser/calcline.lua b/server-beta/src/parser/calcline.lua deleted file mode 100644 index 26f475d9..00000000 --- a/server-beta/src/parser/calcline.lua +++ /dev/null @@ -1,93 +0,0 @@ -local m = require 'lpeglabel' - -local row -local fl -local NL = (m.P'\r\n' + m.S'\r\n') * m.Cp() / function (pos) - row = row + 1 - fl = pos -end -local ROWCOL = (NL + m.P(1))^0 -local function rowcol(str, n) - row = 1 - fl = 1 - ROWCOL:match(str:sub(1, n)) - local col = n - fl + 1 - return row, col -end - -local function rowcol_utf8(str, n) - row = 1 - fl = 1 - ROWCOL:match(str:sub(1, n)) - return row, utf8.len(str, fl, n) -end - -local function position(str, _row, _col) - local cur = 1 - local row = 1 - while true do - if row == _row then - return cur + _col - 1 - elseif row > _row then - return cur - 1 - end - local pos = str:find('[\r\n]', cur) - if not pos then - return #str - end - row = row + 1 - if str:sub(pos, pos+1) == '\r\n' then - cur = pos + 2 - else - cur = pos + 1 - end - end -end - -local function position_utf8(str, _row, _col) - local cur = 1 - local row = 1 - while true do - if row == _row then - return utf8.offset(str, _col, cur) - elseif row > _row then - return cur - 1 - end - local pos = str:find('[\r\n]', cur) - if not pos then - return #str - end - row = row + 1 - if str:sub(pos, pos+1) == '\r\n' then - cur = pos + 2 - else - cur = pos + 1 - end - end -end - -local NL = m.P'\r\n' + m.S'\r\n' - -local function line(str, row) - local count = 0 - local res - local LINE = m.Cmt((1 - NL)^0, function (_, _, c) - count = count + 1 - if count == row then - res = c - return false - end - return true - end) - local MATCH = (LINE * NL)^0 * LINE - MATCH:match(str) - return res -end - -return { - rowcol = rowcol, - rowcol_utf8 = rowcol_utf8, - position = position, - position_utf8 = position_utf8, - line = line, -} diff --git a/server-beta/src/parser/compile.lua b/server-beta/src/parser/compile.lua deleted file mode 100644 index bcd9ecc8..00000000 --- a/server-beta/src/parser/compile.lua +++ /dev/null @@ -1,549 +0,0 @@ -local guide = require 'parser.guide' -local type = type - -local specials = { - ['_G'] = true, - ['rawset'] = true, - ['rawget'] = true, - ['setmetatable'] = true, - ['require'] = true, - ['dofile'] = true, - ['loadfile'] = true, - ['pcall'] = true, - ['xpcall'] = true, -} - -_ENV = nil - -local LocalLimit = 200 -local pushError, Compile, CompileBlock, Block, GoToTag, ENVMode, Compiled, LocalCount, Version, Root - -local function addRef(node, obj) - if not node.ref then - node.ref = {} - end - node.ref[#node.ref+1] = obj - obj.node = node -end - -local function addSpecial(name, obj) - if not Root.specials then - Root.specials = {} - end - if not Root.specials[name] then - Root.specials[name] = {} - end - Root.specials[name][#Root.specials[name]+1] = obj - obj.special = name -end - -local vmMap = { - ['getname'] = function (obj) - local loc = guide.getLocal(obj, obj[1], obj.start) - if loc then - obj.type = 'getlocal' - obj.loc = loc - addRef(loc, obj) - if loc.special then - addSpecial(loc.special, obj) - end - else - obj.type = 'getglobal' - if ENVMode == '_ENV' then - local node = guide.getLocal(obj, '_ENV', obj.start) - if node then - addRef(node, obj) - end - end - local name = obj[1] - if specials[name] then - addSpecial(name, obj) - end - end - return obj - end, - ['getfield'] = function (obj) - Compile(obj.node, obj) - end, - ['call'] = function (obj) - Compile(obj.node, obj) - Compile(obj.args, obj) - end, - ['callargs'] = function (obj) - for i = 1, #obj do - Compile(obj[i], obj) - end - end, - ['binary'] = function (obj) - Compile(obj[1], obj) - Compile(obj[2], obj) - end, - ['unary'] = function (obj) - Compile(obj[1], obj) - end, - ['varargs'] = function (obj) - local func = guide.getParentFunction(obj) - if func then - local index, vararg = guide.getFunctionVarArgs(func) - if not index then - pushError { - type = 'UNEXPECT_DOTS', - start = obj.start, - finish = obj.finish, - } - end - if vararg then - if not vararg.ref then - vararg.ref = {} - end - vararg.ref[#vararg.ref+1] = obj - end - end - end, - ['paren'] = function (obj) - Compile(obj.exp, obj) - end, - ['getindex'] = function (obj) - Compile(obj.node, obj) - Compile(obj.index, obj) - end, - ['setindex'] = function (obj) - Compile(obj.node, obj) - Compile(obj.index, obj) - Compile(obj.value, obj) - end, - ['getmethod'] = function (obj) - Compile(obj.node, obj) - Compile(obj.method, obj) - end, - ['setmethod'] = function (obj) - Compile(obj.node, obj) - Compile(obj.method, obj) - local value = obj.value - value.localself = { - type = 'local', - start = 0, - finish = 0, - method = obj, - effect = obj.finish, - tag = 'self', - [1] = 'self', - } - Compile(value, obj) - end, - ['function'] = function (obj) - local lastBlock = Block - local LastLocalCount = LocalCount - Block = obj - LocalCount = 0 - if obj.localself then - Compile(obj.localself, obj) - obj.localself = nil - end - Compile(obj.args, obj) - for i = 1, #obj do - Compile(obj[i], obj) - end - Block = lastBlock - LocalCount = LastLocalCount - end, - ['funcargs'] = function (obj) - for i = 1, #obj do - Compile(obj[i], obj) - end - end, - ['table'] = function (obj) - for i = 1, #obj do - Compile(obj[i], obj) - end - end, - ['tablefield'] = function (obj) - Compile(obj.value, obj) - end, - ['tableindex'] = function (obj) - Compile(obj.index, obj) - Compile(obj.value, obj) - end, - ['index'] = function (obj) - Compile(obj.index, obj) - end, - ['select'] = function (obj) - local vararg = obj.vararg - if vararg.parent then - if not vararg.extParent then - vararg.extParent = {} - end - vararg.extParent[#vararg.extParent+1] = obj - else - Compile(vararg, obj) - end - end, - ['setname'] = function (obj) - Compile(obj.value, obj) - local loc = guide.getLocal(obj, obj[1], obj.start) - if loc then - obj.type = 'setlocal' - obj.loc = loc - addRef(loc, obj) - if loc.attrs then - local const - for i = 1, #loc.attrs do - local attr = loc.attrs[i][1] - if attr == 'const' - or attr == 'close' then - const = true - break - end - end - if const then - pushError { - type = 'SET_CONST', - start = obj.start, - finish = obj.finish, - } - end - end - else - obj.type = 'setglobal' - if ENVMode == '_ENV' then - local node = guide.getLocal(obj, '_ENV', obj.start) - if node then - addRef(node, obj) - end - end - end - end, - ['local'] = function (obj) - local attrs = obj.attrs - if attrs then - for i = 1, #attrs do - Compile(attrs[i], obj) - end - end - if Block then - if not Block.locals then - Block.locals = {} - end - Block.locals[#Block.locals+1] = obj - LocalCount = LocalCount + 1 - if LocalCount > LocalLimit then - pushError { - type = 'LOCAL_LIMIT', - start = obj.start, - finish = obj.finish, - } - end - end - if obj.localfunction then - obj.localfunction = nil - end - Compile(obj.value, obj) - if obj.value and obj.value.special then - addSpecial(obj.value.special, obj) - end - end, - ['setfield'] = function (obj) - Compile(obj.node, obj) - Compile(obj.value, obj) - end, - ['do'] = function (obj) - local lastBlock = Block - Block = obj - CompileBlock(obj, obj) - if Block.locals then - LocalCount = LocalCount - #Block.locals - end - Block = lastBlock - end, - ['return'] = function (obj) - for i = 1, #obj do - Compile(obj[i], obj) - end - if Block and Block[#Block] ~= obj then - pushError { - type = 'ACTION_AFTER_RETURN', - start = obj.start, - finish = obj.finish, - } - end - local func = guide.getParentFunction(obj) - if func then - if not func.returns then - func.returns = {} - end - func.returns[#func.returns+1] = obj - end - end, - ['label'] = function (obj) - local block = guide.getBlock(obj) - if block then - if not block.labels then - block.labels = {} - end - local name = obj[1] - local label = guide.getLabel(block, name) - if label then - if Version == 'Lua 5.4' - or block == guide.getBlock(label) then - pushError { - type = 'REDEFINED_LABEL', - start = obj.start, - finish = obj.finish, - relative = { - { - label.start, - label.finish, - } - } - } - end - end - block.labels[name] = obj - end - end, - ['goto'] = function (obj) - GoToTag[#GoToTag+1] = obj - end, - ['if'] = function (obj) - for i = 1, #obj do - Compile(obj[i], obj) - end - end, - ['ifblock'] = function (obj) - local lastBlock = Block - Block = obj - Compile(obj.filter, obj) - CompileBlock(obj, obj) - if Block.locals then - LocalCount = LocalCount - #Block.locals - end - Block = lastBlock - end, - ['elseifblock'] = function (obj) - local lastBlock = Block - Block = obj - Compile(obj.filter, obj) - CompileBlock(obj, obj) - if Block.locals then - LocalCount = LocalCount - #Block.locals - end - Block = lastBlock - end, - ['elseblock'] = function (obj) - local lastBlock = Block - Block = obj - CompileBlock(obj, obj) - if Block.locals then - LocalCount = LocalCount - #Block.locals - end - Block = lastBlock - end, - ['loop'] = function (obj) - local lastBlock = Block - Block = obj - Compile(obj.loc, obj) - Compile(obj.max, obj) - Compile(obj.step, obj) - CompileBlock(obj, obj) - if Block.locals then - LocalCount = LocalCount - #Block.locals - end - Block = lastBlock - end, - ['in'] = function (obj) - local lastBlock = Block - Block = obj - local keys = obj.keys - for i = 1, #keys do - Compile(keys[i], obj) - end - CompileBlock(obj, obj) - if Block.locals then - LocalCount = LocalCount - #Block.locals - end - Block = lastBlock - end, - ['while'] = function (obj) - local lastBlock = Block - Block = obj - Compile(obj.filter, obj) - CompileBlock(obj, obj) - if Block.locals then - LocalCount = LocalCount - #Block.locals - end - Block = lastBlock - end, - ['repeat'] = function (obj) - local lastBlock = Block - Block = obj - CompileBlock(obj, obj) - Compile(obj.filter, obj) - if Block.locals then - LocalCount = LocalCount - #Block.locals - end - Block = lastBlock - end, - ['break'] = function (obj) - local block = guide.getBreakBlock(obj) - if block then - if not block.breaks then - block.breaks = {} - end - block.breaks[#block.breaks+1] = obj - else - pushError { - type = 'BREAK_OUTSIDE', - start = obj.start, - finish = obj.finish, - } - end - end, - ['main'] = function (obj) - Block = obj - if ENVMode == '_ENV' then - Compile({ - type = 'local', - start = 0, - finish = 0, - effect = 0, - tag = '_ENV', - special= '_G', - [1] = '_ENV', - }, obj) - end - --- _ENV 是上值,不计入局部变量计数 - LocalCount = 0 - CompileBlock(obj, obj) - Block = nil - 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 - f(act) - end - end -end - -function Compile(obj, parent) - if not obj then - return nil - end - if Compiled[obj] then - return - end - Compiled[obj] = true - obj.parent = parent - local f = vmMap[obj.type] - if not f then - return - end - f(obj) -end - -local function compileGoTo(obj) - local name = obj[1] - local label = guide.getLabel(obj, name) - if not label then - pushError { - type = 'NO_VISIBLE_LABEL', - start = obj.start, - finish = obj.finish, - info = { - label = name, - } - } - return - end - if not label.ref then - label.ref = {} - end - label.ref[#label.ref+1] = obj - - -- 如果有局部变量在 goto 与 label 之间声明, - -- 并在 label 之后使用,则算作语法错误 - - -- 如果 label 在 goto 之前声明,那么不会有中间声明的局部变量 - if obj.start > label.start then - return - end - - local block = guide.getBlock(obj) - local locals = block and block.locals - if not locals then - return - end - - for i = 1, #locals do - local loc = 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 = 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, err = self:parse(lua, mode, version) - if not state then - return nil, err - end - pushError = state.pushError - if version == 'Lua 5.1' or version == 'LuaJIT' then - ENVMode = 'fenv' - else - ENVMode = '_ENV' - end - Compiled = {} - GoToTag = {} - LocalCount = 0 - Version = version - Root = state.ast - if type(state.ast) == 'table' then - Compile(state.ast) - end - PostCompile() - Compiled = nil - GoToTag = nil - return state -end diff --git a/server-beta/src/parser/emmy.lua b/server-beta/src/parser/emmy.lua deleted file mode 100644 index 4c1e087a..00000000 --- a/server-beta/src/parser/emmy.lua +++ /dev/null @@ -1,321 +0,0 @@ -local State -local pushError - -local grammar = [[ -EmmyLua <- ({} '---' EmmyBody {} ShortComment) - -> EmmyLua -EmmySp <- (!'---@' !'---' Comment / %s / %nl)* -EmmyComments <- (EmmyComment (%nl EmmyComMulti / %nl EmmyComSingle)*) -EmmyComment <- EmmySp %s* {(!%nl .)*} -EmmyComMulti <- EmmySp '---|' {} -> en {(!%nl .)*} -EmmyComSingle <- EmmySp '---' !'@' %s* {} -> ' ' {(!%nl .)*} -EmmyBody <- '@class' %s+ EmmyClass -> EmmyClass - / '@type' %s+ EmmyType -> EmmyType - / '@alias' %s+ EmmyAlias -> EmmyAlias - / '@param' %s+ EmmyParam -> EmmyParam - / '@return' %s+ EmmyReturn -> EmmyReturn - / '@field' %s+ EmmyField -> EmmyField - / '@generic' %s+ EmmyGeneric -> EmmyGeneric - / '@vararg' %s+ EmmyVararg -> EmmyVararg - / '@language' %s+ EmmyLanguage -> EmmyLanguage - / '@see' %s+ EmmySee -> EmmySee - / '@overload' %s+ EmmyOverLoad -> EmmyOverLoad - / %s* EmmyComments -> EmmyComment - / EmmyIncomplete - -EmmyName <- ({} {[a-zA-Z_] [a-zA-Z0-9_]*}) - -> EmmyName -MustEmmyName <- EmmyName / DirtyEmmyName -DirtyEmmyName <- {} -> DirtyEmmyName -EmmyLongName <- ({} {(!%nl .)+}) - -> EmmyName -EmmyIncomplete <- MustEmmyName - -> EmmyIncomplete - -EmmyClass <- (MustEmmyName EmmyParentClass?) -EmmyParentClass <- %s* {} ':' %s* MustEmmyName - -EmmyType <- EmmyTypeUnits EmmyTypeEnums -EmmyTypeUnits <- {| - EmmyTypeUnit? - (%s* '|' %s* !String EmmyTypeUnit)* - |} -EmmyTypeEnums <- {| EmmyTypeEnum* |} -EmmyTypeUnit <- EmmyFunctionType - / EmmyTableType - / EmmyArrayType - / EmmyCommonType -EmmyCommonType <- EmmyName - -> EmmyCommonType -EmmyTypeEnum <- %s* (%nl %s* '---')? '|'? EmmyEnum - -> EmmyTypeEnum -EmmyEnum <- %s* {'>'?} %s* String (EmmyEnumComment / (!%nl !'|' .)*) -EmmyEnumComment <- %s* '#' %s* {(!%nl .)*} - -EmmyAlias <- MustEmmyName %s* EmmyType EmmyTypeEnum* - -EmmyParam <- MustEmmyName %s* EmmyType %s* EmmyOption %s* EmmyTypeEnum* -EmmyOption <- Table? - -> EmmyOption - -EmmyReturn <- {} %nil {} Table -> EmmyOption - / {} EmmyType {} EmmyOption - -EmmyField <- (EmmyFieldAccess MustEmmyName %s* EmmyType) -EmmyFieldAccess <- ({'public'} Cut %s*) - / ({'protected'} Cut %s*) - / ({'private'} Cut %s*) - / {} -> 'public' - -EmmyGeneric <- EmmyGenericBlock - (%s* ',' %s* EmmyGenericBlock)* -EmmyGenericBlock<- (MustEmmyName %s* (':' %s* EmmyType)?) - -> EmmyGenericBlock - -EmmyVararg <- EmmyType - -EmmyLanguage <- MustEmmyName - -EmmyArrayType <- ({} MustEmmyName -> EmmyCommonType {} '[' DirtyBR) - -> EmmyArrayType - / ({} PL EmmyCommonType DirtyPR '[' DirtyBR) - -> EmmyArrayType - -EmmyTableType <- ({} 'table' Cut '<' %s* EmmyType %s* ',' %s* EmmyType %s* '>' {}) - -> EmmyTableType - -EmmyFunctionType<- ({} 'fun' Cut %s* EmmyFunctionArgs %s* EmmyFunctionRtns {}) - -> EmmyFunctionType -EmmyFunctionArgs<- ('(' %s* EmmyFunctionArg %s* (',' %s* EmmyFunctionArg %s*)* DirtyPR) - -> EmmyFunctionArgs - / '(' %nil DirtyPR -> None - / %nil -EmmyFunctionRtns<- (':' %s* EmmyType (%s* ',' %s* EmmyType)*) - -> EmmyFunctionRtns - / %nil -EmmyFunctionArg <- MustEmmyName %s* ':' %s* EmmyType - -EmmySee <- {} MustEmmyName %s* '#' %s* MustEmmyName {} -EmmyOverLoad <- EmmyFunctionType -]] - -local ast = { - EmmyLua = function (start, emmy, finish) - emmy.start = start - emmy.finish = finish - 1 - State.emmy[#State.emmy+1] = emmy - end, - EmmyName = function (start, str) - return { - type = 'name', - start = start, - finish = start + #str - 1, - [1] = str, - } - end, - DirtyEmmyName = function (pos) - pushError { - type = 'MISS_NAME', - level = 'warning', - start = pos, - finish = pos, - } - return { - type = 'emmyName', - start = pos-1, - finish = pos-1, - [1] = '' - } - end, - EmmyClass = function (class, startPos, extends) - if extends and extends[1] == '' then - extends.start = startPos - end - return { - type = 'class', - class = class, - extends = extends, - } - end, - EmmyType = function (types, enums) - local result = { - type = 'type', - types = types, - enums = enums, - } - return result - end, - EmmyCommonType = function (name) - return { - type = 'common', - start = name.start, - finish = name.finish, - name = name, - } - end, - EmmyArrayType = function (start, emmy, _, finish) - emmy.type = 'emmyArrayType' - emmy.start = start - emmy.finish = finish - 1 - return emmy - end, - EmmyTableType = function (start, keyType, valueType, finish) - return { - type = 'emmyTableType', - start = start, - finish = finish - 1, - [1] = keyType, - [2] = valueType, - } - end, - EmmyFunctionType = function (start, args, returns, finish) - local result = { - start = start, - finish = finish - 1, - type = 'emmyFunctionType', - args = args, - returns = returns, - } - return result - end, - EmmyFunctionRtns = function (...) - return {...} - end, - EmmyFunctionArgs = function (...) - local args = {...} - args[#args] = nil - return args - end, - EmmyAlias = function (name, emmyName, ...) - return { - type = 'emmyAlias', - start = name.start, - finish = emmyName.finish, - name, - emmyName, - ... - } - end, - EmmyParam = function (argName, emmyName, option, ...) - local emmy = { - type = 'emmyParam', - option = option, - argName, - emmyName, - ... - } - emmy.start = emmy[1].start - emmy.finish = emmy[#emmy].finish - return emmy - end, - EmmyReturn = function (start, type, finish, option) - local emmy = { - type = 'emmyReturn', - option = option, - start = start, - finish = finish - 1, - [1] = type, - } - return emmy - end, - EmmyField = function (access, fieldName, ...) - local obj = { - type = 'emmyField', - access, fieldName, - ... - } - obj.start = obj[2].start - obj.finish = obj[3].finish - return obj - end, - EmmyGenericBlock = function (genericName, parentName) - return { - start = genericName.start, - finish = parentName and parentName.finish or genericName.finish, - genericName, - parentName, - } - end, - EmmyGeneric = function (...) - local emmy = { - type = 'emmyGeneric', - ... - } - emmy.start = emmy[1].start - emmy.finish = emmy[#emmy].finish - return emmy - end, - EmmyVararg = function (typeName) - return { - type = 'emmyVararg', - start = typeName.start, - finish = typeName.finish, - typeName, - } - end, - EmmyLanguage = function (language) - return { - type = 'emmyLanguage', - start = language.start, - finish = language.finish, - language, - } - end, - EmmySee = function (start, className, methodName, finish) - return { - type = 'emmySee', - start = start, - finish = finish - 1, - className, methodName - } - end, - EmmyOverLoad = function (EmmyFunctionType) - EmmyFunctionType.type = 'emmyOverLoad' - return EmmyFunctionType - end, - EmmyIncomplete = function (emmyName) - emmyName.type = 'emmyIncomplete' - return emmyName - end, - EmmyComment = function (...) - return { - type = 'emmyComment', - [1] = table.concat({...}), - } - end, - EmmyOption = function (options) - if not options or options == '' then - return nil - end - local option = {} - for _, pair in ipairs(options) do - if pair.type == 'pair' then - local key = pair[1] - local value = pair[2] - if key.type == 'name' then - option[key[1]] = value[1] - end - end - end - return option - end, - EmmyTypeEnum = function (default, enum, comment) - enum.type = 'enum' - if default ~= '' then - enum.default = true - end - enum.comment = comment - return enum - end, -} - -local function init(state) - State = state - pushError = state.pushError -end - -return { - grammar = grammar, - ast = ast, - init = init, -} diff --git a/server-beta/src/parser/grammar.lua b/server-beta/src/parser/grammar.lua deleted file mode 100644 index fd699bd4..00000000 --- a/server-beta/src/parser/grammar.lua +++ /dev/null @@ -1,537 +0,0 @@ -local re = require 'parser.relabel' -local m = require 'lpeglabel' -local emmy = require 'parser.emmy' -local ast = require 'parser.ast' - -local scriptBuf = '' -local compiled = {} -local defs = ast.defs - --- goto 可以作为名字,合法性之后处理 -local RESERVED = { - ['and'] = true, - ['break'] = true, - ['do'] = true, - ['else'] = true, - ['elseif'] = true, - ['end'] = true, - ['false'] = true, - ['for'] = true, - ['function'] = true, - ['if'] = true, - ['in'] = true, - ['local'] = true, - ['nil'] = true, - ['not'] = true, - ['or'] = true, - ['repeat'] = true, - ['return'] = true, - ['then'] = true, - ['true'] = true, - ['until'] = true, - ['while'] = true, -} - -defs.nl = (m.P'\r\n' + m.S'\r\n') -defs.s = m.S' \t' -defs.S = - defs.s -defs.ea = '\a' -defs.eb = '\b' -defs.ef = '\f' -defs.en = '\n' -defs.er = '\r' -defs.et = '\t' -defs.ev = '\v' -defs['nil'] = m.Cp() / function () return nil end -defs['false'] = m.Cp() / function () return false end -defs.NotReserved = function (_, _, str) - if RESERVED[str] then - return false - end - return true -end -defs.Reserved = function (_, _, str) - if RESERVED[str] then - return true - end - return false -end -defs.None = function () end -defs.np = m.Cp() / function (n) return n+1 end - -m.setmaxstack(1000) - -local eof = re.compile '!. / %{SYNTAX_ERROR}' - -local function grammar(tag) - return function (script) - scriptBuf = script .. '\r\n' .. scriptBuf - compiled[tag] = re.compile(scriptBuf, defs) * eof - end -end - -local function errorpos(pos, err) - return { - type = 'UNKNOWN', - start = pos or 0, - finish = pos or 0, - err = err, - } -end - -grammar 'Comment' [[ -Comment <- LongComment - / '--' ShortComment -LongComment <- ('--[' {} {:eq: '='* :} {} '[' - {(!CommentClose .)*} - (CommentClose / {})) - -> LongComment - / ( - {} '/*' {} - (!'*/' .)* - {} '*/' {} - ) - -> CLongComment -CommentClose <- ']' =eq ']' -ShortComment <- (!%nl .)* -]] - -grammar 'Sp' [[ -Sp <- (EmmyLua / Comment / %nl / %s)* -Sps <- (EmmyLua / Comment / %nl / %s)+ - --- 占位 -EmmyLua <- !. . -]] - -grammar 'Common' [[ -Word <- [a-zA-Z0-9_] -Cut <- !Word -X16 <- [a-fA-F0-9] -Rest <- (!%nl .)* - -AND <- Sp {'and'} Cut -BREAK <- Sp 'break' Cut -FALSE <- Sp 'false' Cut -GOTO <- Sp 'goto' Cut -LOCAL <- Sp 'local' Cut -NIL <- Sp 'nil' Cut -NOT <- Sp 'not' Cut -OR <- Sp {'or'} Cut -RETURN <- Sp 'return' Cut -TRUE <- Sp 'true' Cut - -DO <- Sp {} 'do' {} Cut - / Sp({} 'then' {} Cut) -> ErrDo -IF <- Sp {} 'if' {} Cut -ELSE <- Sp {} 'else' {} Cut -ELSEIF <- Sp {} 'elseif' {} Cut -END <- Sp {} 'end' {} Cut -FOR <- Sp {} 'for' {} Cut -FUNCTION <- Sp {} 'function' {} Cut -IN <- Sp {} 'in' {} Cut -REPEAT <- Sp {} 'repeat' {} Cut -THEN <- Sp {} 'then' {} Cut - / Sp({} 'do' {} Cut) -> ErrThen -UNTIL <- Sp {} 'until' {} Cut -WHILE <- Sp {} 'while' {} Cut - - -Esc <- '\' -> '' - EChar -EChar <- 'a' -> ea - / 'b' -> eb - / 'f' -> ef - / 'n' -> en - / 'r' -> er - / 't' -> et - / 'v' -> ev - / '\' - / '"' - / "'" - / %nl - / ('z' (%nl / %s)*) -> '' - / ({} 'x' {X16 X16}) -> Char16 - / ([0-9] [0-9]? [0-9]?) -> Char10 - / ('u{' {} {Word*} '}') -> CharUtf8 - -- 错误处理 - / 'x' {} -> MissEscX - / 'u' !'{' {} -> MissTL - / 'u{' Word* !'}' {} -> MissTR - / {} -> ErrEsc - -BOR <- Sp {'|'} -BXOR <- Sp {'~'} !'=' -BAND <- Sp {'&'} -Bshift <- Sp {BshiftList} -BshiftList <- '<<' - / '>>' -Concat <- Sp {'..'} -Adds <- Sp {AddsList} -AddsList <- '+' - / '-' -Muls <- Sp {MulsList} -MulsList <- '*' - / '//' - / '/' - / '%' -Unary <- Sp {} {UnaryList} -UnaryList <- NOT - / '#' - / '-' - / '~' !'=' -POWER <- Sp {'^'} - -BinaryOp <-( Sp {} {'or'} Cut - / Sp {} {'and'} Cut - / Sp {} {'<=' / '>=' / '<'!'<' / '>'!'>' / '~=' / '=='} - / Sp {} ({} '=' {}) -> ErrEQ - / Sp {} ({} '!=' {}) -> ErrUEQ - / Sp {} {'|'} - / Sp {} {'~'} - / Sp {} {'&'} - / Sp {} {'<<' / '>>'} - / Sp {} {'..'} !'.' - / Sp {} {'+' / '-'} - / Sp {} {'*' / '//' / '/' / '%'} - / Sp {} {'^'} - )-> BinaryOp -UnaryOp <-( Sp {} {'not' Cut / '#' / '~' !'=' / '-' !'-'} - )-> UnaryOp - -PL <- Sp '(' -PR <- Sp ')' -BL <- Sp '[' !'[' !'=' -BR <- Sp ']' -TL <- Sp '{' -TR <- Sp '}' -COMMA <- Sp ({} ',') - -> COMMA -SEMICOLON <- Sp ({} ';') - -> SEMICOLON -DOTS <- Sp ({} '...') - -> DOTS -DOT <- Sp ({} '.' !'.') - -> DOT -COLON <- Sp ({} ':' !':') - -> COLON -LABEL <- Sp '::' -ASSIGN <- Sp '=' !'=' -AssignOrEQ <- Sp ({} '==' {}) - -> ErrAssign - / Sp '=' - -DirtyBR <- BR / {} -> MissBR -DirtyTR <- TR / {} -> MissTR -DirtyPR <- PR / {} -> MissPR -DirtyLabel <- LABEL / {} -> MissLabel -NeedEnd <- END / {} -> MissEnd -NeedDo <- DO / {} -> MissDo -NeedAssign <- ASSIGN / {} -> MissAssign -NeedComma <- COMMA / {} -> MissComma -NeedIn <- IN / {} -> MissIn -NeedUntil <- UNTIL / {} -> MissUntil -NeedThen <- THEN / {} -> MissThen -]] - -grammar 'Nil' [[ -Nil <- Sp ({} -> Nil) NIL -]] - -grammar 'Boolean' [[ -Boolean <- Sp ({} -> True) TRUE - / Sp ({} -> False) FALSE -]] - -grammar 'String' [[ -String <- Sp ({} StringDef {}) - -> String -StringDef <- {'"'} - {~(Esc / !%nl !'"' .)*~} -> 1 - ('"' / {} -> MissQuote1) - / {"'"} - {~(Esc / !%nl !"'" .)*~} -> 1 - ("'" / {} -> MissQuote2) - / ('[' {} {:eq: '='* :} {} '[' %nl? - {(!StringClose .)*} -> 1 - (StringClose / {})) - -> LongString -StringClose <- ']' =eq ']' -]] - -grammar 'Number' [[ -Number <- Sp ({} {NumberDef} {}) -> Number - NumberSuffix? - ErrNumber? -NumberDef <- Number16 / Number10 -NumberSuffix<- ({} {[uU]? [lL] [lL]}) -> FFINumber - / ({} {[iI]}) -> ImaginaryNumber -ErrNumber <- ({} {([0-9a-zA-Z] / '.')+}) -> UnknownSymbol - -Number10 <- Float10 Float10Exp? - / Integer10 Float10? Float10Exp? -Integer10 <- [0-9]+ ('.' [0-9]*)? -Float10 <- '.' [0-9]+ -Float10Exp <- [eE] [+-]? [0-9]+ - / ({} [eE] [+-]? {}) -> MissExponent - -Number16 <- '0' [xX] Float16 Float16Exp? - / '0' [xX] Integer16 Float16? Float16Exp? -Integer16 <- X16+ ('.' X16*)? - / ({} {Word*}) -> MustX16 -Float16 <- '.' X16+ - / '.' ({} {Word*}) -> MustX16 -Float16Exp <- [pP] [+-]? [0-9]+ - / ({} [pP] [+-]? {}) -> MissExponent -]] - -grammar 'Name' [[ -Name <- Sp ({} NameBody {}) - -> Name -NameBody <- {[a-zA-Z_] [a-zA-Z0-9_]*} -FreeName <- Sp ({} {NameBody=>NotReserved} {}) - -> Name -KeyWord <- Sp NameBody=>Reserved -MustName <- Name / DirtyName -DirtyName <- {} -> DirtyName -]] - -grammar 'Exp' [[ -Exp <- (UnUnit BinUnit*) - -> Binary -BinUnit <- (BinaryOp UnUnit?) - -> SubBinary -UnUnit <- ExpUnit - / (UnaryOp+ (ExpUnit / MissExp)) - -> Unary -ExpUnit <- Nil - / Boolean - / String - / Number - / Dots - / Table - / Function - / Simple - -Simple <- {| Prefix (Sp Suffix)* |} - -> Simple -Prefix <- Sp ({} PL DirtyExp DirtyPR {}) - -> Paren - / Single -Single <- FreeName - -> Single -Suffix <- SuffixWithoutCall - / ({} PL SuffixCall DirtyPR {}) - -> Call -SuffixCall <- Sp ({} {| (COMMA / Exp)+ |} {}) - -> PackExpList - / %nil -SuffixWithoutCall - <- (DOT (Name / MissField)) - -> GetField - / ({} BL DirtyExp DirtyBR {}) - -> GetIndex - / (COLON (Name / MissMethod) NeedCall) - -> GetMethod - / ({} {| Table |} {}) - -> Call - / ({} {| String |} {}) - -> Call -NeedCall <- (!(Sp CallStart) {} -> MissPL)? -MissField <- {} -> MissField -MissMethod <- {} -> MissMethod -CallStart <- PL - / TL - / '"' - / "'" - / '[' '='* '[' - -DirtyExp <- Exp - / {} -> DirtyExp -MaybeExp <- Exp / MissExp -MissExp <- {} -> MissExp -ExpList <- Sp {| MaybeExp (Sp ',' MaybeExp)* |} - -Dots <- DOTS - -> VarArgs - -Table <- Sp ({} TL {| TableField* |} DirtyTR {}) - -> Table -TableField <- COMMA - / SEMICOLON - / NewIndex - / NewField - / Exp -Index <- BL DirtyExp DirtyBR -NewIndex <- Sp ({} Index NeedAssign DirtyExp {}) - -> NewIndex -NewField <- Sp ({} MustName ASSIGN DirtyExp {}) - -> NewField - -Function <- FunctionBody - -> Function -FuncArgs <- Sp ({} PL {| FuncArg+ |} DirtyPR {}) - -> FuncArgs - / PL DirtyPR %nil - / {} -> MissPL DirtyPR %nil -FuncArg <- DOTS - / Name - / COMMA -FunctionBody<- FUNCTION FuncArgs - {| (!END Action)* |} - NeedEnd - --- 纯占位,修改了 `relabel.lua` 使重复定义不抛错 -Action <- !END . -]] - -grammar 'Action' [[ -Action <- Sp (CrtAction / UnkAction) -CrtAction <- Semicolon - / Do - / Break - / Return - / Label - / GoTo - / If - / For - / While - / Repeat - / NamedFunction - / LocalFunction - / Local - / Set - / Call - / ExpInAction -UnkAction <- ({} {Word+}) - -> UnknownAction - / ({} '//' {} (LongComment / ShortComment)) - -> CCommentPrefix - / ({} {. (!Sps !CrtAction .)*}) - -> UnknownAction -ExpInAction <- Sp ({} Exp {}) - -> ExpInAction - -Semicolon <- Sp ';' -SimpleList <- {| Simple (Sp ',' Simple)* |} - -Do <- Sp ({} - 'do' Cut - {| (!END Action)* |} - NeedEnd) - -> Do - -Break <- Sp ({} BREAK {}) - -> Break - -Return <- Sp ({} RETURN ReturnExpList {}) - -> Return -ReturnExpList - <- Sp {| Exp (Sp ',' MaybeExp)* |} - / Sp {| !Exp !',' |} - / ExpList - -Label <- Sp ({} LABEL MustName DirtyLabel {}) - -> Label - -GoTo <- Sp ({} GOTO MustName {}) - -> GoTo - -If <- Sp ({} {| IfHead IfBody* |} NeedEnd) - -> If - -IfHead <- Sp (IfPart {}) -> IfBlock - / Sp (ElseIfPart {}) -> ElseIfBlock - / Sp (ElsePart {}) -> ElseBlock -IfBody <- Sp (ElseIfPart {}) -> ElseIfBlock - / Sp (ElsePart {}) -> ElseBlock -IfPart <- IF DirtyExp NeedThen - {| (!ELSEIF !ELSE !END Action)* |} -ElseIfPart <- ELSEIF DirtyExp NeedThen - {| (!ELSEIF !ELSE !END Action)* |} -ElsePart <- ELSE - {| (!ELSEIF !ELSE !END Action)* |} - -For <- Loop / In - -Loop <- LoopBody - -> Loop -LoopBody <- FOR LoopArgs NeedDo - {} {| (!END Action)* |} - NeedEnd -LoopArgs <- MustName AssignOrEQ - ({} {| (COMMA / !DO !END Exp)* |} {}) - -> PackLoopArgs - -In <- InBody - -> In -InBody <- FOR InNameList NeedIn InExpList NeedDo - {} {| (!END Action)* |} - NeedEnd -InNameList <- ({} {| (COMMA / !IN !DO !END Name)* |} {}) - -> PackInNameList -InExpList <- ({} {| (COMMA / !DO !DO !END Exp)* |} {}) - -> PackInExpList - -While <- WhileBody - -> While -WhileBody <- WHILE DirtyExp NeedDo - {| (!END Action)* |} - NeedEnd - -Repeat <- (RepeatBody {}) - -> Repeat -RepeatBody <- REPEAT - {| (!UNTIL Action)* |} - NeedUntil DirtyExp - -LocalAttr <- {| (Sp '<' Sp MustName Sp LocalAttrEnd)+ |} - -> LocalAttr -LocalAttrEnd<- '>' / {} -> MissGT -Local <- Sp ({} LOCAL LocalNameList ((AssignOrEQ ExpList) / %nil) {}) - -> Local -Set <- Sp ({} SimpleList AssignOrEQ ExpList {}) - -> Set -LocalNameList - <- {| LocalName (Sp ',' LocalName)* |} -LocalName <- (MustName LocalAttr?) - -> LocalName - -Call <- Simple - -> SimpleCall - -LocalFunction - <- Sp ({} LOCAL FunctionNamedBody) - -> LocalFunction - -NamedFunction - <- FunctionNamedBody - -> NamedFunction -FunctionNamedBody - <- FUNCTION FuncName FuncArgs - {| (!END Action)* |} - NeedEnd -FuncName <- {| Single (Sp SuffixWithoutCall)* |} - -> Simple - / {} -> MissName %nil -]] - ---grammar 'EmmyLua' (emmy.grammar) - -grammar 'Lua' [[ -Lua <- Head? - ({} {| Action* |} {}) -> Lua - Sp -Head <- '#' (!%nl .)* -]] - -return function (self, lua, mode) - local gram = compiled[mode] or compiled['Lua'] - local r, _, pos = gram:match(lua) - if not r then - local err = errorpos(pos) - return nil, err - end - - return r -end diff --git a/server-beta/src/parser/guide.lua b/server-beta/src/parser/guide.lua deleted file mode 100644 index af511555..00000000 --- a/server-beta/src/parser/guide.lua +++ /dev/null @@ -1,621 +0,0 @@ -local error = error -local type = type -local next = next -local tostring = tostring - -_ENV = nil - -local m = {} - -local blockTypes = { - ['while'] = true, - ['in'] = true, - ['loop'] = true, - ['repeat'] = true, - ['do'] = true, - ['function'] = true, - ['ifblock'] = true, - ['elseblock'] = true, - ['elseifblock'] = true, - ['main'] = true, -} - -local breakBlockTypes = { - ['while'] = true, - ['in'] = true, - ['loop'] = true, - ['repeat'] = true, -} - -m.childMap = { - ['main'] = {'#'}, - ['repeat'] = {'#', 'filter'}, - ['while'] = {'filter', '#'}, - ['in'] = {'keys', '#'}, - ['loop'] = {'loc', 'max', 'step', '#'}, - ['if'] = {'#'}, - ['ifblock'] = {'filter', '#'}, - ['elseifblock'] = {'filter', '#'}, - ['elseblock'] = {'#'}, - ['setfield'] = {'node', 'field', 'value'}, - ['setglobal'] = {'value'}, - ['local'] = {'attrs', 'value'}, - ['setlocal'] = {'value'}, - ['return'] = {'#'}, - ['do'] = {'#'}, - ['select'] = {'vararg'}, - ['table'] = {'#'}, - ['tableindex'] = {'index', 'value'}, - ['tablefield'] = {'field', 'value'}, - ['function'] = {'args', '#'}, - ['funcargs'] = {'#'}, - ['setmethod'] = {'node', 'method', 'value'}, - ['getmethod'] = {'node', 'method'}, - ['setindex'] = {'node', 'index', 'value'}, - ['getindex'] = {'node', 'index'}, - ['paren'] = {'exp'}, - ['call'] = {'node', 'args'}, - ['callargs'] = {'#'}, - ['getfield'] = {'node', 'field'}, - ['list'] = {'#'}, - ['binary'] = {1, 2}, - ['unary'] = {1} -} - -m.actionMap = { - ['main'] = {'#'}, - ['repeat'] = {'#'}, - ['while'] = {'#'}, - ['in'] = {'#'}, - ['loop'] = {'#'}, - ['if'] = {'#'}, - ['ifblock'] = {'#'}, - ['elseifblock'] = {'#'}, - ['elseblock'] = {'#'}, - ['do'] = {'#'}, - ['function'] = {'#'}, - ['funcargs'] = {'#'}, -} - ---- 是否是字面量 -function m.isLiteral(obj) - local tp = obj.type - return tp == 'nil' - or tp == 'boolean' - or tp == 'string' - or tp == 'number' - or tp == 'table' -end - ---- 获取字面量 -function m.getLiteral(obj) - local tp = obj.type - if tp == 'boolean' then - return obj[1] - elseif tp == 'string' then - return obj[1] - elseif tp == 'number' then - return obj[1] - end - return nil -end - ---- 寻找父函数 -function m.getParentFunction(obj) - for _ = 1, 1000 do - obj = obj.parent - if not obj then - break - end - local tp = obj.type - if tp == 'function' or tp == 'main' then - return obj - end - end - return nil -end - ---- 寻找所在区块 -function m.getBlock(obj) - for _ = 1, 1000 do - if not obj then - return nil - end - local tp = obj.type - if blockTypes[tp] then - return obj - end - obj = obj.parent - end - error('guide.getBlock overstack') -end - ---- 寻找所在父区块 -function m.getParentBlock(obj) - for _ = 1, 1000 do - obj = obj.parent - if not obj then - return nil - end - local tp = obj.type - if blockTypes[tp] then - return obj - end - end - error('guide.getParentBlock overstack') -end - ---- 寻找所在可break的父区块 -function m.getBreakBlock(obj) - for _ = 1, 1000 do - obj = obj.parent - if not obj then - return nil - end - local tp = obj.type - if breakBlockTypes[tp] then - return obj - end - if tp == 'function' then - return nil - end - end - error('guide.getBreakBlock overstack') -end - ---- 寻找根区块 -function m.getRoot(obj) - for _ = 1, 1000 do - local parent = obj.parent - if not parent then - return obj - end - obj = parent - end - error('guide.getRoot overstack') -end - ---- 寻找函数的不定参数,返回不定参在第几个参数上,以及该参数对象。 ---- 如果函数是主函数,则返回`0, nil`。 ----@return table ----@return integer -function m.getFunctionVarArgs(func) - if func.type == 'main' then - return 0, nil - end - if func.type ~= 'function' then - return nil, nil - end - local args = func.args - if not args then - return nil, nil - end - for i = 1, #args do - local arg = args[i] - if arg.type == '...' then - return i, arg - end - end - return nil, nil -end - ---- 获取指定区块中可见的局部变量 ----@param block table ----@param name string {comment = '变量名'} ----@param pos integer {comment = '可见位置'} -function m.getLocal(block, name, pos) - block = m.getBlock(block) - for _ = 1, 1000 do - if not block then - return nil - end - local locals = block.locals - local res - if not locals then - goto CONTINUE - end - for i = 1, #locals do - local loc = locals[i] - if loc.effect > pos then - break - end - if loc[1] == name then - if not res or res.effect < loc.effect then - res = loc - end - end - end - if res then - return res, res - end - ::CONTINUE:: - block = m.getParentBlock(block) - end - error('guide.getLocal overstack') -end - ---- 获取指定区块中可见的标签 ----@param block table ----@param name string {comment = '标签名'} -function m.getLabel(block, name) - block = m.getBlock(block) - for _ = 1, 1000 do - if not block then - return nil - end - local labels = block.labels - if labels then - local label = labels[name] - if label then - return label - end - end - if block.type == 'function' then - return nil - end - block = m.getParentBlock(block) - end - error('guide.getLocal overstack') -end - ---- 判断source是否包含offset -function m.isContain(source, offset) - return source.start <= offset and source.finish >= offset - 1 -end - ---- 判断offset在source的影响范围内 ---- ---- 主要针对赋值等语句时,key包含value -function m.isInRange(source, offset) - return (source.vstart or source.start) <= offset and (source.range or source.finish) >= offset - 1 -end - ---- 添加child -function m.addChilds(list, obj, map) - local keys = map[obj.type] - if keys then - for i = 1, #keys do - local key = keys[i] - if key == '#' then - for i = 1, #obj do - list[#list+1] = obj[i] - end - else - list[#list+1] = obj[key] - end - end - end -end - ---- 遍历所有包含offset的source -function m.eachSourceContain(ast, offset, callback) - local list = { ast } - while true do - local len = #list - if len == 0 then - return - end - local obj = list[len] - list[len] = nil - if m.isInRange(obj, offset) then - if m.isContain(obj, offset) then - local res = callback(obj) - if res ~= nil then - return res - end - end - m.addChilds(list, obj, m.childMap) - end - end -end - ---- 遍历所有指定类型的source -function m.eachSourceType(ast, type, callback) - local cache = ast.typeCache - if not cache then - local mark = {} - cache = {} - ast.typeCache = cache - m.eachSource(ast, function (source) - if mark[source] then - return - end - mark[source] = true - local tp = source.type - if not tp then - return - end - local myCache = cache[tp] - if not myCache then - myCache = {} - cache[tp] = myCache - end - myCache[#myCache+1] = source - end) - end - local myCache = cache[type] - if not myCache then - return - end - for i = 1, #myCache do - callback(myCache[i]) - end -end - ---- 遍历所有的source -function m.eachSource(ast, callback) - local list = { ast } - while true do - local len = #list - if len == 0 then - return - end - local obj = list[len] - list[len] = nil - callback(obj) - m.addChilds(list, obj, m.childMap) - end -end - ---- 获取指定的 special -function m.eachSpecialOf(ast, name, callback) - local root = m.getRoot(ast) - if not root.specials then - return - end - local specials = root.specials[name] - if not specials then - return - end - for i = 1, #specials do - callback(specials[i]) - end -end - ---- 获取偏移对应的坐标 ----@param lines table ----@return integer {name = 'row'} ----@return integer {name = 'col'} -function m.positionOf(lines, offset) - if offset < 1 then - return 0, 0 - end - local lastLine = lines[#lines] - if offset > lastLine.finish then - return #lines, lastLine.finish - lastLine.start + 1 - end - local min = 1 - local max = #lines - for _ = 1, 100 do - if max <= min then - local line = lines[min] - return min, offset - line.start + 1 - end - local row = (max - min) // 2 + min - local line = lines[row] - if offset < line.start then - max = row - 1 - elseif offset > line.finish then - min = row + 1 - else - return row, offset - line.start + 1 - end - end - error('Stack overflow!') -end - ---- 获取坐标对应的偏移 ----@param lines table ----@param row integer ----@param col integer ----@return integer {name = 'offset'} -function m.offsetOf(lines, row, col) - if row < 1 then - return 0 - end - if row > #lines then - local lastLine = lines[#lines] - return lastLine.finish - end - local line = lines[row] - local len = line.finish - line.start + 1 - if col < 0 then - return line.start - elseif col > len then - return line.finish - else - return line.start + col - 1 - end -end - -function m.lineContent(lines, text, row) - local line = lines[row] - if not line then - return '' - end - return text:sub(line.start, line.finish) -end - -function m.lineRange(lines, row) - local line = lines[row] - if not line then - return 0, 0 - end - return line.start, line.finish -end - -function m.getName(obj) - local tp = obj.type - if tp == 'getglobal' - or tp == 'setglobal' then - return obj[1] - elseif tp == 'local' - or tp == 'getlocal' - or tp == 'setlocal' then - return obj[1] - elseif tp == 'getfield' - or tp == 'setfield' - or tp == 'tablefield' then - return obj.field[1] - elseif tp == 'getmethod' - or tp == 'setmethod' then - return obj.method[1] - elseif tp == 'getindex' - or tp == 'setindex' - or tp == 'tableindex' then - return m.getName(obj.index) - elseif tp == 'field' - or tp == 'method' then - return obj[1] - elseif tp == 'index' then - return m.getName(obj.index) - elseif tp == 'string' then - return obj[1] - end - return nil -end - -function m.getKeyName(obj) - local tp = obj.type - if tp == 'getglobal' - or tp == 'setglobal' then - return 's|' .. obj[1] - elseif tp == 'getfield' - or tp == 'setfield' - or tp == 'tablefield' then - if obj.field then - return 's|' .. obj.field[1] - end - elseif tp == 'getmethod' - or tp == 'setmethod' then - if obj.method then - return 's|' .. obj.method[1] - end - elseif tp == 'getindex' - or tp == 'setindex' - or tp == 'tableindex' then - if obj.index then - return m.getKeyName(obj.index) - end - elseif tp == 'field' - or tp == 'method' then - return 's|' .. obj[1] - elseif tp == 'string' then - local s = obj[1] - if s then - return 's|' .. s - else - return s - end - elseif tp == 'number' then - local n = obj[1] - if n then - return ('n|%q'):format(obj[1]) - else - return 'n' - end - elseif tp == 'boolean' then - local b = obj[1] - if b then - return 'b|' .. tostring(b) - else - return 'b' - end - end - return nil -end - -function m.getENV(ast) - if ast.type ~= 'main' then - return nil - end - return ast.locals[1] -end - ---- 测试 a 到 b 的路径(不经过函数,不考虑 goto), ---- 每个路径是一个 block 。 ---- ---- 如果 a 在 b 的前面,返回 `"before"` 加上 2个`list<block>` ---- ---- 如果 a 在 b 的后面,返回 `"after"` 加上 2个`list<block>` ---- ---- 否则返回 `false` ---- ---- 返回的2个 `list` 分别为基准block到达 a 与 b 的路径。 ----@param a table ----@param b table ----@return string|boolean mode ----@return table|nil pathA ----@return table|nil pathB -function m.getPath(a, b) - --- 首先测试双方在同一个函数内 - if m.getParentFunction(a) ~= m.getParentFunction(b) then - return false - end - local mode - local objA - local objB - if a.finish < b.start then - mode = 'before' - objA = a - objB = b - elseif a.start > b.finish then - mode = 'after' - objA = b - objB = a - else - return 'equal', {}, {} - end - local pathA = {} - local pathB = {} - for _ = 1, 1000 do - objA = m.getParentBlock(objA) - pathA[#pathA+1] = objA - if objA.type == 'function' or objA.type == 'main' then - break - end - end - for _ = 1, 1000 do - objB = m.getParentBlock(objB) - pathB[#pathB+1] = objB - if objB.type == 'function' or objB.type == 'main' then - break - end - end - -- pathA: {1, 2, 3, 4, 5} - -- pathB: {5, 6, 2, 3} - local top = #pathB - local start - for i = #pathA, 1, -1 do - local currentBlock = pathA[i] - if currentBlock == pathB[top] then - start = i - break - end - end - -- pathA: { 1, 2, 3} - -- pathB: {5, 6, 2, 3} - local extra = 0 - local align = top - start - for i = start, 1, -1 do - local currentA = pathA[i] - local currentB = pathB[i+align] - if currentA ~= currentB then - extra = i - break - end - end - -- pathA: {1} - local resultA = {} - for i = extra, 1, -1 do - resultA[#resultA+1] = pathA[i] - end - -- pathB: {5, 6} - local resultB = {} - for i = extra + align, 1, -1 do - resultB[#resultB+1] = pathB[i] - end - return mode, resultA, resultB -end - -return m diff --git a/server-beta/src/parser/init.lua b/server-beta/src/parser/init.lua deleted file mode 100644 index 5eeb0da2..00000000 --- a/server-beta/src/parser/init.lua +++ /dev/null @@ -1,11 +0,0 @@ -local api = { - grammar = require 'parser.grammar', - parse = require 'parser.parse', - compile = require 'parser.compile', - split = require 'parser.split', - calcline = require 'parser.calcline', - lines = require 'parser.lines', - guide = require 'parser.guide', -} - -return api diff --git a/server-beta/src/parser/lines.lua b/server-beta/src/parser/lines.lua deleted file mode 100644 index c7961d13..00000000 --- a/server-beta/src/parser/lines.lua +++ /dev/null @@ -1,46 +0,0 @@ -local m = require 'lpeglabel' -local utf8Len = utf8.len - -_ENV = nil - -local function Line(start, line, range, finish) - line.start = start - line.finish = finish - 1 - line.range = range - 1 - return line -end - -local function Space(...) - local line = {...} - local sp = 0 - local tab = 0 - for i = 1, #line do - if line[i] == ' ' then - sp = sp + 1 - elseif line[i] == '\t' then - tab = tab + 1 - end - line[i] = nil - end - line.sp = sp - line.tab = tab - return line -end - -local parser = m.P{ -'Lines', -Lines = m.Ct(m.V'Line'^0 * m.V'LastLine'), -Line = m.Cp() * m.V'Indent' * (1 - m.V'Nl')^0 * m.Cp() * m.V'Nl' * m.Cp() / Line, -LastLine= m.Cp() * m.V'Indent' * (1 - m.V'Nl')^0 * m.Cp() * m.Cp() / Line, -Nl = m.P'\r\n' + m.S'\r\n', -Indent = m.C(m.S' \t')^0 / Space, -} - -return function (self, text) - local lines, err = parser:match(text) - if not lines then - return nil, err - end - - return lines -end diff --git a/server-beta/src/parser/parse.lua b/server-beta/src/parser/parse.lua deleted file mode 100644 index bbc01b10..00000000 --- a/server-beta/src/parser/parse.lua +++ /dev/null @@ -1,45 +0,0 @@ -local ast = require 'parser.ast' - -return function (self, lua, mode, version) - local errs = {} - local diags = {} - local state = { - version = version, - lua = lua, - emmy = {}, - root = {}, - errs = errs, - diags = diags, - pushError = function (err) - if err.finish < err.start then - err.finish = err.start - end - local last = errs[#errs] - if last then - if last.start <= err.start and last.finish >= err.finish then - return - end - end - err.level = err.level or 'error' - errs[#errs+1] = err - return err - end, - pushDiag = function (code, info) - if not diags[code] then - diags[code] = {} - end - diags[code][#diags[code]+1] = info - end - } - ast.init(state) - local suc, res, err = xpcall(self.grammar, debug.traceback, self, lua, mode) - ast.close() - if not suc then - return nil, res - end - if not res then - state.pushError(err) - end - state.ast = res - return state -end diff --git a/server-beta/src/parser/relabel.lua b/server-beta/src/parser/relabel.lua deleted file mode 100644 index ac902403..00000000 --- a/server-beta/src/parser/relabel.lua +++ /dev/null @@ -1,361 +0,0 @@ --- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $ - --- imported functions and modules -local tonumber, type, print, error = tonumber, type, print, error -local pcall = pcall -local setmetatable = setmetatable -local tinsert, concat = table.insert, table.concat -local rep = string.rep -local m = require"lpeglabel" - --- 'm' will be used to parse expressions, and 'mm' will be used to --- create expressions; that is, 're' runs on 'm', creating patterns --- on 'mm' -local mm = m - --- pattern's metatable -local mt = getmetatable(mm.P(0)) - - - --- No more global accesses after this point -_ENV = nil - - -local any = m.P(1) -local dummy = mm.P(false) - - -local errinfo = { - NoPatt = "no pattern found", - ExtraChars = "unexpected characters after the pattern", - - ExpPatt1 = "expected a pattern after '/'", - - ExpPatt2 = "expected a pattern after '&'", - ExpPatt3 = "expected a pattern after '!'", - - ExpPatt4 = "expected a pattern after '('", - ExpPatt5 = "expected a pattern after ':'", - ExpPatt6 = "expected a pattern after '{~'", - ExpPatt7 = "expected a pattern after '{|'", - - ExpPatt8 = "expected a pattern after '<-'", - - ExpPattOrClose = "expected a pattern or closing '}' after '{'", - - ExpNumName = "expected a number, '+', '-' or a name (no space) after '^'", - ExpCap = "expected a string, number, '{}' or name after '->'", - - ExpName1 = "expected the name of a rule after '=>'", - ExpName2 = "expected the name of a rule after '=' (no space)", - ExpName3 = "expected the name of a rule after '<' (no space)", - - ExpLab1 = "expected a label after '{'", - - ExpNameOrLab = "expected a name or label after '%' (no space)", - - ExpItem = "expected at least one item after '[' or '^'", - - MisClose1 = "missing closing ')'", - MisClose2 = "missing closing ':}'", - MisClose3 = "missing closing '~}'", - MisClose4 = "missing closing '|}'", - MisClose5 = "missing closing '}'", -- for the captures - - MisClose6 = "missing closing '>'", - MisClose7 = "missing closing '}'", -- for the labels - - MisClose8 = "missing closing ']'", - - MisTerm1 = "missing terminating single quote", - MisTerm2 = "missing terminating double quote", -} - -local function expect (pattern, label) - return pattern + m.T(label) -end - - --- Pre-defined names -local Predef = { nl = m.P"\n" } - - -local mem -local fmem -local gmem - - -local function updatelocale () - mm.locale(Predef) - Predef.a = Predef.alpha - Predef.c = Predef.cntrl - Predef.d = Predef.digit - Predef.g = Predef.graph - Predef.l = Predef.lower - Predef.p = Predef.punct - Predef.s = Predef.space - Predef.u = Predef.upper - Predef.w = Predef.alnum - Predef.x = Predef.xdigit - Predef.A = any - Predef.a - Predef.C = any - Predef.c - Predef.D = any - Predef.d - Predef.G = any - Predef.g - Predef.L = any - Predef.l - Predef.P = any - Predef.p - Predef.S = any - Predef.s - Predef.U = any - Predef.u - Predef.W = any - Predef.w - Predef.X = any - Predef.x - mem = {} -- restart memoization - fmem = {} - gmem = {} - local mt = {__mode = "v"} - setmetatable(mem, mt) - setmetatable(fmem, mt) - setmetatable(gmem, mt) -end - - -updatelocale() - - - -local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end) - - -local function getdef (id, defs) - local c = defs and defs[id] - if not c then - error("undefined name: " .. id) - end - return c -end - - -local function mult (p, n) - local np = mm.P(true) - while n >= 1 do - if n%2 >= 1 then np = np * p end - p = p * p - n = n/2 - end - return np -end - -local function equalcap (s, i, c) - if type(c) ~= "string" then return nil end - local e = #c + i - if s:sub(i, e - 1) == c then return e else return nil end -end - - -local S = (Predef.space + "--" * (any - Predef.nl)^0)^0 - -local name = m.C(m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0) - -local arrow = S * "<-" - --- a defined name only have meaning in a given environment -local Def = name * m.Carg(1) - -local num = m.C(m.R"09"^1) * S / tonumber - -local String = "'" * m.C((any - "'" - m.P"\n")^0) * expect("'", "MisTerm1") - + '"' * m.C((any - '"' - m.P"\n")^0) * expect('"', "MisTerm2") - - -local defined = "%" * Def / function (c,Defs) - local cat = Defs and Defs[c] or Predef[c] - if not cat then - error("name '" .. c .. "' undefined") - end - return cat -end - -local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R - -local item = defined + Range + m.C(any - m.P"\n") - -local Class = - "[" - * (m.C(m.P"^"^-1)) -- optional complement symbol - * m.Cf(expect(item, "ExpItem") * (item - "]")^0, mt.__add) - / function (c, p) return c == "^" and any - p or p end - * expect("]", "MisClose8") - -local function adddef (t, k, exp) - if t[k] then - -- TODO 改了一下这里的代码,重复定义不会抛错 - --error("'"..k.."' already defined as a rule") - else - t[k] = exp - end - return t -end - -local function firstdef (n, r) return adddef({n}, n, r) end - - -local function NT (n, b) - if not b then - error("rule '"..n.."' used outside a grammar") - else return mm.V(n) - end -end - - -local exp = m.P{ "Exp", - Exp = S * ( m.V"Grammar" - + m.Cf(m.V"Seq" * (S * "/" * expect(S * m.V"Seq", "ExpPatt1"))^0, mt.__add) ); - Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix" * (S * m.V"Prefix")^0, mt.__mul); - Prefix = "&" * expect(S * m.V"Prefix", "ExpPatt2") / mt.__len - + "!" * expect(S * m.V"Prefix", "ExpPatt3") / mt.__unm - + m.V"Suffix"; - Suffix = m.Cf(m.V"Primary" * - ( S * ( m.P"+" * m.Cc(1, mt.__pow) - + m.P"*" * m.Cc(0, mt.__pow) - + m.P"?" * m.Cc(-1, mt.__pow) - + "^" * expect( m.Cg(num * m.Cc(mult)) - + m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow) - + name * m.Cc"lab" - ), - "ExpNumName") - + "->" * expect(S * ( m.Cg((String + num) * m.Cc(mt.__div)) - + m.P"{}" * m.Cc(nil, m.Ct) - + m.Cg(Def / getdef * m.Cc(mt.__div)) - ), - "ExpCap") - + "=>" * expect(S * m.Cg(Def / getdef * m.Cc(m.Cmt)), - "ExpName1") - ) - )^0, function (a,b,f) if f == "lab" then return a + mm.T(b) else return f(a,b) end end ); - Primary = "(" * expect(m.V"Exp", "ExpPatt4") * expect(S * ")", "MisClose1") - + String / mm.P - + Class - + defined - + "%" * expect(m.P"{", "ExpNameOrLab") - * expect(S * m.V"Label", "ExpLab1") - * expect(S * "}", "MisClose7") / mm.T - + "{:" * (name * ":" + m.Cc(nil)) * expect(m.V"Exp", "ExpPatt5") - * expect(S * ":}", "MisClose2") - / function (n, p) return mm.Cg(p, n) end - + "=" * expect(name, "ExpName2") - / function (n) return mm.Cmt(mm.Cb(n), equalcap) end - + m.P"{}" / mm.Cp - + "{~" * expect(m.V"Exp", "ExpPatt6") - * expect(S * "~}", "MisClose3") / mm.Cs - + "{|" * expect(m.V"Exp", "ExpPatt7") - * expect(S * "|}", "MisClose4") / mm.Ct - + "{" * expect(m.V"Exp", "ExpPattOrClose") - * expect(S * "}", "MisClose5") / mm.C - + m.P"." * m.Cc(any) - + (name * -arrow + "<" * expect(name, "ExpName3") - * expect(">", "MisClose6")) * m.Cb("G") / NT; - Label = num + name; - Definition = name * arrow * expect(m.V"Exp", "ExpPatt8"); - Grammar = m.Cg(m.Cc(true), "G") - * m.Cf(m.V"Definition" / firstdef * (S * m.Cg(m.V"Definition"))^0, - adddef) / mm.P; -} - -local pattern = S * m.Cg(m.Cc(false), "G") * expect(exp, "NoPatt") / mm.P - * S * expect(-any, "ExtraChars") - -local function lineno (s, i) - if i == 1 then return 1, 1 end - local adjustment = 0 - -- report the current line if at end of line, not the next - if s:sub(i,i) == '\n' then - i = i-1 - adjustment = 1 - end - local rest, num = s:sub(1,i):gsub("[^\n]*\n", "") - local r = #rest - return 1 + num, (r ~= 0 and r or 1) + adjustment -end - -local function calcline (s, i) - if i == 1 then return 1, 1 end - local rest, line = s:sub(1,i):gsub("[^\n]*\n", "") - local col = #rest - return 1 + line, col ~= 0 and col or 1 -end - - -local function splitlines(str) - local t = {} - local function helper(line) tinsert(t, line) return "" end - helper((str:gsub("(.-)\r?\n", helper))) - return t -end - -local function compile (p, defs) - if mm.type(p) == "pattern" then return p end -- already compiled - p = p .. " " -- for better reporting of column numbers in errors when at EOF - local ok, cp, label, poserr = pcall(function() return pattern:match(p, 1, defs) end) - if not ok and cp then - if type(cp) == "string" then - cp = cp:gsub("^[^:]+:[^:]+: ", "") - end - error(cp, 3) - end - if not cp then - local lines = splitlines(p) - local line, col = lineno(p, poserr) - local err = {} - tinsert(err, "L" .. line .. ":C" .. col .. ": " .. errinfo[label]) - tinsert(err, lines[line]) - tinsert(err, rep(" ", col-1) .. "^") - error("syntax error(s) in pattern\n" .. concat(err, "\n"), 3) - end - return cp -end - -local function match (s, p, i) - local cp = mem[p] - if not cp then - cp = compile(p) - mem[p] = cp - end - return cp:match(s, i or 1) -end - -local function find (s, p, i) - local cp = fmem[p] - if not cp then - cp = compile(p) / 0 - cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) } - fmem[p] = cp - end - local i, e = cp:match(s, i or 1) - if i then return i, e - 1 - else return i - end -end - -local function gsub (s, p, rep) - local g = gmem[p] or {} -- ensure gmem[p] is not collected while here - gmem[p] = g - local cp = g[rep] - if not cp then - cp = compile(p) - cp = mm.Cs((cp / rep + 1)^0) - g[rep] = cp - end - return cp:match(s) -end - - --- exported names -local re = { - compile = compile, - match = match, - find = find, - gsub = gsub, - updatelocale = updatelocale, - calcline = calcline -} - -return re diff --git a/server-beta/src/parser/split.lua b/server-beta/src/parser/split.lua deleted file mode 100644 index 6ce4a4e7..00000000 --- a/server-beta/src/parser/split.lua +++ /dev/null @@ -1,9 +0,0 @@ -local m = require 'lpeglabel' - -local NL = m.P'\r\n' + m.S'\r\n' -local LINE = m.C(1 - NL) - -return function (str) - local MATCH = m.Ct((LINE * NL)^0 * LINE) - return MATCH:match(str) -end diff --git a/server-beta/src/proto/define.lua b/server-beta/src/proto/define.lua deleted file mode 100644 index 61c4037c..00000000 --- a/server-beta/src/proto/define.lua +++ /dev/null @@ -1,140 +0,0 @@ -local guide = require 'parser.guide' - -local m = {} - ---- 获取 position 对应的光标位置 ----@param lines table ----@param text string ----@param position position ----@return integer -function m.offset(lines, text, position) - local row = position.line + 1 - local start = guide.lineRange(lines, row) - local offset = utf8.offset(text, position.character + 1, start) - if text:sub(offset-1, offset):match '[%w_][^%w_]' then - offset = offset - 1 - end - return offset -end - ---- 将光标位置转化为 position ----@alias position table ----@param lines table ----@param text string ----@param offset integer ----@return position -function m.position(lines, text, offset) - local row, col = guide.positionOf(lines, offset) - local start = guide.lineRange(lines, row) - if start < 1 then - start = 1 - end - local ucol = utf8.len(text, start, start + col - 1, true) - if row < 1 then - row = 1 - end - return { - line = row - 1, - character = ucol, - } -end - ---- 将起点与终点位置转化为 range ----@alias range table ----@param lines table ----@param text string ----@param offset1 integer ----@param offset2 integer -function m.range(lines, text, offset1, offset2) - local range = { - start = m.position(lines, text, offset1), - ['end'] = m.position(lines, text, offset2), - } - if range.start.character > 0 then - range.start.character = range.start.character - 1 - end - return range -end - ----@alias location table ----@param uri string ----@param range range ----@return location -function m.location(uri, range) - return { - uri = uri, - range = range, - } -end - ----@alias locationLink table ----@param uri string ----@param range range ----@param selection range ----@param origin range -function m.locationLink(uri, range, selection, origin) - return { - targetUri = uri, - targetRange = range, - targetSelectionRange = selection, - originSelectionRange = origin, - } -end - -function m.textEdit(range, newtext) - return { - range = range, - newText = newtext, - } -end - ---- 诊断等级 -m.DiagnosticSeverity = { - Error = 1, - Warning = 2, - Information = 3, - Hint = 4, -} - ---- 诊断类型与默认等级 -m.DiagnosticDefaultSeverity = { - ['unused-local'] = 'Hint', - ['unused-function'] = 'Hint', - ['undefined-global'] = 'Warning', - ['global-in-nil-env'] = 'Warning', - ['unused-label'] = 'Hint', - ['unused-vararg'] = 'Hint', - ['trailing-space'] = 'Hint', - ['redefined-local'] = 'Hint', - ['newline-call'] = 'Information', - ['newfield-call'] = 'Warning', - ['redundant-parameter'] = 'Hint', - ['ambiguity-1'] = 'Warning', - ['lowercase-global'] = 'Information', - ['undefined-env-child'] = 'Information', - ['duplicate-index'] = 'Warning', - ['empty-block'] = 'Hint', - ['redundant-value'] = 'Hint', - ['emmy-lua'] = 'Warning', -} - ---- 诊断报告标签 -m.DiagnosticTag = { - Unnecessary = 1, - Deprecated = 2, -} - -m.DocumentHighlightKind = { - Text = 1, - Read = 2, - Write = 3, -} - -m.MessageType = { - Error = 1, - Warning = 2, - Info = 3, - Log = 4, -} - -return m diff --git a/server-beta/src/proto/init.lua b/server-beta/src/proto/init.lua deleted file mode 100644 index 33e637f6..00000000 --- a/server-beta/src/proto/init.lua +++ /dev/null @@ -1,3 +0,0 @@ -local proto = require 'proto.proto' - -return proto diff --git a/server-beta/src/proto/proto.lua b/server-beta/src/proto/proto.lua deleted file mode 100644 index f04653d5..00000000 --- a/server-beta/src/proto/proto.lua +++ /dev/null @@ -1,133 +0,0 @@ -local subprocess = require 'bee.subprocess' -local util = require 'utility' -local await = require 'await' -local pub = require 'pub' -local jsonrpc = require 'jsonrpc' -local ErrorCodes = require 'define.ErrorCodes' - -local reqCounter = util.counter() - -local m = {} - -m.ability = {} -m.waiting = {} - -function m.getMethodName(proto) - if proto.method:sub(1, 2) == '$/' then - return proto.method:sub(3), true - else - return proto.method, false - end -end - -function m.on(method, callback) - m.ability[method] = callback -end - -function m.response(id, res) - if id == nil then - log.error('Response id is nil!', util.dump(res)) - return - end - -- res 可能是nil,为了转成json时保留nil,使用 container 容器 - local data = util.container() - data.id = id - data.result = res - local buf = jsonrpc.encode(data) - log.debug('Response', id, #buf) - io.stdout:write(buf) -end - -function m.responseErr(id, code, message) - if id == nil then - log.error('Response id is nil!', util.dump(message)) - return - end - local buf = jsonrpc.encode { - id = id, - error = { - code = code, - message = message, - } - } - log.debug('ResponseErr', id, #buf) - io.stdout:write(buf) -end - -function m.notify(name, params) - local buf = jsonrpc.encode { - method = name, - params = params, - } - log.debug('Notify', name, #buf) - io.stdout:write(buf) -end - -function m.awaitRequest(name, params) - local id = reqCounter() - local buf = jsonrpc.encode { - id = id, - method = name, - params = params, - } - log.debug('Request', name, #buf) - io.stdout:write(buf) - return await.wait(function (waker) - m.waiting[id] = waker - end) -end - -function m.doMethod(proto) - local method, optional = m.getMethodName(proto) - local abil = m.ability[method] - if not abil then - if not optional then - log.warn('Recieved unknown proto: ' .. method) - end - if proto.id then - m.responseErr(proto.id, ErrorCodes.MethodNotFound, method) - end - return - end - await.create(function () - local clock = os.clock() - local ok, res = xpcall(abil, log.error, proto.params) - local passed = os.clock() - clock - if passed > 0.2 then - log.debug(('Method [%s] takes [%.3f]sec.'):format(method, passed)) - end - if not proto.id then - return - end - if ok then - m.response(proto.id, res) - else - m.responseErr(proto.id, ErrorCodes.InternalError, res) - end - end) -end - -function m.doResponse(proto) - local id = proto.id - local waker = m.waiting[id] - if not waker then - log.warn('Response id not found: ' .. util.dump(proto)) - return - end - m.waiting[id] = nil - if proto.error then - log.warn(('Response error [%d]: %s'):format(proto.error.code, proto.error.message)) - return - end - waker(proto.result) -end - -function m.listen() - subprocess.filemode(io.stdin, 'b') - subprocess.filemode(io.stdout, 'b') - io.stdin:setvbuf 'no' - io.stdout:setvbuf 'no' - pub.task('loadProto') -end - -return m diff --git a/server-beta/src/provider/capability.lua b/server-beta/src/provider/capability.lua deleted file mode 100644 index aa95c758..00000000 --- a/server-beta/src/provider/capability.lua +++ /dev/null @@ -1,42 +0,0 @@ -local m = {} - -m.initer = { - -- 文本同步方式 - textDocumentSync = { - -- 打开关闭文本时通知 - openClose = true, - -- 文本改变时完全通知 TODO 支持差量更新(2) - change = 1, - }, - - hoverProvider = true, - definitionProvider = true, - referencesProvider = true, - renameProvider = { - prepareProvider = true, - }, - --documentSymbolProvider = true, - documentHighlightProvider = true, - --codeActionProvider = true, - --signatureHelpProvider = { - -- triggerCharacters = { '(', ',' }, - --}, - --workspace = { - -- workspaceFolders = { - -- supported = true, - -- changeNotifications = true, - -- } - --}, - --documentOnTypeFormattingProvider = { - -- firstTriggerCharacter = '}', - --}, - --executeCommandProvider = { - -- commands = { - -- 'config', - -- 'removeSpace', - -- 'solve', - -- }, - --}, -} - -return m diff --git a/server-beta/src/provider/completion.lua b/server-beta/src/provider/completion.lua deleted file mode 100644 index d2df44d2..00000000 --- a/server-beta/src/provider/completion.lua +++ /dev/null @@ -1,53 +0,0 @@ -local proto = require 'proto' - -local isEnable = false - -local function allWords() - local str = [[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.:('"[,#*@| ]] - local list = {} - for c in str:gmatch '.' do - list[#list+1] = c - end - return list -end - -local function enable() - if isEnable then - return - end - isEnable = true - log.debug('Enable completion.') - proto.awaitRequest('client/registerCapability', { - registrations = { - { - id = 'completion', - method = 'textDocument/completion', - registerOptions = { - resolveProvider = false, - triggerCharacters = allWords(), - }, - }, - } - }) -end - -local function disable() - if not isEnable then - return - end - isEnable = false - log.debug('Disable completion.') - proto.awaitRequest('client/unregisterCapability', { - unregisterations = { - { - id = 'completion', - method = 'textDocument/completion', - }, - } - }) -end - -return { - enable = enable, - disable = disable, -} diff --git a/server-beta/src/provider/diagnostic.lua b/server-beta/src/provider/diagnostic.lua deleted file mode 100644 index ba95f2bf..00000000 --- a/server-beta/src/provider/diagnostic.lua +++ /dev/null @@ -1,209 +0,0 @@ -local await = require 'await' -local proto = require 'proto.proto' -local define = require 'proto.define' -local lang = require 'language' -local files = require 'files' -local config = require 'config' -local core = require 'core.diagnostics' -local util = require 'utility' - -local m = {} -m._start = false -m.cache = {} - -local function concat(t, sep) - if type(t) ~= 'table' then - return t - end - return table.concat(t, sep) -end - -local function buildSyntaxError(uri, err) - local lines = files.getLines(uri) - local text = files.getText(uri) - local message = lang.script('PARSER_'..err.type, err.info) - - if err.version then - local version = err.info and err.info.version or config.config.runtime.version - message = message .. ('(%s)'):format(lang.script('DIAG_NEED_VERSION' - , concat(err.version, '/') - , version - )) - end - - local related = err.info and err.info.related - local relatedInformation - if related then - relatedInformation = {} - for _, rel in ipairs(related) do - local rmessage - if rel.message then - rmessage = lang.script('PARSER_'..rel.message) - else - rmessage = text:sub(rel.start, rel.finish) - end - relatedInformation[#relatedInformation+1] = { - message = rmessage, - location = define.location(uri, define.range(lines, text, rel.start, rel.finish)), - } - end - end - - return { - range = define.range(lines, text, err.start, err.finish), - severity = define.DiagnosticSeverity.Error, - source = lang.script.DIAG_SYNTAX_CHECK, - message = message, - relatedInformation = relatedInformation, - } -end - -local function buildDiagnostic(uri, diag) - local lines = files.getLines(uri) - local text = files.getText(uri) - - local relatedInformation - if diag.related then - relatedInformation = {} - for _, rel in ipairs(diag.related) do - local rtext = files.getText(rel.uri) - local rlines = files.getLines(rel.uri) - relatedInformation[#relatedInformation+1] = { - message = rel.message or rtext:sub(rel.start, rel.finish), - location = define.location(rel.uri, define.range(rlines, rtext, rel.start, rel.finish)) - } - end - end - - return { - range = define.range(lines, text, diag.start, diag.finish), - source = lang.script.DIAG_DIAGNOSTICS, - severity = diag.level, - message = diag.message, - code = diag.code, - tags = diag.tags, - relatedInformation = relatedInformation, - } -end - -local function merge(a, b) - if not a and not b then - return nil - end - local t = {} - if a then - for i = 1, #a do - t[#t+1] = a[i] - end - end - if b then - for i = 1, #b do - t[#t+1] = b[i] - end - end - return t -end - -function m.clear(uri) - if not m.cache[uri] then - return - end - m.cache[uri] = nil - proto.notify('textDocument/publishDiagnostics', { - uri = uri, - diagnostics = {}, - }) -end - -function m.syntaxErrors(uri, ast) - if #ast.errs == 0 then - return nil - end - - local results = {} - - for _, err in ipairs(ast.errs) do - results[#results+1] = buildSyntaxError(uri, err) - end - - return results -end - -function m.diagnostics(uri, syntaxOnly) - if syntaxOnly or not m._start then - return m.cache[uri] - end - - local diags = core(uri) - if not diags then - return nil - end - - local results = {} - for _, diag in ipairs(diags) do - results[#results+1] = buildDiagnostic(uri, diag) - end - - return results -end - -function m.doDiagnostic(uri, syntaxOnly) - local ast = files.getAst(uri) - if not ast then - m.clear(uri) - return - end - - local syntax = m.syntaxErrors(uri, ast) - local diagnostics = m.diagnostics(uri, syntaxOnly) - local full = merge(syntax, diagnostics) - if not full then - m.clear(uri) - return - end - - if util.equal(m.cache[uri], full) then - return - end - m.cache[uri] = full - - proto.notify('textDocument/publishDiagnostics', { - uri = uri, - diagnostics = full, - }) -end - -function m.refresh(uri) - await.create(function () - await.delay(function () - return files.globalVersion - end) - if uri then - m.doDiagnostic(uri, true) - end - if not m._start then - return - end - local clock = os.clock() - if uri then - m.doDiagnostic(uri) - end - for destUri in files.eachFile() do - if destUri ~= uri then - m.doDiagnostic(files.getOriginUri(destUri)) - await.delay(function () - return files.globalVersion - end) - end - end - local passed = os.clock() - clock - log.info(('Finish diagnostics, takes [%.3f] sec.'):format(passed)) - end) -end - -function m.start() - m._start = true - m.refresh() -end - -return m diff --git a/server-beta/src/provider/init.lua b/server-beta/src/provider/init.lua deleted file mode 100644 index 95f4b3d1..00000000 --- a/server-beta/src/provider/init.lua +++ /dev/null @@ -1,298 +0,0 @@ -local util = require 'utility' -local cap = require 'provider.capability' -local completion= require 'provider.completion' -local await = require 'await' -local files = require 'files' -local proto = require 'proto.proto' -local define = require 'proto.define' -local workspace = require 'workspace' -local config = require 'config' -local library = require 'library' -local markdown = require 'provider.markdown' - -local function updateConfig() - local configs = proto.awaitRequest('workspace/configuration', { - items = { - { - scopeUri = workspace.uri, - section = 'Lua', - }, - { - scopeUri = workspace.uri, - section = 'files.associations', - }, - { - scopeUri = workspace.uri, - section = 'files.exclude', - } - }, - }) - - local updated = configs[1] - local other = { - associations = configs[2], - exclude = configs[3], - } - - local oldConfig = util.deepCopy(config.config) - local oldOther = util.deepCopy(config.other) - config.setConfig(updated, other) - local newConfig = config.config - local newOther = config.other - if not util.equal(oldConfig.runtime, newConfig.runtime) then - library.reload() - end - if not util.equal(oldConfig.diagnostics, newConfig.diagnostics) then - end - if not util.equal(oldConfig.plugin, newConfig.plugin) then - end - if not util.equal(oldConfig.workspace, newConfig.workspace) - or not util.equal(oldConfig.plugin, newConfig.plugin) - or not util.equal(oldOther.associations, newOther.associations) - or not util.equal(oldOther.exclude, newOther.exclude) - then - end - - if newConfig.completion.enable then - --completion.enable() - else - completion.disable() - end -end - -proto.on('initialize', function (params) - --log.debug(util.dump(params)) - if params.workspaceFolders then - local name = params.workspaceFolders[1].name - local uri = params.workspaceFolders[1].uri - workspace.init(name, uri) - end - return { - capabilities = cap.initer, - } -end) - -proto.on('initialized', function (params) - updateConfig() - proto.awaitRequest('client/registerCapability', { - registrations = { - -- 监视文件变化 - { - id = '0', - method = 'workspace/didChangeWatchedFiles', - registerOptions = { - watchers = { - { - globPattern = '**/', - kind = 1 | 2 | 4, - } - }, - }, - }, - -- 配置变化 - { - id = '1', - method = 'workspace/didChangeConfiguration', - } - } - }) - await.create(workspace.awaitPreload) - return true -end) - -proto.on('exit', function () - log.info('Server exited.') - os.exit(true) -end) - -proto.on('shutdown', function () - log.info('Server shutdown.') - return true -end) - -proto.on('workspace/configuration', function () - updateConfig() -end) - -proto.on('workspace/didChangeWatchedFiles', function (params) -end) - -proto.on('textDocument/didOpen', function (params) - local doc = params.textDocument - local uri = doc.uri - local text = doc.text - files.open(uri) - files.setText(uri, text) -end) - -proto.on('textDocument/didClose', function (params) - local doc = params.textDocument - local uri = doc.uri - files.close(uri) - if not files.isLua(uri) then - files.remove(uri) - end -end) - -proto.on('textDocument/didChange', function (params) - local doc = params.textDocument - local change = params.contentChanges - local uri = doc.uri - local text = change[1].text - if files.isLua(uri) or files.isOpen(uri) then - files.setText(uri, text) - end -end) - -proto.on('textDocument/hover', function (params) - local core = require 'core.hover' - local doc = params.textDocument - local uri = doc.uri - if not files.exists(uri) then - return nil - end - local lines = files.getLines(uri) - local text = files.getText(uri) - local offset = define.offset(lines, text, params.position) - local hover = core(uri, offset) - if not hover then - return nil - end - local md = markdown() - md:add('lua', hover.label) - return { - contents = { - value = md:string(), - kind = 'markdown', - }, - range = define.range(lines, text, hover.source.start, hover.source.finish), - } -end) - -proto.on('textDocument/definition', function (params) - local core = require 'core.definition' - local uri = params.textDocument.uri - if not files.exists(uri) then - return nil - end - local lines = files.getLines(uri) - local text = files.getText(uri) - local offset = define.offset(lines, text, params.position) - local result = core(uri, offset) - if not result then - return nil - end - local response = {} - for i, info in ipairs(result) do - local targetUri = info.uri - local targetLines = files.getLines(targetUri) - local targetText = files.getText(targetUri) - response[i] = define.locationLink(targetUri - , define.range(targetLines, targetText, info.target.start, info.target.finish) - , define.range(targetLines, targetText, info.target.start, info.target.finish) - , define.range(lines, text, info.source.start, info.source.finish) - ) - end - return response -end) - -proto.on('textDocument/references', function (params) - local core = require 'core.reference' - local uri = params.textDocument.uri - if not files.exists(uri) then - return nil - end - local lines = files.getLines(uri) - local text = files.getText(uri) - local offset = define.offset(lines, text, params.position) - local result = core(uri, offset) - if not result then - return nil - end - local response = {} - for i, info in ipairs(result) do - local targetUri = info.uri - local targetLines = files.getLines(targetUri) - local targetText = files.getText(targetUri) - response[i] = define.location(targetUri - , define.range(targetLines, targetText, info.target.start, info.target.finish) - ) - end - return response -end) - -proto.on('textDocument/documentHighlight', function (params) - local core = require 'core.highlight' - local uri = params.textDocument.uri - if not files.exists(uri) then - return nil - end - local lines = files.getLines(uri) - local text = files.getText(uri) - local offset = define.offset(lines, text, params.position) - local result = core(uri, offset) - if not result then - return nil - end - local response = {} - for _, info in ipairs(result) do - response[#response+1] = { - range = define.range(lines, text, info.start, info.finish), - kind = info.kind, - } - end - return response -end) - -proto.on('textDocument/rename', function (params) - local core = require 'core.rename' - local uri = params.textDocument.uri - if not files.exists(uri) then - return nil - end - local lines = files.getLines(uri) - local text = files.getText(uri) - local offset = define.offset(lines, text, params.position) - local result = core.rename(uri, offset, params.newName) - if not result then - return nil - end - local workspaceEdit = { - changes = {}, - } - for _, info in ipairs(result) do - local ruri = info.uri - local rlines = files.getLines(ruri) - local rtext = files.getText(ruri) - if not workspaceEdit.changes[ruri] then - workspaceEdit.changes[ruri] = {} - end - local textEdit = define.textEdit(define.range(rlines, rtext, info.start, info.finish), info.text) - workspaceEdit.changes[ruri][#workspaceEdit.changes[ruri]+1] = textEdit - end - return workspaceEdit -end) - -proto.on('textDocument/prepareRename', function (params) - local core = require 'core.rename' - local uri = params.textDocument.uri - if not files.exists(uri) then - return nil - end - local lines = files.getLines(uri) - local text = files.getText(uri) - local offset = define.offset(lines, text, params.position) - local result = core.prepareRename(uri, offset) - if not result then - return nil - end - return { - range = define.range(lines, text, result.start, result.finish), - placeholder = result.text, - } -end) - -proto.on('textDocument/completion', function (params) - --log.info(util.dump(params)) - return nil -end) diff --git a/server-beta/src/provider/markdown.lua b/server-beta/src/provider/markdown.lua deleted file mode 100644 index 0f69ad87..00000000 --- a/server-beta/src/provider/markdown.lua +++ /dev/null @@ -1,22 +0,0 @@ -local mt = {} -mt.__index = mt -mt.__name = 'markdown' - -function mt:add(language, text) - if not text then - return - end - if language == 'lua' then - self[#self+1] = ('```lua\n%s\n```'):format(text) - else - self[#self+1] = text:gsub('\n', '\n\n') - end -end - -function mt:string() - return table.concat(self, '\n') -end - -return function () - return setmetatable({}, mt) -end diff --git a/server-beta/src/pub/init.lua b/server-beta/src/pub/init.lua deleted file mode 100644 index 61b43da7..00000000 --- a/server-beta/src/pub/init.lua +++ /dev/null @@ -1,4 +0,0 @@ -local pub = require 'pub.pub' -require 'pub.report' - -return pub diff --git a/server-beta/src/pub/pub.lua b/server-beta/src/pub/pub.lua deleted file mode 100644 index 2cb1b4e8..00000000 --- a/server-beta/src/pub/pub.lua +++ /dev/null @@ -1,236 +0,0 @@ -local thread = require 'bee.thread' -local utility = require 'utility' -local await = require 'await' -local timer = require 'timer' - -local errLog = thread.channel 'errlog' -local type = type -local counter = utility.counter() - -local braveTemplate = [[ -package.path = %q -package.cpath = %q - -collectgarbage 'generational' - -log = require 'brave.log' - -dofile(%q) -local brave = require 'brave' -brave.register(%d) -]] - ----@class pub -local m = {} -m.type = 'pub' -m.braves = {} -m.ability = {} -m.taskQueue = {} - ---- 注册酒馆的功能 -function m.on(name, callback) - m.ability[name] = callback -end - ---- 招募勇者,勇者会从公告板上领取任务,完成任务后到看板娘处交付任务 ----@param num integer -function m.recruitBraves(num) - for _ = 1, num do - local id = #m.braves + 1 - log.info('Create brave:', id) - thread.newchannel('taskpad' .. id) - thread.newchannel('waiter' .. id) - m.braves[id] = { - id = id, - taskpad = thread.channel('taskpad' .. id), - waiter = thread.channel('waiter' .. id), - thread = thread.thread(braveTemplate:format( - package.path, - package.cpath, - (ROOT / 'debugger.lua'):string(), - id - )), - taskMap = {}, - currentTask = nil, - memory = 0, - } - end -end - ---- 勇者是否有空 -function m.isIdle(brave) - return next(brave.taskMap) == nil -end - ---- 给勇者推送任务 -function m.pushTask(brave, info) - if info.removed then - return false - end - brave.taskpad:push(info.name, info.id, info.params) - brave.taskMap[info.id] = info - --log.info(('Push task %q(%d) to # %d, queue length %d'):format(info.name, info.id, brave.id, #m.taskQueue)) - return true -end - ---- 从勇者处接收任务反馈 -function m.popTask(brave, id, result) - local info = brave.taskMap[id] - if not info then - log.warn(('Brave pushed unknown task result: # %d => [%d]'):format(brave.id, id)) - return - end - brave.taskMap[id] = nil - --log.info(('Pop task %q(%d) from # %d'):format(info.name, info.id, brave.id)) - m.checkWaitingTask(brave) - if not info.removed then - info.removed = true - if info.callback then - xpcall(info.callback, log.error, result) - end - end -end - ---- 从勇者处接收报告 -function m.popReport(brave, name, params) - local abil = m.ability[name] - if not abil then - log.warn(('Brave pushed unknown report: # %d => %q'):format(brave.id, name)) - return - end - xpcall(abil, log.error, params, brave) -end - ---- 发布任务 ----@parma name string ----@param params any -function m.awaitTask(name, params) - local info = { - id = counter(), - name = name, - params = params, - } - for _, brave in ipairs(m.braves) do - if m.isIdle(brave) then - if m.pushTask(brave, info) then - return await.wait(function (waker) - info.callback = waker - end) - else - return nil - end - end - end - -- 如果所有勇者都在战斗,那么把任务缓存到队列里 - -- 当有勇者提交任务反馈后,尝试把按顺序将堆积任务 - -- 交给该勇者 - m.taskQueue[#m.taskQueue+1] = info - --log.info(('Add task %q(%d) in queue, length %d.'):format(name, info.id, #m.taskQueue)) - return await.wait(function (waker) - info.callback = waker - end) -end - ---- 发布同步任务,如果任务进入了队列,会返回执行器 ---- 通过 jumpQueue 可以插队 ----@parma name string ----@param params any ----@param callback function -function m.task(name, params, callback) - local info = { - id = counter(), - name = name, - params = params, - callback = callback, - } - for _, brave in ipairs(m.braves) do - if m.isIdle(brave) then - m.pushTask(brave, info) - return nil - end - end - -- 如果所有勇者都在战斗,那么把任务缓存到队列里 - -- 当有勇者提交任务反馈后,尝试把按顺序将堆积任务 - -- 交给该勇者 - m.taskQueue[#m.taskQueue+1] = info - --log.info(('Add task %q(%d) in queue, length %d.'):format(name, info.id, #m.taskQueue)) - return info -end - ---- 插队 -function m.jumpQueue(info) - for i = 2, #m.taskQueue do - if m.taskQueue[i] == info then - m.taskQueue[i] = nil - table.move(m.taskQueue, 1, i - 1, 2) - m.taskQueue[1] = info - return - end - end -end - ---- 移除任务 -function m.remove(info) - info.removed = true - for i = 1, #m.taskQueue do - if m.taskQueue[i] == info then - table.remove(m.taskQueue[i], i) - return - end - end -end - ---- 检查堆积任务 -function m.checkWaitingTask(brave) - if #m.taskQueue == 0 then - return - end - -- 如果勇者还有其他活要忙,那么让他继续忙去吧 - if next(brave.taskMap) then - return - end - while #m.taskQueue > 0 do - local info = table.remove(m.taskQueue, 1) - if m.pushTask(brave, info) then - break - end - end -end - ---- 接收反馈 ----|返回接收到的反馈数量 ----@return integer -function m.recieve() - for _, brave in ipairs(m.braves) do - while true do - local suc, id, result = brave.waiter:pop() - if not suc then - goto CONTINUE - end - if type(id) == 'string' then - m.popReport(brave, id, result) - else - m.popTask(brave, id, result) - end - end - ::CONTINUE:: - end -end - ---- 检查伤亡情况 -function m.checkDead() - while true do - local suc, err = errLog:pop() - if not suc then - break - end - log.error('Brave is dead!: ' .. err) - end -end - -function m.step() - m.checkDead() - m.recieve() -end - -return m diff --git a/server-beta/src/pub/report.lua b/server-beta/src/pub/report.lua deleted file mode 100644 index edd3ee0e..00000000 --- a/server-beta/src/pub/report.lua +++ /dev/null @@ -1,21 +0,0 @@ -local pub = require 'pub.pub' -local await = require 'await' - -pub.on('log', function (params, brave) - log.raw(brave.id, params.level, params.msg, params.src, params.line) -end) - -pub.on('mem', function (count, brave) - brave.memory = count -end) - -pub.on('proto', function (params) - local proto = require 'proto' - await.create(function () - if params.method then - proto.doMethod(params) - else - proto.doResponse(params) - end - end) -end) diff --git a/server-beta/src/service/init.lua b/server-beta/src/service/init.lua deleted file mode 100644 index eb0bd057..00000000 --- a/server-beta/src/service/init.lua +++ /dev/null @@ -1,3 +0,0 @@ -local service = require 'service.service' - -return service diff --git a/server-beta/src/service/service.lua b/server-beta/src/service/service.lua deleted file mode 100644 index e1cb604b..00000000 --- a/server-beta/src/service/service.lua +++ /dev/null @@ -1,137 +0,0 @@ -local pub = require 'pub' -local thread = require 'bee.thread' -local await = require 'await' -local timer = require 'timer' -local proto = require 'proto' -local vm = require 'vm' - -local m = {} -m.type = 'service' - -local function countMemory() - local mems = {} - local total = 0 - mems[0] = collectgarbage 'count' - total = total + collectgarbage 'count' - for id, brave in ipairs(pub.braves) do - mems[id] = brave.memory - total = total + brave.memory - end - return total, mems -end - -function m.reportMemoryCollect() - local totalMemBefore = countMemory() - local clock = os.clock() - collectgarbage() - local passed = os.clock() - clock - local totalMemAfter, mems = countMemory() - - local lines = {} - lines[#lines+1] = ' --------------- Memory ---------------' - lines[#lines+1] = (' Total: %.3f(%.3f) MB'):format(totalMemAfter / 1000.0, totalMemBefore / 1000.0) - for i = 0, #mems do - lines[#lines+1] = (' # %02d : %.3f MB'):format(i, mems[i] / 1000.0) - end - lines[#lines+1] = (' Collect garbage takes [%.3f] sec'):format(passed) - return table.concat(lines, '\n') -end - -function m.reportMemory() - local totalMem, mems = countMemory() - - local lines = {} - lines[#lines+1] = ' --------------- Memory ---------------' - lines[#lines+1] = (' Total: %.3f MB'):format(totalMem / 1000.0) - for i = 0, #mems do - lines[#lines+1] = (' # %02d : %.3f MB'):format(i, mems[i] / 1000.0) - end - return table.concat(lines, '\n') -end - -function m.reportTask() - local total = 0 - local running = 0 - local suspended = 0 - local normal = 0 - local dead = 0 - - for co in pairs(await.coTracker) do - total = total + 1 - local status = coroutine.status(co) - if status == 'running' then - running = running + 1 - elseif status == 'suspended' then - suspended = suspended + 1 - elseif status == 'normal' then - normal = normal + 1 - elseif status == 'dead' then - dead = dead + 1 - end - end - - local lines = {} - lines[#lines+1] = ' --------------- Coroutine ---------------' - lines[#lines+1] = (' Total: %d'):format(total) - lines[#lines+1] = (' Running: %d'):format(running) - lines[#lines+1] = (' Suspended: %d'):format(suspended) - lines[#lines+1] = (' Normal: %d'):format(normal) - lines[#lines+1] = (' Dead: %d'):format(dead) - return table.concat(lines, '\n') -end - -function m.reportCache() - local total = 0 - local dead = 0 - - for cache in pairs(vm.cacheTracker) do - total = total + 1 - if cache.dead then - dead = dead + 1 - end - end - - local lines = {} - lines[#lines+1] = ' --------------- Cache ---------------' - lines[#lines+1] = (' Total: %d'):format(total) - lines[#lines+1] = (' Dead: %d'):format(dead) - return table.concat(lines, '\n') -end - -function m.report() - local t = timer.loop(60.0, function () - local lines = {} - lines[#lines+1] = '' - lines[#lines+1] = '========= Medical Examination Report =========' - lines[#lines+1] = m.reportMemory() - lines[#lines+1] = m.reportTask() - lines[#lines+1] = m.reportCache() - lines[#lines+1] = '==============================================' - - log.debug(table.concat(lines, '\n')) - end) - t:onTimer() -end - -function m.startTimer() - while true do - pub.step() - if not await.step() then - thread.sleep(0.001) - timer.update() - end - end -end - -function m.start() - await.setErrorHandle(log.error) - pub.recruitBraves(4) - proto.listen() - m.report() - - require 'provider' - - m.startTimer() -end - -return m diff --git a/server-beta/src/timer.lua b/server-beta/src/timer.lua deleted file mode 100644 index 1d4343f1..00000000 --- a/server-beta/src/timer.lua +++ /dev/null @@ -1,218 +0,0 @@ -local setmetatable = setmetatable -local mathMax = math.max -local mathFloor = math.floor -local osClock = os.clock - -_ENV = nil - -local curFrame = 0 -local maxFrame = 0 -local curIndex = 0 -local freeQueue = {} -local timer = {} - -local function allocQueue() - local n = #freeQueue - if n > 0 then - local r = freeQueue[n] - freeQueue[n] = nil - return r - else - return {} - end -end - -local function mTimeout(self, timeout) - if self._pauseRemaining or self._running then - return - end - local ti = curFrame + timeout - local q = timer[ti] - if q == nil then - q = allocQueue() - timer[ti] = q - end - self._timeoutFrame = ti - self._running = true - q[#q + 1] = self -end - -local function mWakeup(self) - if self._removed then - return - end - self._running = false - if self._onTimer then - self:_onTimer() - end - if self._removed then - return - end - if self._timerCount then - if self._timerCount > 1 then - self._timerCount = self._timerCount - 1 - mTimeout(self, self._timeout) - else - self._removed = true - end - else - mTimeout(self, self._timeout) - end -end - -local function getRemaining(self) - if self._removed then - return 0 - end - if self._pauseRemaining then - return self._pauseRemaining - end - if self._timeoutFrame == curFrame then - return self._timeout or 0 - end - return self._timeoutFrame - curFrame -end - -local function onTick() - local q = timer[curFrame] - if q == nil then - curIndex = 0 - return - end - for i = curIndex + 1, #q do - local callback = q[i] - curIndex = i - q[i] = nil - if callback then - mWakeup(callback) - end - end - curIndex = 0 - timer[curFrame] = nil - freeQueue[#freeQueue + 1] = q -end - -local m = {} -local mt = {} -mt.__index = mt -mt.type = 'timer' - -function mt:__tostring() - return '[table:timer]' -end - -function mt:__call() - if self._onTimer then - self:_onTimer() - end -end - -function mt:remove() - self._removed = true -end - -function mt:pause() - if self._removed or self._pauseRemaining then - return - end - self._pauseRemaining = getRemaining(self) - self._running = false - local ti = self._timeoutFrame - local q = timer[ti] - if q then - for i = #q, 1, -1 do - if q[i] == self then - q[i] = false - return - end - end - end -end - -function mt:resume() - if self._removed or not self._pauseRemaining then - return - end - local timeout = self._pauseRemaining - self._pauseRemaining = nil - mTimeout(self, timeout) -end - -function mt:restart() - if self._removed or self._pauseRemaining or not self._running then - return - end - local ti = self._timeoutFrame - local q = timer[ti] - if q then - for i = #q, 1, -1 do - if q[i] == self then - q[i] = false - break - end - end - end - self._running = false - mTimeout(self, self._timeout) -end - -function mt:remaining() - return getRemaining(self) / 1000.0 -end - -function mt:onTimer() - self:_onTimer() -end - -function m.wait(timeout, onTimer) - local t = setmetatable({ - ['_timeout'] = mathMax(mathFloor(timeout * 1000.0), 1), - ['_onTimer'] = onTimer, - ['_timerCount'] = 1, - }, mt) - mTimeout(t, t._timeout) - return t -end - -function m.loop(timeout, onTimer) - local t = setmetatable({ - ['_timeout'] = mathFloor(timeout * 1000.0), - ['_onTimer'] = onTimer, - }, mt) - mTimeout(t, t._timeout) - return t -end - -function m.timer(timeout, count, onTimer) - if count == 0 then - return m.loop(timeout, onTimer) - end - local t = setmetatable({ - ['_timeout'] = mathFloor(timeout * 1000.0), - ['_onTimer'] = onTimer, - ['_timerCount'] = count, - }, mt) - mTimeout(t, t._timeout) - return t -end - -function m.clock() - return curFrame / 1000.0 -end - -local lastClock = osClock() -function m.update() - local currentClock = osClock() - local delta = currentClock - lastClock - lastClock = currentClock - if curIndex ~= 0 then - curFrame = curFrame - 1 - end - maxFrame = maxFrame + delta * 1000.0 - while curFrame < maxFrame do - curFrame = curFrame + 1 - onTick() - end -end - -return m diff --git a/server-beta/src/utility.lua b/server-beta/src/utility.lua deleted file mode 100644 index c9defebc..00000000 --- a/server-beta/src/utility.lua +++ /dev/null @@ -1,452 +0,0 @@ -local tableSort = table.sort -local stringRep = string.rep -local tableConcat = table.concat -local tostring = tostring -local type = type -local pairs = pairs -local ipairs = ipairs -local next = next -local rawset = rawset -local move = table.move -local setmetatable = setmetatable -local mathType = math.type -local mathCeil = math.ceil -local getmetatable = getmetatable -local mathAbs = math.abs -local ioOpen = io.open - -_ENV = nil - -local function formatNumber(n) - local str = ('%.10f'):format(n) - str = str:gsub('%.?0*$', '') - return str -end - -local function isInteger(n) - if mathType then - return mathType(n) == 'integer' - else - return type(n) == 'number' and n % 1 == 0 - end -end - -local TAB = setmetatable({}, { __index = function (self, n) - self[n] = stringRep(' ', n) - return self[n] -end}) - -local RESERVED = { - ['and'] = true, - ['break'] = true, - ['do'] = true, - ['else'] = true, - ['elseif'] = true, - ['end'] = true, - ['false'] = true, - ['for'] = true, - ['function'] = true, - ['goto'] = true, - ['if'] = true, - ['in'] = true, - ['local'] = true, - ['nil'] = true, - ['not'] = true, - ['or'] = true, - ['repeat'] = true, - ['return'] = true, - ['then'] = true, - ['true'] = true, - ['until'] = true, - ['while'] = true, -} - -local m = {} - ---- 打印表的结构 ----@param tbl table ----@param option table {optional = 'self'} ----@return string -function m.dump(tbl, option) - if not option then - option = {} - end - if type(tbl) ~= 'table' then - return ('%s'):format(tbl) - end - local lines = {} - local mark = {} - lines[#lines+1] = '{' - local function unpack(tbl, tab) - mark[tbl] = (mark[tbl] or 0) + 1 - local keys = {} - local keymap = {} - local integerFormat = '[%d]' - local alignment = 0 - if #tbl >= 10 then - local width = #tostring(#tbl) - integerFormat = ('[%%0%dd]'):format(mathCeil(width)) - end - for key in pairs(tbl) do - if type(key) == 'string' then - if not key:match('^[%a_][%w_]*$') - or RESERVED[key] - or option['longStringKey'] - then - keymap[key] = ('[%q]'):format(key) - else - keymap[key] = ('%s'):format(key) - end - elseif isInteger(key) then - keymap[key] = integerFormat:format(key) - else - keymap[key] = ('["<%s>"]'):format(tostring(key)) - end - keys[#keys+1] = key - if option['alignment'] then - if #keymap[key] > alignment then - alignment = #keymap[key] - end - end - end - local mt = getmetatable(tbl) - if not mt or not mt.__pairs then - if option['sorter'] then - option['sorter'](keys, keymap) - else - tableSort(keys, function (a, b) - return keymap[a] < keymap[b] - end) - end - end - for _, key in ipairs(keys) do - local keyWord = keymap[key] - if option['noArrayKey'] - and isInteger(key) - and key <= #tbl - then - keyWord = '' - else - if #keyWord < alignment then - keyWord = keyWord .. (' '):rep(alignment - #keyWord) .. ' = ' - else - keyWord = keyWord .. ' = ' - end - end - local value = tbl[key] - local tp = type(value) - if option['format'] and option['format'][key] then - lines[#lines+1] = ('%s%s%s,'):format(TAB[tab+1], keyWord, option['format'][key](value, unpack, tab+1)) - elseif tp == 'table' then - if mark[value] and mark[value] > 0 then - lines[#lines+1] = ('%s%s%s,'):format(TAB[tab+1], keyWord, option['loop'] or '"<Loop>"') - else - lines[#lines+1] = ('%s%s{'):format(TAB[tab+1], keyWord) - unpack(value, tab+1) - lines[#lines+1] = ('%s},'):format(TAB[tab+1]) - end - elseif tp == 'string' then - lines[#lines+1] = ('%s%s%q,'):format(TAB[tab+1], keyWord, value) - elseif tp == 'number' then - lines[#lines+1] = ('%s%s%s,'):format(TAB[tab+1], keyWord, (option['number'] or formatNumber)(value)) - elseif tp == 'nil' then - else - lines[#lines+1] = ('%s%s%s,'):format(TAB[tab+1], keyWord, tostring(value)) - end - end - mark[tbl] = mark[tbl] - 1 - end - unpack(tbl, 0) - lines[#lines+1] = '}' - return tableConcat(lines, '\r\n') -end - ---- 递归判断A与B是否相等 ----@param a any ----@param b any ----@return boolean -function m.equal(a, b) - local tp1 = type(a) - local tp2 = type(b) - if tp1 ~= tp2 then - return false - end - if tp1 == 'table' then - local mark = {} - for k, v in pairs(a) do - mark[k] = true - local res = m.equal(v, b[k]) - if not res then - return false - end - end - for k in pairs(b) do - if not mark[k] then - return false - end - end - return true - elseif tp1 == 'number' then - return mathAbs(a - b) <= 1e-10 - else - return a == b - end -end - -local function sortTable(tbl) - if not tbl then - tbl = {} - end - local mt = {} - local keys = {} - local mark = {} - local n = 0 - for key in next, tbl do - n=n+1;keys[n] = key - mark[key] = true - end - tableSort(keys) - function mt:__newindex(key, value) - rawset(self, key, value) - n=n+1;keys[n] = key - mark[key] = true - if type(value) == 'table' then - sortTable(value) - end - end - function mt:__pairs() - local list = {} - local m = 0 - for key in next, self do - if not mark[key] then - m=m+1;list[m] = key - end - end - if m > 0 then - move(keys, 1, n, m+1) - tableSort(list) - for i = 1, m do - local key = list[i] - keys[i] = key - mark[key] = true - end - n = n + m - end - local i = 0 - return function () - i = i + 1 - local key = keys[i] - return key, self[key] - end - end - - return setmetatable(tbl, mt) -end - ---- 创建一个有序表 ----@param tbl table {optional = 'self'} ----@return table -function m.container(tbl) - return sortTable(tbl) -end - ---- 读取文件 ----@param path string -function m.loadFile(path) - local f, e = ioOpen(path, 'rb') - if not f then - return nil, e - end - if f:read(3) ~= '\xEF\xBB\xBF' then - f:seek("set") - end - local buf = f:read 'a' - f:close() - return buf -end - ---- 写入文件 ----@param path string ----@param content string -function m.saveFile(path, content) - local f, e = ioOpen(path, "wb") - - if f then - f:write(content) - f:close() - return true - else - return false, e - end -end - ---- 计数器 ----@param init integer {optional = 'after'} ----@param step integer {optional = 'after'} ----@return fun():integer -function m.counter(init, step) - if not step then - step = 1 - end - local current = init and (init - 1) or 0 - return function () - current = current + step - return current - end -end - ---- 排序后遍历 ----@param t table -function m.sortPairs(t) - local keys = {} - for k in pairs(t) do - keys[#keys+1] = k - end - tableSort(keys) - local i = 0 - return function () - i = i + 1 - local k = keys[i] - return k, t[k] - end -end - ---- 深拷贝(不处理元表) ----@param source table ----@param target table {optional = 'self'} -function m.deepCopy(source, target) - local mark = {} - local function copy(a, b) - if type(a) ~= 'table' then - return a - end - if mark[a] then - return mark[a] - end - if not b then - b = {} - end - mark[a] = b - for k, v in pairs(a) do - b[copy(k)] = copy(v) - end - return b - end - return copy(source, target) -end - ---- 序列化 -function m.unpack(t) - local result = {} - local tid = 0 - local cache = {} - local function unpack(o) - local id = cache[o] - if not id then - tid = tid + 1 - id = tid - cache[o] = tid - if type(o) == 'table' then - local new = {} - result[tid] = new - for k, v in next, o do - new[unpack(k)] = unpack(v) - end - else - result[id] = o - end - end - return id - end - unpack(t) - return result -end - ---- 反序列化 -function m.pack(t) - local cache = {} - local function pack(id) - local o = cache[id] - if o then - return o - end - o = t[id] - if type(o) == 'table' then - local new = {} - cache[id] = new - for k, v in next, o do - new[pack(k)] = pack(v) - end - return new - else - cache[id] = o - return o - end - end - return pack(1) -end - ---- defer -local deferMT = { __close = function (self) self[1]() end } -function m.defer(callback) - return setmetatable({ callback }, deferMT) -end - -local esc = { - ["'"] = [[\']], - ['"'] = [[\"]], - ['\r'] = [[\r]], - ['\n'] = '\\\n', -} - -function m.viewString(str, quo) - if not quo then - if not str:find("'", 1, true) and str:find('"', 1, true) then - quo = "'" - else - quo = '"' - end - end - if quo == "'" then - return quo .. str:gsub([=[['\r\n]]=], esc) .. quo - elseif quo == '"' then - return quo .. str:gsub([=[["\r\n]]=], esc) .. quo - else - if str:find '\r' then - return m.viewString(str) - end - local eqnum = #quo - 2 - local fsymb = ']' .. ('='):rep(eqnum) .. ']' - if not str:find(fsymb, 1, true) then - return quo .. str .. fsymb - end - for i = 0, 10 do - local fsymb = ']' .. ('='):rep(i) .. ']' - if not str:find(fsymb, 1, true) then - local ssymb = '[' .. ('='):rep(i) .. '[' - return ssymb .. str .. fsymb - end - end - return m.viewString(str) - end -end - -function m.viewLiteral(v) - local tp = type(v) - if tp == 'nil' then - return 'nil' - elseif tp == 'string' then - return m.viewString(v) - elseif tp == 'boolean' then - return tostring(v) - elseif tp == 'number' then - if isInteger(v) then - return tostring(v) - else - return formatNumber(v) - end - end - return nil -end - -return m diff --git a/server-beta/src/vm/dummySource.lua b/server-beta/src/vm/dummySource.lua deleted file mode 100644 index 50ff13e7..00000000 --- a/server-beta/src/vm/dummySource.lua +++ /dev/null @@ -1,13 +0,0 @@ -local vm = require 'vm.vm' - -vm.librarySourceCache = setmetatable({}, { __mode = 'kv'}) - -function vm.librarySource(lib) - if not vm.librarySourceCache[lib] then - vm.librarySourceCache[lib] = { - type = 'library', - library = lib, - } - end - return vm.librarySourceCache[lib] -end diff --git a/server-beta/src/vm/eachDef.lua b/server-beta/src/vm/eachDef.lua deleted file mode 100644 index 0274cbee..00000000 --- a/server-beta/src/vm/eachDef.lua +++ /dev/null @@ -1,65 +0,0 @@ -local vm = require 'vm.vm' -local guide = require 'parser.guide' -local files = require 'files' - -local function checkPath(source, info) - if source.type == 'goto' then - return true - end - local src = info.source - local mode = guide.getPath(source, src) - if not mode then - return true - end - if mode == 'before' then - return false - end - return true -end - -function vm.eachDef(source, callback) - local results = {} - local valueUris = {} - local sourceUri = guide.getRoot(source).uri - vm.eachRef(source, function (info) - if info.mode == 'declare' - or info.mode == 'set' - or info.mode == 'return' - or info.mode == 'value' then - results[#results+1] = info - local src = info.source - if info.mode == 'return' then - local uri = guide.getRoot(src).uri - valueUris[uri] = info.source - end - end - end) - - for _, info in ipairs(results) do - local src = info.source - local destUri = guide.getRoot(src).uri - -- 如果是同一个文件,则检查位置关系后放行 - if sourceUri == destUri then - if checkPath(source, info) then - callback(info) - end - goto CONTINUE - end - -- 如果是global或field,则直接放行(因为无法确定顺序) - if src.type == 'setindex' - or src.type == 'setfield' - or src.type == 'setmethod' - or src.type == 'tablefield' - or src.type == 'tableindex' - or src.type == 'setglobal' then - callback(info) - goto CONTINUE - end - -- 如果不是同一个文件,则必须在该文件 return 后才放行 - if valueUris[destUri] then - callback(info) - goto CONTINUE - end - ::CONTINUE:: - end -end diff --git a/server-beta/src/vm/eachField.lua b/server-beta/src/vm/eachField.lua deleted file mode 100644 index 1d3d222d..00000000 --- a/server-beta/src/vm/eachField.lua +++ /dev/null @@ -1,169 +0,0 @@ -local guide = require 'parser.guide' -local vm = require 'vm.vm' - -local function ofTabel(value, callback) - for _, field in ipairs(value) do - if field.type == 'tablefield' - or field.type == 'tableindex' then - callback { - source = field, - key = guide.getKeyName(field), - value = field.value, - mode = 'set', - } - end - end -end - -local function ofENV(source, callback) - if source.type == 'getlocal' then - local parent = source.parent - if parent.type == 'getfield' - or parent.type == 'getmethod' - or parent.type == 'getindex' then - callback { - source = parent, - key = guide.getKeyName(parent), - mode = 'get', - } - end - elseif source.type == 'getglobal' then - callback { - source = source, - key = guide.getKeyName(source), - mode = 'get', - } - elseif source.type == 'setglobal' then - callback { - source = source, - key = guide.getKeyName(source), - mode = 'set', - value = source.value, - } - end -end - -local function ofSpecialArg(source, callback) - local args = source.parent - local call = args.parent - local func = call.node - local name = func.special - if name == 'rawset' then - if args[1] == source and args[2] then - callback { - source = call, - key = guide.getKeyName(args[2]), - value = args[3], - mode = 'set', - } - end - elseif name == 'rawget' then - if args[1] == source and args[2] then - callback { - source = call, - key = guide.getKeyName(args[2]), - mode = 'get', - } - end - elseif name == 'setmetatable' then - if args[1] == source and args[2] then - vm.eachField(args[2], function (info) - if info.key == 's|__index' and info.value then - vm.eachField(info.value, callback) - end - end) - end - end -end - -local function ofVar(source, callback) - local parent = source.parent - if not parent then - return - end - if parent.type == 'getfield' - or parent.type == 'getmethod' - or parent.type == 'getindex' then - callback { - source = parent, - key = guide.getKeyName(parent), - mode = 'get', - } - return - end - if parent.type == 'setfield' - or parent.type == 'setmethod' - or parent.type == 'setindex' then - callback { - source = parent, - key = guide.getKeyName(parent), - value = parent.value, - mode = 'set', - } - return - end - if parent.type == 'callargs' then - ofSpecialArg(source, callback) - end -end - -local function eachField(source, callback) - vm.eachRef(source, function (info) - local src = info.source - if src.tag == '_ENV' then - if src.ref then - for _, ref in ipairs(src.ref) do - ofENV(ref, callback) - end - end - elseif src.type == 'getlocal' - or src.type == 'getglobal' - or src.type == 'getfield' - or src.type == 'getmethod' - or src.type == 'getindex' then - ofVar(src, callback) - elseif src.type == 'table' then - ofTabel(src, callback) - end - end) -end - ---- 获取所有的field -function vm.eachField(source, callback) - local cache = vm.cache.eachField[source] - if cache then - for i = 1, #cache do - local res = callback(cache[i]) - if res ~= nil then - return res - end - end - return - end - local unlock = vm.lock('eachField', source) - if not unlock then - return - end - cache = {} - vm.cache.eachField[source] = cache - local mark = {} - eachField(source, function (info) - local src = info.source - if mark[src] then - return - end - mark[src] = true - cache[#cache+1] = info - end) - unlock() - vm.eachRef(source, function (info) - local src = info.source - vm.cache.eachField[src] = cache - end) - for i = 1, #cache do - local res = callback(cache[i]) - if res ~= nil then - return res - end - end -end diff --git a/server-beta/src/vm/eachRef.lua b/server-beta/src/vm/eachRef.lua deleted file mode 100644 index cfb2bef8..00000000 --- a/server-beta/src/vm/eachRef.lua +++ /dev/null @@ -1,500 +0,0 @@ -local guide = require 'parser.guide' -local files = require 'files' -local vm = require 'vm.vm' - -local function ofCall(func, index, callback) - vm.eachRef(func, function (info) - local src = info.source - local returns - if src.type == 'main' or src.type == 'function' then - returns = src.returns - end - if returns then - -- 搜索函数第 index 个返回值 - for _, rtn in ipairs(returns) do - local val = rtn[index] - if val then - callback { - source = val, - mode = 'return', - } - vm.eachRef(val, callback) - end - end - end - end) -end - -local function ofCallSelect(call, index, callback) - local slc = call.parent - if slc.index == index then - vm.eachRef(slc.parent, callback) - return - end - if call.extParent then - for i = 1, #call.extParent do - slc = call.extParent[i] - if slc.index == index then - vm.eachRef(slc.parent, callback) - return - end - end - end -end - -local function ofReturn(rtn, index, callback) - local func = guide.getParentFunction(rtn) - if not func then - return - end - -- 搜索函数调用的第 index 个接收值 - if func.type == 'main' then - local myUri = func.uri - local uris = files.findLinkTo(myUri) - if not uris then - return - end - for _, uri in ipairs(uris) do - local ast = files.getAst(uri) - if ast then - local links = vm.getLinks(ast.ast) - if links then - for linkUri, calls in pairs(links) do - if files.eq(linkUri, myUri) then - for i = 1, #calls do - ofCallSelect(calls[i], 1, callback) - end - end - end - end - end - end - else - vm.eachRef(func, function (info) - local source = info.source - local call = source.parent - if not call or call.type ~= 'call' then - return - end - ofCallSelect(call, index, callback) - end) - end -end - -local function ofSpecialCall(call, func, index, callback) - local name = func.special - if name == 'setmetatable' then - if index == 1 then - local args = call.args - if args[1] then - vm.eachRef(args[1], callback) - end - if args[2] then - vm.eachField(args[2], function (info) - if info.key == 's|__index' then - vm.eachRef(info.source, callback) - if info.value then - vm.eachRef(info.value, callback) - end - end - end) - end - end - elseif name == 'require' then - if index == 1 then - local result = vm.getLinkUris(call) - if result then - local myUri = guide.getRoot(call).uri - for _, uri in ipairs(result) do - if not files.eq(uri, myUri) then - local ast = files.getAst(uri) - if ast then - ofCall(ast.ast, 1, callback) - end - end - end - end - end - end -end - -local function ofValue(value, callback) - if value.type == 'select' then - -- 检查函数返回值 - local call = value.vararg - if call.type == 'call' then - ofCall(call.node, value.index, callback) - ofSpecialCall(call, call.node, value.index, callback) - end - return - end - - if value.type == 'table' - or value.type == 'string' - or value.type == 'number' - or value.type == 'boolean' - or value.type == 'nil' - or value.type == 'function' then - callback { - source = value, - mode = 'value', - } - end - - vm.eachRef(value, callback) - - local parent = value.parent - if parent.type == 'local' - or parent.type == 'setglobal' - or parent.type == 'setlocal' - or parent.type == 'setfield' - or parent.type == 'setmethod' - or parent.type == 'setindex' - or parent.type == 'tablefield' - or parent.type == 'tableindex' then - if parent.value == value then - vm.eachRef(parent, callback) - end - end - if parent.type == 'return' then - for i = 1, #parent do - if parent[i] == value then - ofReturn(parent, i, callback) - break - end - end - end -end - -local function ofSelf(loc, callback) - -- self 的2个特殊引用位置: - -- 1. 当前方法定义时的对象(mt) - local method = loc.method - local node = method.node - vm.eachRef(node, callback) - -- 2. 调用该方法时传入的对象 -end - ---- 自己作为赋值的值 -local function asValue(source, callback) - local parent = source.parent - if parent and parent.value == source then - if guide.getName(parent) == '__index' then - if parent.type == 'tablefield' - or parent.type == 'tableindex' then - local t = parent.parent - local args = t.parent - if args[2] == t then - local call = args.parent - local func = call.node - if func.special == 'setmetatable' then - vm.eachRef(args[1], callback) - end - end - end - end - end -end - -local function getCallRecvs(call) - local parent = call.parent - if parent.type ~= 'select' then - return nil - end - local exParent = call.exParent - local recvs = {} - recvs[1] = parent.parent - if exParent then - for _, p in ipairs(exParent) do - recvs[#recvs+1] = p.parent - end - end - return recvs -end - ---- 自己作为函数的参数 -local function asArg(source, callback) - local parent = source.parent - if not parent then - return - end - if parent.type == 'callargs' then - local call = parent.parent - local func = call.node - local name = func.special - if name == 'setmetatable' then - if parent[1] == source then - if parent[2] then - vm.eachField(parent[2], function (info) - if info.key == 's|__index' then - vm.eachRef(info.source, callback) - if info.value then - vm.eachRef(info.value, callback) - end - end - end) - end - end - local recvs = getCallRecvs(call) - if recvs and recvs[1] then - vm.eachRef(recvs[1], callback) - end - end - end -end - -local function ofLocal(loc, callback) - -- 方法中的 self 使用了一个虚拟的定义位置 - if loc.tag ~= 'self' then - callback { - source = loc, - mode = 'declare', - } - end - if loc.ref then - for _, ref in ipairs(loc.ref) do - if ref.type == 'getlocal' then - callback { - source = ref, - mode = 'get', - } - asValue(ref, callback) - elseif ref.type == 'setlocal' then - callback { - source = ref, - mode = 'set', - } - if ref.value then - ofValue(ref.value, callback) - end - end - end - end - if loc.tag == 'self' then - ofSelf(loc, callback) - end - if loc.value then - ofValue(loc.value, callback) - end - if loc.tag == '_ENV' and loc.ref then - for _, ref in ipairs(loc.ref) do - if ref.type == 'getlocal' then - local parent = ref.parent - if parent.type == 'getfield' - or parent.type == 'getindex' then - if guide.getKeyName(parent) == '_G' then - callback { - source = parent, - mode = 'get', - } - end - end - elseif ref.type == 'getglobal' then - if guide.getName(ref) == '_G' then - callback { - source = ref, - mode = 'get', - } - end - end - end - end -end - -local function ofGlobal(source, callback) - local key = guide.getKeyName(source) - local node = source.node - if node.tag == '_ENV' then - local uris = files.findGlobals(key) - for _, uri in ipairs(uris) do - local ast = files.getAst(uri) - local globals = vm.getGlobals(ast.ast) - if globals[key] then - for _, info in ipairs(globals[key]) do - callback(info) - if info.value then - ofValue(info.value, callback) - end - end - end - end - else - vm.eachField(node, function (info) - if key == info.key then - callback { - source = info.source, - mode = info.mode, - } - if info.value then - ofValue(info.value, callback) - end - end - end) - end -end - -local function ofField(source, callback) - local parent = source.parent - local key = guide.getKeyName(source) - if parent.type == 'tablefield' - or parent.type == 'tableindex' then - local tbl = parent.parent - vm.eachField(tbl, function (info) - if key == info.key then - callback { - source = info.source, - mode = info.mode, - } - if info.value then - ofValue(info.value, callback) - end - end - end) - else - local node = parent.node - vm.eachField(node, function (info) - if key == info.key then - callback { - source = info.source, - mode = info.mode, - } - if info.value then - ofValue(info.value, callback) - end - end - end) - end -end - -local function ofLiteral(source, callback) - local parent = source.parent - if not parent then - return - end - if parent.type == 'setindex' - or parent.type == 'getindex' - or parent.type == 'tableindex' then - ofField(source, callback) - end -end - -local function ofLabel(source, callback) - callback { - source = source, - mode = 'set', - } - if source.ref then - for _, ref in ipairs(source.ref) do - callback { - source = ref, - mode = 'get', - } - end - end -end - -local function ofGoTo(source, callback) - local name = source[1] - local label = guide.getLabel(source, name) - if label then - ofLabel(label, callback) - end -end - -local function ofMain(source, callback) - callback { - source = source, - mode = 'main', - } -end - -local function eachRef(source, callback) - local stype = source.type - if stype == 'local' then - ofLocal(source, callback) - elseif stype == 'getlocal' - or stype == 'setlocal' then - ofLocal(source.node, callback) - elseif stype == 'setglobal' - or stype == 'getglobal' then - ofGlobal(source, callback) - elseif stype == 'field' - or stype == 'method' then - ofField(source, callback) - elseif stype == 'setfield' - or stype == 'getfield' then - ofField(source.field, callback) - elseif stype == 'setmethod' - or stype == 'getmethod' then - ofField(source.method, callback) - elseif stype == 'number' - or stype == 'boolean' - or stype == 'string' then - ofLiteral(source, callback) - elseif stype == 'goto' then - ofGoTo(source, callback) - elseif stype == 'label' then - ofLabel(source, callback) - elseif stype == 'table' - or stype == 'function' then - ofValue(source, callback) - elseif stype == 'main' then - ofMain(source, callback) - end - asArg(source, callback) -end - ---- 判断2个对象是否拥有相同的引用 -function vm.isSameRef(a, b) - local cache = vm.cache.eachRef[a] - if cache then - -- 相同引用的source共享同一份cache - return cache == vm.cache.eachRef[b] - else - return vm.eachRef(a, function (info) - if info.source == b then - return true - end - end) or false - end -end - ---- 获取所有的引用 -function vm.eachRef(source, callback) - local cache = vm.cache.eachRef[source] - if cache then - for i = 1, #cache do - local res = callback(cache[i]) - if res ~= nil then - return res - end - end - return - end - local unlock = vm.lock('eachRef', source) - if not unlock then - return - end - cache = {} - vm.cache.eachRef[source] = cache - local mark = {} - eachRef(source, function (info) - local src = info.source - if mark[src] then - return - end - mark[src] = true - cache[#cache+1] = info - end) - unlock() - for i = 1, #cache do - local src = cache[i].source - vm.cache.eachRef[src] = cache - end - for i = 1, #cache do - local res = callback(cache[i]) - if res ~= nil then - return res - end - end -end diff --git a/server-beta/src/vm/getGlobal.lua b/server-beta/src/vm/getGlobal.lua deleted file mode 100644 index 373c907e..00000000 --- a/server-beta/src/vm/getGlobal.lua +++ /dev/null @@ -1,6 +0,0 @@ -local vm = require 'vm.vm' - -function vm.getGlobal(source) - vm.getGlobals(source) - return vm.cache.getGlobal[source] -end diff --git a/server-beta/src/vm/getGlobals.lua b/server-beta/src/vm/getGlobals.lua deleted file mode 100644 index 699dd270..00000000 --- a/server-beta/src/vm/getGlobals.lua +++ /dev/null @@ -1,45 +0,0 @@ -local guide = require 'parser.guide' -local vm = require 'vm.vm' - -local function getGlobals(root) - local env = guide.getENV(root) - local cache = {} - local mark = {} - vm.eachField(env, function (info) - local src = info.source - if mark[src] then - return - end - mark[src] = true - local name = info.key - if not name then - return - end - if not cache[name] then - cache[name] = { - key = name, - mode = {}, - } - end - cache[name][#cache[name]+1] = info - cache[name].mode[info.mode] = true - vm.cache.getGlobal[src] = name - end) - return cache -end - -function vm.getGlobals(source) - source = guide.getRoot(source) - local cache = vm.cache.getGlobals[source] - if cache ~= nil then - return cache - end - local unlock = vm.lock('getGlobals', source) - if not unlock then - return nil - end - cache = getGlobals(source) or false - vm.cache.getGlobals[source] = cache - unlock() - return cache -end diff --git a/server-beta/src/vm/getLibrary.lua b/server-beta/src/vm/getLibrary.lua deleted file mode 100644 index fd05347e..00000000 --- a/server-beta/src/vm/getLibrary.lua +++ /dev/null @@ -1,89 +0,0 @@ -local vm = require 'vm.vm' -local library = require 'library' -local guide = require 'parser.guide' - -local function checkStdLibrary(source) - local globalName = vm.getGlobal(source) - if not globalName then - return nil - end - local name = globalName:match '^s|(.+)$' - if library.global[name] then - return library.global[name] - end -end - -local function getLibInNode(source, nodeLib) - if not nodeLib then - return nil - end - if not nodeLib.child then - return nil - end - local key = guide.getName(source) - local defLib = nodeLib.child[key] - return defLib -end - -local function getNodeAsTable(source) - local node = source.node - local nodeGlobalName = vm.getGlobal(node) - if not nodeGlobalName then - return nil - end - local nodeName = nodeGlobalName:match '^s|(.+)$' - return getLibInNode(source, library.global[nodeName]) -end - -local function getNodeAsObject(source) - local node = source.node - local values = vm.getValue(node) - if not values then - return nil - end - for i = 1, #values do - local value = values[i] - local type = value.type - local nodeLib = library.object[type] - local lib = getLibInNode(source, nodeLib) - if lib then - return lib - end - end - return nil -end - -local function checkNode(source) - if source.type ~= 'getfield' - and source.type ~= 'getmethod' - and source.type ~= 'getindex' then - return nil - end - return getNodeAsTable(source) - or getNodeAsObject(source) -end - -local function getLibrary(source) - local lib = checkStdLibrary(source) - if lib then - return lib - end - return checkNode(source) or vm.eachRef(source, function (info) - return checkNode(info.source) - end) -end - -function vm.getLibrary(source) - local cache = vm.cache.getLibrary[source] - if cache ~= nil then - return cache - end - local unlock = vm.lock('getLibrary', source) - if not unlock then - return - end - cache = getLibrary(source) or false - vm.cache.getLibrary[source] = cache - unlock() - return cache -end diff --git a/server-beta/src/vm/getLinks.lua b/server-beta/src/vm/getLinks.lua deleted file mode 100644 index 6875771f..00000000 --- a/server-beta/src/vm/getLinks.lua +++ /dev/null @@ -1,48 +0,0 @@ -local guide = require 'parser.guide' -local vm = require 'vm.vm' - -local function getLinks(root) - local cache = {} - local ok - guide.eachSpecialOf(root, 'require', function (source) - local call = source.parent - if call.type == 'call' then - local uris = vm.getLinkUris(call) - if uris then - ok = true - for i = 1, #uris do - local uri = uris[i] - if not cache[uri] then - cache[uri] = {} - end - cache[uri][#cache[uri]+1] = call - end - end - end - end) - if not ok then - return nil - end - return cache -end - -function vm.getLinks(source) - source = guide.getRoot(source) - local cache = vm.cache.getLinks[source] - if cache ~= nil then - return cache - end - local unlock = vm.lock('getLinks', source) - if not unlock then - return nil - end - local clock = os.clock() - cache = getLinks(source) or false - local passed = os.clock() - clock - if passed > 0.1 then - log.warn(('getLinks takes [%.3f] sec!'):format(passed)) - end - vm.cache.getLinks[source] = cache - unlock() - return cache -end diff --git a/server-beta/src/vm/getValue.lua b/server-beta/src/vm/getValue.lua deleted file mode 100644 index ee486a54..00000000 --- a/server-beta/src/vm/getValue.lua +++ /dev/null @@ -1,895 +0,0 @@ -local vm = require 'vm.vm' - -local typeSort = { - ['boolean'] = 1, - ['string'] = 2, - ['integer'] = 3, - ['number'] = 4, - ['table'] = 5, - ['function'] = 6, - ['nil'] = math.maxinteger, -} - -NIL = setmetatable({'<nil>'}, { __tostring = function () return 'nil' end }) - -local function merge(t, b) - if not t then - t = {} - end - if not b then - return t - end - for i = 1, #b do - local o = b[i] - if not t[o] then - t[o] = true - t[#t+1] = o - end - end - return t -end - -local function alloc(o) - -- TODO - assert(o.type) - if type(o.type) == 'table' then - local values = {} - for i = 1, #o.type do - local sub = { - type = o.type[i], - value = o.value, - source = o.source, - } - values[i] = sub - values[sub] = true - end - return values - else - return { - [1] = o, - [o] = true, - } - end -end - -local function insert(t, o) - if not o then - return - end - if not t[o] then - t[o] = true - t[#t+1] = o - end - return t -end - -local function checkLiteral(source) - if source.type == 'string' then - return alloc { - type = 'string', - value = source[1], - source = source, - } - elseif source.type == 'nil' then - return alloc { - type = 'nil', - value = NIL, - source = source, - } - elseif source.type == 'boolean' then - return alloc { - type = 'boolean', - value = source[1], - source = source, - } - elseif source.type == 'number' then - if math.type(source[1]) == 'integer' then - return alloc { - type = 'integer', - value = source[1], - source = source, - } - else - return alloc { - type = 'number', - value = source[1], - source = source, - } - end - elseif source.type == 'table' then - return alloc { - type = 'table', - source = source, - } - elseif source.type == 'function' then - return alloc { - type = 'function', - source = source, - } - end -end - -local function checkUnary(source) - if source.type ~= 'unary' then - return - end - local op = source.op - if op.type == 'not' then - local checkTrue = vm.checkTrue(source[1]) - local value = nil - if checkTrue == true then - value = false - elseif checkTrue == false then - value = true - end - return alloc { - type = 'boolean', - value = value, - source = source, - } - elseif op.type == '#' then - return alloc { - type = 'integer', - source = source, - } - elseif op.type == '~' then - local l = vm.getLiteral(source[1], 'integer') - return alloc { - type = 'integer', - value = l and ~l or nil, - source = source, - } - elseif op.type == '-' then - local v = vm.getLiteral(source[1], 'integer') - if v then - return alloc { - type = 'integer', - value = - v, - source = source, - } - end - v = vm.getLiteral(source[1], 'number') - return alloc { - type = 'number', - value = v and -v or nil, - source = source, - } - end -end - -local function checkBinary(source) - if source.type ~= 'binary' then - return - end - local op = source.op - if op.type == 'and' then - local isTrue = vm.checkTrue(source[1]) - if isTrue == true then - return vm.getValue(source[2]) - elseif isTrue == false then - return vm.getValue(source[1]) - else - return merge( - vm.getValue(source[1]), - vm.getValue(source[2]) - ) - end - elseif op.type == 'or' then - local isTrue = vm.checkTrue(source[1]) - if isTrue == true then - return vm.getValue(source[1]) - elseif isTrue == false then - return vm.getValue(source[2]) - else - return merge( - vm.getValue(source[1]), - vm.getValue(source[2]) - ) - end - elseif op.type == '==' then - local value = vm.isSameValue(source[1], source[2]) - if value ~= nil then - return alloc { - type = 'boolean', - value = value, - source = source, - } - end - local isSame = vm.isSameRef(source[1], source[2]) - if isSame == true then - value = true - else - value = nil - end - return alloc { - type = 'boolean', - value = value, - source = source, - } - elseif op.type == '~=' then - local value = vm.isSameValue(source[1], source[2]) - if value ~= nil then - return alloc { - type = 'boolean', - value = not value, - source = source, - } - end - local isSame = vm.isSameRef(source[1], source[2]) - if isSame == true then - value = false - else - value = nil - end - return alloc { - type = 'boolean', - value = value, - source = source, - } - elseif op.type == '<=' then - local v1 = vm.getLiteral(source[1], 'integer') or vm.getLiteral(source[1], 'number') - local v2 = vm.getLiteral(source[2], 'integer') or vm.getLiteral(source[2], 'number') - local v - if v1 and v2 then - v = v1 <= v2 - end - return alloc { - type = 'boolean', - value = v, - source = source, - } - elseif op.type == '>=' then - local v1 = vm.getLiteral(source[1], 'integer') or vm.getLiteral(source[1], 'number') - local v2 = vm.getLiteral(source[2], 'integer') or vm.getLiteral(source[2], 'number') - local v - if v1 and v2 then - v = v1 >= v2 - end - return alloc { - type = 'boolean', - value = v, - source = source, - } - elseif op.type == '<' then - local v1 = vm.getLiteral(source[1], 'integer') or vm.getLiteral(source[1], 'number') - local v2 = vm.getLiteral(source[2], 'integer') or vm.getLiteral(source[2], 'number') - local v - if v1 and v2 then - v = v1 < v2 - end - return alloc { - type = 'boolean', - value = v, - source = source, - } - elseif op.type == '>' then - local v1 = vm.getLiteral(source[1], 'integer') or vm.getLiteral(source[1], 'number') - local v2 = vm.getLiteral(source[2], 'integer') or vm.getLiteral(source[2], 'number') - local v - if v1 and v2 then - v = v1 > v2 - end - return alloc { - type = 'boolean', - value = v, - source = source, - } - elseif op.type == '|' then - local v1 = vm.getLiteral(source[1], 'integer') - local v2 = vm.getLiteral(source[2], 'integer') - local v - if v1 and v2 then - v = v1 | v2 - end - return alloc { - type = 'integer', - value = v, - source = source, - } - elseif op.type == '~' then - local v1 = vm.getLiteral(source[1], 'integer') - local v2 = vm.getLiteral(source[2], 'integer') - local v - if v1 and v2 then - v = v1 ~ v2 - end - return alloc { - type = 'integer', - value = v, - source = source, - } - elseif op.type == '&' then - local v1 = vm.getLiteral(source[1], 'integer') - local v2 = vm.getLiteral(source[2], 'integer') - local v - if v1 and v2 then - v = v1 & v2 - end - return alloc { - type = 'integer', - value = v, - source = source, - } - elseif op.type == '<<' then - local v1 = vm.getLiteral(source[1], 'integer') - local v2 = vm.getLiteral(source[2], 'integer') - local v - if v1 and v2 then - v = v1 << v2 - end - return alloc { - type = 'integer', - value = v, - source = source, - } - elseif op.type == '>>' then - local v1 = vm.getLiteral(source[1], 'integer') - local v2 = vm.getLiteral(source[2], 'integer') - local v - if v1 and v2 then - v = v1 >> v2 - end - return alloc { - type = 'integer', - value = v, - source = source, - } - elseif op.type == '..' then - local v1 = vm.getLiteral(source[1], 'string') - local v2 = vm.getLiteral(source[2], 'string') - local v - if v1 and v2 then - v = v1 .. v2 - end - return alloc { - type = 'string', - value = v, - source = source, - } - elseif op.type == '^' then - local v1 = vm.getLiteral(source[1], 'integer') or vm.getLiteral(source[1], 'number') - local v2 = vm.getLiteral(source[2], 'integer') or vm.getLiteral(source[2], 'number') - local v - if v1 and v2 then - v = v1 ^ v2 - end - return alloc { - type = 'number', - value = v, - source = source, - } - elseif op.type == '/' then - local v1 = vm.getLiteral(source[1], 'integer') or vm.getLiteral(source[1], 'number') - local v2 = vm.getLiteral(source[2], 'integer') or vm.getLiteral(source[2], 'number') - local v - if v1 and v2 then - v = v1 > v2 - end - return alloc { - type = 'number', - value = v, - source = source, - } - -- 其他数学运算根据2侧的值决定,当2侧的值均为整数时返回整数 - elseif op.type == '+' then - local v1 = vm.getLiteral(source[1], 'integer') - local v2 = vm.getLiteral(source[2], 'integer') - if v1 and v2 then - return alloc { - type = 'integer', - value = v1 + v2, - source = source, - } - end - v1 = v1 or vm.getLiteral(source[1], 'number') - v2 = v2 or vm.getLiteral(source[1], 'number') - return alloc { - type = 'number', - value = (v1 and v2) and (v1 + v2) or nil, - source = source, - } - elseif op.type == '-' then - local v1 = vm.getLiteral(source[1], 'integer') - local v2 = vm.getLiteral(source[2], 'integer') - if v1 and v2 then - return alloc { - type = 'integer', - value = v1 - v2, - source = source, - } - end - v1 = v1 or vm.getLiteral(source[1], 'number') - v2 = v2 or vm.getLiteral(source[1], 'number') - return alloc { - type = 'number', - value = (v1 and v2) and (v1 - v2) or nil, - source = source, - } - elseif op.type == '*' then - local v1 = vm.getLiteral(source[1], 'integer') - local v2 = vm.getLiteral(source[2], 'integer') - if v1 and v2 then - return alloc { - type = 'integer', - value = v1 * v2, - source = source, - } - end - v1 = v1 or vm.getLiteral(source[1], 'number') - v2 = v2 or vm.getLiteral(source[1], 'number') - return alloc { - type = 'number', - value = (v1 and v2) and (v1 * v2) or nil, - source = source, - } - elseif op.type == '%' then - local v1 = vm.getLiteral(source[1], 'integer') - local v2 = vm.getLiteral(source[2], 'integer') - if v1 and v2 then - return alloc { - type = 'integer', - value = v1 % v2, - source = source, - } - end - v1 = v1 or vm.getLiteral(source[1], 'number') - v2 = v2 or vm.getLiteral(source[1], 'number') - return alloc { - type = 'number', - value = (v1 and v2) and (v1 % v2) or nil, - source = source, - } - elseif op.type == '//' then - local v1 = vm.getLiteral(source[1], 'integer') - local v2 = vm.getLiteral(source[2], 'integer') - if v1 and v2 then - return alloc { - type = 'integer', - value = v1 // v2, - source = source, - } - end - v1 = v1 or vm.getLiteral(source[1], 'number') - v2 = v2 or vm.getLiteral(source[1], 'number') - return alloc { - type = 'number', - value = (v1 and v2) and (v1 // v2) or nil, - source = source, - } - end -end - -local function checkValue(source) - if source.value then - return vm.getValue(source.value) - end - if source.type == 'paren' then - return vm.getValue(source.exp) - end -end - -local function hasTypeInResults(results, type) - for i = 1, #results do - if results[i].type == type then - return true - end - end - return false -end - -local function inferByCall(results, source) - if #results ~= 0 then - return - end - if not source.parent then - return - end - if source.parent.type ~= 'call' then - return - end - if source.parent.node == source then - insert(results, { - type = 'function', - source = source, - }) - return - end -end - -local function inferByGetTable(results, source) - if #results ~= 0 then - return - end - local next = source.next - if not next then - return - end - if next.type == 'getfield' - or next.type == 'getindex' - or next.type == 'getmethod' - or next.type == 'setfield' - or next.type == 'setindex' - or next.type == 'setmethod' then - insert(results, { - type = 'table', - source = source, - }) - end -end - -local function checkDef(results, source) - vm.eachDef(source, function (info) - local src = info.source - local tp = vm.getValue(src) - if tp then - merge(results, tp) - end - end) -end - -local function checkLibrary(source) - local lib = vm.getLibrary(source) - if not lib then - return nil - end - return alloc { - type = lib.type, - value = lib.value, - source = vm.librarySource(lib), - } -end - -local function checkLibraryReturn(source) - if source.type ~= 'select' then - return nil - end - local index = source.index - local call = source.vararg - if call.type ~= 'call' then - return nil - end - local func = call.node - local lib = vm.getLibrary(func) - if not lib then - return nil - end - if lib.type ~= 'function' then - return nil - end - if not lib.returns then - return nil - end - local rtn = lib.returns[index] - if not rtn then - return nil - end - return alloc { - type = rtn.type, - value = rtn.value, - source = vm.librarySource(rtn), - } -end - -local function checkLibraryArg(source) - local args = source.parent - if not args then - return - end - if args.type ~= 'callargs' then - return - end - local call = args.parent - if not call then - return - end - local func = call.node - local index - for i = 1, #args do - if args[i] == source then - index = i - break - end - end - if not index then - return - end - local lib = vm.getLibrary(func) - local arg = lib and lib.args and lib.args[index] - if not arg then - return - end - if arg.type == '...' then - return - end - return alloc { - type = arg.type, - value = arg.value, - source = vm.librarySource(arg), - } -end - -local function inferByUnary(results, source) - if #results ~= 0 then - return - end - local parent = source.parent - if not parent or parent.type ~= 'unary' then - return - end - local op = parent.op - if op.type == '#' then - insert(results, { - type = 'string', - source = vm.librarySource(source) - }) - insert(results, { - type = 'table', - source = vm.librarySource(source) - }) - elseif op.type == '~' then - insert(results, { - type = 'integer', - source = vm.librarySource(source) - }) - elseif op.type == '-' then - insert(results, { - type = 'number', - source = vm.librarySource(source) - }) - end -end - -local function inferByBinary(results, source) - if #results ~= 0 then - return - end - local parent = source.parent - if not parent or parent.type ~= 'binary' then - return - end - local op = parent.op - if op.type == '<=' - or op.type == '>=' - or op.type == '<' - or op.type == '>' - or op.type == '^' - or op.type == '/' - or op.type == '+' - or op.type == '-' - or op.type == '*' - or op.type == '%' then - insert(results, { - type = 'number', - source = vm.librarySource(source) - }) - elseif op.type == '|' - or op.type == '~' - or op.type == '&' - or op.type == '<<' - or op.type == '>>' - -- 整数的可能性比较高 - or op.type == '//' then - insert(results, { - type = 'integer', - source = vm.librarySource(source) - }) - elseif op.type == '..' then - insert(results, { - type = 'string', - source = vm.librarySource(source) - }) - end -end - -local function inferBySetOfLocal(results, source) - if source.ref then - for i = 1, #source.ref do - local ref = source.ref[i] - if ref.type == 'setlocal' then - break - end - merge(results, vm.getValue(ref)) - end - end -end - -local function inferBySet(results, source) - if #results ~= 0 then - return - end - if source.type == 'local' then - inferBySetOfLocal(results, source) - elseif source.type == 'setlocal' - or source.type == 'getlocal' then - inferBySetOfLocal(results, source.node) - end -end - -local function getValue(source) - local results = checkLiteral(source) - or checkValue(source) - or checkUnary(source) - or checkBinary(source) - or checkLibrary(source) - or checkLibraryReturn(source) - or checkLibraryArg(source) - if results then - return results - end - - results = {} - checkDef(results, source) - inferBySet(results, source) - inferByCall(results, source) - inferByGetTable(results, source) - inferByUnary(results, source) - inferByBinary(results, source) - - if #results == 0 then - return nil - end - - return results -end - -function vm.checkTrue(source) - local values = vm.getValue(source) - if not values then - return - end - -- 当前认为的结果 - local current - for i = 1, #values do - -- 新的结果 - local new - local v = values[i] - if v.type == 'nil' then - new = false - elseif v.type == 'boolean' then - if v.value == true then - new = true - elseif v.value == false then - new = false - end - end - if new ~= nil then - if current == nil then - current = new - else - -- 如果2个结果完全相反,则返回 nil 表示不确定 - if new ~= current then - return nil - end - end - end - end - return current -end - ---- 获取特定类型的字面量值 -function vm.getLiteral(source, type) - local values = vm.getValue(source) - if not values then - return nil - end - for i = 1, #values do - local v = values[i] - if v.value ~= nil then - if type == nil or v.type == type then - return v.value - end - end - end - return nil -end - -function vm.isSameValue(a, b) - local valuesA = vm.getValue(a) - local valuesB = vm.getValue(b) - if not valuesA or not valuesB then - return false - end - if valuesA == valuesB then - return true - end - local values = {} - for i = 1, #valuesA do - local value = valuesA[i] - local literal = value.value - if literal then - values[literal] = false - end - end - for i = 1, #valuesB do - local value = valuesA[i] - local literal = value.value - if literal then - if values[literal] == nil then - return false - end - values[literal] = true - end - end - for k, v in pairs(values) do - if v == false then - return false - end - end - return true -end - ---- 是否包含某种类型 -function vm.hasType(source, type) - local values = vm.getValue(source) - if not values then - return false - end - for i = 1, #values do - local value = values[i] - if value.type == type then - return true - end - end - return false -end - -function vm.viewType(values) - if not values then - return 'any' - end - local types = {} - for i = 1, #values do - local tp = values[i].type - if not types[tp] then - types[tp] = true - types[#types+1] = tp - end - end - if #types == 0 then - return 'any' - end - if #types == 1 then - return types[1] - end - table.sort(types, function (a, b) - local sa = typeSort[a] - local sb = typeSort[b] - if sa and sb then - return sa < sb - end - if not sa and not sb then - return a < b - end - if sa and not sb then - return true - end - if not sa and sb then - return false - end - return false - end) - return table.concat(types, '|') -end - -function vm.getType(source) - local values = vm.getValue(source) - return vm.viewType(values) -end - -function vm.getValue(source) - if not source then - return - end - local cache = vm.cache.getValue[source] - if cache ~= nil then - return cache - end - local unlock = vm.lock('getValue', source) - if not unlock then - return - end - cache = getValue(source) or false - vm.cache.getValue[source] = cache - unlock() - return cache -end diff --git a/server-beta/src/vm/init.lua b/server-beta/src/vm/init.lua deleted file mode 100644 index 4249de3d..00000000 --- a/server-beta/src/vm/init.lua +++ /dev/null @@ -1,11 +0,0 @@ -local vm = require 'vm.vm' -require 'vm.eachField' -require 'vm.eachRef' -require 'vm.eachDef' -require 'vm.getGlobals' -require 'vm.getLinks' -require 'vm.getGlobal' -require 'vm.getLibrary' -require 'vm.getValue' -require 'vm.dummySource' -return vm diff --git a/server-beta/src/vm/special.lua b/server-beta/src/vm/special.lua deleted file mode 100644 index e69de29b..00000000 --- a/server-beta/src/vm/special.lua +++ /dev/null diff --git a/server-beta/src/vm/vm.lua b/server-beta/src/vm/vm.lua deleted file mode 100644 index 23a691df..00000000 --- a/server-beta/src/vm/vm.lua +++ /dev/null @@ -1,81 +0,0 @@ -local guide = require 'parser.guide' -local util = require 'utility' - -local setmetatable = setmetatable -local assert = assert -local require = require -local type = type - -_ENV = nil - -local specials = { - ['_G'] = true, - ['rawset'] = true, - ['rawget'] = true, - ['setmetatable'] = true, - ['require'] = true, - ['dofile'] = true, - ['loadfile'] = true, -} - ----@class vm -local m = {} - -function m.lock(tp, source) - if m.locked[tp][source] then - return nil - end - m.locked[tp][source] = true - return function () - m.locked[tp][source] = nil - end -end - ---- 获取link的uri -function m.getLinkUris(call) - local workspace = require 'workspace' - local func = call.node - local name = func.special - if name == 'require' then - local args = call.args - if not args[1] then - return nil - end - local literal = guide.getLiteral(args[1]) - if type(literal) ~= 'string' then - return nil - end - return workspace.findUrisByRequirePath(literal, true) - end -end - -m.cacheTracker = setmetatable({}, { __mode = 'kv' }) - ---- 刷新缓存 -function m.refreshCache() - if m.cache then - m.cache.dead = true - end - m.cache = { - eachRef = {}, - eachField = {}, - getGlobals = {}, - getLinks = {}, - getGlobal = {}, - specialName = {}, - getLibrary = {}, - getValue = {}, - specials = nil, - } - m.locked = { - eachRef = {}, - eachField = {}, - getGlobals = {}, - getLinks = {}, - getLibrary = {}, - getValue = {}, - } - m.cacheTracker[m.cache] = true -end - -return m diff --git a/server-beta/src/workspace/init.lua b/server-beta/src/workspace/init.lua deleted file mode 100644 index 7cbe15d7..00000000 --- a/server-beta/src/workspace/init.lua +++ /dev/null @@ -1,3 +0,0 @@ -local workspace = require 'workspace.workspace' - -return workspace diff --git a/server-beta/src/workspace/workspace.lua b/server-beta/src/workspace/workspace.lua deleted file mode 100644 index 37ec2d7b..00000000 --- a/server-beta/src/workspace/workspace.lua +++ /dev/null @@ -1,194 +0,0 @@ -local pub = require 'pub' -local fs = require 'bee.filesystem' -local furi = require 'file-uri' -local files = require 'files' -local config = require 'config' -local glob = require 'glob' -local platform = require 'bee.platform' -local await = require 'await' -local diagnostic = require 'provider.diagnostic' - -local m = {} -m.type = 'workspace' -m.ignoreVersion = -1 -m.ignoreMatcher = nil - ---- 初始化工作区 -function m.init(name, uri) - m.name = name - m.uri = uri - m.path = furi.decode(uri) - log.info('Workspace inited: ', uri) - local logPath = ROOT / 'log' / (uri:gsub('[/:]+', '_') .. '.log') - log.info('Log path: ', logPath) - log.init(ROOT, logPath) -end - ---- 创建排除文件匹配器 -function m.getIgnoreMatcher() - if m.ignoreVersion == config.version then - return m.ignoreMatcher - end - - local pattern = {} - -- config.workspace.ignoreDir - for path in pairs(config.config.workspace.ignoreDir) do - log.info('Ignore directory:', path) - pattern[#pattern+1] = path - end - -- config.files.exclude - for path, ignore in pairs(config.other.exclude) do - if ignore then - log.info('Ignore by exclude:', path) - pattern[#pattern+1] = path - end - end - -- config.workspace.ignoreSubmodules - if config.config.workspace.ignoreSubmodules then - local buf = pub.awaitTask('loadFile', furi.encode(m.path .. '/.gitmodules')) - if buf then - for path in buf:gmatch('path = ([^\r\n]+)') do - log.info('Ignore by .gitmodules:', path) - pattern[#pattern+1] = path - end - end - end - -- config.workspace.useGitIgnore - if config.config.workspace.useGitIgnore then - local buf = pub.awaitTask('loadFile', furi.encode(m.path .. '/.gitignore')) - if buf then - for line in buf:gmatch '[^\r\n]+' do - log.info('Ignore by .gitignore:', line) - pattern[#pattern+1] = line - end - end - end - -- config.workspace.library - for path in pairs(config.config.workspace.library) do - log.info('Ignore by library:', path) - pattern[#pattern+1] = path - end - - m.ignoreMatcher = glob.gitignore(pattern) - - if platform.OS == "Windows" then - m.ignoreMatcher:setOption 'ignoreCase' - end - - m.ignoreVersion = config.version - return m.ignoreMatcher -end - ---- 文件是否被忽略 -function m.isIgnored(uri) - local path = furi.decode(uri) - local ignore = m.getIgnoreMatcher() - return ignore(path) -end - ---- 预读工作区内所有文件 -function m.awaitPreload() - if not m.uri then - return - end - local max = 0 - local read = 0 - log.info('Preload start.') - local ignore = m.getIgnoreMatcher() - - ignore:setInterface('type', function (path) - if fs.is_directory(fs.path(m.path .. '/' .. path)) then - return 'directory' - else - return 'file' - end - end) - - ignore:setInterface('list', function (path) - local paths = {} - for fullpath in fs.path(m.path .. '/' .. path):list_directory() do - paths[#paths+1] = fullpath:string() - end - return paths - end) - - ignore:scan(function (path) - local uri = furi.encode(m.path .. '/' .. path) - if not files.isLua(uri) then - return - end - max = max + 1 - pub.task('loadFile', uri, function (text) - read = read + 1 - --log.info(('Preload file at: %s , size = %.3f KB'):format(uri, #text / 1000.0)) - files.setText(uri, text) - end) - end) - - log.info(('Found %d files.'):format(max)) - while true do - log.info(('Loaded %d/%d files'):format(read, max)) - if read >= max then - break - end - await.sleep(0.1) - end - - log.info('Preload finish.') - diagnostic.start() -end - ---- 查找符合指定file path的所有uri ----@param path string ----@param whole boolean -function m.findUrisByFilePath(path, whole) - local results = {} - for uri in files.eachFile() do - local pathLen = #path - local uriLen = #uri - if whole then - local seg = uri:sub(uriLen - pathLen, uriLen - pathLen) - if seg == '/' or seg == '\\' or seg == '' then - local see = uri:sub(uriLen - pathLen + 1, uriLen) - if files.eq(see, path) then - results[#results+1] = uri - end - end - else - for i = uriLen, uriLen - pathLen + 1, -1 do - local see = uri:sub(i - pathLen + 1, i) - if files.eq(see, path) then - results[#results+1] = uri - end - end - end - end - return results -end - ---- 查找符合指定require path的所有uri ----@param path string ----@param whole boolean -function m.findUrisByRequirePath(path, whole) - local results = {} - local mark = {} - local input = path:gsub('%.', '/') - for _, luapath in ipairs(config.config.runtime.path) do - local part = luapath:gsub('%?', input) - local uris = m.findUrisByFilePath(part, whole) - for _, uri in ipairs(uris) do - if not mark[uri] then - mark[uri] = true - results[#results+1] = uri - end - end - end - return results -end - -function m.getRelativePath(uri) - local path = furi.decode(uri) - return fs.relative(fs.path(path), fs.path(m.path)):string() -end - -return m |