diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 0000000..8222162 --- /dev/null +++ b/.luarc.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", + "diagnostics": { + "globals": [ + "root", + "log", + "tracy" + ] + }, + "runtime": { + "version": "Lua 5.4" + }, + "workspace": { + "preloadFileSize": 1000 + } +} diff --git a/.vscode/launch.json b/.vscode/launch.json index fa63500..d549ea5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,13 +6,9 @@ "type": "lua", "request": "launch", "stopOnEntry": false, + "luaexe": "${workspaceFolder}/bin/lua.exe", "program": "${workspaceRoot}/test/main.lua", "cwd": "${workspaceRoot}", - "env": { - "PATH": "${workspaceRoot}\\bin\\" - }, - //"luaexe": "${workspaceRoot}\\bin\\lua-language-server.exe", - //"luadll": "${workspaceRoot}\\bin\\lua54.dll", "path": "${workspaceRoot}/bin/?.lua", "cpath": "${workspaceRoot}/bin/?.dll;", "arg0": [ @@ -22,20 +18,19 @@ ], "arg": [ ], - "consoleCoding": "utf8", + "console": "internalConsole", "sourceCoding": "utf8", - "luaRuntime": "5.4 64bit" + "luaVersion": "5.4", + "luaArch": "x86_64", }, { "name": "performance", "type": "lua", "request": "launch", "stopOnEntry": false, + "luaexe": "${workspaceFolder}/bin/lua.exe", "program": "${workspaceRoot}/test/performance.lua", "cwd": "${workspaceRoot}", - "env": { - "PATH": "${workspaceRoot}\\bin\\" - }, "path": "${workspaceRoot}/bin/?.lua", "cpath": "${workspaceRoot}/bin/?.dll;", "arg0": [ @@ -44,10 +39,12 @@ "DEBUG=true", ], "arg": [ + "D:/Github/vscode-lua/server" ], - "consoleCoding": "utf8", + "console": "internalConsole", "sourceCoding": "utf8", - "luaRuntime": "5.4 64bit" + "luaVersion": "5.4", + "luaArch": "x86_64", }, ] } diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 4d35344..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Lua.diagnostics.globals": [ - "root" - ], - "Lua.runtime.version": "Lua 5.4" -} diff --git a/bin/api-ms-win-core-console-l1-1-0.dll b/bin/api-ms-win-core-console-l1-1-0.dll deleted file mode 100644 index 0962b30..0000000 Binary files a/bin/api-ms-win-core-console-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-console-l1-2-0.dll b/bin/api-ms-win-core-console-l1-2-0.dll deleted file mode 100644 index 21bb3ad..0000000 Binary files a/bin/api-ms-win-core-console-l1-2-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-datetime-l1-1-0.dll b/bin/api-ms-win-core-datetime-l1-1-0.dll deleted file mode 100644 index a268730..0000000 Binary files a/bin/api-ms-win-core-datetime-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-debug-l1-1-0.dll b/bin/api-ms-win-core-debug-l1-1-0.dll deleted file mode 100644 index 035e268..0000000 Binary files a/bin/api-ms-win-core-debug-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-errorhandling-l1-1-0.dll b/bin/api-ms-win-core-errorhandling-l1-1-0.dll deleted file mode 100644 index 192ede9..0000000 Binary files a/bin/api-ms-win-core-errorhandling-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-file-l1-1-0.dll b/bin/api-ms-win-core-file-l1-1-0.dll deleted file mode 100644 index 68ee784..0000000 Binary files a/bin/api-ms-win-core-file-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-file-l1-2-0.dll b/bin/api-ms-win-core-file-l1-2-0.dll deleted file mode 100644 index 8600b9e..0000000 Binary files a/bin/api-ms-win-core-file-l1-2-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-file-l2-1-0.dll b/bin/api-ms-win-core-file-l2-1-0.dll deleted file mode 100644 index 9cb6b01..0000000 Binary files a/bin/api-ms-win-core-file-l2-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-handle-l1-1-0.dll b/bin/api-ms-win-core-handle-l1-1-0.dll deleted file mode 100644 index 90678c6..0000000 Binary files a/bin/api-ms-win-core-handle-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-heap-l1-1-0.dll b/bin/api-ms-win-core-heap-l1-1-0.dll deleted file mode 100644 index ed9d316..0000000 Binary files a/bin/api-ms-win-core-heap-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-interlocked-l1-1-0.dll b/bin/api-ms-win-core-interlocked-l1-1-0.dll deleted file mode 100644 index 434e8e7..0000000 Binary files a/bin/api-ms-win-core-interlocked-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-libraryloader-l1-1-0.dll b/bin/api-ms-win-core-libraryloader-l1-1-0.dll deleted file mode 100644 index 0893de0..0000000 Binary files a/bin/api-ms-win-core-libraryloader-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-localization-l1-2-0.dll b/bin/api-ms-win-core-localization-l1-2-0.dll deleted file mode 100644 index 4a3db1f..0000000 Binary files a/bin/api-ms-win-core-localization-l1-2-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-memory-l1-1-0.dll b/bin/api-ms-win-core-memory-l1-1-0.dll deleted file mode 100644 index 9cb8b97..0000000 Binary files a/bin/api-ms-win-core-memory-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-namedpipe-l1-1-0.dll b/bin/api-ms-win-core-namedpipe-l1-1-0.dll deleted file mode 100644 index e88b768..0000000 Binary files a/bin/api-ms-win-core-namedpipe-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-processenvironment-l1-1-0.dll b/bin/api-ms-win-core-processenvironment-l1-1-0.dll deleted file mode 100644 index d91e7eb..0000000 Binary files a/bin/api-ms-win-core-processenvironment-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-processthreads-l1-1-0.dll b/bin/api-ms-win-core-processthreads-l1-1-0.dll deleted file mode 100644 index 49267a8..0000000 Binary files a/bin/api-ms-win-core-processthreads-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-processthreads-l1-1-1.dll b/bin/api-ms-win-core-processthreads-l1-1-1.dll deleted file mode 100644 index 62bee96..0000000 Binary files a/bin/api-ms-win-core-processthreads-l1-1-1.dll and /dev/null differ diff --git a/bin/api-ms-win-core-profile-l1-1-0.dll b/bin/api-ms-win-core-profile-l1-1-0.dll deleted file mode 100644 index 38ea751..0000000 Binary files a/bin/api-ms-win-core-profile-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-rtlsupport-l1-1-0.dll b/bin/api-ms-win-core-rtlsupport-l1-1-0.dll deleted file mode 100644 index a3645c4..0000000 Binary files a/bin/api-ms-win-core-rtlsupport-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-string-l1-1-0.dll b/bin/api-ms-win-core-string-l1-1-0.dll deleted file mode 100644 index 8f4653f..0000000 Binary files a/bin/api-ms-win-core-string-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-synch-l1-1-0.dll b/bin/api-ms-win-core-synch-l1-1-0.dll deleted file mode 100644 index 16bd019..0000000 Binary files a/bin/api-ms-win-core-synch-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-synch-l1-2-0.dll b/bin/api-ms-win-core-synch-l1-2-0.dll deleted file mode 100644 index fadea38..0000000 Binary files a/bin/api-ms-win-core-synch-l1-2-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-sysinfo-l1-1-0.dll b/bin/api-ms-win-core-sysinfo-l1-1-0.dll deleted file mode 100644 index db6b187..0000000 Binary files a/bin/api-ms-win-core-sysinfo-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-timezone-l1-1-0.dll b/bin/api-ms-win-core-timezone-l1-1-0.dll deleted file mode 100644 index c55ea7e..0000000 Binary files a/bin/api-ms-win-core-timezone-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-core-util-l1-1-0.dll b/bin/api-ms-win-core-util-l1-1-0.dll deleted file mode 100644 index 3e91e2e..0000000 Binary files a/bin/api-ms-win-core-util-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-conio-l1-1-0.dll b/bin/api-ms-win-crt-conio-l1-1-0.dll deleted file mode 100644 index 8106a7e..0000000 Binary files a/bin/api-ms-win-crt-conio-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-convert-l1-1-0.dll b/bin/api-ms-win-crt-convert-l1-1-0.dll deleted file mode 100644 index 9d5fbe5..0000000 Binary files a/bin/api-ms-win-crt-convert-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-environment-l1-1-0.dll b/bin/api-ms-win-crt-environment-l1-1-0.dll deleted file mode 100644 index 5989e02..0000000 Binary files a/bin/api-ms-win-crt-environment-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-filesystem-l1-1-0.dll b/bin/api-ms-win-crt-filesystem-l1-1-0.dll deleted file mode 100644 index ccdc59a..0000000 Binary files a/bin/api-ms-win-crt-filesystem-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-heap-l1-1-0.dll b/bin/api-ms-win-crt-heap-l1-1-0.dll deleted file mode 100644 index 2bf76f9..0000000 Binary files a/bin/api-ms-win-crt-heap-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-locale-l1-1-0.dll b/bin/api-ms-win-crt-locale-l1-1-0.dll deleted file mode 100644 index 5b1539a..0000000 Binary files a/bin/api-ms-win-crt-locale-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-math-l1-1-0.dll b/bin/api-ms-win-crt-math-l1-1-0.dll deleted file mode 100644 index 9834a10..0000000 Binary files a/bin/api-ms-win-crt-math-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-multibyte-l1-1-0.dll b/bin/api-ms-win-crt-multibyte-l1-1-0.dll deleted file mode 100644 index 2fbd774..0000000 Binary files a/bin/api-ms-win-crt-multibyte-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-private-l1-1-0.dll b/bin/api-ms-win-crt-private-l1-1-0.dll deleted file mode 100644 index 05130e5..0000000 Binary files a/bin/api-ms-win-crt-private-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-process-l1-1-0.dll b/bin/api-ms-win-crt-process-l1-1-0.dll deleted file mode 100644 index 8f31310..0000000 Binary files a/bin/api-ms-win-crt-process-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-runtime-l1-1-0.dll b/bin/api-ms-win-crt-runtime-l1-1-0.dll deleted file mode 100644 index d369c68..0000000 Binary files a/bin/api-ms-win-crt-runtime-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-stdio-l1-1-0.dll b/bin/api-ms-win-crt-stdio-l1-1-0.dll deleted file mode 100644 index 29f6380..0000000 Binary files a/bin/api-ms-win-crt-stdio-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-string-l1-1-0.dll b/bin/api-ms-win-crt-string-l1-1-0.dll deleted file mode 100644 index 122930c..0000000 Binary files a/bin/api-ms-win-crt-string-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-time-l1-1-0.dll b/bin/api-ms-win-crt-time-l1-1-0.dll deleted file mode 100644 index aed540c..0000000 Binary files a/bin/api-ms-win-crt-time-l1-1-0.dll and /dev/null differ diff --git a/bin/api-ms-win-crt-utility-l1-1-0.dll b/bin/api-ms-win-crt-utility-l1-1-0.dll deleted file mode 100644 index 71ac8e5..0000000 Binary files a/bin/api-ms-win-crt-utility-l1-1-0.dll and /dev/null differ diff --git a/bin/bee.dll b/bin/bee.dll deleted file mode 100644 index bede55c..0000000 Binary files a/bin/bee.dll and /dev/null differ diff --git a/bin/concrt140.dll b/bin/concrt140.dll index ae6ce57..5dd7df9 100644 Binary files a/bin/concrt140.dll and b/bin/concrt140.dll differ diff --git a/bin/lni.dll b/bin/lni.dll deleted file mode 100644 index fd62cd1..0000000 Binary files a/bin/lni.dll and /dev/null differ diff --git a/bin/lpeg-1.0.1-DEBUG.dll b/bin/lpeg-1.0.1-DEBUG.dll deleted file mode 100644 index 8aba294..0000000 Binary files a/bin/lpeg-1.0.1-DEBUG.dll and /dev/null differ diff --git a/bin/lpeg-1.0.1-NDEBUG.dll b/bin/lpeg-1.0.1-NDEBUG.dll deleted file mode 100644 index 30519a0..0000000 Binary files a/bin/lpeg-1.0.1-NDEBUG.dll and /dev/null differ diff --git a/bin/lpeg-1.0.2-DEBUG.dll b/bin/lpeg-1.0.2-DEBUG.dll deleted file mode 100644 index 844613a..0000000 Binary files a/bin/lpeg-1.0.2-DEBUG.dll and /dev/null differ diff --git a/bin/lpeg-1.0.2-NDEBUG.dll b/bin/lpeg-1.0.2-NDEBUG.dll deleted file mode 100644 index 102a7fb..0000000 Binary files a/bin/lpeg-1.0.2-NDEBUG.dll and /dev/null differ diff --git a/bin/lpeglabel-1.0.1.dll b/bin/lpeglabel-1.0.1.dll deleted file mode 100644 index 8f23913..0000000 Binary files a/bin/lpeglabel-1.0.1.dll and /dev/null differ diff --git a/bin/lpeglabel-1.0.2.dll b/bin/lpeglabel-1.0.2.dll deleted file mode 100644 index 31eb92f..0000000 Binary files a/bin/lpeglabel-1.0.2.dll and /dev/null differ diff --git a/bin/lpeglabel.dll b/bin/lpeglabel.dll deleted file mode 100644 index 049dd1a..0000000 Binary files a/bin/lpeglabel.dll and /dev/null differ diff --git a/bin/lua-language-server.exe b/bin/lua-language-server.exe deleted file mode 100644 index 67551f6..0000000 Binary files a/bin/lua-language-server.exe and /dev/null differ diff --git a/bin/lua.exe b/bin/lua.exe new file mode 100644 index 0000000..0e19132 Binary files /dev/null and b/bin/lua.exe differ diff --git a/bin/lua.exp b/bin/lua.exp new file mode 100644 index 0000000..3aa25a8 Binary files /dev/null and b/bin/lua.exp differ diff --git a/bin/lua.lib b/bin/lua.lib new file mode 100644 index 0000000..6b9a48a Binary files /dev/null and b/bin/lua.lib differ diff --git a/bin/lua54.dll b/bin/lua54.dll deleted file mode 100644 index 7d159bc..0000000 Binary files a/bin/lua54.dll and /dev/null differ diff --git a/bin/main.lua b/bin/main.lua new file mode 100644 index 0000000..3f70415 --- /dev/null +++ b/bin/main.lua @@ -0,0 +1,66 @@ +local main, exec +local i = 1 +while arg[i] do + if arg[i] == '-E' then + elseif arg[i] == '-e' then + i = i + 1 + local expr = assert(arg[i], "'-e' needs argument") + assert(load(expr, "=(command line)"))() + -- exit after the executing + exec = true + elseif not main and arg[i]:sub(1, 1) ~= '-' then + main = i + elseif arg[i]:sub(1, 2) == '--' then + break + end + i = i + 1 +end + +if exec and not main then + return +end + +if main then + for i = -1, -999, -1 do + if not arg[i] then + for j = i + 1, -1 do + arg[j - main + 1] = arg[j] + end + break + end + end + for j = 1, #arg do + arg[j - main] = arg[j] + end + for j = #arg - main + 1, #arg do + arg[j] = nil + end +end + +local root +do + if main then + local fs = require 'bee.filesystem' + local mainPath = fs.path(arg[0]) + root = mainPath:parent_path():string() + if root == '' then + root = '.' + end + else + local sep = package.config:sub(1, 1) + if sep == '\\' then + sep = '/\\' + end + local pattern = "[" .. sep .. "]+[^" .. sep .. "]+" + root = package.cpath:match("([^;]+)" .. pattern .. pattern .. "$") + arg[0] = root .. package.config:sub(1, 1) .. 'main.lua' + end + root = root:gsub('[/\\]', package.config:sub(1, 1)) +end + +package.path = table.concat({ + root .. "/?.lua", + root .. "/?/init.lua", +}, ";"):gsub('/', package.config:sub(1, 1)) + +assert(loadfile(arg[0]))(table.unpack(arg)) diff --git a/bin/msvcp140.dll b/bin/msvcp140.dll index 3625b39..464f03c 100644 Binary files a/bin/msvcp140.dll and b/bin/msvcp140.dll differ diff --git a/bin/msvcp140_1.dll b/bin/msvcp140_1.dll index cc376ea..cdfcee8 100644 Binary files a/bin/msvcp140_1.dll and b/bin/msvcp140_1.dll differ diff --git a/bin/msvcp140_2.dll b/bin/msvcp140_2.dll index 6fdb39e..6a3eb07 100644 Binary files a/bin/msvcp140_2.dll and b/bin/msvcp140_2.dll differ diff --git a/bin/msvcp140_atomic_wait.dll b/bin/msvcp140_atomic_wait.dll new file mode 100644 index 0000000..f1efd8a Binary files /dev/null and b/bin/msvcp140_atomic_wait.dll differ diff --git a/bin/msvcp140_codecvt_ids.dll b/bin/msvcp140_codecvt_ids.dll new file mode 100644 index 0000000..9b7967d Binary files /dev/null and b/bin/msvcp140_codecvt_ids.dll differ diff --git a/bin/ucrtbase.dll b/bin/ucrtbase.dll deleted file mode 100644 index 4dcf24c..0000000 Binary files a/bin/ucrtbase.dll and /dev/null differ diff --git a/bin/vcruntime140.dll b/bin/vcruntime140.dll index c666722..dee1372 100644 Binary files a/bin/vcruntime140.dll and b/bin/vcruntime140.dll differ diff --git a/bin/vcruntime140_1.dll b/bin/vcruntime140_1.dll index 9da1792..c9f5125 100644 Binary files a/bin/vcruntime140_1.dll and b/bin/vcruntime140_1.dll differ diff --git a/peformance.bat b/peformance.bat new file mode 100644 index 0000000..65a967a --- /dev/null +++ b/peformance.bat @@ -0,0 +1,2 @@ +%~dp0bin\lua.exe %~dp0test\performance.lua %~df1 +pause diff --git a/src/meta.lua b/src/meta.lua new file mode 100644 index 0000000..b8beceb --- /dev/null +++ b/src/meta.lua @@ -0,0 +1,2 @@ +---@alias uri string +---@class noders diff --git a/src/parser/ast.lua b/src/parser/ast.lua deleted file mode 100644 index a7cb1d0..0000000 --- a/src/parser/ast.lua +++ /dev/null @@ -1,1644 +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 - -_ENV = nil - -local State -local PushError - --- 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 Exp - -local function expSplit(list, start, finish, level) - if start == finish then - return list[start] - end - local info = Exp[level] - if not info then - return - end - local func = info[1] - return func(list, start, finish, level) -end - -local function binaryForward(list, start, finish, level) - local info = Exp[level] - for i = finish-1, start+1, -1 do - local op = list[i] - local opType = op.type - if info[opType] then - local e1 = expSplit(list, start, i-1, level) - if not e1 then - goto CONTINUE - end - local e2 = expSplit(list, i+1, finish, level+1) - if not e2 then - goto CONTINUE - end - checkOpVersion(op) - return { - type = 'binary', - op = op, - start = e1.start, - finish = e2.finish, - [1] = e1, - [2] = e2, - } - end - ::CONTINUE:: - end - return expSplit(list, start, finish, level+1) -end - -local function binaryBackward(list, start, finish, level) - local info = Exp[level] - for i = start+1, finish-1 do - local op = list[i] - local opType = op.type - if info[opType] then - local e1 = expSplit(list, start, i-1, level+1) - if not e1 then - goto CONTINUE - end - local e2 = expSplit(list, i+1, finish, level) - if not e2 then - goto CONTINUE - end - checkOpVersion(op) - return { - type = 'binary', - op = op, - start = e1.start, - finish = e2.finish, - [1] = e1, - [2] = e2, - } - end - ::CONTINUE:: - end - return expSplit(list, start, finish, level+1) -end - -local function unary(list, start, finish, level) - local info = Exp[level] - local op = list[start] - local opType = op.type - if info[opType] then - local e1 = expSplit(list, start+1, finish, level) - if e1 then - checkOpVersion(op) - return { - type = 'unary', - op = op, - start = op.start, - finish = e1.finish, - [1] = e1, - } - end - end - return expSplit(list, start, finish, level+1) -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, finish } - PushError { - type = 'MISS_END', - start = start, - finish = finish, - } -end - -local function getSelect(vararg, index) - return { - type = 'select', - 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 - 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.start = start - list.finish = finish - 1 - return list -end - -Exp = { - { - ['or'] = true, - binaryForward, - }, - { - ['and'] = true, - binaryForward, - }, - { - ['<='] = true, - ['>='] = true, - ['<'] = true, - ['>'] = true, - ['~='] = true, - ['=='] = true, - binaryForward, - }, - { - ['|'] = true, - binaryForward, - }, - { - ['~'] = true, - binaryForward, - }, - { - ['&'] = true, - binaryForward, - }, - { - ['<<'] = true, - ['>>'] = true, - binaryForward, - }, - { - ['..'] = true, - binaryBackward, - }, - { - ['+'] = true, - ['-'] = true, - binaryForward, - }, - { - ['*'] = true, - ['//'] = true, - ['/'] = true, - ['%'] = true, - binaryForward, - }, - { - ['^'] = true, - binaryBackward, - }, - { - ['not'] = true, - ['#'] = true, - ['~'] = true, - ['-'] = true, - unary, - }, -} - -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) - if field then - field.type = 'field' - end - return { - type = 'getfield', - field = field, - dot = dot, - start = dot.start, - finish = (field or dot).finish, - } - end, - GetIndex = function (start, index, finish) - return { - type = 'getindex', - start = start, - finish = finish - 1, - index = index, - } - end, - GetMethod = function (colon, method) - if method then - method.type = 'method' - end - return { - type = 'getmethod', - method = method, - colon = colon, - start = colon.start, - finish = (method or colon).finish, - } - 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 = 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, - Exp = function (first, ...) - if not ... then - return first - end - local list = {first, ...} - return expSplit(list, 1, #list, 1) - 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 (start, args, actions, finish) - actions.type = 'function' - actions.start = start - actions.finish = finish - 1 - actions.args = args - checkMissEnd(start) - return actions - end, - NamedFunction = function (start, name, args, actions, finish) - actions.type = 'function' - actions.start = start - actions.finish = finish - 1 - actions.args = args - checkMissEnd(start) - if not name then - return - end - if name.type == 'getname' then - name.type = 'setname' - name.value = actions - return name - elseif name.type == 'getfield' then - name.type = 'setfield' - name.value = actions - return name - elseif name.type == 'getmethod' then - name.type = 'setmethod' - name.value = actions - return name - end - end, - LocalFunction = function (start, name, args, actions, finish) - actions.type = 'function' - actions.start = start - actions.finish = finish - 1 - actions.args = args - 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, start, actions) - - 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) - field.type = 'field' - return { - type = 'tablefield', - start = start, - finish = finish-1, - field = field, - value = value, - } - end, - Index = function (start, index, finish) - return { - type = 'index', - start = start, - finish = finish - 1, - index = index, - } - end, - NewIndex = function (start, index, value, finish) - return { - type = 'tableindex', - start = start, - finish = finish-1, - index = index, - value = value, - } - 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) - 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 - return tableUnpack(keys) - end, - Do = function (start, actions, finish) - actions.type = 'do' - actions.start = start - actions.finish = finish - 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 (start, exp, actions, finish) - actions.type = 'ifblock' - actions.start = start - actions.finish = finish - 1 - actions.filter = exp - return actions - end, - ElseIfBlock = function (start, exp, actions, finish) - actions.type = 'elseifblock' - actions.start = start - actions.finish = finish - 1 - actions.filter = exp - return actions - end, - ElseBlock = function (start, actions, finish) - actions.type = 'elseblock' - actions.start = start - actions.finish = finish - 1 - return actions - end, - If = function (start, blocks, finish) - blocks.type = 'if' - blocks.start = start - blocks.finish = finish - 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 (start, arg, steps, blockStart, block, finish) - local loc = createLocal(arg, blockStart, steps[1]) - block.type = 'loop' - block.start = start - block.finish = finish - 1 - block.loc = loc - block.max = steps[2] - block.step = steps[3] - checkMissEnd(start) - return block - end, - In = function (start, keys, exp, blockStart, block, finish) - local func = tableRemove(exp, 1) - block.type = 'in' - block.start = start - block.finish = finish - 1 - block.keys = {} - - local values - if func then - local call = createCall(exp, func.finish + 1, exp.finish) - call.node = func - values = { call } - end - for i = 1, #keys do - local loc = keys[i] - if values then - block.keys[i] = createLocal(loc, blockStart, getValue(values, i)) - else - block.keys[i] = createLocal(loc, blockStart) - end - end - checkMissEnd(start) - return block - end, - While = function (start, filter, block, finish) - block.type = 'while' - block.start = start - block.finish = finish - 1 - block.filter = filter - checkMissEnd(start) - return block - end, - Repeat = function (start, block, filter, finish) - block.type = 'repeat' - block.start = start - block.finish = finish - block.filter = filter - return block - end, - Lua = function (actions) - actions.type = 'main' - 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', - } - } - end, - MissDo = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = 'do', - } - } - 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', - } - } - end, - MissUntil = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = 'until', - } - } - end, - MissThen = function (pos) - PushError { - type = 'MISS_SYMBOL', - start = pos, - finish = pos, - info = { - symbol = 'then', - } - } - 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', - } - } - } - 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', - } - } - } - end, -} - ---for k, v in pairs(emmy.ast) do --- Defs[k] = v ---end - -local function init(state) - State = state - PushError = state.pushError - emmy.init(State) -end - -local function close() - State = nil - PushError = nil -end - -return { - defs = Defs, - init = init, - close = close, -} diff --git a/src/parser/calcline.lua b/src/parser/calcline.lua deleted file mode 100644 index 26f475d..0000000 --- a/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/src/parser/compile.lua b/src/parser/compile.lua index f6efb6e..6a2f7d8 100644 --- a/src/parser/compile.lua +++ b/src/parser/compile.lua @@ -1,501 +1,769 @@ -local guide = require 'parser.guide' -local type = type +local tokens = require 'parser.tokens' +local guide = require 'parser.guide' + +local sbyte = string.byte +local sfind = string.find +local smatch = string.match +local sgsub = string.gsub +local ssub = string.sub +local schar = string.char +local supper = string.upper +local uchar = utf8.char +local tconcat = table.concat +local tinsert = table.insert +local tointeger = math.tointeger +local tonumber = tonumber +local maxinteger = math.maxinteger +local assert = assert _ENV = nil -local pushError, Root, Compile, CompileBlock, Cache, Block, GoToTag - -local vmMap = { - ['nil'] = function (obj) - Root[#Root+1] = obj - return #Root - end, - ['boolean'] = function (obj) - Root[#Root+1] = obj - return #Root - end, - ['string'] = function (obj) - Root[#Root+1] = obj - return #Root - end, - ['number'] = function (obj) - Root[#Root+1] = obj - return #Root - end, - ['...'] = function (obj) - Root[#Root+1] = obj - return #Root - end, - ['getname'] = function (obj) - Root[#Root+1] = obj - local id = #Root - local loc = guide.getLocal(Root, obj, obj[1], obj.start) - if loc then - obj.type = 'getlocal' - obj.loc = Cache[loc] - if not loc.ref then - loc.ref = {} - end - loc.ref[#loc.ref+1] = id - else - obj.type = 'getglobal' - end - return id - end, - ['getfield'] = function (obj) - local node = obj.node - Root[#Root+1] = obj - local id = #Root - obj.node = Compile(node, id) - return id - end, - ['call'] = function (obj) - Root[#Root+1] = obj - local id = #Root - local node = obj.node - local args = obj.args - if node then - obj.node = Compile(node, id) - end - if args then - obj.args = Compile(args, id) - end - return id - end, - ['callargs'] = function (obj) - Root[#Root+1] = obj - local id = #Root - for i = 1, #obj do - local arg = obj[i] - obj[i] = Compile(arg, id) - end - return id - end, - ['binary'] = function (obj) - local e1 = obj[1] - local e2 = obj[2] - Root[#Root+1] = obj - local id = #Root - obj[1] = Compile(e1, id) - obj[2] = Compile(e2, id) - return id - end, - ['unary'] = function (obj) - local e = obj[1] - Root[#Root+1] = obj - local id = #Root - obj[1] = Compile(e, id) - return id - end, - ['varargs'] = function (obj) - local func = guide.getParentFunction(Root, obj) - if func then - local index = guide.getFunctionVarArgs(Root, func) - if not index then - pushError { - type = 'UNEXPECT_DOTS', - start = obj.start, - finish = obj.finish, - } +---@alias parser.position integer + +---@param str string +---@return table +local function stringToCharMap(str) + local map = {} + local pos = 1 + while pos <= #str do + local byte = sbyte(str, pos, pos) + map[schar(byte)] = true + pos = pos + 1 + if ssub(str, pos, pos) == '-' + and pos < #str then + pos = pos + 1 + local byte2 = sbyte(str, pos, pos) + assert(byte < byte2) + for b = byte + 1, byte2 do + map[schar(b)] = true end + pos = pos + 1 end - Root[#Root+1] = obj - return #Root - end, - ['paren'] = function (obj) - local exp = obj.exp - Root[#Root+1] = obj - local id = #Root - obj.exp = Compile(exp, id) - return id - end, - ['getindex'] = function (obj) - Root[#Root+1] = obj - local id = #Root - local node = obj.node - obj.node = Compile(node, id) - local index = obj.index - if index then - obj.index = Compile(index, id) - end - return id - end, - ['getmethod'] = function (obj) - Root[#Root+1] = obj - local id = #Root - local node = obj.node - local method = obj.method - obj.node = Compile(node, id) - if method then - obj.method = Compile(method, id) - end - return id - end, - ['setmethod'] = function (obj) - Root[#Root+1] = obj - local id = #Root - local node = obj.node - local method = obj.method - local value = obj.value - obj.node = Compile(node, id) - if method then - obj.method = Compile(method, id) - end - obj.value = Compile(value, id) - return id - end, - ['method'] = function (obj) - Root[#Root+1] = obj - return #Root - end, - ['function'] = function (obj) - local lastBlock = Block - Block = obj - Root[#Root+1] = obj - local id = #Root - local args = obj.args - if args then - obj.args = Compile(args, id) - end - for i = 1, #obj do - local act = obj[i] - obj[i] = Compile(act, id) - end - Block = lastBlock - return id - end, - ['funcargs'] = function (obj) - Root[#Root+1] = obj - local id = #Root - for i = 1, #obj do - local arg = obj[i] - obj[i] = Compile(arg, id) - end - return id - end, - ['table'] = function (obj) - Root[#Root+1] = obj - local id = #Root - for i = 1, #obj do - local v = obj[i] - obj[i] = Compile(v, id) - end - return id - end, - ['tablefield'] = function (obj) - Root[#Root+1] = obj - local id = #Root - local value = obj.value - if value then - obj.value = Compile(value, id) - end - return id - end, - ['tableindex'] = function (obj) - Root[#Root+1] = obj - local id = #Root - local index = obj.index - local value = obj.value - obj.index = Compile(index, id) - obj.value = Compile(value, id) - return id - end, - ['index'] = function (obj) - Root[#Root+1] = obj - local id = #Root - local index = obj.index - obj.index = Compile(index, id) - return id - end, - ['select'] = function (obj) - Root[#Root+1] = obj - local id = #Root - local vararg = obj.vararg - if not Cache[vararg] then - obj.vararg = Compile(vararg, id) - Cache[vararg] = obj.vararg - else - obj.vararg = Cache[vararg] - if not vararg.extParent then - vararg.extParent = {} - end - vararg.extParent[#vararg.extParent+1] = id - end - return id - end, - ['setname'] = function (obj) - Root[#Root+1] = obj - local id = #Root - local value = obj.value - if value then - obj.value = Compile(value, id) - end - local loc = guide.getLocal(Root, obj, obj[1], obj.start) - if loc then - obj.type = 'setlocal' - obj.loc = Cache[loc] - if not loc.ref then - loc.ref = {} - end - loc.ref[#loc.ref+1] = id - else - obj.type = 'setglobal' - end - return id - end, - ['local'] = function (obj) - Root[#Root+1] = obj - local id = #Root - local attrs = obj.attrs - if attrs then - for i = 1, #attrs do - local attr = attrs[i] - attrs[i] = Compile(attr, id) - end - end - if Block then - if not Block.locals then - Block.locals = {} - end - Block.locals[#Block.locals+1] = id - end - local value = obj.value - if value then - obj.value = Compile(value, id) - end - Cache[obj] = id - return id - end, - ['localattr'] = function (obj) - Root[#Root+1] = obj - return #Root - end, - ['setfield'] = function (obj) - Root[#Root+1] = obj - local id = #Root - local node = obj.node - local field = obj.field - local value = obj.value - obj.node = Compile(node, id) - obj.field = Compile(field, id) - obj.value = Compile(value, id) - return id - end, - ['do'] = function (obj) - local lastBlock = Block - Block = obj - Root[#Root+1] = obj - local id = #Root - CompileBlock(obj, id) - Block = lastBlock - return id - end, - ['return'] = function (obj) - Root[#Root+1] = obj - local id = #Root - for i = 1, #obj do - local act = obj[i] - obj[i] = Compile(act, id) - end - if Block and Block[#Block] ~= obj then + end + return map +end + +local CharMapNumber = stringToCharMap '0-9' +local CharMapN16 = stringToCharMap 'xX' +local CharMapN2 = stringToCharMap 'bB' +local CharMapE10 = stringToCharMap 'eE' +local CharMapE16 = stringToCharMap 'pP' +local CharMapSign = stringToCharMap '+-' +local CharMapSB = stringToCharMap 'ao|~&=<>.*/%^+-' +local CharMapSU = stringToCharMap 'n#~!-' +local CharMapSimple = stringToCharMap '.:([\'"{' +local CharMapStrSH = stringToCharMap '\'"`' +local CharMapStrLH = stringToCharMap '[' +local CharMapTSep = stringToCharMap ',;' +local CharMapWord = stringToCharMap '_a-zA-Z\x80-\xff' + +local EscMap = { + ['a'] = '\a', + ['b'] = '\b', + ['f'] = '\f', + ['n'] = '\n', + ['r'] = '\r', + ['t'] = '\t', + ['v'] = '\v', + ['\\'] = '\\', + ['\''] = '\'', + ['\"'] = '\"', +} + +local NLMap = { + ['\n'] = true, + ['\r'] = true, + ['\r\n'] = true, +} + +local LineMulti = 10000 + +-- goto 单独处理 +local KeyWord = { + ['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 Specials = { + ['_G'] = true, + ['rawset'] = true, + ['rawget'] = true, + ['setmetatable'] = true, + ['require'] = true, + ['dofile'] = true, + ['loadfile'] = true, + ['pcall'] = true, + ['xpcall'] = true, + ['pairs'] = true, + ['ipairs'] = true, + ['assert'] = true, + ['error'] = true, + ['type'] = true, + ['os.exit'] = true, +} + +local UnarySymbol = { + ['not'] = 11, + ['#'] = 11, + ['~'] = 11, + ['-'] = 11, +} + +local BinarySymbol = { + ['or'] = 1, + ['and'] = 2, + ['<='] = 3, + ['>='] = 3, + ['<'] = 3, + ['>'] = 3, + ['~='] = 3, + ['=='] = 3, + ['|'] = 4, + ['~'] = 5, + ['&'] = 6, + ['<<'] = 7, + ['>>'] = 7, + ['..'] = 8, + ['+'] = 9, + ['-'] = 9, + ['*'] = 10, + ['//'] = 10, + ['/'] = 10, + ['%'] = 10, + ['^'] = 12, +} + +local BinaryAlias = { + ['&&'] = 'and', + ['||'] = 'or', + ['!='] = '~=', +} + +local BinaryActionAlias = { + ['='] = '==', +} + +local UnaryAlias = { + ['!'] = 'not', +} + +local SymbolForward = { + [01] = true, + [02] = true, + [03] = true, + [04] = true, + [05] = true, + [06] = true, + [07] = true, + [08] = false, + [09] = true, + [10] = true, + [11] = true, + [12] = false, +} + +local GetToSetMap = { + ['getglobal'] = 'setglobal', + ['getlocal'] = 'setlocal', + ['getfield'] = 'setfield', + ['getindex'] = 'setindex', + ['getmethod'] = 'setmethod', +} + +local ChunkFinishMap = { + ['end'] = true, + ['else'] = true, + ['elseif'] = true, + ['in'] = true, + ['then'] = true, + ['until'] = true, + [';'] = true, + [']'] = true, + [')'] = true, + ['}'] = true, +} + +local ChunkStartMap = { + ['do'] = true, + ['else'] = true, + ['elseif'] = true, + ['for'] = true, + ['function'] = true, + ['if'] = true, + ['local'] = true, + ['repeat'] = true, + ['return'] = true, + ['then'] = true, + ['until'] = true, + ['while'] = true, +} + +local ListFinishMap = { + ['end'] = true, + ['else'] = true, + ['elseif'] = true, + ['in'] = true, + ['then'] = true, + ['do'] = true, + ['until'] = true, + ['for'] = true, + ['if'] = true, + ['local'] = true, + ['repeat'] = true, + ['return'] = true, + ['while'] = true, +} + +local State, Lua, Line, LineOffset, Chunk, Tokens, Index, LastTokenFinish, Mode, LocalCount, LocalLimited + +local LocalLimit = 200 + +local parseExp, parseAction + +---@class parser.state.err +---@field type string +---@field start? parser.position +---@field finish? parser.position +---@field info? table +---@field fix? table +---@field version? string[]|string +---@field level? string | 'Error' | 'Warning' + +---@type fun(err:parser.state.err):parser.state.err|nil +local pushError + +local function addSpecial(name, obj) + if not State.specials then + State.specials = {} + end + if not State.specials[name] then + State.specials[name] = {} + end + State.specials[name][#State.specials[name]+1] = obj + obj.special = name +end + +---@param offset integer +---@param leftOrRight '"left"'|'"right"' +local function getPosition(offset, leftOrRight) + if not offset or offset > #Lua then + return LineMulti * Line + #Lua - LineOffset + 1 + end + if leftOrRight == 'left' then + return LineMulti * Line + offset - LineOffset + else + return LineMulti * Line + offset - LineOffset + 1 + end +end + +---@return string? word +---@return parser.position? startPosition +---@return parser.position? finishPosition +local function peekWord() + local word = Tokens[Index + 1] + if not word then + return nil + end + if not CharMapWord[ssub(word, 1, 1)] then + return nil + end + local startPos = getPosition(Tokens[Index] , 'left') + local finishPos = getPosition(Tokens[Index] + #word - 1, 'right') + return word, startPos, finishPos +end + +local function lastRightPosition() + if Index < 2 then + return 0 + end + local token = Tokens[Index - 1] + if NLMap[token] then + return LastTokenFinish + elseif token then + return getPosition(Tokens[Index - 2] + #token - 1, 'right') + else + return getPosition(#Lua, 'right') + end +end + +local function missSymbol(symbol, start, finish) + pushError { + type = 'MISS_SYMBOL', + start = start or lastRightPosition(), + finish = finish or start or lastRightPosition(), + info = { + symbol = symbol, + } + } +end + +local function missExp() + pushError { + type = 'MISS_EXP', + start = lastRightPosition(), + finish = lastRightPosition(), + } +end + +local function missName(pos) + pushError { + type = 'MISS_NAME', + start = pos or lastRightPosition(), + finish = pos or lastRightPosition(), + } +end + +local function missEnd(relatedStart, relatedFinish) + pushError { + type = 'MISS_SYMBOL', + start = lastRightPosition(), + finish = lastRightPosition(), + info = { + symbol = 'end', + related = { + { + start = relatedStart, + finish = relatedFinish, + } + } + } + } + pushError { + type = 'MISS_END', + start = relatedStart, + finish = relatedFinish, + } +end + +local function unknownSymbol(start, finish, word) + local token = word or Tokens[Index + 1] + if not token then + return false + end + pushError { + type = 'UNKNOWN_SYMBOL', + start = start or getPosition(Tokens[Index], 'left'), + finish = finish or getPosition(Tokens[Index] + #token - 1, 'right'), + info = { + symbol = token, + } + } + return true +end + +local function skipUnknownSymbol() + if unknownSymbol() then + Index = Index + 2 + return true + end + return false +end + +local function skipNL() + local token = Tokens[Index + 1] + if NLMap[token] then + if Index >= 2 and not NLMap[Tokens[Index - 1]] then + LastTokenFinish = getPosition(Tokens[Index - 2] + #Tokens[Index - 1] - 1, 'right') + end + Line = Line + 1 + LineOffset = Tokens[Index] + #token + Index = Index + 2 + State.lines[Line] = LineOffset + return true + end + return false +end + +local function getSavePoint() + local index = Index + local line = Line + local lineOffset = LineOffset + local errs = State.errs + local errCount = #errs + return function () + Index = index + Line = line + LineOffset = lineOffset + for i = errCount + 1, #errs do + errs[i] = nil + end + end +end + +local function fastForwardToken(offset) + while true do + local myOffset = Tokens[Index] + if not myOffset + or myOffset >= offset then + break + end + local token = Tokens[Index + 1] + if NLMap[token] then + Line = Line + 1 + LineOffset = Tokens[Index] + #token + State.lines[Line] = LineOffset + end + Index = Index + 2 + end +end + +local function resolveLongString(finishMark) + skipNL() + local miss + local start = Tokens[Index] + local finishOffset = sfind(Lua, finishMark, start, true) + if not finishOffset then + finishOffset = #Lua + 1 + miss = true + end + local stringResult = start and ssub(Lua, start, finishOffset - 1) or '' + local lastLN = stringResult:find '[\r\n][^\r\n]*$' + if lastLN then + local result = stringResult + : gsub('\r\n?', '\n') + stringResult = result + end + if finishMark == ']]' and State.version == 'Lua 5.1' then + local nestOffset = sfind(Lua, '[[', start, true) + if nestOffset and nestOffset < finishOffset then + fastForwardToken(nestOffset) + local nestStartPos = getPosition(nestOffset, 'left') + local nestFinishPos = getPosition(nestOffset + 1, 'right') pushError { - type = 'ACTION_AFTER_RETURN', - start = obj.start, - finish = obj.finish, + type = 'NESTING_LONG_MARK', + start = nestStartPos, + finish = nestFinishPos, } end - return id - end, - ['label'] = function (obj) - Root[#Root+1] = obj - local id = #Root - local block = guide.getBlock(Root, obj) - if block then - if not block.labels then - block.labels = {} + end + fastForwardToken(finishOffset + #finishMark) + if miss then + local pos = getPosition(finishOffset - 1, 'right') + pushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = finishMark, + }, + fix = { + title = 'ADD_LSTRING_END', + { + start = pos, + finish = pos, + text = finishMark, + } + }, + } + end + return stringResult, getPosition(finishOffset + #finishMark - 1, 'right') +end + +local function parseLongString() + local start, finish, mark = sfind(Lua, '^(%[%=*%[)', Tokens[Index]) + if not start then + return nil + end + fastForwardToken(finish + 1) + local startPos = getPosition(start, 'left') + local finishMark = sgsub(mark, '%[', ']') + local stringResult, finishPos = resolveLongString(finishMark) + return { + type = 'string', + start = startPos, + finish = finishPos, + [1] = stringResult, + [2] = mark, + } +end + +local function pushCommentHeadError(left) + if State.options.nonstandardSymbol['//'] then + return + end + pushError { + type = 'ERR_COMMENT_PREFIX', + start = left, + finish = left + 2, + fix = { + title = 'FIX_COMMENT_PREFIX', + { + start = left, + finish = left + 2, + text = '--', + }, + } + } +end + +local function pushLongCommentError(left, right) + if State.options.nonstandardSymbol['/**/'] then + return + end + pushError { + type = 'ERR_C_LONG_COMMENT', + start = left, + finish = right, + fix = { + title = 'FIX_C_LONG_COMMENT', + { + start = left, + finish = left + 2, + text = '--[[', + }, + { + start = right - 2, + finish = right, + text = '--]]' + }, + } + } +end + +local function skipComment(isAction) + local token = Tokens[Index + 1] + if token == '--' + or ( + token == '//' + and ( + isAction + or State.options.nonstandardSymbol['//'] + ) + ) then + local start = Tokens[Index] + local left = getPosition(start, 'left') + local chead = false + if token == '//' then + chead = true + pushCommentHeadError(left) + end + Index = Index + 2 + local longComment = start + 2 == Tokens[Index] and parseLongString() + if longComment then + longComment.type = 'comment.long' + longComment.text = longComment[1] + longComment.mark = longComment[2] + longComment[1] = nil + longComment[2] = nil + State.comms[#State.comms+1] = longComment + return true + end + while true do + local nl = Tokens[Index + 1] + if not nl or NLMap[nl] then + break + end + Index = Index + 2 + end + local right = Tokens[Index] and (Tokens[Index] - 1) or #Lua + State.comms[#State.comms+1] = { + type = chead and 'comment.cshort' or 'comment.short', + start = left, + finish = getPosition(right, 'right'), + text = ssub(Lua, start + 2, right), + } + return true + end + if token == '/*' then + local start = Tokens[Index] + local left = getPosition(start, 'left') + Index = Index + 2 + local result, right = resolveLongString '*/' + pushLongCommentError(left, right) + State.comms[#State.comms+1] = { + type = 'comment.clong', + start = left, + finish = right, + text = result, + } + return true + end + return false +end + +local function skipSpace(isAction) + repeat until not skipNL() + and not skipComment(isAction) +end + +local function expectAssign(isAction) + local token = Tokens[Index + 1] + if token == '=' then + Index = Index + 2 + return true + end + if token == '==' then + local left = getPosition(Tokens[Index], 'left') + local right = getPosition(Tokens[Index] + #token - 1, 'right') + pushError { + type = 'ERR_ASSIGN_AS_EQ', + start = left, + finish = right, + fix = { + title = 'FIX_ASSIGN_AS_EQ', + { + start = left, + finish = right, + text = '=', + } + } + } + Index = Index + 2 + return true + end + if isAction then + if token == '+=' + or token == '-=' + or token == '*=' + or token == '/=' + or token == '%=' + or token == '^=' + or token == '//=' + or token == '|=' + or token == '&=' + or token == '>>=' + or token == '<<=' then + if not State.options.nonstandardSymbol[token] then + unknownSymbol() end - local name = obj[1] - local label = guide.getLabel(Root, block, name) - if label then + Index = Index + 2 + return true + end + end + return false +end + +local function parseLocalAttrs() + local attrs + while true do + skipSpace() + local token = Tokens[Index + 1] + if token ~= '<' then + break + end + if not attrs then + attrs = { + type = 'localattrs', + } + end + local attr = { + type = 'localattr', + parent = attrs, + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index], 'right'), + } + attrs[#attrs+1] = attr + Index = Index + 2 + skipSpace() + local word, wstart, wfinish = peekWord() + if word then + attr[1] = word + attr.finish = wfinish + Index = Index + 2 + if word ~= 'const' + and word ~= 'close' then pushError { - type = 'REDEFINED_LABEL', - start = obj.start, - finish = obj.finish, - relative = { - { - label.start, - label.finish, - } - } + type = 'UNKNOWN_ATTRIBUTE', + start = wstart, + finish = wfinish, } end - block.labels[name] = id - end - return id - end, - ['goto'] = function (obj) - Root[#Root+1] = obj - local id = #Root - GoToTag[#GoToTag+1] = obj - return id - end, - ['if'] = function (obj) - Root[#Root+1] = obj - local id = #Root - for i = 1, #obj do - local block = obj[i] - obj[i] = Compile(block, id) - end - return id - end, - ['ifblock'] = function (obj) - local lastBlock = Block - Block = obj - Root[#Root+1] = obj - local id = #Root - local filter = obj.filter - obj.filter = Compile(filter, id) - CompileBlock(obj, id) - Block = lastBlock - return id - end, - ['elseifblock'] = function (obj) - local lastBlock = Block - Block = obj - Root[#Root+1] = obj - local id = #Root - local filter = obj.filter - if filter then - obj.filter = Compile(filter, id) - end - CompileBlock(obj, id) - Block = lastBlock - return id - end, - ['elseblock'] = function (obj) - local lastBlock = Block - Block = obj - Root[#Root+1] = obj - local id = #Root - CompileBlock(obj, id) - Block = lastBlock - return id - end, - ['loop'] = function (obj) - local lastBlock = Block - Block = obj - Root[#Root+1] = obj - local id = #Root - local loc = obj.loc - local max = obj.max - local step = obj.step - if loc then - obj.loc = Compile(loc, id) - end - if max then - obj.max = Compile(max, id) - end - if step then - obj.step = Compile(step, id) - end - CompileBlock(obj, id) - Block = lastBlock - return id - end, - ['in'] = function (obj) - local lastBlock = Block - Block = obj - Root[#Root+1] = obj - local id = #Root - local keys = obj.keys - for i = 1, #keys do - local loc = keys[i] - keys[i] = Compile(loc, id) - end - CompileBlock(obj, id) - Block = lastBlock - return id - end, - ['while'] = function (obj) - local lastBlock = Block - Block = obj - Root[#Root+1] = obj - local id = #Root - local filter = obj.filter - obj.filter = Compile(filter, id) - CompileBlock(obj, id) - Block = lastBlock - return id - end, - ['repeat'] = function (obj) - local lastBlock = Block - Block = obj - Root[#Root+1] = obj - local id = #Root - CompileBlock(obj, id) - local filter = obj.filter - obj.filter = Compile(filter, id) - Block = lastBlock - return id - end, - ['break'] = function (obj) - if not guide.getBreakBlock(Root, obj) then + else + missName() + end + attr.finish = lastRightPosition() + skipSpace() + if Tokens[Index + 1] == '>' then + attr.finish = getPosition(Tokens[Index], 'right') + Index = Index + 2 + elseif Tokens[Index + 1] == '>=' then + attr.finish = getPosition(Tokens[Index], 'right') pushError { - type = 'BREAK_OUTSIDE', - start = obj.start, - finish = obj.finish, + type = 'MISS_SPACE_BETWEEN', + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index] + 1, 'right'), } + Index = Index + 2 + else + missSymbol '>' end - Root[#Root+1] = obj - return #Root - end, - ['main'] = function (obj) - Block = obj - Root[#Root+1] = obj - local id = #Root - CompileBlock(obj, id) - Block = nil - return id - end, -} - -function CompileBlock(obj, parent) - for i = 1, #obj do - local act = obj[i] - local f = vmMap[act.type] - if f then - act.parent = parent - obj[i] = f(act) + if State.version ~= 'Lua 5.4' + and State.version ~= 'Lua 5.5' then + pushError { + type = 'UNSUPPORT_SYMBOL', + start = attr.start, + finish = attr.finish, + version = {'Lua 5.4', 'Lua 5.5'}, + info = { + version = State.version + } + } end end + return attrs end -function Compile(obj, parent) - if not obj then - return nil +---@param obj table +local function createLocal(obj, attrs) + obj.type = 'local' + obj.effect = obj.finish + + if attrs then + obj.attrs = attrs + attrs.parent = obj end - local f = vmMap[obj.type] - if not f then - return nil + + local chunk = Chunk[#Chunk] + if chunk then + local locals = chunk.locals + if not locals then + locals = {} + chunk.locals = locals + end + locals[#locals+1] = obj + LocalCount = LocalCount + 1 + if not LocalLimited and LocalCount > LocalLimit then + LocalLimited = true + pushError { + type = 'LOCAL_LIMIT', + start = obj.start, + finish = obj.finish, + } + end end - obj.parent = parent - return f(obj) + return obj end -local function compileGoTo(obj) - local name = obj[1] - local label = guide.getLabel(Root, obj, name) - if not label then - pushError { - type = 'NO_VISIBLE_LABEL', - start = obj.start, - finish = obj.finish, - info = { - label = name, - } - } - return +local function pushChunk(chunk) + Chunk[#Chunk+1] = chunk +end + +local function resolveLable(label, obj) + if not label.ref then + label.ref = {} end + label.ref[#label.ref+1] = obj + obj.node = label + -- 如果有局部变量在 goto 与 label 之间声明, -- 并在 label 之后使用,则算作语法错误 @@ -504,14 +772,14 @@ local function compileGoTo(obj) return end - local block = guide.getBlock(Root, obj) + local block = guide.getBlock(obj) local locals = block and block.locals if not locals then return end for i = 1, #locals do - local loc = Root[locals[i]] + local loc = locals[i] -- 检查局部变量声明位置为 goto 与 label 之间 if loc.start < obj.start or loc.finish > label.finish then goto CONTINUE @@ -522,7 +790,7 @@ local function compileGoTo(obj) goto CONTINUE end for j = 1, #refs do - local ref = Root[refs[j]] + local ref = refs[j] if ref.finish > label.finish then pushError { type = 'JUMP_LOCAL_SCOPE', @@ -549,26 +817,3363 @@ local function compileGoTo(obj) end end -local function PostCompile() - for i = 1, #GoToTag do - compileGoTo(GoToTag[i]) +local function resolveGoTo(gotos) + for i = 1, #gotos do + local action = gotos[i] + local label = guide.getLabel(action, action[1]) + if label then + resolveLable(label, action) + else + pushError { + type = 'NO_VISIBLE_LABEL', + start = action.start, + finish = action.finish, + info = { + label = action[1], + } + } + end + end +end + +local function popChunk() + local chunk = Chunk[#Chunk] + if chunk.gotos then + resolveGoTo(chunk.gotos) + chunk.gotos = nil + end + local lastAction = chunk[#chunk] + if lastAction then + chunk.finish = lastAction.finish + end + Chunk[#Chunk] = nil +end + +local function parseNil() + if Tokens[Index + 1] ~= 'nil' then + return nil + end + local offset = Tokens[Index] + Index = Index + 2 + return { + type = 'nil', + start = getPosition(offset, 'left'), + finish = getPosition(offset + 2, 'right'), + } +end + +local function parseBoolean() + local word = Tokens[Index+1] + if word ~= 'true' + and word ~= 'false' then + return nil end + local start = getPosition(Tokens[Index], 'left') + local finish = getPosition(Tokens[Index] + #word - 1, 'right') + Index = Index + 2 + return { + type = 'boolean', + start = start, + finish = finish, + [1] = word == 'true' and true or false, + } end -return function (self, lua, mode, version) - local state, errs = self:parse(lua, mode, version) - if not state then - return errs +local function parseStringUnicode() + local offset = Tokens[Index] + 1 + if ssub(Lua, offset, offset) ~= '{' then + local pos = getPosition(offset, 'left') + missSymbol('{', pos) + return nil, offset + end + local leftPos = getPosition(offset, 'left') + local x16 = smatch(Lua, '^%w*', offset + 1) + local rightPos = getPosition(offset + #x16, 'right') + offset = offset + #x16 + 1 + if ssub(Lua, offset, offset) == '}' then + offset = offset + 1 + rightPos = rightPos + 1 + else + missSymbol('}', rightPos) + end + offset = offset + 1 + if #x16 == 0 then + pushError { + type = 'UTF8_SMALL', + start = leftPos, + finish = rightPos, + } + return '', offset + end + if State.version ~= 'Lua 5.3' + and State.version ~= 'Lua 5.4' + and State.version ~= 'Lua 5.5' + and State.version ~= 'LuaJIT' + then + pushError { + type = 'ERR_ESC', + start = leftPos - 2, + finish = rightPos, + version = {'Lua 5.3', 'Lua 5.4', 'Lua 5.5', 'LuaJIT'}, + info = { + version = State.version, + } + } + return nil, offset + end + local byte = tonumber(x16, 16) + if not byte then + for i = 1, #x16 do + if not tonumber(ssub(x16, i, i), 16) then + pushError { + type = 'MUST_X16', + start = leftPos + i, + finish = leftPos + i + 1, + } + end + end + return nil, offset + end + if State.version == 'Lua 5.4' or State.version == 'Lua 5.5' then + if byte < 0 or byte > 0x7FFFFFFF then + pushError { + type = 'UTF8_MAX', + start = leftPos, + finish = rightPos, + info = { + min = '00000000', + max = '7FFFFFFF', + } + } + return nil, offset + end + else + if byte < 0 or byte > 0x10FFFF then + pushError { + type = 'UTF8_MAX', + start = leftPos, + finish = rightPos, + version = byte <= 0x7FFFFFFF and {'Lua 5.4', 'Lua 5.5'} or nil, + info = { + min = '000000', + max = '10FFFF', + } + } + end + end + if byte >= 0 and byte <= 0x10FFFF then + return uchar(byte), offset end - pushError = state.pushError - Root = state.root - Cache = {} - GoToTag = {} - if type(state.ast) == 'table' then - Compile(state.ast) + return '', offset +end + +local stringPool = {} +local function parseShortString() + local mark = Tokens[Index+1] + local startOffset = Tokens[Index] + local startPos = getPosition(startOffset, 'left') + Index = Index + 2 + local stringIndex = 0 + local currentOffset = startOffset + 1 + local escs = {} + while true do + local token = Tokens[Index + 1] + if token == mark then + stringIndex = stringIndex + 1 + stringPool[stringIndex] = ssub(Lua, currentOffset, Tokens[Index] - 1) + Index = Index + 2 + break + end + if NLMap[token] then + stringIndex = stringIndex + 1 + stringPool[stringIndex] = ssub(Lua, currentOffset, Tokens[Index] - 1) + missSymbol(mark) + break + end + if not token then + stringIndex = stringIndex + 1 + stringPool[stringIndex] = ssub(Lua, currentOffset or -1) + missSymbol(mark) + break + end + if token == '\\' then + stringIndex = stringIndex + 1 + stringPool[stringIndex] = ssub(Lua, currentOffset, Tokens[Index] - 1) + currentOffset = Tokens[Index] + Index = Index + 2 + if not Tokens[Index] then + goto CONTINUE + end + local escLeft = getPosition(currentOffset, 'left') + -- has space? + if Tokens[Index] - currentOffset > 1 then + local right = getPosition(currentOffset + 1, 'right') + pushError { + type = 'ERR_ESC', + start = escLeft, + finish = right, + } + escs[#escs+1] = escLeft + escs[#escs+1] = right + escs[#escs+1] = 'err' + goto CONTINUE + end + local nextToken = ssub(Tokens[Index + 1], 1, 1) + if EscMap[nextToken] then + stringIndex = stringIndex + 1 + stringPool[stringIndex] = EscMap[nextToken] + currentOffset = Tokens[Index] + #nextToken + Index = Index + 2 + escs[#escs+1] = escLeft + escs[#escs+1] = escLeft + 2 + escs[#escs+1] = 'normal' + goto CONTINUE + end + if nextToken == mark then + stringIndex = stringIndex + 1 + stringPool[stringIndex] = mark + currentOffset = Tokens[Index] + #nextToken + Index = Index + 2 + escs[#escs+1] = escLeft + escs[#escs+1] = escLeft + 2 + escs[#escs+1] = 'normal' + goto CONTINUE + end + if nextToken == 'z' then + Index = Index + 2 + repeat until not skipNL() + currentOffset = Tokens[Index] + escs[#escs+1] = escLeft + escs[#escs+1] = escLeft + 2 + escs[#escs+1] = 'normal' + goto CONTINUE + end + if CharMapNumber[nextToken] then + local numbers = smatch(Tokens[Index + 1], '^%d+') + if #numbers > 3 then + numbers = ssub(numbers, 1, 3) + end + currentOffset = Tokens[Index] + #numbers + fastForwardToken(currentOffset) + local right = getPosition(currentOffset - 1, 'right') + local byte = tointeger(numbers) + if byte and byte <= 255 then + stringIndex = stringIndex + 1 + stringPool[stringIndex] = schar(byte) + else + pushError { + type = 'ERR_ESC', + start = escLeft, + finish = right, + } + end + escs[#escs+1] = escLeft + escs[#escs+1] = right + escs[#escs+1] = 'byte' + goto CONTINUE + end + if nextToken == 'x' then + local left = getPosition(Tokens[Index] - 1, 'left') + local x16 = ssub(Tokens[Index + 1], 2, 3) + local byte = tonumber(x16, 16) + if byte then + currentOffset = Tokens[Index] + 3 + stringIndex = stringIndex + 1 + stringPool[stringIndex] = schar(byte) + else + currentOffset = Tokens[Index] + 1 + pushError { + type = 'MISS_ESC_X', + start = getPosition(currentOffset, 'left'), + finish = getPosition(currentOffset + 1, 'right'), + } + end + local right = getPosition(currentOffset + 1, 'right') + escs[#escs+1] = escLeft + escs[#escs+1] = right + escs[#escs+1] = 'byte' + if State.version == 'Lua 5.1' then + pushError { + type = 'ERR_ESC', + start = left, + finish = left + 4, + version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'Lua 5.5', 'LuaJIT'}, + info = { + version = State.version, + } + } + end + Index = Index + 2 + goto CONTINUE + end + if nextToken == 'u' then + local str, newOffset = parseStringUnicode() + if str then + stringIndex = stringIndex + 1 + stringPool[stringIndex] = str + end + currentOffset = newOffset + fastForwardToken(currentOffset - 1) + local right = getPosition(currentOffset + 1, 'right') + escs[#escs+1] = escLeft + escs[#escs+1] = right + escs[#escs+1] = 'unicode' + goto CONTINUE + end + if NLMap[nextToken] then + stringIndex = stringIndex + 1 + stringPool[stringIndex] = '\n' + currentOffset = Tokens[Index] + #nextToken + skipNL() + escs[#escs+1] = escLeft + escs[#escs+1] = escLeft + 1 + escs[#escs+1] = 'normal' + goto CONTINUE + end + local right = getPosition(currentOffset + 1, 'right') + pushError { + type = 'ERR_ESC', + start = escLeft, + finish = right, + } + escs[#escs+1] = escLeft + escs[#escs+1] = right + escs[#escs+1] = 'err' + end + Index = Index + 2 + ::CONTINUE:: end - PostCompile() - state.ast = nil - Cache = nil - return state, errs + local stringResult = tconcat(stringPool, '', 1, stringIndex) + local str = { + type = 'string', + start = startPos, + finish = lastRightPosition(), + escs = #escs > 0 and escs or nil, + [1] = stringResult, + [2] = mark, + } + if mark == '`' then + if not State.options.nonstandardSymbol[mark] then + pushError { + type = 'ERR_NONSTANDARD_SYMBOL', + start = startPos, + finish = str.finish, + info = { + symbol = '"', + }, + fix = { + title = 'FIX_NONSTANDARD_SYMBOL', + symbol = '"', + { + start = startPos, + finish = startPos + 1, + text = '"', + }, + { + start = str.finish - 1, + finish = str.finish, + text = '"', + }, + } + } + end + end + return str +end + +local function parseString() + local c = Tokens[Index + 1] + if CharMapStrSH[c] then + return parseShortString() + end + if CharMapStrLH[c] then + return parseLongString() + end + return nil +end + +local function parseNumber10(start) + local integer = true + local integerPart = smatch(Lua, '^%d*', start) + local offset = start + #integerPart + -- float part + if ssub(Lua, offset, offset) == '.' then + local floatPart = smatch(Lua, '^%d*', offset + 1) + integer = false + offset = offset + #floatPart + 1 + end + -- exp part + local echar = ssub(Lua, offset, offset) + if CharMapE10[echar] then + integer = false + offset = offset + 1 + local nextChar = ssub(Lua, offset, offset) + if CharMapSign[nextChar] then + offset = offset + 1 + end + local exp = smatch(Lua, '^%d*', offset) + offset = offset + #exp + if #exp == 0 then + pushError { + type = 'MISS_EXPONENT', + start = getPosition(offset - 1, 'right'), + finish = getPosition(offset - 1, 'right'), + } + end + end + return tonumber(ssub(Lua, start, offset - 1)), offset, integer +end + +local function parseNumber16(start) + local integerPart = smatch(Lua, '^[%da-fA-F]*', start) + local offset = start + #integerPart + local integer = true + -- float part + if ssub(Lua, offset, offset) == '.' then + local floatPart = smatch(Lua, '^[%da-fA-F]*', offset + 1) + integer = false + offset = offset + #floatPart + 1 + if #integerPart == 0 and #floatPart == 0 then + pushError { + type = 'MUST_X16', + start = getPosition(offset - 1, 'right'), + finish = getPosition(offset - 1, 'right'), + } + end + else + if #integerPart == 0 then + pushError { + type = 'MUST_X16', + start = getPosition(offset - 1, 'right'), + finish = getPosition(offset - 1, 'right'), + } + return 0, offset + end + end + -- exp part + local echar = ssub(Lua, offset, offset) + if CharMapE16[echar] then + integer = false + offset = offset + 1 + local nextChar = ssub(Lua, offset, offset) + if CharMapSign[nextChar] then + offset = offset + 1 + end + local exp = smatch(Lua, '^%d*', offset) + offset = offset + #exp + end + local n = tonumber(ssub(Lua, start - 2, offset - 1)) + return n, offset, integer +end + +local function parseNumber2(start) + local bins = smatch(Lua, '^[01]*', start) + local offset = start + #bins + if State.version ~= 'LuaJIT' then + pushError { + type = 'UNSUPPORT_SYMBOL', + start = getPosition(start - 2, 'left'), + finish = getPosition(offset - 1, 'right'), + version = 'LuaJIT', + info = { + version = State.version, + } + } + end + return tonumber(bins, 2), offset +end + +local function dropNumberTail(offset, integer) + local _, finish, word = sfind(Lua, '^([%.%w_\x80-\xff]+)', offset) + if not finish then + return offset + end + if integer then + if supper(ssub(word, 1, 2)) == 'LL' then + if State.version ~= 'LuaJIT' then + pushError { + type = 'UNSUPPORT_SYMBOL', + start = getPosition(offset, 'left'), + finish = getPosition(offset + 1, 'right'), + version = 'LuaJIT', + info = { + version = State.version, + } + } + end + offset = offset + 2 + word = ssub(word, offset) + elseif supper(ssub(word, 1, 3)) == 'ULL' then + if State.version ~= 'LuaJIT' then + pushError { + type = 'UNSUPPORT_SYMBOL', + start = getPosition(offset, 'left'), + finish = getPosition(offset + 2, 'right'), + version = 'LuaJIT', + info = { + version = State.version, + } + } + end + offset = offset + 3 + word = ssub(word, offset) + end + end + if supper(ssub(word, 1, 1)) == 'I' then + if State.version ~= 'LuaJIT' then + pushError { + type = 'UNSUPPORT_SYMBOL', + start = getPosition(offset, 'left'), + finish = getPosition(offset, 'right'), + version = 'LuaJIT', + info = { + version = State.version, + } + } + end + offset = offset + 1 + word = ssub(word, offset) + end + if #word > 0 then + pushError { + type = 'MALFORMED_NUMBER', + start = getPosition(offset, 'left'), + finish = getPosition(finish, 'right'), + } + end + return finish + 1 +end + +local function parseNumber() + local offset = Tokens[Index] + if not offset then + return nil + end + local startPos = getPosition(offset, 'left') + local neg + if ssub(Lua, offset, offset) == '-' then + neg = true + offset = offset + 1 + end + local number, integer + local firstChar = ssub(Lua, offset, offset) + if firstChar == '.' then + number, offset = parseNumber10(offset) + integer = false + elseif firstChar == '0' then + local nextChar = ssub(Lua, offset + 1, offset + 1) + if CharMapN16[nextChar] then + number, offset, integer = parseNumber16(offset + 2) + elseif CharMapN2[nextChar] then + number, offset = parseNumber2(offset + 2) + integer = true + else + number, offset, integer = parseNumber10(offset) + end + elseif CharMapNumber[firstChar] then + number, offset, integer = parseNumber10(offset) + else + return nil + end + if not number then + number = 0 + end + if neg then + number = - number + end + local result = { + type = integer and 'integer' or 'number', + start = startPos, + finish = getPosition(offset - 1, 'right'), + [1] = number, + } + offset = dropNumberTail(offset, integer) + fastForwardToken(offset) + return result +end + +local function isKeyWord(word, nextToken) + if KeyWord[word] then + return true + end + if word == 'goto' then + if State.version == 'Lua 5.1' then + return false + end + if State.version == 'LuaJIT' then + if not nextToken then + return false + end + if CharMapWord[ssub(nextToken, 1, 1)] then + return true + end + return false + end + return true + end + return false +end + +local function parseName(asAction) + local word = peekWord() + if not word then + return nil + end + if ChunkFinishMap[word] then + return nil + end + if asAction and ChunkStartMap[word] then + return nil + end + local startPos = getPosition(Tokens[Index], 'left') + local finishPos = getPosition(Tokens[Index] + #word - 1, 'right') + Index = Index + 2 + if not State.options.unicodeName and word:find '[\x80-\xff]' then + pushError { + type = 'UNICODE_NAME', + start = startPos, + finish = finishPos, + } + end + if isKeyWord(word, Tokens[Index + 1]) then + pushError { + type = 'KEYWORD', + start = startPos, + finish = finishPos, + } + end + return { + type = 'name', + start = startPos, + finish = finishPos, + [1] = word, + } +end + +local function parseNameOrList(parent) + local first = parseName() + if not first then + return nil + end + skipSpace() + local list + while true do + if Tokens[Index + 1] ~= ',' then + break + end + Index = Index + 2 + skipSpace() + local name = parseName(true) + if not name then + missName() + break + end + if not list then + list = { + type = 'list', + start = first.start, + finish = first.finish, + parent = parent, + [1] = first + } + end + list[#list+1] = name + list.finish = name.finish + end + return list or first +end + +local function parseExpList(mini) + local list + local wantSep = false + while true do + skipSpace() + local token = Tokens[Index + 1] + if not token then + break + end + if ListFinishMap[token] then + break + end + if token == ',' then + local sepPos = getPosition(Tokens[Index], 'right') + if not wantSep then + pushError { + type = 'UNEXPECT_SYMBOL', + start = getPosition(Tokens[Index], 'left'), + finish = sepPos, + info = { + symbol = ',', + } + } + end + wantSep = false + Index = Index + 2 + goto CONTINUE + else + if mini then + if wantSep then + break + end + local nextToken = peekWord() + if isKeyWord(nextToken, Tokens[Index + 2]) + and nextToken ~= 'function' + and nextToken ~= 'true' + and nextToken ~= 'false' + and nextToken ~= 'nil' + and nextToken ~= 'not' then + break + end + end + local exp = parseExp() + if not exp then + break + end + if wantSep then + missSymbol(',', list[#list].finish, exp.start) + end + wantSep = true + if not list then + list = { + type = 'list', + start = exp.start, + } + end + list[#list+1] = exp + list.finish = exp.finish + exp.parent = list + end + ::CONTINUE:: + end + if not list then + return nil + end + if not wantSep then + missExp() + end + return list +end + +local function parseIndex() + local start = getPosition(Tokens[Index], 'left') + Index = Index + 2 + skipSpace() + local exp = parseExp() + local index = { + type = 'index', + start = start, + finish = exp and exp.finish or (start + 1), + index = exp + } + if exp then + exp.parent = index + else + missExp() + end + skipSpace() + if Tokens[Index + 1] == ']' then + index.finish = getPosition(Tokens[Index], 'right') + Index = Index + 2 + else + missSymbol ']' + end + return index +end + +local function parseTable() + local tbl = { + type = 'table', + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index], 'right'), + } + tbl.bstart = tbl.finish + Index = Index + 2 + local index = 0 + local tindex = 0 + local wantSep = false + while true do + skipSpace(true) + local token = Tokens[Index + 1] + if token == '}' then + tbl.bfinish = getPosition(Tokens[Index], 'left') + Index = Index + 2 + break + end + if CharMapTSep[token] then + if not wantSep then + missExp() + end + wantSep = false + Index = Index + 2 + goto CONTINUE + end + local lastRight = lastRightPosition() + + if peekWord() then + local savePoint = getSavePoint() + local name = parseName() + if name then + skipSpace() + if Tokens[Index + 1] == '=' then + Index = Index + 2 + if wantSep then + pushError { + type = 'MISS_SEP_IN_TABLE', + start = lastRight, + finish = getPosition(Tokens[Index], 'left'), + } + end + wantSep = true + skipSpace() + local fvalue = parseExp() + local tfield = { + type = 'tablefield', + start = name.start, + finish = name.finish, + range = fvalue and fvalue.finish, + node = tbl, + parent = tbl, + field = name, + value = fvalue, + } + name.type = 'field' + name.parent = tfield + if fvalue then + fvalue.parent = tfield + else + missExp() + end + index = index + 1 + tbl[index] = tfield + goto CONTINUE + end + end + savePoint() + end + + local exp = parseExp(true) + if exp then + if wantSep then + pushError { + type = 'MISS_SEP_IN_TABLE', + start = lastRight, + finish = exp.start, + } + end + wantSep = true + if exp.type == 'varargs' then + index = index + 1 + tbl[index] = exp + exp.parent = tbl + goto CONTINUE + end + index = index + 1 + tindex = tindex + 1 + local texp = { + type = 'tableexp', + start = exp.start, + finish = exp.finish, + tindex = tindex, + parent = tbl, + value = exp, + } + exp.parent = texp + tbl[index] = texp + goto CONTINUE + end + + if token == '[' then + if wantSep then + pushError { + type = 'MISS_SEP_IN_TABLE', + start = lastRight, + finish = getPosition(Tokens[Index], 'left'), + } + end + wantSep = true + local tindex = parseIndex() + skipSpace() + tindex.type = 'tableindex' + tindex.node = tbl + tindex.parent = tbl + index = index + 1 + tbl[index] = tindex + if expectAssign() then + skipSpace() + local ivalue = parseExp() + if ivalue then + ivalue.parent = tindex + tindex.range = ivalue.finish + tindex.value = ivalue + else + missExp() + end + else + missSymbol '=' + end + goto CONTINUE + end + + missSymbol '}' + skipSpace() + tbl.bfinish = getPosition(Tokens[Index], 'left') + break + ::CONTINUE:: + end + tbl.finish = lastRightPosition() + return tbl +end + +local function addDummySelf(node, call) + if not node then + return + end + if node.type ~= 'getmethod' then + return + end + -- dummy param `self` + if not call.args then + call.args = { + type = 'callargs', + start = call.start, + finish = call.finish, + parent = call, + } + end + local self = { + type = 'self', + start = node.colon.start, + finish = node.colon.finish, + parent = call.args, + [1] = 'self', + } + tinsert(call.args, 1, self) +end + +local function checkAmbiguityCall(call, parenPos) + if State.version ~= 'Lua 5.1' then + return + end + local node = call.node + if not node then + return + end + local nodeRow = guide.rowColOf(node.finish) + local callRow = guide.rowColOf(parenPos) + if nodeRow == callRow then + return + end + pushError { + type = 'AMBIGUOUS_SYNTAX', + start = parenPos, + finish = call.finish, + } +end + +local function bindSpecial(source, name) + if Specials[name] then + addSpecial(name, source) + else + local ospeicals = State.options.special + if ospeicals and ospeicals[name] then + addSpecial(ospeicals[name], source) + end + end +end + +local function parseSimple(node, funcName) + local currentName + if node.type == 'getglobal' + or node.type == 'getlocal' then + currentName = node[1] + end + local lastMethod + while true do + if lastMethod and node.node == lastMethod then + if node.type ~= 'call' then + missSymbol('(', node.node.finish, node.node.finish) + end + lastMethod = nil + end + skipSpace() + local token = Tokens[Index + 1] + if token == '.' then + local dot = { + type = token, + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index], 'right'), + } + Index = Index + 2 + skipSpace() + local field = parseName(true) + local getfield = { + type = 'getfield', + start = node.start, + finish = lastRightPosition(), + node = node, + dot = dot, + field = field + } + if field then + field.parent = getfield + field.type = 'field' + if currentName then + if node.type == 'getlocal' + or node.type == 'getglobal' + or node.type == 'getfield' then + currentName = currentName .. '.' .. field[1] + bindSpecial(getfield, currentName) + else + currentName = nil + end + end + else + pushError { + type = 'MISS_FIELD', + start = lastRightPosition(), + finish = lastRightPosition(), + } + end + node.parent = getfield + node.next = getfield + node = getfield + elseif token == ':' then + local colon = { + type = token, + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index], 'right'), + } + Index = Index + 2 + skipSpace() + local method = parseName(true) + local getmethod = { + type = 'getmethod', + start = node.start, + finish = lastRightPosition(), + node = node, + colon = colon, + method = method + } + if method then + method.parent = getmethod + method.type = 'method' + else + pushError { + type = 'MISS_METHOD', + start = lastRightPosition(), + finish = lastRightPosition(), + } + end + node.parent = getmethod + node.next = getmethod + node = getmethod + if lastMethod then + missSymbol('(', node.node.finish, node.node.finish) + end + lastMethod = getmethod + elseif token == '(' then + if funcName then + break + end + local startPos = getPosition(Tokens[Index], 'left') + local call = { + type = 'call', + start = node.start, + node = node, + } + Index = Index + 2 + local args = parseExpList() + if Tokens[Index + 1] == ')' then + call.finish = getPosition(Tokens[Index], 'right') + Index = Index + 2 + else + call.finish = lastRightPosition() + missSymbol ')' + end + if args then + args.type = 'callargs' + args.start = startPos + args.finish = call.finish + args.parent = call + call.args = args + end + addDummySelf(node, call) + checkAmbiguityCall(call, startPos) + node.parent = call + node = call + elseif token == '{' then + if funcName then + break + end + local tbl = parseTable() + local call = { + type = 'call', + start = node.start, + finish = tbl.finish, + node = node, + } + local args = { + type = 'callargs', + start = tbl.start, + finish = tbl.finish, + parent = call, + [1] = tbl, + } + call.args = args + addDummySelf(node, call) + tbl.parent = args + node.parent = call + node = call + elseif CharMapStrSH[token] then + if funcName then + break + end + local str = parseShortString() + local call = { + type = 'call', + start = node.start, + finish = str.finish, + node = node, + } + local args = { + type = 'callargs', + start = str.start, + finish = str.finish, + parent = call, + [1] = str, + } + call.args = args + addDummySelf(node, call) + str.parent = args + node.parent = call + node = call + elseif CharMapStrLH[token] then + local str = parseLongString() + if str then + if funcName then + break + end + local call = { + type = 'call', + start = node.start, + finish = str.finish, + node = node, + } + local args = { + type = 'callargs', + start = str.start, + finish = str.finish, + parent = call, + [1] = str, + } + call.args = args + addDummySelf(node, call) + str.parent = args + node.parent = call + node = call + else + local index = parseIndex() + local bstart = index.start + index.type = 'getindex' + index.start = node.start + index.node = node + node.next = index + node.parent = index + node = index + if funcName then + pushError { + type = 'INDEX_IN_FUNC_NAME', + start = bstart, + finish = index.finish, + } + end + end + else + break + end + end + if node.type == 'call' + and node.node == lastMethod then + lastMethod = nil + end + if node.type == 'call' then + if node.node.special == 'error' + or node.node.special == 'os.exit' then + node.hasExit = true + end + end + if node == lastMethod then + if funcName then + lastMethod = nil + end + end + if lastMethod then + missSymbol('(', lastMethod.finish) + end + return node +end + +local function parseVarargs() + local varargs = { + type = 'varargs', + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index] + 2, 'right'), + } + Index = Index + 2 + for i = #Chunk, 1, -1 do + local chunk = Chunk[i] + if chunk.vararg then + if not chunk.vararg.ref then + chunk.vararg.ref = {} + end + chunk.vararg.ref[#chunk.vararg.ref+1] = varargs + varargs.node = chunk.vararg + break + end + if chunk.type == 'main' then + break + end + if chunk.type == 'function' then + pushError { + type = 'UNEXPECT_DOTS', + start = varargs.start, + finish = varargs.finish, + } + break + end + end + return varargs +end + +local function parseParen() + local pl = Tokens[Index] + local paren = { + type = 'paren', + start = getPosition(pl, 'left'), + finish = getPosition(pl, 'right') + } + Index = Index + 2 + skipSpace() + local exp = parseExp() + if exp then + paren.exp = exp + paren.finish = exp.finish + exp.parent = paren + else + missExp() + end + skipSpace() + if Tokens[Index + 1] == ')' then + paren.finish = getPosition(Tokens[Index], 'right') + Index = Index + 2 + else + missSymbol ')' + end + return paren +end + +local function getLocal(name, pos) + for i = #Chunk, 1, -1 do + local chunk = Chunk[i] + local locals = chunk.locals + if locals then + local res + for n = 1, #locals do + local loc = locals[n] + 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 + end + end + end +end + +local function resolveName(node) + if not node then + return nil + end + local loc = getLocal(node[1], node.start) + if loc then + node.type = 'getlocal' + node.node = loc + if not loc.ref then + loc.ref = {} + end + loc.ref[#loc.ref+1] = node + if loc.special then + addSpecial(loc.special, node) + end + else + node.type = 'getglobal' + local env = getLocal(State.ENVMode, node.start) + if env then + node.node = env + if not env.ref then + env.ref = {} + end + env.ref[#env.ref+1] = node + end + end + local name = node[1] + bindSpecial(node, name) + return node +end + +local function isChunkFinishToken(token) + local currentChunk = Chunk[#Chunk] + if not currentChunk then + return false + end + local tp = currentChunk.type + if tp == 'main' then + return false + end + if tp == 'for' + or tp == 'in' + or tp == 'loop' + or tp == 'function' then + return token == 'end' + end + if tp == 'if' + or tp == 'ifblock' + or tp == 'elseifblock' + or tp == 'elseblock' then + return token == 'then' + or token == 'end' + or token == 'else' + or token == 'elseif' + end + if tp == 'repeat' then + return token == 'until' + end + return true +end + +local function parseActions() + local rtn, last + while true do + skipSpace(true) + local token = Tokens[Index + 1] + if token == ';' then + Index = Index + 2 + goto CONTINUE + end + if ChunkFinishMap[token] + and isChunkFinishToken(token) then + break + end + local action, failed = parseAction() + if failed then + if not skipUnknownSymbol() then + break + end + end + if action then + if not rtn and action.type == 'return' then + rtn = action + end + last = action + end + ::CONTINUE:: + end + if rtn and rtn ~= last then + pushError { + type = 'ACTION_AFTER_RETURN', + start = rtn.start, + finish = rtn.finish, + } + end +end + +local function parseParams(params, isLambda) + local lastSep + local hasDots + local endToken = isLambda and '|' or ')' + while true do + skipSpace() + local token = Tokens[Index + 1] + if not token or token == endToken then + if lastSep then + missName() + end + break + end + if token == ',' then + if lastSep or lastSep == nil then + missName() + else + lastSep = true + end + Index = Index + 2 + goto CONTINUE + end + if token == '...' then + if lastSep == false then + missSymbol ',' + end + lastSep = false + if not params then + params = {} + end + local vararg = { + type = '...', + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index] + 2, 'right'), + parent = params, + [1] = '...', + } + local chunk = Chunk[#Chunk] + chunk.vararg = vararg + params[#params+1] = vararg + if hasDots then + pushError { + type = 'ARGS_AFTER_DOTS', + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index] + 2, 'right'), + } + end + hasDots = true + Index = Index + 2 + goto CONTINUE + end + if CharMapWord[ssub(token, 1, 1)] then + if lastSep == false then + missSymbol ',' + end + lastSep = false + if not params then + params = {} + end + params[#params+1] = createLocal { + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index] + #token - 1, 'right'), + parent = params, + [1] = token, + } + if hasDots then + pushError { + type = 'ARGS_AFTER_DOTS', + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index] + #token - 1, 'right'), + } + end + if isKeyWord(token, Tokens[Index + 3]) then + pushError { + type = 'KEYWORD', + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index] + #token - 1, 'right'), + } + end + Index = Index + 2 + goto CONTINUE + end + skipUnknownSymbol() + ::CONTINUE:: + end + return params +end + +local function parseFunction(isLocal, isAction) + local funcLeft = getPosition(Tokens[Index], 'left') + local funcRight = getPosition(Tokens[Index] + 7, 'right') + local func = { + type = 'function', + start = funcLeft, + finish = funcRight, + bstart = funcRight, + keyword = { + [1] = funcLeft, + [2] = funcRight, + }, + } + Index = Index + 2 + skipSpace(true) + local hasLeftParen = Tokens[Index + 1] == '(' + if not hasLeftParen then + local name = parseName() + if name then + local simple = parseSimple(name, true) + if isLocal then + if simple == name then + createLocal(name) + else + resolveName(name) + pushError { + type = 'UNEXPECT_LFUNC_NAME', + start = simple.start, + finish = simple.finish, + } + end + else + resolveName(name) + end + func.name = simple + func.finish = simple.finish + func.bstart = simple.finish + if not isAction then + simple.parent = func + pushError { + type = 'UNEXPECT_EFUNC_NAME', + start = simple.start, + finish = simple.finish, + } + end + skipSpace(true) + hasLeftParen = Tokens[Index + 1] == '(' + end + end + local LastLocalCount = LocalCount + LocalCount = 0 + pushChunk(func) + local params + if func.name and func.name.type == 'getmethod' then + if func.name.type == 'getmethod' then + params = { + type = 'funcargs', + start = funcRight, + finish = funcRight, + parent = func + } + params[1] = createLocal { + start = funcRight, + finish = funcRight, + parent = params, + [1] = 'self', + } + params[1].type = 'self' + end + end + if hasLeftParen then + params = params or {} + local parenLeft = getPosition(Tokens[Index], 'left') + Index = Index + 2 + params = parseParams(params) + params.type = 'funcargs' + params.start = parenLeft + params.finish = lastRightPosition() + params.parent = func + func.args = params + skipSpace(true) + if Tokens[Index + 1] == ')' then + local parenRight = getPosition(Tokens[Index], 'right') + func.finish = parenRight + func.bstart = parenRight + if params then + params.finish = parenRight + end + Index = Index + 2 + skipSpace(true) + else + func.finish = lastRightPosition() + func.bstart = func.finish + if params then + params.finish = func.finish + end + missSymbol ')' + end + else + missSymbol '(' + end + parseActions() + popChunk() + func.bfinish = getPosition(Tokens[Index], 'left') + if Tokens[Index + 1] == 'end' then + local endLeft = getPosition(Tokens[Index], 'left') + local endRight = getPosition(Tokens[Index] + 2, 'right') + func.keyword[3] = endLeft + func.keyword[4] = endRight + func.finish = endRight + Index = Index + 2 + else + func.finish = lastRightPosition() + missEnd(funcLeft, funcRight) + end + LocalCount = LastLocalCount + return func +end + +local function parseLambda(isDoublePipe) + local lambdaLeft = getPosition(Tokens[Index], 'left') + local lambdaRight = getPosition(Tokens[Index], 'right') + local lambda = { + type = 'function', + start = lambdaLeft, + finish = lambdaRight, + bstart = lambdaRight, + keyword = { + [1] = lambdaLeft, + [2] = lambdaRight, + }, + hasReturn = true + } + Index = Index + 2 + local pipeLeft = getPosition(Tokens[Index], 'left') + local pipeRight = getPosition(Tokens[Index], 'right') + skipSpace(true) + local params + local LastLocalCount = LocalCount + -- if nonstandardSymbol for '||' is true it is possible for token to be || when there are no params + if isDoublePipe then + params = { + start = pipeLeft, + finish = pipeRight, + parent = lambda, + type = 'funcargs' + } + else + -- fake chunk to store locals + pushChunk(lambda) + LocalCount = 0 + params = parseParams({}, true) + params.type = 'funcargs' + params.start = pipeLeft + params.finish = lastRightPosition() + params.parent = lambda + lambda.args = params + skipSpace() + if Tokens[Index + 1] == '|' then + pipeRight = getPosition(Tokens[Index], 'right') + lambda.finish = pipeRight + lambda.bstart = pipeRight + if params then + params.finish = pipeRight + end + Index = Index + 2 + skipSpace() + else + lambda.finish = lastRightPosition() + lambda.bstart = lambda.finish + if params then + params.finish = lambda.finish + end + missSymbol '|' + end + end + local child = parseExp() + + + -- don't want popChunk logic here as this is not a real chunk + Chunk[#Chunk] = nil + + if child then + -- create dummy return + local rtn = { + type = 'return', + start = child.start, + finish = child.finish, + parent = lambda, + [1] = child + } + child.parent = rtn + lambda[1] = rtn + lambda.returns = {rtn} + lambda.finish = child.finish + lambda.keyword[3] = child.finish + lambda.keyword[4] = child.finish + else + lambda.finish = lastRightPosition() + missExp() + end + lambda.bfinish = getPosition(Tokens[Index], 'left') + LocalCount = LastLocalCount + return lambda +end + +local function checkNeedParen(source) + local token = Tokens[Index + 1] + if token ~= '.' + and token ~= ':' then + return source + end + local exp = parseSimple(source, false) + if exp == source then + return exp + end + pushError { + type = 'NEED_PAREN', + start = source.start, + finish = source.finish, + fix = { + title = 'FIX_ADD_PAREN', + { + start = source.start, + finish = source.start, + text = '(', + }, + { + start = source.finish, + finish = source.finish, + text = ')', + } + } + } + return exp +end + +local function parseExpUnit() + local token = Tokens[Index + 1] + if token == '(' then + local paren = parseParen() + return parseSimple(paren, false) + end + + if token == '...' then + local varargs = parseVarargs() + return varargs + end + + if token == '{' then + local table = parseTable() + if not table then + return nil + end + local exp = checkNeedParen(table) + return exp + end + + if CharMapStrSH[token] then + local string = parseShortString() + if not string then + return nil + end + local exp = checkNeedParen(string) + return exp + end + + if CharMapStrLH[token] then + local string = parseLongString() + if not string then + return nil + end + local exp = checkNeedParen(string) + return exp + end + + local number = parseNumber() + if number then + return number + end + + if ChunkFinishMap[token] then + return nil + end + + if token == 'nil' then + return parseNil() + end + + if token == 'true' + or token == 'false' then + return parseBoolean() + end + + if token == 'function' then + return parseFunction() + end + + -- FIXME: Use something other than nonstandardSymbol to check for lambda support + if State.options.nonstandardSymbol['|lambda|'] and (token == '|' + or token == '||') then + return parseLambda(token == '||') + end + + local node = parseName() + if node then + local nameNode = resolveName(node) + if nameNode then + return parseSimple(nameNode, false) + end + end + + return nil +end + +local function parseUnaryOP() + local token = Tokens[Index + 1] + local symbol = UnarySymbol[token] and token or UnaryAlias[token] + if not symbol then + return nil + end + local myLevel = UnarySymbol[symbol] + local op = { + type = symbol, + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index] + #symbol - 1, 'right'), + } + Index = Index + 2 + return op, myLevel +end + +---@param level integer # op level must greater than this level +local function parseBinaryOP(asAction, level) + local token = Tokens[Index + 1] + local symbol = (BinarySymbol[token] and token) + or BinaryAlias[token] + or (not asAction and BinaryActionAlias[token]) + if not symbol then + return nil + end + if symbol == '//' and State.options.nonstandardSymbol['//'] then + return nil + end + local myLevel = BinarySymbol[symbol] + if level and myLevel < level then + return nil + end + local op = { + type = symbol, + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index] + #token - 1, 'right'), + } + if not asAction then + if token == '=' then + pushError { + type = 'ERR_EQ_AS_ASSIGN', + start = op.start, + finish = op.finish, + fix = { + title = 'FIX_EQ_AS_ASSIGN', + { + start = op.start, + finish = op.finish, + text = '==', + } + } + } + end + end + if BinaryAlias[token] then + if not State.options.nonstandardSymbol[token] then + pushError { + type = 'ERR_NONSTANDARD_SYMBOL', + start = op.start, + finish = op.finish, + info = { + symbol = symbol, + }, + fix = { + title = 'FIX_NONSTANDARD_SYMBOL', + symbol = symbol, + { + start = op.start, + finish = op.finish, + text = symbol, + }, + } + } + end + end + if token == '//' + or token == '<<' + or token == '>>' then + if State.version ~= 'Lua 5.3' + and State.version ~= 'Lua 5.4' + and State.version ~= 'Lua 5.5' then + pushError { + type = 'UNSUPPORT_SYMBOL', + version = {'Lua 5.3', 'Lua 5.4', 'Lua 5.5'}, + start = op.start, + finish = op.finish, + info = { + version = State.version, + } + } + end + end + Index = Index + 2 + return op, myLevel +end + +function parseExp(asAction, level) + local exp + local uop, uopLevel = parseUnaryOP() + if uop then + skipSpace() + local child = parseExp(asAction, uopLevel) + -- 预计算负数 + if uop.type == '-' + and child + and (child.type == 'number' or child.type == 'integer') then + child.start = uop.start + child[1] = - child[1] + exp = child + else + exp = { + type = 'unary', + op = uop, + start = uop.start, + finish = child and child.finish or uop.finish, + [1] = child, + } + if child then + child.parent = exp + else + missExp() + end + end + else + exp = parseExpUnit() + if not exp then + return nil + end + end + + while true do + skipSpace() + local bop, bopLevel = parseBinaryOP(asAction, level) + if not bop then + break + end + + ::AGAIN:: + skipSpace() + local isForward = SymbolForward[bopLevel] + local child = parseExp(asAction, isForward and (bopLevel + 0.5) or bopLevel) + if not child then + if skipUnknownSymbol() then + goto AGAIN + else + missExp() + end + end + local bin = { + type = 'binary', + start = exp.start, + finish = child and child.finish or bop.finish, + op = bop, + [1] = exp, + [2] = child + } + exp.parent = bin + if child then + child.parent = bin + end + exp = bin + end + + return exp +end + +local function skipSeps() + while true do + skipSpace() + if Tokens[Index + 1] == ',' then + missExp() + Index = Index + 2 + else + break + end + end +end + +---@return parser.object? first +---@return parser.object? second +---@return parser.object[]? rest +local function parseSetValues() + skipSpace() + local first = parseExp() + if not first then + return nil + end + skipSpace() + if Tokens[Index + 1] ~= ',' then + return first + end + Index = Index + 2 + skipSeps() + local second = parseExp() + if not second then + missExp() + return first + end + skipSpace() + if Tokens[Index + 1] ~= ',' then + return first, second + end + Index = Index + 2 + skipSeps() + local third = parseExp() + if not third then + missExp() + return first, second + end + + local rest = { third } + while true do + skipSpace() + if Tokens[Index + 1] ~= ',' then + return first, second, rest + end + Index = Index + 2 + skipSeps() + local exp = parseExp() + if not exp then + missExp() + return first, second, rest + end + rest[#rest+1] = exp + end +end + +local function pushActionIntoCurrentChunk(action) + local chunk = Chunk[#Chunk] + if chunk then + chunk[#chunk+1] = action + action.parent = chunk + end +end + +---@return parser.object? second +---@return parser.object[]? rest +local function parseVarTails(parser, isLocal) + if Tokens[Index + 1] ~= ',' then + return nil + end + Index = Index + 2 + skipSpace() + local second = parser(true) + if not second then + missName() + return nil + end + if isLocal then + createLocal(second, parseLocalAttrs()) + end + skipSpace() + if Tokens[Index + 1] ~= ',' then + return second + end + Index = Index + 2 + skipSeps() + local third = parser(true) + if not third then + missName() + return second + end + if isLocal then + createLocal(third, parseLocalAttrs()) + end + local rest = { third } + while true do + skipSpace() + if Tokens[Index + 1] ~= ',' then + return second, rest + end + Index = Index + 2 + skipSeps() + local name = parser(true) + if not name then + missName() + return second, rest + end + if isLocal then + createLocal(name, parseLocalAttrs()) + end + rest[#rest+1] = name + end +end + +local function bindValue(n, v, index, lastValue, isLocal, isSet) + if isLocal then + if v and v.special then + addSpecial(v.special, n) + end + elseif isSet then + n.type = GetToSetMap[n.type] or n.type + if n.type == 'setlocal' then + local loc = n.node + if loc.attrs then + pushError { + type = 'SET_CONST', + start = n.start, + finish = n.finish, + } + end + end + end + if not v and lastValue then + if lastValue.type == 'call' + or lastValue.type == 'varargs' then + v = lastValue + if not v.extParent then + v.extParent = {} + end + end + end + if v then + if v.type == 'call' + or v.type == 'varargs' then + local select = { + type = 'select', + sindex = index, + start = v.start, + finish = v.finish, + vararg = v + } + if v.parent then + v.extParent[#v.extParent+1] = select + else + v.parent = select + end + v = select + end + n.value = v + n.range = v.finish + v.parent = n + end +end + +local function parseMultiVars(n1, parser, isLocal) + local hasClose = guide.hasAttribute(n1, 'close') + local n2, nrest = parseVarTails(parser, isLocal) + skipSpace() + local v1, v2, vrest + local isSet + local max = 1 + if expectAssign(not isLocal) then + v1, v2, vrest = parseSetValues() + isSet = true + if not v1 then + missExp() + end + end + local index = 1 + bindValue(n1, v1, index, nil, isLocal, isSet) + local lastValue = v1 + local lastVar = n1 + if n2 then + max = 2 + if not v2 then + index = 2 + end + bindValue(n2, v2, index, lastValue, isLocal, isSet) + lastValue = v2 or lastValue + lastVar = n2 + pushActionIntoCurrentChunk(n2) + if guide.hasAttribute(n2, 'close') then + if hasClose then + pushError { + type = 'MULTI_CLOSE', + start = n2.attrs[1].start, + finish = n2.attrs[1].finish, + } + end + hasClose = true + end + end + if nrest then + for i = 1, #nrest do + local n = nrest[i] + local v = vrest and vrest[i] + max = i + 2 + if not v then + index = index + 1 + end + bindValue(n, v, index, lastValue, isLocal, isSet) + lastValue = v or lastValue + lastVar = n + pushActionIntoCurrentChunk(n) + if guide.hasAttribute(n, 'close') then + if hasClose then + pushError { + type = 'MULTI_CLOSE', + start = n.attrs[1].start, + finish = n.attrs[1].finish, + } + end + hasClose = true + end + end + end + + if isLocal then + local effect = lastValue and lastValue.finish or lastVar.finish + n1.effect = effect + if n2 then + n2.effect = effect + end + if nrest then + for i = 1, #nrest do + nrest[i].effect = effect + end + end + end + + if v2 and not n2 then + v2.redundant = { + max = max, + passed = 2, + } + pushActionIntoCurrentChunk(v2) + end + if vrest then + for i = 1, #vrest do + local v = vrest[i] + if not nrest or not nrest[i] then + v.redundant = { + max = max, + passed = i + 2, + } + pushActionIntoCurrentChunk(v) + end + end + end + + return n1, isSet +end + +local function compileExpAsAction(exp) + pushActionIntoCurrentChunk(exp) + if GetToSetMap[exp.type] then + skipSpace() + local isLocal + if exp.type == 'getlocal' and exp[1] == State.ENVMode then + exp.special = nil + -- TODO: need + 1 at the end + LocalCount = LocalCount - 1 + local loc = createLocal(exp, parseLocalAttrs()) + loc.locPos = exp.start + loc.effect = maxinteger + isLocal = true + skipSpace() + end + local action, isSet = parseMultiVars(exp, parseExp, isLocal) + if isSet + or action.type == 'getmethod' then + return action + end + end + + if exp.type == 'call' then + if exp.hasExit then + for i = #Chunk, 1, -1 do + local block = Chunk[i] + if block.type == 'ifblock' + or block.type == 'elseifblock' + or block.type == 'elseblock' + or block.type == 'function' then + block.hasExit = true + break + end + end + end + return exp + end + + if exp.type == 'binary' then + if GetToSetMap[exp[1].type] then + local op = exp.op + if op.type == '==' then + pushError { + type = 'ERR_ASSIGN_AS_EQ', + start = op.start, + finish = op.finish, + fix = { + title = 'FIX_ASSIGN_AS_EQ', + { + start = op.start, + finish = op.finish, + text = '=', + } + } + } + return + end + end + end + + pushError { + type = 'EXP_IN_ACTION', + start = exp.start, + finish = exp.finish, + } + + return exp +end + +local function parseLocal() + local locPos = getPosition(Tokens[Index], 'left') + Index = Index + 2 + skipSpace() + local word = peekWord() + if not word then + missName() + return nil + end + + if word == 'function' then + local func = parseFunction(true, true) + local name = func.name + if name then + func.name = nil + name.value = func + name.vstart = func.start + name.range = func.finish + name.locPos = locPos + func.parent = name + pushActionIntoCurrentChunk(name) + return name + else + missName(func.keyword[2]) + pushActionIntoCurrentChunk(func) + return func + end + end + + local name = parseName(true) + if not name then + missName() + return nil + end + local loc = createLocal(name, parseLocalAttrs()) + loc.locPos = locPos + loc.effect = maxinteger + pushActionIntoCurrentChunk(loc) + skipSpace() + parseMultiVars(loc, parseName, true) + + return loc +end + +local function parseDo() + local doLeft = getPosition(Tokens[Index], 'left') + local doRight = getPosition(Tokens[Index] + 1, 'right') + local obj = { + type = 'do', + start = doLeft, + finish = doRight, + bstart = doRight, + keyword = { + [1] = doLeft, + [2] = doRight, + }, + } + Index = Index + 2 + pushActionIntoCurrentChunk(obj) + pushChunk(obj) + parseActions() + popChunk() + obj.bfinish = getPosition(Tokens[Index], 'left') + if Tokens[Index + 1] == 'end' then + obj.finish = getPosition(Tokens[Index] + 2, 'right') + obj.keyword[3] = getPosition(Tokens[Index], 'left') + obj.keyword[4] = getPosition(Tokens[Index] + 2, 'right') + Index = Index + 2 + else + missEnd(doLeft, doRight) + end + if obj.locals then + LocalCount = LocalCount - #obj.locals + end + + return obj +end + +local function parseReturn() + local returnLeft = getPosition(Tokens[Index], 'left') + local returnRight = getPosition(Tokens[Index] + 5, 'right') + Index = Index + 2 + skipSpace() + local rtn = parseExpList(true) + if rtn then + rtn.type = 'return' + rtn.start = returnLeft + else + rtn = { + type = 'return', + start = returnLeft, + finish = returnRight, + } + end + pushActionIntoCurrentChunk(rtn) + for i = #Chunk, 1, -1 do + local block = Chunk[i] + if block.type == 'function' + or block.type == 'main' then + if not block.returns then + block.returns = {} + end + block.returns[#block.returns+1] = rtn + break + end + end + for i = #Chunk, 1, -1 do + local block = Chunk[i] + if block.type == 'ifblock' + or block.type == 'elseifblock' + or block.type == 'elseblock' + or block.type == 'function' then + block.hasReturn = true + break + end + end + + return rtn +end + +local function parseLabel() + local left = getPosition(Tokens[Index], 'left') + Index = Index + 2 + skipSpace() + local label = parseName() + skipSpace() + + if not label then + missName() + end + + if Tokens[Index + 1] == '::' then + Index = Index + 2 + else + if label then + missSymbol '::' + end + end + + if not label then + return nil + end + + label.type = 'label' + pushActionIntoCurrentChunk(label) + + local block = guide.getBlock(label) + if block then + if not block.labels then + block.labels = {} + end + local name = label[1] + local olabel = guide.getLabel(block, name) + if olabel then + if State.version == 'Lua 5.4' + or State.version == 'Lua 5.5' + or block == guide.getBlock(olabel) then + pushError { + type = 'REDEFINED_LABEL', + start = label.start, + finish = label.finish, + relative = { + { + olabel.start, + olabel.finish, + } + } + } + end + end + block.labels[name] = label + end + + if State.version == 'Lua 5.1' then + pushError { + type = 'UNSUPPORT_SYMBOL', + start = left, + finish = lastRightPosition(), + version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'Lua 5.5', 'LuaJIT'}, + info = { + version = State.version, + } + } + return + end + return label +end + +local function parseGoTo() + local start = getPosition(Tokens[Index], 'left') + Index = Index + 2 + skipSpace() + + local action = parseName() + if not action then + missName() + return nil + end + + action.type = 'goto' + action.keyStart = start + + for i = #Chunk, 1, -1 do + local chunk = Chunk[i] + if chunk.type == 'function' + or chunk.type == 'main' then + if not chunk.gotos then + chunk.gotos = {} + end + chunk.gotos[#chunk.gotos+1] = action + break + end + end + for i = #Chunk, 1, -1 do + local chunk = Chunk[i] + if chunk.type == 'ifblock' + or chunk.type == 'elseifblock' + or chunk.type == 'elseblock' then + chunk.hasGoTo = true + break + end + end + + pushActionIntoCurrentChunk(action) + return action +end + +local function parseIfBlock(parent) + local ifLeft = getPosition(Tokens[Index], 'left') + local ifRight = getPosition(Tokens[Index] + 1, 'right') + Index = Index + 2 + local ifblock = { + type = 'ifblock', + parent = parent, + start = ifLeft, + finish = ifRight, + bstart = ifRight, + keyword = { + [1] = ifLeft, + [2] = ifRight, + } + } + skipSpace() + local filter = parseExp() + if filter then + ifblock.filter = filter + ifblock.finish = filter.finish + ifblock.bstart = ifblock.finish + filter.parent = ifblock + else + missExp() + end + skipSpace() + local thenToken = Tokens[Index + 1] + if thenToken == 'then' + or thenToken == 'do' then + ifblock.finish = getPosition(Tokens[Index] + #thenToken - 1, 'right') + ifblock.bstart = ifblock.finish + ifblock.keyword[3] = getPosition(Tokens[Index], 'left') + ifblock.keyword[4] = ifblock.finish + if thenToken == 'do' then + pushError { + type = 'ERR_THEN_AS_DO', + start = ifblock.keyword[3], + finish = ifblock.keyword[4], + fix = { + title = 'FIX_THEN_AS_DO', + { + start = ifblock.keyword[3], + finish = ifblock.keyword[4], + text = 'then', + } + } + } + end + Index = Index + 2 + else + missSymbol 'then' + end + pushChunk(ifblock) + parseActions() + popChunk() + ifblock.finish = getPosition(Tokens[Index], 'left') + ifblock.bfinish = ifblock.finish + if ifblock.locals then + LocalCount = LocalCount - #ifblock.locals + end + return ifblock +end + +local function parseElseIfBlock(parent) + local ifLeft = getPosition(Tokens[Index], 'left') + local ifRight = getPosition(Tokens[Index] + 5, 'right') + local elseifblock = { + type = 'elseifblock', + parent = parent, + start = ifLeft, + finish = ifRight, + bstart = ifRight, + keyword = { + [1] = ifLeft, + [2] = ifRight, + } + } + Index = Index + 2 + skipSpace() + local filter = parseExp() + if filter then + elseifblock.filter = filter + elseifblock.finish = filter.finish + elseifblock.bstart = elseifblock.finish + filter.parent = elseifblock + else + missExp() + end + skipSpace() + local thenToken = Tokens[Index + 1] + if thenToken == 'then' + or thenToken == 'do' then + elseifblock.finish = getPosition(Tokens[Index] + #thenToken - 1, 'right') + elseifblock.bstart = elseifblock.finish + elseifblock.keyword[3] = getPosition(Tokens[Index], 'left') + elseifblock.keyword[4] = elseifblock.finish + if thenToken == 'do' then + pushError { + type = 'ERR_THEN_AS_DO', + start = elseifblock.keyword[3], + finish = elseifblock.keyword[4], + fix = { + title = 'FIX_THEN_AS_DO', + { + start = elseifblock.keyword[3], + finish = elseifblock.keyword[4], + text = 'then', + } + } + } + end + Index = Index + 2 + else + missSymbol 'then' + end + pushChunk(elseifblock) + parseActions() + popChunk() + elseifblock.finish = getPosition(Tokens[Index], 'left') + elseifblock.bfinish = elseifblock.finish + if elseifblock.locals then + LocalCount = LocalCount - #elseifblock.locals + end + return elseifblock +end + +local function parseElseBlock(parent) + local ifLeft = getPosition(Tokens[Index], 'left') + local ifRight = getPosition(Tokens[Index] + 3, 'right') + local elseblock = { + type = 'elseblock', + parent = parent, + start = ifLeft, + finish = ifRight, + bstart = ifRight, + keyword = { + [1] = ifLeft, + [2] = ifRight, + } + } + Index = Index + 2 + skipSpace() + pushChunk(elseblock) + parseActions() + popChunk() + elseblock.finish = getPosition(Tokens[Index], 'left') + elseblock.bfinish = elseblock.finish + if elseblock.locals then + LocalCount = LocalCount - #elseblock.locals + end + return elseblock +end + +local function parseIf() + local token = Tokens[Index + 1] + local left = getPosition(Tokens[Index], 'left') + local action = { + type = 'if', + start = left, + finish = getPosition(Tokens[Index] + #token - 1, 'right'), + } + pushActionIntoCurrentChunk(action) + if token ~= 'if' then + missSymbol('if', left, left) + end + local hasElse + while true do + local word = Tokens[Index + 1] + local child + if word == 'if' then + child = parseIfBlock(action) + elseif word == 'elseif' then + child = parseElseIfBlock(action) + elseif word == 'else' then + child = parseElseBlock(action) + end + if not child then + break + end + if hasElse then + pushError { + type = 'BLOCK_AFTER_ELSE', + start = child.start, + finish = child.finish, + } + end + if word == 'else' then + hasElse = true + end + action[#action+1] = child + action.finish = child.finish + skipSpace() + end + + if Tokens[Index + 1] == 'end' then + action.finish = getPosition(Tokens[Index] + 2, 'right') + Index = Index + 2 + else + missEnd(action[1].keyword[1], action[1].keyword[2]) + end + + return action +end + +local function parseFor() + local action = { + type = 'for', + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index] + 2, 'right'), + keyword = {}, + } + action.bstart = action.finish + action.keyword[1] = action.start + action.keyword[2] = action.finish + Index = Index + 2 + pushActionIntoCurrentChunk(action) + pushChunk(action) + skipSpace() + local nameOrList = parseNameOrList(action) + if not nameOrList then + missName() + end + skipSpace() + local forStateVars + -- for i = + if expectAssign() then + action.type = 'loop' + + skipSpace() + local expList = parseExpList() + local name + if nameOrList then + if nameOrList.type == 'name' then + name = nameOrList + else + name = nameOrList[1] + end + end + -- for x in ... uses 4 variables + forStateVars = 3 + LocalCount = LocalCount + forStateVars + if name then + ---@cast name parser.object + -- In Lua 5.5, for loop variables are treated as constants + local attrs + if State.version == 'Lua 5.5' then + attrs = { + type = 'localattrs', + [1] = { + type = 'localattr', + start = name.start, + finish = name.finish, + parent = nil, -- will be set by createLocal + [1] = 'const', + } + } + attrs[1].parent = attrs + end + local loc = createLocal(name, attrs) + loc.parent = action + action.finish = name.finish + action.bstart = action.finish + action.loc = loc + end + if expList then + expList.parent = action + local value = expList[1] + if value then + value.parent = expList + action.init = value + action.finish = expList[#expList].finish + action.bstart = action.finish + end + local max = expList[2] + if max then + max.parent = expList + action.max = max + action.finish = max.finish + action.bstart = action.finish + else + pushError { + type = 'MISS_LOOP_MAX', + start = lastRightPosition(), + finish = lastRightPosition(), + } + end + local step = expList[3] + if step then + step.parent = expList + action.step = step + action.finish = step.finish + action.bstart = action.finish + end + else + pushError { + type = 'MISS_LOOP_MIN', + start = lastRightPosition(), + finish = lastRightPosition(), + } + end + + if action.loc then + action.loc.effect = action.finish + end + elseif Tokens[Index + 1] == 'in' then + action.type = 'in' + local inLeft = getPosition(Tokens[Index], 'left') + local inRight = getPosition(Tokens[Index] + 1, 'right') + Index = Index + 2 + skipSpace() + + local exps = parseExpList() + + action.finish = inRight + action.bstart = action.finish + action.keyword[3] = inLeft + action.keyword[4] = inRight + + local list + if nameOrList and nameOrList.type == 'name' then + list = { + type = 'list', + start = nameOrList.start, + finish = nameOrList.finish, + parent = action, + [1] = nameOrList, + } + else + list = nameOrList + end + + if exps then + local lastExp = exps[#exps] + if lastExp then + action.finish = lastExp.finish + action.bstart = action.finish + end + + action.exps = exps + exps.parent = action + for i = 1, #exps do + local exp = exps[i] + exp.parent = exps + end + else + missExp() + end + + if State.version == 'Lua 5.4' + or State.version == 'Lua 5.5' then + forStateVars = 4 + else + forStateVars = 3 + end + LocalCount = LocalCount + forStateVars + + if list then + local lastName = list[#list] + list.range = lastName and lastName.range or inRight + action.keys = list + for i = 1, #list do + local obj = list[i] + ---@cast obj parser.object + -- In Lua 5.5, for loop variables are treated as constants + local attrs + if State.version == 'Lua 5.5' then + attrs = { + type = 'localattrs', + [1] = { + type = 'localattr', + start = obj.start, + finish = obj.finish, + parent = nil, -- will be set by createLocal + [1] = 'const', + } + } + attrs[1].parent = attrs + end + local loc = createLocal(obj, attrs) + loc.parent = action + loc.effect = action.finish + end + end + else + missSymbol 'in' + end + + skipSpace() + local doToken = Tokens[Index + 1] + if doToken == 'do' + or doToken == 'then' then + local left = getPosition(Tokens[Index], 'left') + local right = getPosition(Tokens[Index] + #doToken - 1, 'right') + action.finish = right + action.bstart = action.finish + action.keyword[#action.keyword+1] = left + action.keyword[#action.keyword+1] = right + if doToken == 'then' then + pushError { + type = 'ERR_DO_AS_THEN', + start = left, + finish = right, + fix = { + title = 'FIX_DO_AS_THEN', + { + start = left, + finish = right, + text = 'do', + } + } + } + end + Index = Index + 2 + else + missSymbol 'do' + end + + skipSpace() + parseActions() + popChunk() + skipSpace() + action.bfinish = getPosition(Tokens[Index], 'left') + if Tokens[Index + 1] == 'end' then + action.finish = getPosition(Tokens[Index] + 2, 'right') + action.keyword[#action.keyword+1] = getPosition(Tokens[Index], 'left') + action.keyword[#action.keyword+1] = action.finish + Index = Index + 2 + else + missEnd(action.keyword[1], action.keyword[2]) + end + + if action.locals then + LocalCount = LocalCount - #action.locals + end + if forStateVars then + LocalCount = LocalCount - forStateVars + end + + return action +end + +local function parseWhile() + local action = { + type = 'while', + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index] + 4, 'right'), + keyword = {}, + } + action.bstart = action.finish + action.keyword[1] = action.start + action.keyword[2] = action.finish + Index = Index + 2 + + skipSpace() + local nextToken = Tokens[Index + 1] + local filter = nextToken ~= 'do' + and nextToken ~= 'then' + and parseExp() + if filter then + action.filter = filter + action.finish = filter.finish + filter.parent = action + else + missExp() + end + + skipSpace() + local doToken = Tokens[Index + 1] + if doToken == 'do' + or doToken == 'then' then + local left = getPosition(Tokens[Index], 'left') + local right = getPosition(Tokens[Index] + #doToken - 1, 'right') + action.finish = left + action.bstart = right + action.keyword[#action.keyword+1] = left + action.keyword[#action.keyword+1] = right + if doToken == 'then' then + pushError { + type = 'ERR_DO_AS_THEN', + start = left, + finish = right, + fix = { + title = 'FIX_DO_AS_THEN', + { + start = left, + finish = right, + text = 'do', + } + } + } + end + Index = Index + 2 + else + missSymbol 'do' + end + + pushActionIntoCurrentChunk(action) + pushChunk(action) + skipSpace() + parseActions() + popChunk() + + skipSpace() + action.bfinish = getPosition(Tokens[Index], 'left') + if Tokens[Index + 1] == 'end' then + action.finish = getPosition(Tokens[Index] + 2, 'right') + action.keyword[#action.keyword+1] = getPosition(Tokens[Index], 'left') + action.keyword[#action.keyword+1] = action.finish + Index = Index + 2 + else + missEnd(action.keyword[1], action.keyword[2]) + end + + if action.locals then + LocalCount = LocalCount - #action.locals + end + + return action +end + +local function parseRepeat() + local action = { + type = 'repeat', + start = getPosition(Tokens[Index], 'left'), + finish = getPosition(Tokens[Index] + 5, 'right'), + keyword = {}, + } + action.bstart = action.finish + action.keyword[1] = action.start + action.keyword[2] = action.finish + Index = Index + 2 + + pushActionIntoCurrentChunk(action) + pushChunk(action) + skipSpace() + parseActions() + + skipSpace() + action.bfinish = getPosition(Tokens[Index], 'left') + if Tokens[Index + 1] == 'until' then + action.finish = getPosition(Tokens[Index] + 4, 'right') + action.keyword[#action.keyword+1] = getPosition(Tokens[Index], 'left') + action.keyword[#action.keyword+1] = action.finish + Index = Index + 2 + + skipSpace() + local filter = parseExp() + if filter then + action.filter = filter + filter.parent = action + else + missExp() + end + + else + missSymbol 'until' + end + + popChunk() + if action.filter then + action.finish = action.filter.finish + end + + if action.locals then + LocalCount = LocalCount - #action.locals + end + + return action +end + +local function parseBreak() + local returnLeft = getPosition(Tokens[Index], 'left') + local returnRight = getPosition(Tokens[Index] + #Tokens[Index + 1] - 1, 'right') + Index = Index + 2 + skipSpace() + local action = { + type = 'break', + start = returnLeft, + finish = returnRight, + } + + local ok + for i = #Chunk, 1, -1 do + local chunk = Chunk[i] + if chunk.type == 'function' then + break + end + if chunk.type == 'while' + or chunk.type == 'in' + or chunk.type == 'loop' + or chunk.type == 'repeat' + or chunk.type == 'for' then + if not chunk.breaks then + chunk.breaks = {} + end + chunk.breaks[#chunk.breaks+1] = action + ok = true + break + end + end + for i = #Chunk, 1, -1 do + local chunk = Chunk[i] + if chunk.type == 'ifblock' + or chunk.type == 'elseifblock' + or chunk.type == 'elseblock' then + chunk.hasBreak = true + break + end + end + if not ok and Mode == 'Lua' then + pushError { + type = 'BREAK_OUTSIDE', + start = action.start, + finish = action.finish, + } + end + + pushActionIntoCurrentChunk(action) + return action +end + +function parseAction() + local token = Tokens[Index + 1] + + if token == '::' then + return parseLabel() + end + + if token == 'local' then + return parseLocal() + end + + if token == 'if' + or token == 'elseif' + or token == 'else' then + return parseIf() + end + + if token == 'for' then + return parseFor() + end + + if token == 'do' then + return parseDo() + end + + if token == 'return' then + return parseReturn() + end + + if token == 'break' then + return parseBreak() + end + + if token == 'continue' and State.options.nonstandardSymbol['continue'] then + return parseBreak() + end + + if token == 'while' then + return parseWhile() + end + + if token == 'repeat' then + return parseRepeat() + end + + if token == 'goto' and isKeyWord('goto', Tokens[Index + 3]) then + return parseGoTo() + end + + if token == 'function' then + local exp = parseFunction(false, true) + local name = exp.name + if name then + exp.name = nil + name.type = GetToSetMap[name.type] + name.value = exp + name.vstart = exp.start + name.range = exp.finish + exp.parent = name + if name.type == 'setlocal' then + local loc = name.node + if loc.attrs then + pushError { + type = 'SET_CONST', + start = name.start, + finish = name.finish, + } + end + end + pushActionIntoCurrentChunk(name) + return name + else + pushActionIntoCurrentChunk(exp) + missName(exp.keyword[2]) + return exp + end + end + + local exp = parseExp(true) + if exp then + local action = compileExpAsAction(exp) + if action then + return action + end + end + return nil, true +end + +local function skipFirstComment() + if Tokens[Index + 1] ~= '#' then + return + end + while true do + Index = Index + 2 + local token = Tokens[Index + 1] + if not token then + break + end + if NLMap[token] then + skipNL() + break + end + end +end + +local function parseLua() + local main = { + type = 'main', + start = 0, + finish = 0, + bstart = 0, + } + pushChunk(main) + createLocal{ + type = 'local', + start = -1, + finish = -1, + effect = -1, + parent = main, + tag = '_ENV', + special= '_G', + [1] = State.ENVMode, + } + LocalCount = 0 + skipFirstComment() + while true do + parseActions() + if Index <= #Tokens then + unknownSymbol() + Index = Index + 2 + else + break + end + end + popChunk() + main.finish = getPosition(#Lua, 'right') + main.bfinish = main.finish + + return main +end + +local function initState(lua, version, options) + Lua = lua + Line = 0 + LineOffset = 1 + LastTokenFinish = 0 + LocalCount = 0 + LocalLimited = false + Chunk = {} + Tokens = tokens(lua) + Index = 1 + ---@class parser.state + ---@field uri uri + ---@field lines integer[] + local state = { + version = version, + lua = lua, + ast = {}, + errs = {}, + comms = {}, + lines = { + [0] = 1, + size = #lua, + }, + options = options or {}, + } + if not state.options.nonstandardSymbol then + state.options.nonstandardSymbol = {} + end + State = state + if version == 'Lua 5.1' or version == 'LuaJIT' then + state.ENVMode = '@fenv' + else + state.ENVMode = '_ENV' + end + + pushError = function (err) + local errs = state.errs + 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 +end + +return function (lua, mode, version, options) + Mode = mode + initState(lua, version, options) + skipSpace() + if mode == 'Lua' then + State.ast = parseLua() + elseif mode == 'Nil' then + State.ast = parseNil() + elseif mode == 'Boolean' then + State.ast = parseBoolean() + elseif mode == 'String' then + State.ast = parseString() + elseif mode == 'Number' then + State.ast = parseNumber() + elseif mode == 'Name' then + State.ast = parseName() + elseif mode == 'Exp' then + State.ast = parseExp() + elseif mode == 'Action' then + State.ast = parseAction() + end + + if State.ast then + State.ast.state = State + end + + while true do + if Index <= #Tokens then + unknownSymbol() + Index = Index + 2 + else + break + end + end + + return State end diff --git a/src/parser/emmy.lua b/src/parser/emmy.lua deleted file mode 100644 index 4c1e087..0000000 --- a/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/src/parser/grammar.lua b/src/parser/grammar.lua deleted file mode 100644 index dfee8a1..0000000 --- a/src/parser/grammar.lua +++ /dev/null @@ -1,534 +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 -DO <- Sp 'do' Cut - / Sp ({} 'then' Cut {}) -> ErrDo -ELSE <- Sp 'else' Cut -ELSEIF <- Sp 'elseif' Cut -END <- Sp 'end' Cut -FALSE <- Sp 'false' Cut -FOR <- Sp 'for' Cut -FUNCTION <- Sp 'function' Cut -GOTO <- Sp 'goto' Cut -IF <- Sp 'if' Cut -IN <- Sp 'in' Cut -LOCAL <- Sp 'local' Cut -NIL <- Sp 'nil' Cut -NOT <- Sp 'not' Cut -OR <- Sp {'or'} Cut -REPEAT <- Sp 'repeat' Cut -RETURN <- Sp 'return' Cut -THEN <- Sp 'then' Cut - / Sp ({} 'do' Cut {}) -> ErrThen -TRUE <- Sp 'true' Cut -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 (BinaryOp (UnUnit / {} -> MissExp))*) - -> Exp -UnUnit <- ExpUnit - / UnaryOp+ (ExpUnit / {} -> DirtyExp) -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 {}) - -> Index -NewIndex <- Sp ({} Index NeedAssign DirtyExp {}) - -> NewIndex -NewField <- Sp ({} MustName ASSIGN DirtyExp {}) - -> NewField - -Function <- Sp ({} 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 <- Sp ({} LoopBody {}) - -> Loop -LoopBody <- FOR LoopArgs NeedDo - {} {| (!END Action)* |} - NeedEnd -LoopArgs <- MustName AssignOrEQ - ({} {| (COMMA / !DO !END Exp)* |} {}) - -> PackLoopArgs - -In <- Sp ({} 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 <- Sp ({} WhileBody {}) - -> While -WhileBody <- WHILE DirtyExp NeedDo - {| (!END Action)* |} - NeedEnd - -Repeat <- Sp ({} 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 - <- Sp ({} 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/src/parser/guide.lua b/src/parser/guide.lua index d7f3965..2ff57fc 100644 --- a/src/parser/guide.lua +++ b/src/parser/guide.lua @@ -1,9 +1,97 @@ -local error = error +local error = error +local type = type -_ENV = nil +---@class parser.object +---@field bindDocs parser.object[] +---@field bindGroup parser.object[] +---@field bindSource parser.object +---@field value parser.object +---@field parent parser.object +---@field type string +---@field special string +---@field tag string +---@field args { [integer]: parser.object, start: integer, finish: integer, type: string } +---@field locals parser.object[] +---@field returns? parser.object[] +---@field breaks? parser.object[] +---@field exps parser.object[] +---@field keys parser.object +---@field uri uri +---@field start integer +---@field finish integer +---@field range integer +---@field effect integer +---@field bstart integer +---@field bfinish integer +---@field attrs table +---@field specials parser.object[] +---@field labels parser.object[] +---@field node parser.object +---@field field parser.object +---@field method parser.object +---@field index parser.object +---@field extends parser.object[]|parser.object +---@field types parser.object[] +---@field fields parser.object[] +---@field tkey parser.object +---@field tvalue parser.object +---@field tindex integer +---@field op parser.object +---@field next parser.object +---@field docParam parser.object +---@field sindex integer +---@field name parser.object +---@field call parser.object +---@field closure parser.object +---@field proto parser.object +---@field exp parser.object +---@field alias parser.object +---@field class parser.object +---@field enum parser.object +---@field vararg parser.object +---@field param parser.object +---@field overload parser.object +---@field docParamMap table +---@field upvalues table +---@field ref parser.object[] +---@field returnIndex integer +---@field assignIndex integer +---@field docIndex integer +---@field docs parser.object +---@field state table +---@field comment table +---@field optional boolean +---@field max parser.object +---@field init parser.object +---@field step parser.object +---@field redundant { max: integer, passed: integer } +---@field filter parser.object +---@field loc parser.object +---@field keyword integer[] +---@field casts parser.object[] +---@field mode? '+' | '-' +---@field hasGoTo? true +---@field hasReturn? true +---@field hasBreak? true +---@field hasExit? true +---@field [integer] parser.object|any +---@field dot { type: string, start: integer, finish: integer } +---@field colon { type: string, start: integer, finish: integer } +---@field package _root parser.object +---@field package _eachCache? parser.object[] +---@field package _isGlobal? boolean +---@field package _typeCache? parser.object[][] +---@class guide +---@field debugMode boolean local m = {} +m.ANY = {""} + +m.notNamePattern = '[^%w_\x80-\xff]' +m.namePattern = '[%a_\x80-\xff][%w_\x80-\xff]*' +m.namePatternFull = '^' .. m.namePattern .. '$' + local blockTypes = { ['while'] = true, ['in'] = true, @@ -11,6 +99,19 @@ local blockTypes = { ['repeat'] = true, ['do'] = true, ['function'] = true, + ['if'] = true, + ['ifblock'] = true, + ['elseblock'] = true, + ['elseifblock'] = true, + ['main'] = true, +} + +m.blockTypes = blockTypes + +local topBlockTypes = { + ['while'] = true, + ['function'] = true, + ['if'] = true, ['ifblock'] = true, ['elseblock'] = true, ['elseifblock'] = true, @@ -22,17 +123,206 @@ local breakBlockTypes = { ['in'] = true, ['loop'] = true, ['repeat'] = true, + ['for'] = true, } ---- 寻找所在函数 -function m.getParentFunction(root, obj) - for _ = 1, 1000 do - obj = root[obj.parent] +local childMap = { + ['main'] = {'#', 'docs'}, + ['repeat'] = {'#', 'filter'}, + ['while'] = {'filter', '#'}, + ['in'] = {'keys', 'exps', '#'}, + ['loop'] = {'loc', 'init', 'max', 'step', '#'}, + ['do'] = {'#'}, + ['if'] = {'#'}, + ['ifblock'] = {'filter', '#'}, + ['elseifblock'] = {'filter', '#'}, + ['elseblock'] = {'#'}, + ['setfield'] = {'node', 'field', 'value'}, + ['getfield'] = {'node', 'field'}, + ['setmethod'] = {'node', 'method', 'value'}, + ['getmethod'] = {'node', 'method'}, + ['setindex'] = {'node', 'index', 'value'}, + ['getindex'] = {'node', 'index'}, + ['tableindex'] = {'index', 'value'}, + ['tablefield'] = {'field', 'value'}, + ['tableexp'] = {'value'}, + ['setglobal'] = {'value'}, + ['local'] = {'attrs', 'value'}, + ['setlocal'] = {'value'}, + ['return'] = {'#'}, + ['select'] = {'vararg'}, + ['table'] = {'#'}, + ['function'] = {'args', '#'}, + ['funcargs'] = {'#'}, + ['paren'] = {'exp'}, + ['call'] = {'node', 'args'}, + ['callargs'] = {'#'}, + ['list'] = {'#'}, + ['binary'] = {1, 2}, + ['unary'] = {1}, + + ['doc'] = {'#'}, + ['doc.class'] = {'class', '#extends', '#signs', 'docAttr', 'comment'}, + ['doc.type'] = {'#types', 'name', 'comment'}, + ['doc.alias'] = {'alias', 'docAttr', 'extends', 'comment'}, + ['doc.enum'] = {'enum', 'extends', 'comment', 'docAttr'}, + ['doc.param'] = {'param', 'extends', 'comment'}, + ['doc.return'] = {'#returns', 'comment'}, + ['doc.field'] = {'field', 'extends', 'comment'}, + ['doc.generic'] = {'#generics', 'comment'}, + ['doc.generic.object'] = {'generic', 'extends', 'comment'}, + ['doc.vararg'] = {'vararg', 'comment'}, + ['doc.type.array'] = {'node'}, + ['doc.type.function'] = {'#args', '#returns', 'comment'}, + ['doc.type.table'] = {'#fields', 'comment'}, + ['doc.type.literal'] = {'node'}, + ['doc.type.arg'] = {'name', 'extends'}, + ['doc.type.field'] = {'name', 'extends'}, + ['doc.type.sign'] = {'node', '#signs'}, + ['doc.overload'] = {'overload', 'comment'}, + ['doc.see'] = {'name', 'comment'}, + ['doc.version'] = {'#versions'}, + ['doc.diagnostic'] = {'#names'}, + ['doc.as'] = {'as'}, + ['doc.cast'] = {'name', '#casts'}, + ['doc.cast.block'] = {'extends'}, + ['doc.operator'] = {'op', 'exp', 'extends'}, + ['doc.meta'] = {'name'}, + ['doc.attr'] = {'#names'}, +} + +---@type table +local compiledChildMap = setmetatable({}, {__index = function (self, name) + local defs = childMap[name] + if not defs then + self[name] = false + return false + end + local text = {} + text[#text+1] = 'local obj, list = ...' + for _, def in ipairs(defs) do + if def == '#' then + text[#text+1] = [[ +for i = 1, #obj do + list[#list+1] = obj[i] +end +]] + elseif type(def) == 'string' and def:sub(1, 1) == '#' then + local key = def:sub(2) + text[#text+1] = ([[ +local childs = obj.%s +if childs then + for i = 1, #childs do + list[#list+1] = childs[i] + end +end +]]):format(key) + elseif type(def) == 'string' then + text[#text+1] = ('list[#list+1] = obj.%s'):format(def) + else + text[#text+1] = ('list[#list+1] = obj[%q]'):format(def) + end + end + local buf = table.concat(text, '\n') + local f = load(buf, buf, 't') + self[name] = f + return f +end}) + +local eachChildMap = setmetatable({}, {__index = function (self, name) + local defs = childMap[name] + if not defs then + self[name] = false + return false + end + local text = {} + text[#text+1] = 'local obj, callback = ...' + for _, def in ipairs(defs) do + if def == '#' then + text[#text+1] = [[ +for i = 1, #obj do + callback(obj[i]) +end +]] + elseif type(def) == 'string' and def:sub(1, 1) == '#' then + local key = def:sub(2) + text[#text+1] = ([[ +local childs = obj.%s +if childs then + for i = 1, #childs do + callback(childs[i]) + end +end +]]):format(key) + elseif type(def) == 'string' then + text[#text+1] = ('callback(obj.%s)'):format(def) + else + text[#text+1] = ('callback(obj[%q])'):format(def) + end + end + local buf = table.concat(text, '\n') + local f = load(buf, buf, 't') + self[name] = f + return f +end}) + +m.actionMap = { + ['main'] = {'#'}, + ['repeat'] = {'#'}, + ['while'] = {'#'}, + ['in'] = {'#'}, + ['loop'] = {'#'}, + ['if'] = {'#'}, + ['ifblock'] = {'#'}, + ['elseifblock'] = {'#'}, + ['elseblock'] = {'#'}, + ['do'] = {'#'}, + ['function'] = {'#'}, + ['funcargs'] = {'#'}, +} + +--- 是否是字面量 +---@param obj table +---@return boolean +function m.isLiteral(obj) + local tp = obj.type + return tp == 'nil' + or tp == 'boolean' + or tp == 'string' + or tp == 'number' + or tp == 'integer' + or tp == 'table' + or tp == 'function' + or tp == 'doc.type.function' + or tp == 'doc.type.table' + or tp == 'doc.type.string' + or tp == 'doc.type.integer' + or tp == 'doc.type.boolean' + or tp == 'doc.type.code' + or tp == 'doc.type.array' +end + +--- 获取字面量 +---@param obj table +---@return any +function m.getLiteral(obj) + if m.isLiteral(obj) then + return obj[1] + end + return nil +end + +--- 寻找父函数 +---@param obj parser.object +---@return parser.object? +function m.getParentFunction(obj) + for _ = 1, 10000 do + obj = obj.parent if not obj then break end local tp = obj.type - if tp == 'function' then + if tp == 'function' or tp == 'main' then return obj end end @@ -40,8 +330,10 @@ function m.getParentFunction(root, obj) end --- 寻找所在区块 -function m.getBlock(root, obj) - for _ = 1, 1000 do +---@param obj parser.object +---@return parser.object? +function m.getBlock(obj) + for _ = 1, 10000 do if not obj then return nil end @@ -49,15 +341,29 @@ function m.getBlock(root, obj) if blockTypes[tp] then return obj end - obj = root[obj.parent] + if obj == obj.parent then + error('obj == obj.parent?' .. obj.type) + end + obj = obj.parent end - error('guide.getBlock overstack') + -- make stack + local stack = {} + for _ = 1, 10 do + stack[#stack+1] = ('%s:%s'):format(obj.type, obj.finish) + obj = obj.parent + if not obj then + break + end + end + error('guide.getBlock overstack:' .. table.concat(stack, ' -> ')) end --- 寻找所在父区块 -function m.getParentBlock(root, obj) - for _ = 1, 1000 do - obj = root[obj.parent] +---@param obj parser.object +---@return parser.object? +function m.getParentBlock(obj) + for _ = 1, 10000 do + obj = obj.parent if not obj then return nil end @@ -70,9 +376,11 @@ function m.getParentBlock(root, obj) end --- 寻找所在可break的父区块 -function m.getBreakBlock(root, obj) - for _ = 1, 1000 do - obj = root[obj.parent] +---@param obj parser.object +---@return parser.object? +function m.getBreakBlock(obj) + for _ = 1, 10000 do + obj = obj.parent if not obj then return nil end @@ -87,89 +395,977 @@ function m.getBreakBlock(root, obj) error('guide.getBreakBlock overstack') end ---- 寻找函数的不定参数,返回不定参在第几个参数上,以及该参数对象。 ---- 如果函数是主函数,则返回`0, nil`。 ----@return table ----@return integer -function m.getFunctionVarArgs(root, func) - if func.type == 'main' then - return 0, nil +--- 寻找doc的主体 +---@param obj parser.object +---@return parser.object +function m.getDocState(obj) + for _ = 1, 10000 do + local parent = obj.parent + if not parent then + return obj + end + if parent.type == 'doc' then + return obj + end + obj = parent end - if func.type ~= 'function' then - return nil, nil + error('guide.getDocState overstack') +end + +--- 寻找所在父类型 +---@param obj parser.object +---@return parser.object? +function m.getParentType(obj, want) + for _ = 1, 10000 do + obj = obj.parent + if not obj then + return nil + end + if want == obj.type then + return obj + end end - local args = root[func.args] - if not args then - return nil, nil + error('guide.getParentType overstack') +end + +--- 寻找所在父类型 +---@param obj parser.object +---@return parser.object? +function m.getParentTypes(obj, wants) + for _ = 1, 10000 do + obj = obj.parent + if not obj then + return nil + end + if wants[obj.type] then + return obj + end end - for i = 1, #args do - local arg = root[args[i]] - if arg.type == '...' then - return i, arg + error('guide.getParentTypes overstack') +end + +--- 寻找根区块 +---@param obj parser.object +---@return parser.object +function m.getRoot(obj) + local source = obj + if source._root then + return source._root + end + for _ = 1, 10000 do + if obj.type == 'main' then + source._root = obj + return obj end + if obj._root then + source._root = obj._root + return source._root + end + local parent = obj.parent + if not parent then + error('Can not find out root:' .. tostring(obj.type)) + end + obj = parent + end + error('guide.getRoot overstack') +end + +---@param obj parser.object | { uri: uri } +---@return uri +function m.getUri(obj) + if obj.uri then + return obj.uri + end + local root = m.getRoot(obj) + if root then + return root.uri or '' + end + return '' +end + +---@return parser.object? +function m.getENV(source, start) + if not start then + start = 1 end - return nil, nil + return m.getLocal(source, '_ENV', start) + or m.getLocal(source, '@fenv', start) end --- 获取指定区块中可见的局部变量 ----@param root table ----@param block table ----@param name string {comment = '变量名'} ----@param pos integer {comment = '可见位置'} -function m.getLocal(root, block, name, pos) - block = m.getBlock(root, block) - for _ = 1, 1000 do +---@param source parser.object +---@param name string # 变量名 +---@param pos integer # 可见位置 +---@return parser.object? +function m.getLocal(source, name, pos) + local block = source + -- find nearest source + for _ = 1, 10000 do if not block then return nil end - local locals = block.locals - local res - if not locals then - goto CONTINUE + if block.type == 'main' then + break end - for i = 1, #locals do - local loc = root[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 + if block.start <= pos + and block.finish >= pos + and blockTypes[block.type] then + break + end + block = block.parent + end + + m.eachSourceContain(block, pos, function (src) + if blockTypes[src.type] + and (src.finish - src.start) < (block.finish - src.start) then + block = src + end + end) + + for _ = 1, 10000 do + if not block then + break + end + local res + if block.locals then + for _, loc in ipairs(block.locals) do + if loc[1] == name + and loc.effect <= pos then + if not res or res.effect < loc.effect then + res = loc + end end end end if res then return res end - ::CONTINUE:: - block = m.getParentBlock(root, block) + block = block.parent end - error('guide.getLocal overstack') + return nil +end + +--- 获取指定区块中所有的可见局部变量名称 +function m.getVisibleLocals(block, pos) + local result = {} + m.eachSourceContain(m.getRoot(block), pos, function (source) + local locals = source.locals + if locals then + for i = 1, #locals do + local loc = locals[i] + local name = loc[1] + if loc.effect <= pos then + result[name] = loc + end + end + end + end) + return result end --- 获取指定区块中可见的标签 ----@param root table ----@param block table ----@param name string {comment = '标签名'} -function m.getLabel(root, block, name) - block = m.getBlock(root, block) - for _ = 1, 1000 do - if not block then +---@param block parser.object +---@param name string +function m.getLabel(block, name) + local current = m.getBlock(block) + for _ = 1, 10000 do + if not current then return nil end - local labels = block.labels + local labels = current.labels if labels then local label = labels[name] if label then - return root[label] + return label end end - if block.type == 'function' then + if current.type == 'function' then return nil end - block = m.getParentBlock(root, block) + current = m.getParentBlock(current) end error('guide.getLocal overstack') end +function m.getStartFinish(source) + local start = source.start + local finish = source.finish + if source.bfinish and source.bfinish > finish then + finish = source.bfinish + end + if not start then + local first = source[1] + if not first then + return nil, nil + end + local last = source[#source] + start = first.start + finish = last.finish + end + return start, finish +end + +function m.getRange(source) + local start = source.vstart or source.start + local finish = source.range or source.finish + if source.bfinish and source.bfinish > finish then + finish = source.bfinish + end + if not start then + local first = source[1] + if not first then + return nil, nil + end + local last = source[#source] + start = first.vstart or first.start + finish = last.range or last.finish + end + return start, finish +end + +--- 判断source是否包含position +function m.isContain(source, position) + local start, finish = m.getStartFinish(source) + if not start then + return false + end + return start <= position and finish >= position +end + +--- 判断position在source的影响范围内 +--- +--- 主要针对赋值等语句时,key包含value +function m.isInRange(source, position) + local start, finish = m.getRange(source) + if not start then + return false + end + return start <= position and finish >= position +end + +function m.isBetween(source, tStart, tFinish) + local start, finish = m.getStartFinish(source) + if not start then + return false + end + return start <= tFinish and finish >= tStart +end + +function m.isBetweenRange(source, tStart, tFinish) + local start, finish = m.getRange(source) + if not start then + return false + end + return start <= tFinish and finish >= tStart +end + +--- 添加child +local function addChilds(list, obj) + local tp = obj.type + if not tp then + return + end + local f = compiledChildMap[tp] + if not f then + return + end + f(obj, list) +end + +--- 遍历所有包含position的source +---@param ast parser.object +---@param position integer +---@param callback fun(src: parser.object): any +function m.eachSourceContain(ast, position, callback) + local list = { ast } + local mark = {} + while true do + local len = #list + if len == 0 then + return + end + local obj = list[len] + list[len] = nil + if not mark[obj] then + mark[obj] = true + if m.isInRange(obj, position) then + if m.isContain(obj, position) then + local res = callback(obj) + if res ~= nil then + return res + end + end + addChilds(list, obj) + end + end + end +end + +--- 遍历所有在某个范围内的source +function m.eachSourceBetween(ast, start, finish, callback) + local list = { ast } + local mark = {} + while true do + local len = #list + if len == 0 then + return + end + local obj = list[len] + list[len] = nil + if not mark[obj] then + mark[obj] = true + if m.isBetweenRange(obj, start, finish) then + if m.isBetween(obj, start, finish) then + local res = callback(obj) + if res ~= nil then + return res + end + end + addChilds(list, obj) + end + end + end +end + +local function getSourceTypeCache(ast) + local cache = ast._typeCache + if not cache then + cache = {} + ast._typeCache = cache + m.eachSource(ast, function (source) + 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 + return cache +end + +--- 遍历所有指定类型的source +---@param ast parser.object +---@param ty string +---@param callback fun(src: parser.object): any +---@return any +function m.eachSourceType(ast, ty, callback) + local cache = getSourceTypeCache(ast) + local myCache = cache[ty] + if not myCache then + return + end + for i = 1, #myCache do + local res = callback(myCache[i]) + if res ~= nil then + return res + end + end +end + +---@param ast parser.object +---@param tps string[] +---@param callback fun(src: parser.object) +function m.eachSourceTypes(ast, tps, callback) + local cache = getSourceTypeCache(ast) + for x = 1, #tps do + local tpCache = cache[tps[x]] + if tpCache then + for i = 1, #tpCache do + callback(tpCache[i]) + end + end + end +end + +--- 遍历所有的source +---@param ast parser.object +---@param callback fun(src: parser.object): boolean? +function m.eachSource(ast, callback) + local cache = ast._eachCache + if not cache then + cache = { ast } + ast._eachCache = cache + local mark = {} + local index = 1 + while true do + local obj = cache[index] + if not obj then + break + end + index = index + 1 + if not mark[obj] then + mark[obj] = true + addChilds(cache, obj) + end + end + end + for i = 1, #cache do + local res = callback(cache[i]) + if res == false then + return + end + end +end + +---@param source parser.object +---@param callback fun(src: parser.object) +function m.eachChild(source, callback) + local f = eachChildMap[source.type] + if not f then + return + end + f(source, callback) +end + +--- 获取指定的 special +---@param ast parser.object +---@param name string +---@param callback fun(src: parser.object) +function m.eachSpecialOf(ast, name, callback) + local root = m.getRoot(ast) + local state = root.state + if not state.specials then + return + end + local specials = state.specials[name] + if not specials then + return + end + for i = 1, #specials do + callback(specials[i]) + end +end + +--- 将 position 拆分成行号与列号 +--- +--- 第一行是0 +---@param position integer +---@return integer row +---@return integer col +function m.rowColOf(position) + return position // 10000, position % 10000 +end + +--- 将行列合并为 position +--- +--- 第一行是0 +---@param row integer +---@param col integer +---@return integer +function m.positionOf(row, col) + return row * 10000 + math.min(col, 10000 - 1) +end + +function m.positionToOffsetByLines(lines, position) + local row, col = m.rowColOf(position) + if row < 0 then + return 0 + end + if row > #lines then + return lines.size + end + local offset = lines[row] + col - 1 + if lines[row + 1] and offset >= lines[row + 1] then + return lines[row + 1] - 1 + elseif offset > lines.size then + return lines.size + end + return offset +end + +--- 返回全文光标位置 +---@param state any +---@param position integer +function m.positionToOffset(state, position) + return m.positionToOffsetByLines(state.lines, position) +end + +---@param lines integer[] +---@param offset integer +function m.offsetToPositionByLines(lines, offset) + local left = 0 + local right = #lines + local row = 0 + while true do + row = (left + right) // 2 + if row == left then + if right ~= left then + if lines[right] - 1 <= offset then + row = right + end + end + break + end + local start = lines[row] - 1 + if start > offset then + right = row + else + left = row + end + end + local col = offset - lines[row] + 1 + return m.positionOf(row, col) +end + +function m.offsetToPosition(state, offset) + return m.offsetToPositionByLines(state.lines, offset) +end + +function m.getLineRange(state, row) + if not state.lines[row] then + return 0 + end + local nextLineStart = state.lines[row + 1] or #state.lua + for i = nextLineStart - 1, state.lines[row], -1 do + local w = state.lua:sub(i, i) + if w ~= '\r' and w ~= '\n' then + return i - state.lines[row] + 1 + end + end + return 0 +end + +local assignTypeMap = { + ['setglobal'] = true, + ['local'] = true, + ['self'] = true, + ['setlocal'] = true, + ['setfield'] = true, + ['setmethod'] = true, + ['setindex'] = true, + ['tablefield'] = true, + ['tableindex'] = true, + ['tableexp'] = true, + ['label'] = true, + ['doc.class'] = true, + ['doc.alias'] = true, + ['doc.enum'] = true, + ['doc.field'] = true, + ['doc.class.name'] = true, + ['doc.alias.name'] = true, + ['doc.enum.name'] = true, + ['doc.field.name'] = true, + ['doc.type.field'] = true, + ['doc.type.array'] = true, +} +function m.isAssign(source) + local tp = source.type + if assignTypeMap[tp] then + return true + end + if tp == 'call' then + local special = m.getSpecial(source.node) + if special == 'rawset' then + return true + end + end + return false +end + +local getTypeMap = { + ['getglobal'] = true, + ['getlocal'] = true, + ['getfield'] = true, + ['getmethod'] = true, + ['getindex'] = true, +} +function m.isGet(source) + local tp = source.type + if getTypeMap[tp] then + return true + end + if tp == 'call' then + local special = m.getSpecial(source.node) + if special == 'rawget' then + return true + end + end + return false +end + +function m.getSpecial(source) + if not source then + return nil + end + return source.special +end + +function m.getKeyNameOfLiteral(obj) + if not obj then + return nil + end + local tp = obj.type + if tp == 'field' + or tp == 'method' then + return obj[1] + elseif tp == 'string' + or tp == 'number' + or tp == 'integer' + or tp == 'boolean' + or tp == 'doc.type.integer' + or tp == 'doc.type.string' + or tp == 'doc.type.boolean' then + return obj[1] + end +end + +---@return string? +function m.getKeyName(obj) + if not obj then + return nil + end + local tp = obj.type + if tp == 'getglobal' + or tp == 'setglobal' then + return obj[1] + elseif tp == 'local' + or tp == 'self' + or tp == 'getlocal' + or tp == 'setlocal' then + return obj[1] + elseif tp == 'getfield' + or tp == 'setfield' + or tp == 'tablefield' then + if obj.field then + return obj.field[1] + end + elseif tp == 'getmethod' + or tp == 'setmethod' then + if obj.method then + return obj.method[1] + end + elseif tp == 'getindex' + or tp == 'setindex' + or tp == 'tableindex' then + return m.getKeyNameOfLiteral(obj.index) + elseif tp == 'tableexp' then + return obj.tindex + elseif tp == 'field' + or tp == 'method' then + return obj[1] + elseif tp == 'doc.class' then + return obj.class[1] + elseif tp == 'doc.alias' then + return obj.alias[1] + elseif tp == 'doc.enum' then + return obj.enum[1] + elseif tp == 'doc.field' then + return obj.field[1] + elseif tp == 'doc.field.name' + or tp == 'doc.type.name' + or tp == 'doc.class.name' + or tp == 'doc.alias.name' + or tp == 'doc.enum.name' + or tp == 'doc.extends.name' then + return obj[1] + elseif tp == 'doc.type.field' then + return m.getKeyName(obj.name) + end + return m.getKeyNameOfLiteral(obj) +end + +function m.getKeyTypeOfLiteral(obj) + if not obj then + return nil + end + local tp = obj.type + if tp == 'field' + or tp == 'method' then + return 'string' + elseif tp == 'string' then + return 'string' + elseif tp == 'number' then + return 'number' + elseif tp == 'integer' then + return 'integer' + elseif tp == 'boolean' then + return 'boolean' + end +end + +function m.getKeyType(obj) + if not obj then + return nil + end + local tp = obj.type + if tp == 'getglobal' + or tp == 'setglobal' then + return 'string' + elseif tp == 'local' + or tp == 'self' + or tp == 'getlocal' + or tp == 'setlocal' then + return 'local' + elseif tp == 'getfield' + or tp == 'setfield' + or tp == 'tablefield' then + return 'string' + elseif tp == 'getmethod' + or tp == 'setmethod' then + return 'string' + elseif tp == 'getindex' + or tp == 'setindex' + or tp == 'tableindex' then + return m.getKeyTypeOfLiteral(obj.index) + elseif tp == 'tableexp' then + return 'integer' + elseif tp == 'field' + or tp == 'method' then + return 'string' + elseif tp == 'doc.class' then + return 'string' + elseif tp == 'doc.alias' then + return 'string' + elseif tp == 'doc.enum' then + return 'string' + elseif tp == 'doc.field' then + return type(obj.field[1]) + elseif tp == 'doc.type.field' then + return type(obj.name[1]) + end + if tp == 'doc.field.name' then + return type(obj[1]) + end + return m.getKeyTypeOfLiteral(obj) +end + +---是否是全局变量(包括 _G.XXX 形式) +---@param source parser.object +---@return boolean +function m.isGlobal(source) + if source._isGlobal ~= nil then + return source._isGlobal + end + if source.tag == '_ENV' then + source._isGlobal = true + return false + end + if source.special == '_G' then + source._isGlobal = true + return true + end + if source.type == 'setglobal' + or source.type == 'getglobal' then + if source.node and source.node.tag == '_ENV' then + source._isGlobal = true + return true + end + end + if source.type == 'setfield' + or source.type == 'getfield' + or source.type == 'setindex' + or source.type == 'getindex' then + local current = source + while current do + local node = current.node + if not node then + break + end + if node.special == '_G' then + source._isGlobal = true + return true + end + if m.getKeyName(node) ~= '_G' then + break + end + current = node + end + end + if source.type == 'call' then + local node = source.node + if node.special == 'rawget' + or node.special == 'rawset' then + if source.args and source.args[1] then + local isGlobal = source.args[1].special == '_G' + source._isGlobal = isGlobal + return isGlobal + end + end + end + source._isGlobal = false + return false +end + +function m.isInString(ast, position) + return m.eachSourceContain(ast, position, function (source) + if source.type == 'string' + and source.start < position then + return true + end + end) +end + +function m.isInComment(ast, offset) + for _, com in ipairs(ast.state.comms) do + if offset >= com.start and offset <= com.finish then + return true + end + end + return false +end + +function m.isOOP(source) + if source.type == 'setmethod' + or source.type == 'getmethod' then + return true + end + if source.type == 'method' + or source.type == 'field' + or source.type == 'function' then + return m.isOOP(source.parent) + end + return false +end + +local basicTypeMap = { + ['unknown'] = true, + ['any'] = true, + ['true'] = true, + ['false'] = true, + ['nil'] = true, + ['boolean'] = true, + ['integer'] = true, + ['number'] = true, + ['string'] = true, + ['table'] = true, + ['function'] = true, + ['thread'] = true, + ['userdata'] = true, +} + +---@param str string +---@return boolean +function m.isBasicType(str) + return basicTypeMap[str] == true +end + +---@param source parser.object +---@return boolean +function m.isBlockType(source) + return blockTypes[source.type] == true +end + +---@param source parser.object +---@return parser.object? +function m.getSelfNode(source) + if source.type == 'getlocal' + or source.type == 'setlocal' then + source = source.node + end + if source.type ~= 'self' then + return nil + end + local args = source.parent + if args.type == 'callargs' then + local call = args.parent + if call.type ~= 'call' then + return nil + end + local getmethod = call.node + if getmethod.type ~= 'getmethod' then + return nil + end + return getmethod.node + end + if args.type == 'funcargs' then + return m.getFunctionSelfNode(args.parent) + end + return nil +end + +---@param func parser.object +---@return parser.object? +function m.getFunctionSelfNode(func) + if func.type ~= 'function' then + return nil + end + local parent = func.parent + if parent.type == 'setmethod' + or parent.type == 'setfield' then + return parent.node + end + return nil +end + +---@param source parser.object +---@return parser.object? +function m.getTopBlock(source) + for _ = 1, 1000 do + local block = source.parent + if not block then + return nil + end + if topBlockTypes[block.type] then + return block + end + source = block + end + return nil +end + +---@param source parser.object +---@return boolean +function m.isParam(source) + if source.type ~= 'local' + and source.type ~= 'self' then + return false + end + if source.parent.type ~= 'funcargs' then + return false + end + return true +end + +---@param source parser.object +---@return parser.object[]? +function m.getParams(source) + if source.type == 'call' then + local args = source.args + if not args then + return + end + assert(args.type == 'callargs', 'call.args type is\'t callargs') + return args + elseif source.type == 'callargs' then + return source + elseif source.type == 'function' then + local args = source.args + if not args then + return + end + assert(args.type == 'funcargs', 'function.args type is\'t callargs') + return args + end + return nil +end + +---@param source parser.object +---@param index integer +---@return parser.object? +function m.getParam(source, index) + local args = m.getParams(source) + return args and args[index] or nil +end + +function m.hasAttribute(source, name) + if not source then + return false + end + local attrs = source.attrs + if not attrs or attrs.type ~= 'localattrs' then + return false + end + for _, attr in ipairs(attrs) do + if attr[1] == name then + return true + end + end + return false +end + return m diff --git a/src/parser/init.lua b/src/parser/init.lua index 5eeb0da..bc004f7 100644 --- a/src/parser/init.lua +++ b/src/parser/init.lua @@ -1,11 +1,8 @@ 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', + compile = require 'parser.compile', + lines = require 'parser.lines', + guide = require 'parser.guide', + luadoc = require 'parser.luadoc', } return api diff --git a/src/parser/lines.lua b/src/parser/lines.lua index f2f076e..964aabf 100644 --- a/src/parser/lines.lua +++ b/src/parser/lines.lua @@ -1,187 +1,24 @@ -local m = require 'lpeglabel' - -local function utf8_len(buf, start, finish) - local len, pos = utf8.len(buf, start, finish) - if len then - return len - end - return 1 + utf8_len(buf, start, pos-1) + utf8_len(buf, pos+1, finish) -end - -local function Line(start, line, finish) - line.start = start - line.finish = finish - 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' / Line, -LastLine= m.Cp() * m.V'Indent' * (1 - m.V'Nl')^0 * m.Cp() / Line, -Nl = m.P'\r\n' + m.S'\r\n', -Indent = m.C(m.S' \t')^0 / Space, -} - -local mt = {} -mt.__index = mt - -function mt:position(row, col, code) - if row < 1 then - return 1 - end - code = code or self.code - if row > #self then - if code == 'utf8' then - return utf8_len(self.buf) + 1 - else - return #self.buf + 1 - end - end - local line = self[row] - local next_line = self[row+1] - local start = line.start - local finish - if next_line then - finish = next_line.start - 1 - else - finish = #self.buf + 1 - end - local pos - if code == 'utf8' then - pos = utf8.offset(self.buf, col, start) or finish - else - pos = start + col - 1 - end - if pos < start then - pos = start - elseif pos > finish then - pos = finish - end - return pos -end - -local function isCharByte(byte) - if not byte then - return false - end - -- [0-9] - if byte >= 48 and byte <= 57 then - return true - end - -- [A-Z] - if byte >= 65 and byte <= 90 then - return true - end - -- [a-z] - if byte >= 97 and byte <= 122 then - return true - end - -- - if byte >= 128 then - return true - end - return false -end - -function mt:positionAsChar(row, col, code) - local pos = self:position(row, col, code) - if isCharByte(self.buf:byte(pos, pos)) then - return pos - elseif isCharByte(self.buf:byte(pos+1, pos+1)) then - return pos + 1 - end - return pos -end - -function mt:rowcol(pos, code) - if pos < 1 then - return 1, 1 - end - code = code or self.code - if pos >= #self.buf + 1 then - local start = self[#self].start - if code == 'utf8' then - return #self, utf8_len(self.buf, start) + 1 - else - return #self, #self.buf - start + 2 - end - end - local min = 1 - local max = #self - for _ = 1, 100 do - if max == min then - local start = self[min].start - if code == 'utf8' then - return min, utf8_len(self.buf, start, pos) - else - return min, pos - start + 1 - end +local sfind = string.find +local ssub = string.sub + +---@param text string +return function (text) + local current = 1 + local lines = {} + lines[0] = 1 + local i = 0 + while true do + local pos = sfind(text,'[\r\n]', current) + if not pos then + break end - local row = (max - min) // 2 + min - local start = self[row].start - if pos < start then - max = row - elseif pos > start then - local next_start = self[row + 1].start - if pos < next_start then - if code == 'utf8' then - return row, utf8_len(self.buf, start, pos) - else - return row, pos - start + 1 - end - elseif pos > next_start then - min = row + 1 - else - return row + 1, 1 - end + i = i + 1 + if ssub(text, pos, pos + 1) == '\r\n' then + current = pos + 2 else - return row, 1 + current = pos + 1 end + lines[i] = current end - error('rowcol failed!') -end - -function mt:line(i) - local start, finish = self:range(i) - return self.buf:sub(start, finish) -end - -function mt:range(i) - if i < 1 or i > #self then - return 0, 0 - end - return self[i].start, self[i].finish -end - -function mt:set_code(code) - self.code = code -end - -return function (self, buf, code) - local lines, err = parser:match(buf) - if not lines then - return nil, err - end - lines.buf = buf - lines.code = code - - return setmetatable(lines, mt) + return lines end diff --git a/src/parser/luadoc.lua b/src/parser/luadoc.lua new file mode 100644 index 0000000..72e5dc1 --- /dev/null +++ b/src/parser/luadoc.lua @@ -0,0 +1,2327 @@ +local m = require 'lpeglabel' +local re = require 'parser.relabel' +local guide = require 'parser.guide' +local compile = require 'parser.compile' +local util = require 'utility' + +local TokenTypes, TokenStarts, TokenFinishs, TokenContents, TokenMarks +---@type integer +local Ci +---@type integer +local Offset +local pushWarning, NextComment, Lines +local parseType, parseTypeUnit +---@type any +local Parser = re.compile([[ +Main <- (Token / Sp)* +Sp <- %s+ +X16 <- [a-fA-F0-9] +Token <- Integer / Name / String / Code / Symbol +Name <- ({} {%name} {}) + -> Name +Integer <- ({} {'-'? [0-9]+} !'.' {}) + -> Integer +Code <- ({} '`' { (!'`' .)*} '`' {}) + -> Code +String <- ({} StringDef {}) + -> String +StringDef <- {'"'} + {~(Esc / !'"' .)*~} -> 1 + ('"'?) + / {"'"} + {~(Esc / !"'" .)*~} -> 1 + ("'"?) + / '[' {:eq: '='* :} '[' + =eq -> LongStringMark + {(!StringClose .)*} -> 1 + StringClose? +StringClose <- ']' =eq ']' +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{' {X16*} '}') -> CharUtf8 +Symbol <- ({} { + [:|,;<>()?+#{}*] + / '[]' + / '...' + / '[' + / ']' + / '-' !'-' + / '.' !'..' + } {}) + -> Symbol +]], { + s = m.S' \t\v\f', + ea = '\a', + eb = '\b', + ef = '\f', + en = '\n', + er = '\r', + et = '\t', + ev = '\v', + name = (m.R('az', 'AZ', '09', '\x80\xff') + m.S('_')) * (m.R('az', 'AZ', '09', '\x80\xff') + m.S('_.*-'))^0, + Char10 = function (char) + ---@type integer? + char = tonumber(char) + if not char or char < 0 or char > 255 then + return '' + end + return string.char(char) + end, + Char16 = function (char) + return string.char(tonumber(char, 16)) + end, + CharUtf8 = function (char) + if #char == 0 then + return '' + end + local v = tonumber(char, 16) + if not v then + return '' + end + if v >= 0 and v <= 0x10FFFF then + return utf8.char(v) + end + return '' + end, + LongStringMark = function (back) + return '[' .. back .. '[' + end, + Name = function (start, content, finish) + Ci = Ci + 1 + TokenTypes[Ci] = 'name' + TokenStarts[Ci] = start + TokenFinishs[Ci] = finish - 1 + TokenContents[Ci] = content + end, + String = function (start, mark, content, finish) + Ci = Ci + 1 + TokenTypes[Ci] = 'string' + TokenStarts[Ci] = start + TokenFinishs[Ci] = finish - 1 + TokenContents[Ci] = content + TokenMarks[Ci] = mark + end, + Integer = function (start, content, finish) + Ci = Ci + 1 + TokenTypes[Ci] = 'integer' + TokenStarts[Ci] = start + TokenFinishs[Ci] = finish - 1 + TokenContents[Ci] = math.tointeger(content) + end, + Code = function (start, content, finish) + Ci = Ci + 1 + TokenTypes[Ci] = 'code' + TokenStarts[Ci] = start + TokenFinishs[Ci] = finish - 1 + TokenContents[Ci] = content + end, + Symbol = function (start, content, finish) + Ci = Ci + 1 + TokenTypes[Ci] = 'symbol' + TokenStarts[Ci] = start + TokenFinishs[Ci] = finish - 1 + TokenContents[Ci] = content + end, +}) + +---@alias parser.visibleType 'public' | 'protected' | 'private' | 'package' + +---@class parser.object +---@field literal boolean +---@field signs parser.object[] +---@field originalComment parser.object +---@field as? parser.object +---@field touch? integer +---@field module? string +---@field async? boolean +---@field versions? table[] +---@field names? parser.object[] +---@field path? string +---@field bindComments? parser.object[] +---@field visible? parser.visibleType +---@field operators? parser.object[] +---@field calls? parser.object[] +---@field generics? parser.object[] +---@field generic? parser.object +---@field docAttr? parser.object +---@field pattern? string + +local function parseTokens(text, offset) + Ci = 0 + Offset = offset + TokenTypes = {} + TokenStarts = {} + TokenFinishs = {} + TokenContents = {} + TokenMarks = {} + Parser:match(text) + Ci = 0 +end + +local function peekToken(offset) + offset = offset or 1 + return TokenTypes[Ci + offset], TokenContents[Ci + offset] +end + +---@return string? tokenType +---@return string? tokenContent +local function nextToken() + Ci = Ci + 1 + if not TokenTypes[Ci] then + Ci = Ci - 1 + return nil, nil + end + return TokenTypes[Ci], TokenContents[Ci] +end + +local function checkToken(tp, content, offset) + offset = offset or 0 + return TokenTypes[Ci + offset] == tp + and TokenContents[Ci + offset] == content +end + +local function getStart() + if Ci == 0 then + return Offset + end + return TokenStarts[Ci] + Offset +end + +---@return integer +local function getFinish() + if Ci == 0 then + return Offset + end + return TokenFinishs[Ci] + Offset + 1 +end + +local function getMark() + return TokenMarks[Ci] +end + +local function try(callback) + local savePoint = Ci + -- rollback + local suc = callback() + if not suc then + Ci = savePoint + end + return suc +end + +local function parseName(tp, parent) + local nameTp, nameText = peekToken() + if nameTp ~= 'name' then + return nil + end + nextToken() + local name = { + type = tp, + start = getStart(), + finish = getFinish(), + parent = parent, + [1] = nameText, + } + return name +end + +local function nextSymbolOrError(symbol) + if checkToken('symbol', symbol, 1) then + nextToken() + return true + end + pushWarning { + type = 'LUADOC_MISS_SYMBOL', + start = getFinish(), + finish = getFinish(), + info = { + symbol = symbol, + } + } + return false +end + +local function parseDocAttr(parent) + if not checkToken('symbol', '(', 1) then + return nil + end + nextToken() + + local attrs = { + type = 'doc.attr', + parent = parent, + start = getStart(), + finish = getStart(), + names = {}, + } + + while true do + if checkToken('symbol', ',', 1) then + nextToken() + goto continue + end + local name = parseName('doc.attr.name', attrs) + if not name then + break + end + attrs.names[#attrs.names+1] = name + attrs.finish = name.finish + ::continue:: + end + + nextSymbolOrError(')') + attrs.finish = getFinish() + + return attrs +end + +local function parseIndexField(parent) + if not checkToken('symbol', '[', 1) then + return nil + end + nextToken() + local field = parseType(parent) + nextSymbolOrError ']' + return field +end + +local function slideToNextLine() + if peekToken() then + return + end + local nextComment = NextComment(0, true) + if not nextComment then + return + end + local currentComment = NextComment(-1, true) + local currentLine = guide.rowColOf(currentComment.start) + local nextLine = guide.rowColOf(nextComment.start) + if currentLine + 1 ~= nextLine then + return + end + if nextComment.text:sub(1, 1) ~= '-' then + return + end + if nextComment.text:match '^%-%s*%@' then + return + end + NextComment() + parseTokens(nextComment.text:sub(2), nextComment.start + 2) +end + +local function parseTable(parent) + if not checkToken('symbol', '{', 1) then + return nil + end + nextToken() + local typeUnit = { + type = 'doc.type.table', + start = getStart(), + parent = parent, + fields = {}, + } + + while true do + slideToNextLine() + if checkToken('symbol', '}', 1) then + nextToken() + break + end + local field = { + type = 'doc.type.field', + parent = typeUnit, + } + + do + local needCloseParen + if checkToken('symbol', '(', 1) then + nextToken() + needCloseParen = true + end + field.name = parseName('doc.field.name', field) + or parseIndexField(field) + if not field.name then + pushWarning { + type = 'LUADOC_MISS_FIELD_NAME', + start = getFinish(), + finish = getFinish(), + } + break + end + if not field.start then + field.start = field.name.start + end + if checkToken('symbol', '?', 1) then + nextToken() + field.optional = true + end + field.finish = getFinish() + if not nextSymbolOrError(':') then + break + end + field.extends = parseType(field) + if not field.extends then + break + end + field.finish = getFinish() + if needCloseParen then + nextSymbolOrError ')' + end + end + + typeUnit.fields[#typeUnit.fields+1] = field + if checkToken('symbol', ',', 1) + or checkToken('symbol', ';', 1) then + nextToken() + else + nextSymbolOrError('}') + break + end + end + typeUnit.finish = getFinish() + return typeUnit +end + +local function parseTuple(parent) + if not checkToken('symbol', '[', 1) then + return nil + end + nextToken() + local typeUnit = { + type = 'doc.type.table', + start = getStart(), + parent = parent, + fields = {}, + isTuple = true, + } + + local index = 1 + while true do + slideToNextLine() + if checkToken('symbol', ']', 1) then + nextToken() + break + end + local field = { + type = 'doc.type.field', + parent = typeUnit, + } + + do + local needCloseParen + if checkToken('symbol', '(', 1) then + nextToken() + needCloseParen = true + end + field.name = { + type = 'doc.type', + start = getFinish(), + firstFinish = getFinish(), + finish = getFinish(), + parent = field, + } + field.name.types = { + [1] = { + type = 'doc.type.integer', + start = getFinish(), + finish = getFinish(), + parent = field.name, + [1] = index, + } + } + index = index + 1 + field.extends = parseType(field) + if not field.extends then + break + end + field.optional = field.extends.optional + field.start = field.extends.start + field.finish = field.extends.finish + if needCloseParen then + nextSymbolOrError ')' + end + end + + typeUnit.fields[#typeUnit.fields+1] = field + if checkToken('symbol', ',', 1) + or checkToken('symbol', ';', 1) then + nextToken() + else + nextSymbolOrError(']') + break + end + end + typeUnit.finish = getFinish() + return typeUnit +end + +local function parseSigns(parent) + if not checkToken('symbol', '<', 1) then + return nil + end + nextToken() + local signs = {} + while true do + local sign = parseName('doc.generic.name', parent) + if not sign then + pushWarning { + type = 'LUADOC_MISS_SIGN_NAME', + start = getFinish(), + finish = getFinish(), + } + break + end + signs[#signs+1] = sign + if checkToken('symbol', ',', 1) then + nextToken() + else + break + end + end + nextSymbolOrError '>' + return signs +end + +local function parseDots(tp, parent) + if not checkToken('symbol', '...', 1) then + return + end + nextToken() + local dots = { + type = tp, + start = getStart(), + finish = getFinish(), + parent = parent, + [1] = '...', + } + return dots +end + +local function parseTypeUnitFunction(parent) + if not checkToken('name', 'fun', 1) then + return nil + end + nextToken() + local typeUnit = { + type = 'doc.type.function', + parent = parent, + start = getStart(), + args = {}, + returns = {}, + } + if not nextSymbolOrError('(') then + return nil + end + while true do + slideToNextLine() + if checkToken('symbol', ')', 1) then + nextToken() + break + end + local arg = { + type = 'doc.type.arg', + parent = typeUnit, + } + arg.name = parseName('doc.type.arg.name', arg) + or parseDots('doc.type.arg.name', arg) + if not arg.name then + pushWarning { + type = 'LUADOC_MISS_ARG_NAME', + start = getFinish(), + finish = getFinish(), + } + break + end + if not arg.start then + arg.start = arg.name.start + end + if checkToken('symbol', '?', 1) then + nextToken() + arg.optional = true + end + arg.finish = getFinish() + if checkToken('symbol', ':', 1) then + nextToken() + arg.extends = parseType(arg) + end + arg.finish = getFinish() + typeUnit.args[#typeUnit.args+1] = arg + if checkToken('symbol', ',', 1) then + nextToken() + else + nextSymbolOrError(')') + break + end + end + slideToNextLine() + if checkToken('symbol', ':', 1) then + nextToken() + slideToNextLine() + local needCloseParen + if checkToken('symbol', '(', 1) then + nextToken() + needCloseParen = true + end + while true do + slideToNextLine() + local name + try(function () + local returnName = parseName('doc.return.name', typeUnit) + or parseDots('doc.return.name', typeUnit) + if not returnName then + return false + end + if checkToken('symbol', ':', 1) then + nextToken() + name = returnName + return true + end + if returnName[1] == '...' then + name = returnName + return false + end + return false + end) + local rtn = parseType(typeUnit) + if not rtn then + break + end + rtn.name = name + if checkToken('symbol', '?', 1) then + nextToken() + rtn.optional = true + end + typeUnit.returns[#typeUnit.returns+1] = rtn + if checkToken('symbol', ',', 1) then + nextToken() + else + break + end + end + if needCloseParen then + nextSymbolOrError ')' + end + end + typeUnit.finish = getFinish() + return typeUnit +end + +local function parseFunction(parent) + local _, content = peekToken() + if content == 'async' then + nextToken() + local pos = getStart() + local tp, cont = peekToken() + if tp == 'name' then + if cont == 'fun' then + local func = parseTypeUnit(parent) + if func then + func.async = true + func.asyncPos = pos + return func + end + end + end + end + if content == 'fun' then + return parseTypeUnitFunction(parent) + end +end + +local function parseTypeUnitArray(parent, node) + if not checkToken('symbol', '[]', 1) then + return nil + end + nextToken() + local result = { + type = 'doc.type.array', + start = node.start, + finish = getFinish(), + node = node, + parent = parent, + } + node.parent = result + return result +end + +local function parseTypeUnitSign(parent, node) + if not checkToken('symbol', '<', 1) then + return nil + end + nextToken() + local result = { + type = 'doc.type.sign', + start = node.start, + finish = getFinish(), + node = node, + parent = parent, + signs = {}, + } + node.parent = result + while true do + local sign = parseType(result) + if not sign then + pushWarning { + type = 'LUA_DOC_MISS_SIGN', + start = getFinish(), + finish = getFinish(), + } + break + end + result.signs[#result.signs+1] = sign + if checkToken('symbol', ',', 1) then + nextToken() + else + break + end + end + nextSymbolOrError '>' + result.finish = getFinish() + return result +end + +local function parseString(parent) + local tp, content = peekToken() + if not tp or tp ~= 'string' then + return nil + end + + nextToken() + local mark = getMark() + -- compatibility + if content:sub(1, 1) == '"' + or content:sub(1, 1) == "'" then + if #content > 1 and content:sub(1, 1) == content:sub(-1, -1) then + mark = content:sub(1, 1) + content = content:sub(2, -2) + end + end + local str = { + type = 'doc.type.string', + start = getStart(), + finish = getFinish(), + parent = parent, + [1] = content, + [2] = mark, + } + return str +end + +local function parseCodePattern(parent) + local tp, pattern = peekToken() + if not tp or (tp ~= 'name' and tp ~= 'code') then + return nil + end + local codeOffset + local content + local i = 1 + if tp == 'code' then + codeOffset = i + content = pattern + pattern = '%s' + end + while true do + i = i+1 + local nextTp, nextContent = peekToken(i) + if not nextTp or TokenFinishs[Ci+i-1] + 1 ~= TokenStarts[Ci+i] then + ---不连续的name,无效的 + break + end + if nextTp == 'name' then + pattern = pattern .. nextContent + elseif nextTp == 'code' then + if codeOffset then + -- 暂时不支持多generic + break + end + codeOffset = i + pattern = pattern .. '%s' + content = nextContent + elseif codeOffset then + -- should be match with Parser "name" mask + if nextTp == 'integer' then + pattern = pattern .. nextContent + elseif nextTp == 'symbol' and (nextContent == '.' or nextContent == '*' or nextContent == '-') then + pattern = pattern .. nextContent + else + break + end + else + break + end + end + if not codeOffset then + return nil + end + nextToken() + local start = getStart() + local finishOffset = i-1 + if finishOffset == 1 then + -- code only, no pattern + pattern = nil + else + for _ = 2, finishOffset do + nextToken() + end + end + local code = { + type = 'doc.type.code', + start = start, + finish = getFinish(), + parent = parent, + pattern = pattern, + [1] = content, + } + return code +end + +local function parseInteger(parent) + local tp, content = peekToken() + if not tp or tp ~= 'integer' then + return nil + end + + nextToken() + local integer = { + type = 'doc.type.integer', + start = getStart(), + finish = getFinish(), + parent = parent, + [1] = content, + } + return integer +end + +local function parseBoolean(parent) + local tp, content = peekToken() + if not tp + or tp ~= 'name' + or (content ~= 'true' and content ~= 'false') then + return nil + end + + nextToken() + local boolean = { + type = 'doc.type.boolean', + start = getStart(), + finish = getFinish(), + parent = parent, + [1] = content == 'true' and true or false, + } + return boolean +end + +local function parseParen(parent) + if not checkToken('symbol', '(', 1) then + return + end + nextToken() + local tp = parseType(parent) + nextSymbolOrError(')') + return tp +end + +function parseTypeUnit(parent) + local result = parseFunction(parent) + or parseTable(parent) + or parseTuple(parent) + or parseString(parent) + or parseInteger(parent) + or parseBoolean(parent) + or parseParen(parent) + or parseCodePattern(parent) + if not result then + result = parseName('doc.type.name', parent) + or parseDots('doc.type.name', parent) + if not result then + return nil + end + if result[1] == '...' then + result[1] = 'unknown' + end + end + while true do + local newResult = parseTypeUnitSign(parent, result) + if not newResult then + break + end + result = newResult + end + while true do + local newResult = parseTypeUnitArray(parent, result) + if not newResult then + break + end + result = newResult + end + return result +end + +local function parseResume(parent) + local default, additional + if checkToken('symbol', '>', 1) then + nextToken() + default = true + end + + if checkToken('symbol', '+', 1) then + nextToken() + additional = true + end + + local result = parseTypeUnit(parent) + if result then + result.default = default + result.additional = additional + end + + return result +end + +local lockResume = false + +function parseType(parent) + local result = { + type = 'doc.type', + parent = parent, + types = {}, + } + while true do + local typeUnit = parseTypeUnit(result) + if not typeUnit then + break + end + + result.types[#result.types+1] = typeUnit + if not result.start then + result.start = typeUnit.start + end + + if not checkToken('symbol', '|', 1) then + break + end + nextToken() + end + if not result.start then + result.start = getFinish() + end + if checkToken('symbol', '?', 1) then + nextToken() + result.optional = true + end + result.finish = getFinish() + result.firstFinish = result.finish + + local row = guide.rowColOf(result.finish) + + local function pushResume() + local comments + for i = 0, 100 do + local nextComm = NextComment(i, true) + if not nextComm then + return false + end + local nextCommRow = guide.rowColOf(nextComm.start) + local currentRow = row + i + 1 + if currentRow < nextCommRow then + return false + end + if nextComm.text:match '^%-%s*%@' then + return false + else + local resumeHead = nextComm.text:match '^%-%s*%|' + if resumeHead then + NextComment(i) + row = row + i + 1 + local finishPos = nextComm.text:find('#', #resumeHead + 1) or #nextComm.text + parseTokens(nextComm.text:sub(#resumeHead + 1, finishPos), nextComm.start + #resumeHead + 1) + local resume = parseResume(result) + if resume then + if comments then + resume.comment = table.concat(comments, '\n') + else + resume.comment = nextComm.text:match('%s*#?%s*(.+)', resume.finish - nextComm.start) + end + result.types[#result.types+1] = resume + result.finish = resume.finish + end + comments = nil + return true + else + if not comments then + comments = {} + end + comments[#comments+1] = nextComm.text:sub(2) + end + end + end + return false + end + + if not lockResume then + lockResume = true + while pushResume() do end + lockResume = false + end + + if #result.types == 0 then + pushWarning { + type = 'LUADOC_MISS_TYPE_NAME', + start = getFinish(), + finish = getFinish(), + } + return nil + end + return result +end + +local docSwitch = util.switch() + : case 'class' + : call(function () + local result = { + type = 'doc.class', + fields = {}, + operators = {}, + calls = {}, + } + result.docAttr = parseDocAttr(result) + result.class = parseName('doc.class.name', result) + if not result.class then + pushWarning { + type = 'LUADOC_MISS_CLASS_NAME', + start = getFinish(), + finish = getFinish(), + } + return nil + end + result.start = getStart() + result.finish = getFinish() + result.signs = parseSigns(result) + if not checkToken('symbol', ':', 1) then + return result + end + nextToken() + + result.extends = {} + + while true do + local extend = parseName('doc.extends.name', result) + or parseTable(result) + or parseTuple(result) + if not extend then + pushWarning { + type = 'LUADOC_MISS_CLASS_EXTENDS_NAME', + start = getFinish(), + finish = getFinish(), + } + return result + end + result.extends[#result.extends+1] = extend + result.finish = getFinish() + if not checkToken('symbol', ',', 1) then + break + end + nextToken() + end + return result + end) + : case 'type' + : call(function () + local first = parseType() + if not first then + return nil + end + local rests + while checkToken('symbol', ',', 1) do + nextToken() + local rest = parseType() + if not rests then + rests = {} + end + rests[#rests+1] = rest + end + return first, rests + end) + : case 'alias' + : call(function () + local result = { + type = 'doc.alias', + } + result.docAttr = parseDocAttr(result) + result.alias = parseName('doc.alias.name', result) + if not result.alias then + pushWarning { + type = 'LUADOC_MISS_ALIAS_NAME', + start = getFinish(), + finish = getFinish(), + } + return nil + end + result.start = getStart() + result.signs = parseSigns(result) + result.extends = parseType(result) + if not result.extends then + pushWarning { + type = 'LUADOC_MISS_ALIAS_EXTENDS', + start = getFinish(), + finish = getFinish(), + } + return nil + end + result.finish = getFinish() + return result + end) + : case 'param' + : call(function () + local result = { + type = 'doc.param', + } + result.param = parseName('doc.param.name', result) + or parseDots('doc.param.name', result) + if not result.param then + pushWarning { + type = 'LUADOC_MISS_PARAM_NAME', + start = getFinish(), + finish = getFinish(), + } + return nil + end + if checkToken('symbol', '?', 1) then + nextToken() + result.optional = true + end + result.start = result.param.start + result.finish = getFinish() + result.extends = parseType(result) + if not result.extends then + pushWarning { + type = 'LUADOC_MISS_PARAM_EXTENDS', + start = getFinish(), + finish = getFinish(), + } + return result + end + result.finish = getFinish() + result.firstFinish = result.extends.firstFinish + return result + end) + : case 'return' + : call(function () + local result = { + type = 'doc.return', + returns = {}, + } + while true do + local dots = parseDots('doc.return.name') + if dots then + Ci = Ci - 1 + end + local docType = parseType(result) + if not docType then + break + end + if not result.start then + result.start = docType.start + end + if checkToken('symbol', '?', 1) then + nextToken() + docType.optional = true + end + if dots then + docType.name = dots + dots.parent = docType + else + docType.name = parseName('doc.return.name', docType) + or parseDots('doc.return.name', docType) + end + result.returns[#result.returns+1] = docType + if not checkToken('symbol', ',', 1) then + break + end + nextToken() + end + if #result.returns == 0 then + return nil + end + result.finish = getFinish() + return result + end) + : case 'field' + : call(function () + local result = { + type = 'doc.field', + } + try(function () + local tp, value = nextToken() + if tp == 'name' then + if value == 'public' + or value == 'protected' + or value == 'private' + or value == 'package' then + local tp2 = peekToken(1) + local tp3 = peekToken(2) + if tp2 == 'name' and not tp3 then + return false + end + result.visible = value + result.start = getStart() + return true + end + end + return false + end) + result.field = parseName('doc.field.name', result) + or parseIndexField(result) + if not result.field then + pushWarning { + type = 'LUADOC_MISS_FIELD_NAME', + start = getFinish(), + finish = getFinish(), + } + return nil + end + if not result.start then + result.start = result.field.start + end + if checkToken('symbol', '?', 1) then + nextToken() + result.optional = true + end + result.extends = parseType(result) + if not result.extends then + pushWarning { + type = 'LUADOC_MISS_FIELD_EXTENDS', + start = getFinish(), + finish = getFinish(), + } + return nil + end + result.finish = getFinish() + return result + end) + : case 'generic' + : call(function () + local result = { + type = 'doc.generic', + generics = {}, + } + while true do + local object = { + type = 'doc.generic.object', + parent = result, + } + object.generic = parseName('doc.generic.name', object) + if not object.generic then + pushWarning { + type = 'LUADOC_MISS_GENERIC_NAME', + start = getFinish(), + finish = getFinish(), + } + return nil + end + object.start = object.generic.start + if not result.start then + result.start = object.start + end + if checkToken('symbol', ':', 1) then + nextToken() + object.extends = parseType(object) + end + object.finish = getFinish() + result.generics[#result.generics+1] = object + if not checkToken('symbol', ',', 1) then + break + end + nextToken() + end + result.finish = getFinish() + return result + end) + : case 'vararg' + : call(function () + local result = { + type = 'doc.vararg', + } + result.vararg = parseType(result) + if not result.vararg then + pushWarning { + type = 'LUADOC_MISS_VARARG_TYPE', + start = getFinish(), + finish = getFinish(), + } + return + end + result.start = result.vararg.start + result.finish = result.vararg.finish + return result + end) + : case 'overload' + : call(function () + local tp, name = peekToken() + if tp ~= 'name' + or (name ~= 'fun' and name ~= 'async') then + pushWarning { + type = 'LUADOC_MISS_FUN_AFTER_OVERLOAD', + start = getFinish(), + finish = getFinish(), + } + return nil + end + local result = { + type = 'doc.overload', + } + result.overload = parseFunction(result) + if not result.overload then + return nil + end + result.overload.parent = result + result.start = result.overload.start + result.finish = result.overload.finish + return result + end) + : case 'deprecated' + : call(function () + return { + type = 'doc.deprecated', + start = getFinish(), + finish = getFinish(), + } + end) + : case 'meta' + : call(function () + local meta = { + type = 'doc.meta', + start = getFinish(), + finish = getFinish(), + } + meta.name = parseName('doc.meta.name', meta) + return meta + end) + : case 'version' + : call(function () + local result = { + type = 'doc.version', + versions = {}, + } + while true do + local tp, text = nextToken() + if not tp then + pushWarning { + type = 'LUADOC_MISS_VERSION', + start = getFinish(), + finish = getFinish(), + } + break + end + if not result.start then + result.start = getStart() + end + local version = { + type = 'doc.version.unit', + parent = result, + start = getStart(), + } + if tp == 'symbol' then + if text == '>' then + version.ge = true + elseif text == '<' then + version.le = true + end + tp, text = nextToken() + end + if tp ~= 'name' then + pushWarning { + type = 'LUADOC_MISS_VERSION', + start = getStart(), + finish = getFinish(), + } + break + end + version.version = tonumber(text) or text + version.finish = getFinish() + result.versions[#result.versions+1] = version + if not checkToken('symbol', ',', 1) then + break + end + nextToken() + end + if #result.versions == 0 then + return nil + end + result.finish = getFinish() + return result + end) + : case 'see' + : call(function () + local result = { + type = 'doc.see', + } + result.name = parseName('doc.see.name', result) + if not result.name then + pushWarning { + type = 'LUADOC_MISS_SEE_NAME', + start = getFinish(), + finish = getFinish(), + } + return nil + end + result.start = result.name.start + result.finish = result.name.finish + return result + end) + : case 'diagnostic' + : call(function () + local result = { + type = 'doc.diagnostic', + } + local nextTP, mode = nextToken() + if nextTP ~= 'name' then + pushWarning { + type = 'LUADOC_MISS_DIAG_MODE', + start = getFinish(), + finish = getFinish(), + } + return nil + end + result.mode = mode + result.start = getStart() + result.finish = getFinish() + if mode ~= 'disable-next-line' + and mode ~= 'disable-line' + and mode ~= 'disable' + and mode ~= 'enable' then + pushWarning { + type = 'LUADOC_ERROR_DIAG_MODE', + start = result.start, + finish = result.finish, + } + end + + if checkToken('symbol', ':', 1) then + nextToken() + result.names = {} + while true do + local name = parseName('doc.diagnostic.name', result) + if not name then + pushWarning { + type = 'LUADOC_MISS_DIAG_NAME', + start = getFinish(), + finish = getFinish(), + } + return result + end + result.names[#result.names+1] = name + if not checkToken('symbol', ',', 1) then + break + end + nextToken() + end + end + + result.finish = getFinish() + + return result + end) + : case 'module' + : call(function () + local result = { + type = 'doc.module', + start = getFinish(), + finish = getFinish(), + } + local tp, content = peekToken() + if tp == 'string' then + result.module = content + nextToken() + result.start = getStart() + result.finish = getFinish() + result.smark = getMark() + else + pushWarning { + type = 'LUADOC_MISS_MODULE_NAME', + start = getFinish(), + finish = getFinish(), + } + end + return result + end) + : case 'async' + : call(function () + return { + type = 'doc.async', + start = getFinish(), + finish = getFinish(), + } + end) + : case 'nodiscard' + : call(function () + return { + type = 'doc.nodiscard', + start = getFinish(), + finish = getFinish(), + } + end) + : case 'as' + : call(function () + local result = { + type = 'doc.as', + start = getFinish(), + finish = getFinish(), + } + result.as = parseType(result) + result.finish = getFinish() + return result + end) + : case 'cast' + : call(function () + local result = { + type = 'doc.cast', + start = getFinish(), + finish = getFinish(), + casts = {}, + } + + local loc = parseName('doc.cast.name', result) + if not loc then + pushWarning { + type = 'LUADOC_MISS_LOCAL_NAME', + start = getFinish(), + finish = getFinish(), + } + return result + end + + result.name = loc + result.finish = loc.finish + + while true do + local block = { + type = 'doc.cast.block', + parent = result, + start = getFinish(), + finish = getFinish(), + } + if checkToken('symbol', '+', 1) then + block.mode = '+' + nextToken() + block.start = getStart() + block.finish = getFinish() + elseif checkToken('symbol', '-', 1) then + block.mode = '-' + nextToken() + block.start = getStart() + block.finish = getFinish() + end + + if checkToken('symbol', '?', 1) then + block.optional = true + nextToken() + block.finish = getFinish() + else + block.extends = parseType(block) + if block.extends then + block.start = block.start or block.extends.start + block.finish = block.extends.finish + end + end + + if block.optional or block.extends then + result.casts[#result.casts+1] = block + end + result.finish = block.finish + + if checkToken('symbol', ',', 1) then + nextToken() + else + break + end + end + + return result + end) + : case 'operator' + : call(function () + local result = { + type = 'doc.operator', + start = getFinish(), + finish = getFinish(), + } + + local op = parseName('doc.operator.name', result) + if not op then + pushWarning { + type = 'LUADOC_MISS_OPERATOR_NAME', + start = getFinish(), + finish = getFinish(), + } + return nil + end + result.op = op + result.finish = op.finish + + if checkToken('symbol', '(', 1) then + nextToken() + if checkToken('symbol', ')', 1) then + nextToken() + else + local exp = parseType(result) + if exp then + result.exp = exp + result.finish = exp.finish + end + nextSymbolOrError ')' + end + end + + nextSymbolOrError ':' + + local ret = parseType(result) + if ret then + result.extends = ret + result.finish = ret.finish + end + + return result + end) + : case 'source' + : call(function (doc) + local fullSource = doc:sub(#'source' + 1) + if not fullSource or fullSource == '' then + return + end + fullSource = util.trim(fullSource) + if fullSource == '' then + return + end + local source, line, char = fullSource:match('^(.-):?(%d*):?(%d*)$') + source = source or fullSource + line = tonumber(line) or 1 + char = tonumber(char) or 0 + local result = { + type = 'doc.source', + start = getStart(), + finish = getFinish(), + path = source, + line = line, + char = char, + } + return result + end) + : case 'enum' + : call(function () + local attr = parseDocAttr() + local name = parseName('doc.enum.name') + if not name then + return nil + end + local result = { + type = 'doc.enum', + start = name.start, + finish = name.finish, + enum = name, + docAttr = attr, + } + name.parent = result + if attr then + attr.parent = result + end + return result + end) + : case 'private' + : call(function () + return { + type = 'doc.private', + start = getFinish(), + finish = getFinish(), + } + end) + : case 'protected' + : call(function () + return { + type = 'doc.protected', + start = getFinish(), + finish = getFinish(), + } + end) + : case 'public' + : call(function () + return { + type = 'doc.public', + start = getFinish(), + finish = getFinish(), + } + end) + : case 'package' + : call(function () + return { + type = 'doc.package', + start = getFinish(), + finish = getFinish(), + } + end) + +local function convertTokens(doc) + local tp, text = nextToken() + if not tp then + return + end + if tp ~= 'name' then + pushWarning { + type = 'LUADOC_MISS_CATE_NAME', + start = getStart(), + finish = getFinish(), + } + return nil + end + return docSwitch(text, doc) +end + +local function trimTailComment(text) + local comment = text + if text:sub(1, 1) == '@' then + comment = util.trim(text:sub(2)) + end + if text:sub(1, 1) == '#' then + comment = util.trim(text:sub(2)) + end + if text:sub(1, 2) == '--' then + comment = util.trim(text:sub(3)) + end + if comment:find '^%s*[\'"[]' + and comment:find '[\'"%]]%s*$' then + local state = compile(comment:gsub('^%s+', ''), 'String') + if state and state.ast then + comment = state.ast[1] --[[@as string]] + end + end + return util.trim(comment) +end + +local function buildLuaDoc(comment) + local headPos = (comment.type == 'comment.short' and comment.text:match '^%-%s*@()') + or (comment.type == 'comment.long' and comment.text:match '^%s*@()') + if not headPos then + return { + type = 'doc.comment', + start = comment.start, + finish = comment.finish, + range = comment.finish, + comment = comment, + } + end + -- absolute position of `@` symbol + local startOffset = comment.start + headPos + if comment.type == 'comment.long' then + startOffset = comment.start + headPos + #comment.mark - 2 + end + + local doc = comment.text:sub(headPos) + + parseTokens(doc, startOffset) + local result, rests = convertTokens(doc) + if result then + result.range = math.max(comment.finish, result.finish) + local finish = result.firstFinish or result.finish + if rests then + for _, rest in ipairs(rests) do + rest.range = math.max(comment.finish, rest.finish) + finish = rest.firstFinish or rest.finish + end + end + + -- `result` can be a multiline annotation or an alias, while `doc` is the first line, so we can't parse comment + if finish >= comment.finish then + return result, rests + end + + local cstart = doc:find('%S', finish - startOffset) + if cstart then + result.comment = { + type = 'doc.tailcomment', + start = startOffset + cstart, + finish = comment.finish, + parent = result, + text = trimTailComment(doc:sub(cstart)), + } + if rests then + for _, rest in ipairs(rests) do + rest.comment = result.comment + end + end + end + + return result, rests + end + + return { + type = 'doc.comment', + start = comment.start, + finish = comment.finish, + range = comment.finish, + comment = comment, + } +end + +local function isTailComment(text, doc) + if not doc then + return false + end + local left = doc.originalComment.start + local row, col = guide.rowColOf(left) + local lineStart = Lines[row] or 0 + local hasCodeBefore = text:sub(lineStart, lineStart + col):find '[%w_]' + return hasCodeBefore +end + +local function isContinuedDoc(lastDoc, nextDoc) + if not nextDoc then + return false + end + if nextDoc.type == 'doc.diagnostic' then + return true + end + if lastDoc.type == 'doc.type' + or lastDoc.type == 'doc.module' + or lastDoc.type == 'doc.enum' then + if nextDoc.type ~= 'doc.comment' then + return false + end + end + if lastDoc.type == 'doc.class' + or lastDoc.type == 'doc.field' + or lastDoc.type == 'doc.operator' then + if nextDoc.type ~= 'doc.field' + and nextDoc.type ~= 'doc.operator' + and nextDoc.type ~= 'doc.comment' + and nextDoc.type ~= 'doc.overload' + and nextDoc.type ~= 'doc.source' then + return false + end + end + if nextDoc.type == 'doc.cast' then + return false + end + return true +end + +local function isNextLine(lastDoc, nextDoc) + if not nextDoc then + return false + end + local lastRow = guide.rowColOf(lastDoc.finish) + local newRow = guide.rowColOf(nextDoc.start) + return newRow - lastRow == 1 +end + +local function bindGeneric(binded) + local generics = {} + for _, doc in ipairs(binded) do + if doc.type == 'doc.generic' then + for _, obj in ipairs(doc.generics) do + local name = obj.generic[1] + generics[name] = obj + end + end + if doc.type == 'doc.class' + or doc.type == 'doc.alias' then + if doc.signs then + for _, sign in ipairs(doc.signs) do + local name = sign[1] + generics[name] = sign + end + end + end + if doc.type == 'doc.param' + or doc.type == 'doc.vararg' + or doc.type == 'doc.return' + or doc.type == 'doc.type' + or doc.type == 'doc.class' + or doc.type == 'doc.alias' then + guide.eachSourceType(doc, 'doc.type.name', function (src) + local name = src[1] + if generics[name] then + src.type = 'doc.generic.name' + src.generic = generics[name] + end + end) + guide.eachSourceType(doc, 'doc.type.code', function (src) + local name = src[1] + if generics[name] then + src.type = 'doc.generic.name' + src.literal = true + end + end) + end + end +end + +local function bindDocWithSource(doc, source) + if not source.bindDocs then + source.bindDocs = {} + end + if source.bindDocs[#source.bindDocs] ~= doc then + source.bindDocs[#source.bindDocs+1] = doc + end + doc.bindSource = source +end + +local function bindDoc(source, binded) + local isParam = source.type == 'self' + or source.type == 'local' + and (source.parent.type == 'funcargs' + or ( source.parent.type == 'in' + and source.finish <= source.parent.keys.finish + ) + ) + local ok = false + for _, doc in ipairs(binded) do + if doc.bindSource then + goto CONTINUE + end + if doc.type == 'doc.class' + or doc.type == 'doc.deprecated' + or doc.type == 'doc.version' + or doc.type == 'doc.module' + or doc.type == 'doc.source' + or doc.type == 'doc.private' + or doc.type == 'doc.protected' + or doc.type == 'doc.public' + or doc.type == 'doc.package' + or doc.type == 'doc.see' then + if source.type == 'function' + or isParam then + goto CONTINUE + end + bindDocWithSource(doc, source) + ok = true + elseif doc.type == 'doc.type' then + if source.type == 'function' + or isParam + or source._bindedDocType then + goto CONTINUE + end + source._bindedDocType = true + bindDocWithSource(doc, source) + ok = true + elseif doc.type == 'doc.overload' then + if not source.bindDocs then + source.bindDocs = {} + end + source.bindDocs[#source.bindDocs+1] = doc + if source.type == 'function' then + bindDocWithSource(doc, source) + end + ok = true + elseif doc.type == 'doc.param' then + if isParam + and doc.param[1] == source[1] then + bindDocWithSource(doc, source) + ok = true + elseif source.type == '...' + and doc.param[1] == '...' then + bindDocWithSource(doc, source) + ok = true + elseif source.type == 'self' + and doc.param[1] == 'self' then + bindDocWithSource(doc, source) + ok = true + elseif source.type == 'function' then + if not source.bindDocs then + source.bindDocs = {} + end + source.bindDocs[#source.bindDocs + 1] = doc + if source.args then + for _, arg in ipairs(source.args) do + if arg[1] == doc.param[1] then + bindDocWithSource(doc, arg) + break + end + end + end + end + elseif doc.type == 'doc.vararg' then + if source.type == '...' then + bindDocWithSource(doc, source) + ok = true + end + elseif doc.type == 'doc.return' + or doc.type == 'doc.generic' + or doc.type == 'doc.async' + or doc.type == 'doc.nodiscard' then + if source.type == 'function' then + bindDocWithSource(doc, source) + ok = true + end + elseif doc.type == 'doc.enum' then + if source.type == 'table' then + bindDocWithSource(doc, source) + ok = true + end + if source.value and source.value.type == 'table' then + bindDocWithSource(doc, source.value) + goto CONTINUE + end + elseif doc.type == 'doc.comment' then + bindDocWithSource(doc, source) + ok = true + end + ::CONTINUE:: + end + return ok +end + +local function bindDocsBetween(sources, binded, start, finish) + -- 用二分法找到第一个 + local max = #sources + local index + local left = 1 + local right = max + for _ = 1, 1000 do + index = left + (right - left) // 2 + if index <= left then + index = left + break + elseif index >= right then + index = right + break + end + local src = sources[index] + if src.start < start then + left = index + 1 + else + right = index + end + end + + local ok = false + -- 从前往后进行绑定 + for i = index, max do + local src = sources[i] + if src and src.start >= start then + if src.start >= finish then + break + end + if src.start >= start then + if src.type == 'local' + or src.type == 'self' + or src.type == 'setlocal' + or src.type == 'setglobal' + or src.type == 'tablefield' + or src.type == 'tableindex' + or src.type == 'setfield' + or src.type == 'setindex' + or src.type == 'setmethod' + or src.type == 'function' + or src.type == 'return' + or src.type == '...' + or src.type == 'call' -- for `rawset` + then + if bindDoc(src, binded) then + ok = true + end + end + end + end + end + + return ok +end + +local function bindReturnIndex(binded) + local returnIndex = 0 + for _, doc in ipairs(binded) do + if doc.type == 'doc.return' then + for _, rtn in ipairs(doc.returns) do + returnIndex = returnIndex + 1 + rtn.returnIndex = returnIndex + end + end + end +end + +local function bindCommentsToDoc(doc, comments) + doc.bindComments = comments + for _, comment in ipairs(comments) do + comment.bindSource = doc + end +end + +local function bindCommentsAndFields(binded) + local class + local comments = {} + local source + for _, doc in ipairs(binded) do + if doc.type == 'doc.class' then + -- 多个class连续写在一起,只有最后一个class可以绑定source + if class then + class.bindSource = nil + end + if source then + doc.source = source + source.bindSource = doc + end + class = doc + bindCommentsToDoc(doc, comments) + comments = {} + elseif doc.type == 'doc.field' then + if class then + class.fields[#class.fields+1] = doc + doc.class = class + end + if source then + doc.source = source + source.bindSource = doc + end + bindCommentsToDoc(doc, comments) + comments = {} + elseif doc.type == 'doc.operator' then + if class then + class.operators[#class.operators+1] = doc + doc.class = class + end + bindCommentsToDoc(doc, comments) + comments = {} + elseif doc.type == 'doc.overload' then + if class then + class.calls[#class.calls+1] = doc + doc.class = class + end + elseif doc.type == 'doc.alias' + or doc.type == 'doc.enum' then + bindCommentsToDoc(doc, comments) + comments = {} + elseif doc.type == 'doc.comment' then + comments[#comments+1] = doc + elseif doc.type == 'doc.source' then + source = doc + goto CONTINUE + end + source = nil + ::CONTINUE:: + end +end + +local function bindDocWithSources(sources, binded) + if not binded then + return + end + local lastDoc = binded[#binded] + if not lastDoc then + return + end + for _, doc in ipairs(binded) do + doc.bindGroup = binded + end + bindGeneric(binded) + bindCommentsAndFields(binded) + bindReturnIndex(binded) + + -- doc is special node + if lastDoc.special then + if bindDoc(lastDoc.special, binded) then + return + end + end + + local row = guide.rowColOf(lastDoc.finish) + local suc = bindDocsBetween(sources, binded, guide.positionOf(row, 0), lastDoc.start) + if not suc then + bindDocsBetween(sources, binded, guide.positionOf(row + 1, 0), guide.positionOf(row + 2, 0)) + end +end + +local bindDocAccept = { + 'local' , 'setlocal' , 'setglobal', + 'setfield' , 'setmethod' , 'setindex' , + 'tablefield', 'tableindex', 'self' , + 'function' , 'return' , '...' , + 'call', +} + +local function bindDocs(state) + local text = state.lua + local sources = {} + guide.eachSourceTypes(state.ast, bindDocAccept, function (src) + -- allow binding docs with rawset(_G, "key", value) + if src.type == 'call' then + if src.node.special ~= 'rawset' or not src.args then + return + end + local g, key = src.args[1], src.args[2] + if not g or not key or g.special ~= '_G' then + return + end + end + sources[#sources+1] = src + end) + table.sort(sources, function (a, b) + return a.start < b.start + end) + local binded + for i, doc in ipairs(state.ast.docs) do + if not binded then + binded = {} + state.ast.docs.groups[#state.ast.docs.groups+1] = binded + end + binded[#binded+1] = doc + if doc.specialBindGroup then + bindDocWithSources(sources, doc.specialBindGroup) + binded = nil + elseif isTailComment(text, doc) and doc.type ~= "doc.field" then + bindDocWithSources(sources, binded) + binded = nil + else + local nextDoc = state.ast.docs[i+1] + if nextDoc and nextDoc.special + or not isNextLine(doc, nextDoc) then + bindDocWithSources(sources, binded) + binded = nil + end + if not isContinuedDoc(doc, nextDoc) + and not isTailComment(text, nextDoc) then + bindDocWithSources(sources, binded) + binded = nil + end + end + end +end + +local function findTouch(state, doc) + local text = state.lua + local pos = guide.positionToOffset(state, doc.originalComment.start) + for i = pos - 2, 1, -1 do + local c = text:sub(i, i) + if c == '\r' + or c == '\n' then + break + elseif c ~= ' ' + and c ~= '\t' then + doc.touch = guide.offsetToPosition(state, i) + break + end + end +end + +local function luadoc(state) + local ast = state.ast + local comments = state.comms + table.sort(comments, function (a, b) + return a.start < b.start + end) + ast.docs = { + type = 'doc', + parent = ast, + groups = {}, + } + + pushWarning = function (err) + local errs = state.errs + 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 'Warning' + errs[#errs+1] = err + return err + end + Lines = state.lines + + local ci = 1 + NextComment = function (offset, peek) + local comment = comments[ci + (offset or 0)] + if not peek then + ci = ci + 1 + (offset or 0) + end + return comment + end + + local function insertDoc(doc, comment) + ast.docs[#ast.docs+1] = doc + doc.parent = ast.docs + if ast.start > doc.start then + ast.start = doc.start + end + if ast.finish < doc.finish then + ast.finish = doc.finish + end + doc.originalComment = comment + if comment.type == 'comment.long' then + findTouch(state, doc) + end + end + + while true do + local comment = NextComment() + if not comment then + break + end + lockResume = false + local doc, rests = buildLuaDoc(comment) + if doc then + insertDoc(doc, comment) + if rests then + for _, rest in ipairs(rests) do + insertDoc(rest, comment) + end + end + end + end + + if ast.state.pluginDocs then + for _, doc in ipairs(ast.state.pluginDocs) do + insertDoc(doc, doc.originalComment) + end + ---@param a unknown + ---@param b unknown + table.sort(ast.docs, function (a, b) + return a.start < b.start + end) + ast.state.pluginDocs = nil + end + + ast.docs.start = ast.start + ast.docs.finish = ast.finish + + if #ast.docs == 0 then + return + end + + bindDocs(state) +end + +return { + buildAndBindDoc = function (ast, src, comment, group) + local doc = buildLuaDoc(comment) + if doc then + local pluginDocs = ast.state.pluginDocs or {} + pluginDocs[#pluginDocs+1] = doc + doc.special = src + doc.originalComment = comment + doc.virtual = true + doc.specialBindGroup = group + ast.state.pluginDocs = pluginDocs + return doc + end + return nil + end, + luadoc = luadoc +} diff --git a/src/parser/parse.lua b/src/parser/parse.lua deleted file mode 100644 index cec946c..0000000 --- a/src/parser/parse.lua +++ /dev/null @@ -1,37 +0,0 @@ -local ast = require 'parser.ast' - -return function (self, lua, mode, version) - local errs = {} - local state = { - version = version, - lua = lua, - emmy = {}, - root = {}, - 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, - } - 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) - return nil, errs - end - state.ast = res - return state, errs -end diff --git a/src/parser/split.lua b/src/parser/split.lua deleted file mode 100644 index 6ce4a4e..0000000 --- a/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/src/parser/tokens.lua b/src/parser/tokens.lua new file mode 100644 index 0000000..5f455be --- /dev/null +++ b/src/parser/tokens.lua @@ -0,0 +1,48 @@ +local m = require 'lpeglabel' + +local Sp = m.S' \t\v\f' +local Nl = m.P'\r\n' + m.S'\r\n' +local Number = m.R'09'^1 +local Word = m.R('AZ', 'az', '__', '\x80\xff') * m.R('AZ', 'az', '09', '__', '\x80\xff')^0 +local Symbol = m.P'==' + + m.P'~=' + + m.P'--' + -- non-standard: + + m.P'<<=' + + m.P'>>=' + + m.P'//=' + -- end non-standard + + m.P'<<' + + m.P'>>' + + m.P'<=' + + m.P'>=' + + m.P'//' + + m.P'...' + + m.P'..' + + m.P'::' + -- non-standard: + + m.P'!=' + + m.P'&&' + + m.P'||' + + m.P'/*' + + m.P'*/' + + m.P'+=' + + m.P'-=' + + m.P'*=' + + m.P'%=' + + m.P'&=' + + m.P'|=' + + m.P'^=' + + m.P'/=' + -- end non-standard + -- singles + + m.S'+-*/!#%^&()={}[]|\\\'":;<>,.?~`' +local Unknown = (1 - Number - Word - Symbol - Sp - Nl)^1 +local Token = m.Cp() * m.C(Nl + Number + Word + Symbol + Unknown) + +local Parser = m.Ct((Sp^1 + Token)^0) + +return function (lua) + local results = Parser:match(lua) + return results +end diff --git a/src/utility.lua b/src/utility.lua index 85cb410..55965cf 100644 --- a/src/utility.lua +++ b/src/utility.lua @@ -8,21 +8,25 @@ local ipairs = ipairs local next = next local rawset = rawset local move = table.move -local setmetatable = setmetatable +local tableRemove = table.remove +local setmetatable = debug.setmetatable local mathType = math.type local mathCeil = math.ceil local getmetatable = getmetatable local mathAbs = math.abs +local mathRandom = math.random local ioOpen = io.open +local utf8Len = utf8.len +local getenv = os.getenv +local getupvalue = debug.getupvalue +local mathHuge = math.huge +local inf = 1 / 0 +local nan = 0 / 0 +local error = error +local assert = assert _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' @@ -31,6 +35,21 @@ local function isInteger(n) end end +local function formatNumber(n) + if n == inf + or n == -inf + or n == nan + or n ~= n then -- IEEE 标准中,NAN 不等于自己。但是某些实现中没有遵守这个规则 + return ('%q'):format(n) + end + if isInteger(n) then + return tostring(n) + end + local str = ('%.10f'):format(n) + str = str:gsub('%.?0*$', '') + return str +end + local TAB = setmetatable({}, { __index = function (self, n) self[n] = stringRep(' ', n) return self[n] @@ -64,8 +83,8 @@ local RESERVED = { local m = {} --- 打印表的结构 ----@param tbl table ----@param option table {optional = 'self'} +---@param tbl any +---@param option? table ---@return string function m.dump(tbl, option) if not option then @@ -76,12 +95,10 @@ function m.dump(tbl, option) end local lines = {} local mark = {} + local stack = {} lines[#lines+1] = '{' - local function unpack(tbl, tab) - if mark[tbl] and mark[tbl] > 0 then - lines[#lines+1] = TAB[tab+1] .. '""' - return - end + local function unpack(tbl) + local deep = #stack mark[tbl] = (mark[tbl] or 0) + 1 local keys = {} local keymap = {} @@ -139,22 +156,35 @@ function m.dump(tbl, option) end local value = tbl[key] local tp = type(value) + local format = option['format'] and option['format'][key] + if format then + value = format(value, unpack, deep+1, stack) + tp = type(value) + end if tp == 'table' then - lines[#lines+1] = ('%s%s{'):format(TAB[tab+1], keyWord) - unpack(value, tab+1) - lines[#lines+1] = ('%s},'):format(TAB[tab+1]) + if mark[value] and mark[value] > 0 then + lines[#lines+1] = ('%s%s%s,'):format(TAB[deep+1], keyWord, option['loop'] or '""') + elseif deep >= (option['deep'] or mathHuge) then + lines[#lines+1] = ('%s%s%s,'):format(TAB[deep+1], keyWord, '""') + else + lines[#lines+1] = ('%s%s{'):format(TAB[deep+1], keyWord) + stack[#stack+1] = key + unpack(value) + stack[#stack] = nil + lines[#lines+1] = ('%s},'):format(TAB[deep+1]) + end elseif tp == 'string' then - lines[#lines+1] = ('%s%s%q,'):format(TAB[tab+1], keyWord, value) + lines[#lines+1] = ('%s%s%q,'):format(TAB[deep+1], keyWord, value) elseif tp == 'number' then - lines[#lines+1] = ('%s%s%s,'):format(TAB[tab+1], keyWord, formatNumber(value)) + lines[#lines+1] = ('%s%s%s,'):format(TAB[deep+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)) + lines[#lines+1] = ('%s%s%s,'):format(TAB[deep+1], keyWord, tostring(value)) end end mark[tbl] = mark[tbl] - 1 end - unpack(tbl, 0) + unpack(tbl) lines[#lines+1] = '}' return tableConcat(lines, '\r\n') end @@ -185,7 +215,10 @@ function m.equal(a, b) end return true elseif tp1 == 'number' then - return mathAbs(a - b) <= 1e-10 + if mathAbs(a - b) <= 1e-10 then + return true + end + return tostring(a) == tostring(b) else return a == b end @@ -242,7 +275,7 @@ local function sortTable(tbl) end --- 创建一个有序表 ----@param tbl table {optional = 'self'} +---@param tbl? table ---@return table function m.container(tbl) return sortTable(tbl) @@ -250,17 +283,26 @@ end --- 读取文件 ---@param path string -function m.loadFile(path) +function m.loadFile(path, keepBom) 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' + local text = f:read 'a' f:close() - return buf + if not text then + return nil + end + if not keepBom then + if text:sub(1, 3) == '\xEF\xBB\xBF' then + return text:sub(4) + end + if text:sub(1, 2) == '\xFF\xFE' + or text:sub(1, 2) == '\xFE\xFF' then + return text:sub(3) + end + end + return text end --- 写入文件 @@ -279,14 +321,14 @@ function m.saveFile(path, content) end --- 计数器 ----@param init integer {optional = 'after'} ----@param step integer {optional = 'after'} +---@param init? integer +---@param step? integer ---@return fun():integer function m.counter(init, step) if not step then step = 1 end - local current = init and (init - 1) or -1 + local current = init and (init - 1) or 0 return function () current = current + step return current @@ -295,12 +337,12 @@ end --- 排序后遍历 ---@param t table -function m.sortPairs(t) +function m.sortPairs(t, sorter) local keys = {} for k in pairs(t) do keys[#keys+1] = k end - tableSort(keys) + tableSort(keys, sorter) local i = 0 return function () i = i + 1 @@ -310,8 +352,8 @@ function m.sortPairs(t) end --- 深拷贝(不处理元表) ----@param source table ----@param target table {optional = 'self'} +---@param source table +---@param target? table function m.deepCopy(source, target) local mark = {} local function copy(a, b) @@ -333,4 +375,499 @@ function m.deepCopy(source, target) 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 + +function m.enableCloseFunction() + setmetatable(function () end, { __close = function (f) f() end }) +end + +local esc = { + ["'"] = [[\']], + ['"'] = [[\"]], + ['\r'] = [[\r]], + ['\n'] = '\\\n', +} + +function m.viewString(str, quo) + if not quo then + if str:find('[\r\n]') then + quo = '[[' + elseif not str:find("'", 1, true) and str:find('"', 1, true) then + quo = "'" + else + quo = '"' + end + end + if quo == "'" then + str = str:gsub('[\000-\008\011-\012\014-\031\127]', function (char) + return ('\\%03d'):format(char:byte()) + end) + return quo .. str:gsub([=[['\r\n]]=], esc) .. quo + elseif quo == '"' then + str = str:gsub('[\000-\008\011-\012\014-\031\127]', function (char) + return ('\\%03d'):format(char:byte()) + end) + return quo .. str:gsub([=[["\r\n]]=], esc) .. quo + else + local eqnum = #quo - 2 + local fsymb = ']' .. ('='):rep(eqnum) .. ']' + if not str:find(fsymb, 1, true) then + str = str:gsub('[\000-\008\011-\012\014-\031\127]', '') + 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) .. '[' + str = str:gsub('[\000-\008\011-\012\014-\031\127]', '') + 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 + +function m.utf8Len(str, start, finish) + local len = 0 + for _ = 1, 10000 do + local clen, pos = utf8Len(str, start, finish, true) + if clen then + len = len + clen + break + else + len = len + 1 + utf8Len(str, start, pos - 1, true) + start = pos + 1 + end + end + return len +end + +function m.revertTable(t) + local len = #t + if len <= 1 then + return t + end + for x = 1, len // 2 do + local y = len - x + 1 + t[x], t[y] = t[y], t[x] + end + return t +end + +function m.revertMap(t) + local nt = {} + for k, v in pairs(t) do + nt[v] = k + end + return nt +end + +function m.randomSortTable(t, max) + local len = #t + if len <= 1 then + return t + end + if not max or max > len then + max = len + end + for x = 1, max do + local y = mathRandom(len) + t[x], t[y] = t[y], t[x] + end + return t +end + +function m.tableMultiRemove(t, index) + local mark = {} + for i = 1, #index do + local v = index[i] + mark[v] = true + end + local offset = 0 + local me = 1 + local len = #t + while true do + local it = me + offset + if it > len then + for i = me, len do + t[i] = nil + end + break + end + if mark[it] then + offset = offset + 1 + else + if me ~= it then + t[me] = t[it] + end + me = me + 1 + end + end +end + +---遍历文本的每一行 +---@param text string +---@param keepNL? boolean # 保留换行符 +---@return fun():string?, integer? +function m.eachLine(text, keepNL) + local offset = 1 + local lineCount = 0 + local lastLine + return function () + lineCount = lineCount + 1 + if offset > #text then + if not lastLine then + lastLine = '' + return '', lineCount + end + return nil + end + local nl = text:find('[\r\n]', offset) + if not nl then + lastLine = text:sub(offset) + offset = #text + 1 + return lastLine, lineCount + end + local line + if text:sub(nl, nl + 1) == '\r\n' then + if keepNL then + line = text:sub(offset, nl + 1) + else + line = text:sub(offset, nl - 1) + end + offset = nl + 2 + else + if keepNL then + line = text:sub(offset, nl) + else + line = text:sub(offset, nl - 1) + end + offset = nl + 1 + end + return line, lineCount + end +end + +function m.sortByScore(tbl, callbacks) + if type(callbacks) ~= 'table' then + callbacks = { callbacks } + end + local size = #callbacks + local scoreCache = {} + for i = 1, size do + scoreCache[i] = {} + end + tableSort(tbl, function (a, b) + for i = 1, size do + local callback = callbacks[i] + local cache = scoreCache[i] + local sa = cache[a] or callback(a) + local sb = cache[b] or callback(b) + cache[a] = sa + cache[b] = sb + if sa > sb then + return true + elseif sa < sb then + return false + end + end + return false + end) +end + +---裁剪字符串 +---@param str string +---@param mode? '"left"'|'"right"' +---@return string +function m.trim(str, mode) + if mode == "left" then + return (str:gsub('^%s+', '')) + end + if mode == "right" then + return (str:gsub('%s+$', '')) + end + return (str:match '^%s*(.-)%s*$') +end + +function m.expandPath(path) + if path:sub(1, 1) == '~' then + local home = getenv('HOME') + if not home then -- has to be Windows + home = getenv 'USERPROFILE' or (getenv 'HOMEDRIVE' .. getenv 'HOMEPATH') + end + return home .. path:sub(2) + elseif path:sub(1, 1) == '$' then + path = path:gsub('%$([%w_]+)', getenv) + return path + end + return path +end + +function m.arrayToHash(l) + local t = {} + for i = 1, #l do + t[l[i]] = true + end + return t +end + +---@class switch +---@field cachedCases string[] +---@field map table +---@field _default fun(...):... +local switchMT = {} +switchMT.__index = switchMT + +---@param name string +---@return switch +function switchMT:case(name) + self.cachedCases[#self.cachedCases+1] = name + return self +end + +---@param callback async fun(...):... +---@return switch +function switchMT:call(callback) + for i = 1, #self.cachedCases do + local name = self.cachedCases[i] + self.cachedCases[i] = nil + if self.map[name] then + error('Repeated fields:' .. tostring(name)) + end + self.map[name] = callback + end + return self +end + +---@param callback fun(...):... +---@return switch +function switchMT:default(callback) + self._default = callback + return self +end + +function switchMT:getMap() + return self.map +end + +---@param name string +---@return boolean +function switchMT:has(name) + return self.map[name] ~= nil +end + +---@param name string +---@return ... +function switchMT:__call(name, ...) + local callback = self.map[name] or self._default + if not callback then + return + end + return callback(...) +end + +---@return switch +function m.switch() + local obj = setmetatable({ + map = {}, + cachedCases = {}, + }, switchMT) + return obj +end + +---@param f async fun() +function m.getUpvalue(f, name) + for i = 1, 999 do + local uname, value = getupvalue(f, i) + if not uname then + break + end + if name == uname then + return value, true + end + end + return nil, false +end + +function m.stringStartWith(str, head) + return str:sub(1, #head) == head +end + +function m.stringEndWith(str, tail) + return str:sub(-#tail) == tail +end + +function m.defaultTable(default) + return setmetatable({}, { __index = function (t, k) + if k == nil then + return nil + end + local v = default(k) + t[k] = v + return v + end }) +end + +function m.multiTable(count, default) + local current + if default then + current = setmetatable({}, { __index = function (t, k) + if k == nil then + return nil + end + local v = default(k) + t[k] = v + return v + end }) + else + current = setmetatable({}, { __index = function (t, k) + if k == nil then + return nil + end + local v = {} + t[k] = v + return v + end }) + end + for _ = 3, count do + current = setmetatable({}, { __index = function (t, k) + if k == nil then + return nil + end + t[k] = current + return current + end }) + end + return current +end + +---@param t table +---@param sorter boolean|function +function m.getTableKeys(t, sorter) + local keys = {} + for k in pairs(t) do + keys[#keys+1] = k + end + if sorter == true then + tableSort(keys) + elseif type(sorter) == 'function' then + tableSort(keys, sorter) + end + return keys +end + +function m.arrayHas(array, value) + for i = 1, #array do + if array[i] == value then + return true + end + end + return false +end + +function m.arrayInsert(array, value) + if not m.arrayHas(array, value) then + array[#array+1] = value + end +end + +function m.arrayRemove(array, value) + for i = 1, #array do + if array[i] == value then + tableRemove(array, i) + return + end + end +end + +m.MODE_K = { __mode = 'k' } +m.MODE_V = { __mode = 'v' } +m.MODE_KV = { __mode = 'kv' } + +---@generic T: fun(param: any):any +---@param func T +---@return T +function m.cacheReturn(func) + local cache = {} + return function (param) + if cache[param] == nil then + cache[param] = func(param) + end + return cache[param] + end +end + return m diff --git a/test/ast/Action.lua b/test/ast/Action.lua index dc83d6c..e835fee 100644 --- a/test/ast/Action.lua +++ b/test/ast/Action.lua @@ -1,501 +1,509 @@ CHECK'x = 1' { - [1] = { - type = "setglobal", - start = 1, - finish = 1, - value = 2, - [1] = "x", - }, - [2] = { - type = "number", - start = 5, + type = "setglobal", + start = 0, + finish = 1, + range = 5, + value = { + type = "integer", + start = 4, finish = 5, - parent = 1, + parent = "", [1] = 1, }, + [1] = "x", } CHECK'local x' { - [1] = { - type = "local", - start = 7, - finish = 7, - effect = 8, - [1] = "x", - }, + type = "local", + start = 6, + finish = 7, + effect = 7, + locPos = 0, + [1] = "x", } CHECK'local x = 1' { - [1] = { - value = 2, - type = "local", - start = 7, - finish = 7, - effect = 12, - [1] = "x", - }, - [2] = { - type = "number", - start = 11, + type = "local", + start = 6, + finish = 7, + effect = 11, + range = 11, + locPos = 0, + value = { + type = "integer", + start = 10, finish = 11, - parent = 1, + parent = "", [1] = 1, }, + [1] = "x", } CHECK'local x = x' { - [1] = { - value = 2, - type = "local", - start = 7, - finish = 7, - effect = 12, - [1] = "x", - }, - [2] = { + type = "local", + start = 6, + finish = 7, + effect = 11, + range = 11, + locPos = 0, + value = { type = "getglobal", - start = 11, + start = 10, finish = 11, - parent = 1, + parent = "", [1] = "x", }, + [1] = "x", } CHECK'local x = 1' { - [1] = { - type = "local", - start = 7, - finish = 7, - effect = 28, - value = 4, - attrs = { - [1] = 2, - [2] = 3, - }, - [1] = "x", - }, - [2] = { - type = "localattr", - start = 10, - finish = 14, - parent = 1, - [1] = "close", - }, - [3] = { - type = "localattr", - start = 18, - finish = 22, - parent = 1, - [1] = "const", - }, - [4] = { - type = "number", - start = 27, + type = "local", + start = 6, + finish = 7, + effect = 27, + range = 27, + locPos = 0, + value = { + type = "integer", + start = 26, finish = 27, - parent = 1, + parent = "", [1] = 1, }, + attrs = { + type = "localattrs", + parent = "", + [1] = { + type = "localattr", + start = 8, + finish = 15, + parent = "", + [1] = "close", + }, + [2] = { + type = "localattr", + start = 16, + finish = 23, + parent = "", + [1] = "const", + }, + }, + [1] = "x", } CHECK'local x < const > = 1' { - [1] = { - type = "local", - start = 7, - finish = 7, - effect = 22, - value = 3, - attrs = { - [1] = 2, - }, - [1] = "x", - }, - [2] = { - type = "localattr", - start = 11, - finish = 15, - parent = 1, - [1] = "const", - }, - [3] = { - type = "number", - start = 21, + type = "local", + start = 6, + finish = 7, + effect = 21, + range = 21, + locPos = 0, + value = { + type = "integer", + start = 20, finish = 21, - parent = 1, + parent = "", [1] = 1, }, + attrs = { + type = "localattrs", + parent = "", + [1] = { + type = "localattr", + start = 8, + finish = 17, + parent = "", + [1] = "const", + }, + }, + [1] = "x", } -CHECK'x = function () end' +CHECK 'x.y = 1' { - [1] = { - type = "setglobal", + type = "setfield", + start = 0, + finish = 3, + range = 7, + node = "", + dot = { + type = ".", start = 1, - finish = 1, - value = 2, - [1] = "x", + finish = 2, + }, + field = { + type = "field", + start = 2, + finish = 3, + parent = "", + [1] = "y", }, - [2] = { - type = "function", - start = 5, - finish = 19, - parent = 1, + value = { + type = "integer", + start = 6, + finish = 7, + parent = "", + [1] = 1, }, } -CHECK'x.y = function () end' +CHECK 'x[y] = 1' { - [1] = { - type = "setfield", - start = 1, - finish = 3, - node = 2, - dot = { - type = ".", - start = 2, - finish = 2, - }, - value = 3, - }, - [2] = { + type = "setindex", + start = 0, + finish = 4, + range = 8, + node = "", + index = { type = "getglobal", - start = 1, - finish = 1, - parent = 1, - [1] = "x", + start = 2, + finish = 3, + parent = "", + [1] = "y", }, - [3] = { - type = "function", + value = { + type = "integer", start = 7, - finish = 21, - parent = 1, + finish = 8, + parent = "", + [1] = 1, }, } -CHECK'func.x(1, 2)' +CHECK'x = function () end' { - [1] = { - type = "call", - start = 1, - finish = 12, - node = 2, - args = 4, - }, - [2] = { - type = "getfield", - start = 1, - finish = 6, - parent = 1, - node = 3, - dot = { - type = ".", - start = 5, - finish = 5, + type = "setglobal", + start = 0, + finish = 1, + range = 19, + value = { + type = "function", + start = 4, + bstart = 15, + finish = 19, + keyword = { + [1] = 4, + [2] = 12, + [3] = 16, + [4] = 19, }, - field = { - type = "field", - start = 6, - finish = 6, - [1] = "x", + parent = "", + args = { + type = "funcargs", + start = 13, + finish = 15, + parent = "", }, + bfinish = 16, }, - [3] = { - type = "getglobal", + [1] = "x", +} +CHECK'x.y = function () end' +{ + type = "setfield", + start = 0, + finish = 3, + range = 21, + node = "", + dot = { + type = ".", start = 1, - finish = 4, - parent = 2, - [1] = "func", + finish = 2, }, - [4] = { - type = "callargs", - start = 7, - finish = 12, - parent = 1, - [1] = 5, - [2] = 6, + field = { + type = "field", + start = 2, + finish = 3, + parent = "", + [1] = "y", + }, + value = { + type = "function", + start = 6, + bstart = 17, + finish = 21, + keyword = { + [1] = 6, + [2] = 14, + [3] = 18, + [4] = 21, + }, + parent = "", + args = { + type = "funcargs", + start = 15, + finish = 17, + parent = "", + }, + bfinish = 18, }, - [5] = { - type = "number", +} +CHECK'require "xxx"' +{ + type = "call", + start = 0, + finish = 13, + node = "", + args = { + type = "callargs", start = 8, - finish = 8, - parent = 4, - [1] = 1, - }, - [6] = { - type = "number", - start = 11, - finish = 11, - parent = 4, - [1] = 2, + finish = 13, + parent = "", + [1] = { + type = "string", + start = 8, + finish = 13, + parent = "", + [1] = "xxx", + [2] = "\"", + }, }, } -CHECK'func:x(1, 2)' +CHECK'func.x(1, 2)' { - [1] = { - type = "call", - start = 1, + type = "call", + start = 0, + finish = 12, + node = "", + args = { + type = "callargs", + start = 6, finish = 12, - node = 2, - args = 5, - }, - [2] = { - type = "getmethod", - start = 1, - finish = 6, - parent = 1, - node = 3, - colon = { - type = ":", - start = 5, - finish = 5, + parent = "", + [1] = { + type = "integer", + start = 7, + finish = 8, + parent = "", + [1] = 1, + }, + [2] = { + type = "integer", + start = 10, + finish = 11, + parent = "", + [1] = 2, }, - method = 4, - }, - [3] = { - type = "getglobal", - start = 1, - finish = 4, - parent = 2, - [1] = "func", - }, - [4] = { - type = "method", - start = 6, - finish = 6, - parent = 2, - [1] = "x", }, - [5] = { +} +CHECK'func:x(1, 2)' +{ + type = "call", + start = 0, + finish = 12, + node = "", + args = { type = "callargs", - start = 7, + start = 6, finish = 12, - parent = 1, - [1] = 6, - [2] = 7, - }, - [6] = { - type = "number", - start = 8, - finish = 8, - parent = 5, - [1] = 1, - }, - [7] = { - type = "number", - start = 11, - finish = 11, - parent = 5, - [1] = 2, + parent = "", + [1] = { + type = "self", + start = 4, + finish = 5, + parent = "", + [1] = "self", + }, + [2] = { + type = "integer", + start = 7, + finish = 8, + parent = "", + [1] = 1, + }, + [3] = { + type = "integer", + start = 10, + finish = 11, + parent = "", + [1] = 2, + }, }, } CHECK'("%s"):format(1)' { - [1] = { - type = "call", - start = 1, + type = "call", + start = 0, + finish = 16, + node = "", + args = { + type = "callargs", + start = 13, finish = 16, - node = 2, - args = 6, - }, - [2] = { - type = "getmethod", - start = 1, - finish = 13, - parent = 1, - node = 3, - colon = { - type = ":", - start = 7, + parent = "", + [1] = { + type = "self", + start = 6, finish = 7, + parent = "", + [1] = "self", + }, + [2] = { + type = "integer", + start = 14, + finish = 15, + parent = "", + [1] = 1, }, - method = 5, - }, - [3] = { - type = "paren", - start = 1, - finish = 6, - parent = 2, - exp = 4, - }, - [4] = { - type = "string", - start = 2, - finish = 5, - parent = 3, - [1] = "%s", - [2] = "\"", - }, - [5] = { - type = "method", - start = 8, - finish = 13, - parent = 2, - [1] = "format", - }, - [6] = { - type = "callargs", - start = 14, - finish = 16, - parent = 1, - [1] = 7, - }, - [7] = { - type = "number", - start = 15, - finish = 15, - parent = 6, - [1] = 1, }, } CHECK'do end' { - [1] = { - type = "do", - start = 1, - finish = 6, - }, + type = "do", + start = 0, + bstart = 2, + finish = 6, + keyword = { + [1] = 0, + [2] = 2, + [3] = 3, + [4] = 6, + }, + bfinish = 3, } CHECK'do x = 1 end' { - [1] = { - type = "do", - start = 1, - finish = 12, - [1] = 2, - }, - [2] = { + type = "do", + start = 0, + bstart = 2, + finish = 12, + keyword = { + [1] = 0, + [2] = 2, + [3] = 9, + [4] = 12, + }, + bfinish = 9, + [1] = { type = "setglobal", - start = 4, + start = 3, finish = 4, - parent = 1, - value = 3, + range = 8, + parent = "", + value = { + type = "integer", + start = 7, + finish = 8, + parent = "", + [1] = 1, + }, [1] = "x", }, - [3] = { - type = "number", - start = 8, - finish = 8, - parent = 2, - [1] = 1, - }, } CHECK'return' { - [1] = { - type = "return", - start = 1, - finish = 6, - }, + type = "return", + start = 0, + finish = 6, } CHECK'return 1' { - [1] = { - type = "return", - start = 1, - finish = 8, - [1] = 2, - }, - [2] = { - type = "number", - start = 8, + type = "return", + start = 0, + finish = 8, + [1] = { + type = "integer", + start = 7, finish = 8, - parent = 1, + parent = "", [1] = 1, }, } CHECK'return 1, 2' { - [1] = { - type = "return", - start = 1, - finish = 11, - [1] = 2, - [2] = 3, - }, - [2] = { - type = "number", - start = 8, + type = "return", + start = 0, + finish = 11, + [1] = { + type = "integer", + start = 7, finish = 8, - parent = 1, + parent = "", [1] = 1, }, - [3] = { - type = "number", - start = 11, + [2] = { + type = "integer", + start = 10, finish = 11, - parent = 1, + parent = "", [1] = 2, }, } CHECK'::CONTINUE::' { - [1] = { - type = "label", - start = 3, - finish = 10, - [1] = "CONTINUE", - }, + type = "label", + start = 2, + finish = 10, + [1] = "CONTINUE", } CHECK'goto CONTINUE' { - [1] = { - type = 'goto', - start = 6, - finish = 13, - [1] = 'CONTINUE', - } + type = "goto", + start = 5, + finish = 13, + keyStart = 0, + [1] = "CONTINUE", } CHECK[[if 1 then end]] { - [1] = { - type = "if", - start = 1, - finish = 13, - [1] = 2, - }, - [2] = { - type = "ifblock", - start = 1, - finish = 9, - parent = 1, - filter = 3, - }, - [3] = { - type = "number", - start = 4, - finish = 4, - parent = 2, - [1] = 1, + type = "if", + start = 0, + finish = 10003, + [1] = { + type = "ifblock", + start = 0, + bstart = 9, + finish = 10000, + keyword = { + [1] = 0, + [2] = 2, + [3] = 5, + [4] = 9, + }, + parent = "", + filter = { + type = "integer", + start = 3, + finish = 4, + parent = "", + [1] = 1, + }, + bfinish = 10000, }, } CHECK[[if 1 then return end]] { - [1] = { - type = "if", - start = 1, - finish = 24, - [1] = 2, - }, - [2] = { - type = "ifblock", - start = 1, - finish = 21, - parent = 1, - filter = 3, - [1] = 4, - }, - [3] = { - type = "number", - start = 4, - finish = 4, - parent = 2, - [1] = 1, - }, - [4] = { - type = "return", - start = 15, - finish = 21, - parent = 2, + type = "if", + start = 0, + finish = 20003, + [1] = { + type = "ifblock", + start = 0, + bstart = 9, + finish = 20000, + keyword = { + [1] = 0, + [2] = 2, + [3] = 5, + [4] = 9, + }, + parent = "", + filter = { + type = "integer", + start = 3, + finish = 4, + parent = "", + [1] = 1, + }, + bfinish = 20000, + hasReturn = true, + [1] = { + type = "return", + start = 10004, + finish = 10010, + parent = "", + }, }, } CHECK[[if 1 then @@ -504,46 +512,55 @@ else return end]] { - [1] = { - type = "if", - start = 1, - finish = 40, - [1] = 2, - [2] = 5, - }, - [2] = { - type = "ifblock", - start = 1, - finish = 21, - parent = 1, - filter = 3, - [1] = 4, - }, - [3] = { - type = "number", - start = 4, - finish = 4, - parent = 2, - [1] = 1, - }, - [4] = { - type = "return", - start = 15, - finish = 21, - parent = 2, - }, - [5] = { - type = "elseblock", - start = 22, - finish = 37, - parent = 1, - [1] = 6, + type = "if", + start = 0, + finish = 40003, + [1] = { + type = "ifblock", + start = 0, + bstart = 9, + finish = 20000, + keyword = { + [1] = 0, + [2] = 2, + [3] = 5, + [4] = 9, + }, + parent = "", + filter = { + type = "integer", + start = 3, + finish = 4, + parent = "", + [1] = 1, + }, + bfinish = 20000, + hasReturn = true, + [1] = { + type = "return", + start = 10004, + finish = 10010, + parent = "", + }, }, - [6] = { - type = "return", - start = 31, - finish = 37, - parent = 5, + [2] = { + type = "elseblock", + start = 20000, + bstart = 20004, + finish = 40000, + keyword = { + [1] = 20000, + [2] = 20004, + }, + parent = "", + bfinish = 40000, + hasReturn = true, + [1] = { + type = "return", + start = 30004, + finish = 30010, + parent = "", + }, }, } CHECK[[if 1 then @@ -552,54 +569,64 @@ elseif 1 then return end]] { - [1] = { - type = "if", - start = 1, - finish = 49, - [1] = 2, - [2] = 5, - }, - [2] = { - type = "ifblock", - start = 1, - finish = 21, - parent = 1, - filter = 3, - [1] = 4, - }, - [3] = { - type = "number", - start = 4, - finish = 4, - parent = 2, - [1] = 1, - }, - [4] = { - type = "return", - start = 15, - finish = 21, - parent = 2, - }, - [5] = { - type = "elseifblock", - start = 22, - finish = 46, - parent = 1, - filter = 6, - [1] = 7, - }, - [6] = { - type = "number", - start = 29, - finish = 29, - parent = 5, - [1] = 1, + type = "if", + start = 0, + finish = 40003, + [1] = { + type = "ifblock", + start = 0, + bstart = 9, + finish = 20000, + keyword = { + [1] = 0, + [2] = 2, + [3] = 5, + [4] = 9, + }, + parent = "", + filter = { + type = "integer", + start = 3, + finish = 4, + parent = "", + [1] = 1, + }, + bfinish = 20000, + hasReturn = true, + [1] = { + type = "return", + start = 10004, + finish = 10010, + parent = "", + }, }, - [7] = { - type = "return", - start = 40, - finish = 46, - parent = 5, + [2] = { + type = "elseifblock", + start = 20000, + bstart = 20013, + finish = 40000, + keyword = { + [1] = 20000, + [2] = 20006, + [3] = 20009, + [4] = 20013, + }, + parent = "", + filter = { + type = "integer", + start = 20007, + finish = 20008, + parent = "", + [1] = 1, + }, + bfinish = 40000, + hasReturn = true, + [1] = { + type = "return", + start = 30004, + finish = 30010, + parent = "", + }, }, } CHECK[[if 1 then @@ -610,68 +637,83 @@ else return end]] { - [1] = { - type = "if", - start = 1, - finish = 65, - [1] = 2, - [2] = 5, - [3] = 8, - }, - [2] = { - type = "ifblock", - start = 1, - finish = 21, - parent = 1, - filter = 3, - [1] = 4, - }, - [3] = { - type = "number", - start = 4, - finish = 4, - parent = 2, - [1] = 1, + type = "if", + start = 0, + finish = 60003, + [1] = { + type = "ifblock", + start = 0, + bstart = 9, + finish = 20000, + keyword = { + [1] = 0, + [2] = 2, + [3] = 5, + [4] = 9, + }, + parent = "", + filter = { + type = "integer", + start = 3, + finish = 4, + parent = "", + [1] = 1, + }, + bfinish = 20000, + hasReturn = true, + [1] = { + type = "return", + start = 10004, + finish = 10010, + parent = "", + }, }, - [4] = { - type = "return", - start = 15, - finish = 21, - parent = 2, - }, - [5] = { - type = "elseifblock", - start = 22, - finish = 46, - parent = 1, - filter = 6, - [1] = 7, - }, - [6] = { - type = "number", - start = 29, - finish = 29, - parent = 5, - [1] = 1, + [2] = { + type = "elseifblock", + start = 20000, + bstart = 20013, + finish = 40000, + keyword = { + [1] = 20000, + [2] = 20006, + [3] = 20009, + [4] = 20013, + }, + parent = "", + filter = { + type = "integer", + start = 20007, + finish = 20008, + parent = "", + [1] = 1, + }, + bfinish = 40000, + hasReturn = true, + [1] = { + type = "return", + start = 30004, + finish = 30010, + parent = "", + }, }, - [7] = { - type = "return", - start = 40, - finish = 46, - parent = 5, - }, - [8] = { - type = "elseblock", - start = 47, - finish = 62, - parent = 1, - [1] = 9, - }, - [9] = { - type = "return", - start = 56, - finish = 62, - parent = 8, + [3] = { + type = "elseblock", + start = 40000, + bstart = 40004, + finish = 60000, + keyword = { + [1] = 40000, + [2] = 40004, + }, + parent = "", + bfinish = 60000, + hasReturn = true, + [1] = { + type = "return", + start = 50004, + finish = 50010, + parent = "", + }, }, } CHECK[[ @@ -681,70 +723,92 @@ elseif 1 then elseif 1 then end]] { - [1] = { - type = "if", - start = 1, - finish = 55, - [1] = 2, - [2] = 4, - [3] = 6, - [4] = 8, - }, - [2] = { - type = "ifblock", - start = 1, - finish = 9, - parent = 1, - filter = 3, - }, - [3] = { - type = "number", - start = 4, - finish = 4, - parent = 2, - [1] = 1, - }, - [4] = { - type = "elseifblock", - start = 11, - finish = 23, - parent = 1, - filter = 5, - }, - [5] = { - type = "number", - start = 18, - finish = 18, - parent = 4, - [1] = 1, - }, - [6] = { - type = "elseifblock", - start = 25, - finish = 37, - parent = 1, - filter = 7, - }, - [7] = { - type = "number", - start = 32, - finish = 32, - parent = 6, - [1] = 1, - }, - [8] = { - type = "elseifblock", - start = 39, - finish = 51, - parent = 1, - filter = 9, - }, - [9] = { - type = "number", - start = 46, - finish = 46, - parent = 8, - [1] = 1, + type = "if", + start = 0, + finish = 40003, + [1] = { + type = "ifblock", + start = 0, + bstart = 9, + finish = 10000, + keyword = { + [1] = 0, + [2] = 2, + [3] = 5, + [4] = 9, + }, + parent = "", + filter = { + type = "integer", + start = 3, + finish = 4, + parent = "", + [1] = 1, + }, + bfinish = 10000, + }, + [2] = { + type = "elseifblock", + start = 10000, + bstart = 10013, + finish = 20000, + keyword = { + [1] = 10000, + [2] = 10006, + [3] = 10009, + [4] = 10013, + }, + parent = "", + filter = { + type = "integer", + start = 10007, + finish = 10008, + parent = "", + [1] = 1, + }, + bfinish = 20000, + }, + [3] = { + type = "elseifblock", + start = 20000, + bstart = 20013, + finish = 30000, + keyword = { + [1] = 20000, + [2] = 20006, + [3] = 20009, + [4] = 20013, + }, + parent = "", + filter = { + type = "integer", + start = 20007, + finish = 20008, + parent = "", + [1] = 1, + }, + bfinish = 30000, + }, + [4] = { + type = "elseifblock", + start = 30000, + bstart = 30013, + finish = 40000, + keyword = { + [1] = 30000, + [2] = 30006, + [3] = 30009, + [4] = 30013, + }, + parent = "", + filter = { + type = "integer", + start = 30007, + finish = 30008, + parent = "", + [1] = 1, + }, + bfinish = 40000, }, } CHECK[[ @@ -753,47 +817,56 @@ if 1 then end end]] { - [1] = { - type = "if", - start = 1, - finish = 35, - [1] = 2, - }, - [2] = { - type = "ifblock", - start = 1, - finish = 31, - parent = 1, - filter = 3, - [1] = 4, - }, - [3] = { - type = "number", - start = 4, - finish = 4, - parent = 2, - [1] = 1, - }, - [4] = { - type = "if", - start = 15, - finish = 31, - parent = 2, - [1] = 5, - }, - [5] = { - type = "ifblock", - start = 15, - finish = 23, - parent = 4, - filter = 6, - }, - [6] = { - type = "number", - start = 18, - finish = 18, - parent = 5, - [1] = 2, + type = "if", + start = 0, + finish = 30003, + [1] = { + type = "ifblock", + start = 0, + bstart = 9, + finish = 30000, + keyword = { + [1] = 0, + [2] = 2, + [3] = 5, + [4] = 9, + }, + parent = "", + filter = { + type = "integer", + start = 3, + finish = 4, + parent = "", + [1] = 1, + }, + bfinish = 30000, + [1] = { + type = "if", + start = 10004, + finish = 20007, + parent = "", + [1] = { + type = "ifblock", + start = 10004, + bstart = 10013, + finish = 20004, + keyword = { + [1] = 10004, + [2] = 10006, + [3] = 10009, + [4] = 10013, + }, + parent = "", + filter = { + type = "integer", + start = 10007, + finish = 10008, + parent = "", + [1] = 2, + }, + bfinish = 20004, + }, + }, }, } CHECK[[ @@ -802,47 +875,62 @@ elseif 1 then else end]] { - [1] = { - type = "if", - start = 1, - finish = 32, - [1] = 2, - [2] = 4, - [3] = 6, - }, - [2] = { - type = "ifblock", - start = 1, - finish = 9, - parent = 1, - filter = 3, - }, - [3] = { - type = "number", - start = 4, - finish = 4, - parent = 2, - [1] = 1, - }, - [4] = { - type = "elseifblock", - start = 11, - finish = 23, - parent = 1, - filter = 5, - }, - [5] = { - type = "number", - start = 18, - finish = 18, - parent = 4, - [1] = 1, - }, - [6] = { - type = "elseblock", - start = 25, - finish = 28, - parent = 1, + type = "if", + start = 0, + finish = 30003, + [1] = { + type = "ifblock", + start = 0, + bstart = 9, + finish = 10000, + keyword = { + [1] = 0, + [2] = 2, + [3] = 5, + [4] = 9, + }, + parent = "", + filter = { + type = "integer", + start = 3, + finish = 4, + parent = "", + [1] = 1, + }, + bfinish = 10000, + }, + [2] = { + type = "elseifblock", + start = 10000, + bstart = 10013, + finish = 20000, + keyword = { + [1] = 10000, + [2] = 10006, + [3] = 10009, + [4] = 10013, + }, + parent = "", + filter = { + type = "integer", + start = 10007, + finish = 10008, + parent = "", + [1] = 1, + }, + bfinish = 20000, + }, + [3] = { + type = "elseblock", + start = 20000, + bstart = 20004, + finish = 30000, + keyword = { + [1] = 20000, + [2] = 20004, + }, + parent = "", + bfinish = 30000, }, } CHECK[[ @@ -850,45 +938,47 @@ for i = 1, i do return end]] { - [1] = { - locals = { - [1] = 2, - }, - type = "loop", - start = 1, - finish = 30, - loc = 2, - max = 4, - [1] = 5, - }, - [2] = { + type = "loop", + start = 0, + bstart = 15, + finish = 20003, + keyword = { + [1] = 0, + [2] = 3, + [3] = 13, + [4] = 15, + [5] = 20000, + [6] = 20003, + }, + loc = { type = "local", - start = 5, + start = 4, finish = 5, - effect = 16, - parent = 1, - value = 3, + effect = 12, + parent = "", [1] = "i", }, - [3] = { - type = "number", - start = 9, + init = { + type = "integer", + start = 8, finish = 9, - parent = 2, + parent = "", [1] = 1, }, - [4] = { + max = { type = "getglobal", - start = 12, + start = 11, finish = 12, - parent = 1, + parent = "", [1] = "i", }, - [5] = { + locals = "", + bfinish = 20000, + [1] = { type = "return", - start = 21, - finish = 27, - parent = 1, + start = 10004, + finish = 10010, + parent = "", }, } CHECK[[ @@ -896,53 +986,54 @@ for i = 1, 10, i do return end]] { - [1] = { - locals = { - [1] = 2, - }, - type = "loop", - start = 1, - finish = 34, - loc = 2, - max = 4, - step = 5, - [1] = 6, - }, - [2] = { + type = "loop", + start = 0, + bstart = 19, + finish = 20003, + keyword = { + [1] = 0, + [2] = 3, + [3] = 17, + [4] = 19, + [5] = 20000, + [6] = 20003, + }, + loc = { type = "local", - start = 5, + start = 4, finish = 5, - effect = 20, - parent = 1, - value = 3, + effect = 16, + parent = "", [1] = "i", }, - [3] = { - type = "number", - start = 9, + init = { + type = "integer", + start = 8, finish = 9, - parent = 2, + parent = "", [1] = 1, }, - [4] = { - type = "number", - start = 12, + max = { + type = "integer", + start = 11, finish = 13, - parent = 1, + parent = "", [1] = 10, }, - [5] = { + step = { type = "getglobal", - start = 16, + start = 15, finish = 16, - parent = 1, + parent = "", [1] = "i", }, - [6] = { + locals = "", + bfinish = 20000, + [1] = { type = "return", - start = 25, - finish = 31, - parent = 1, + start = 10004, + finish = 10010, + parent = "", }, } CHECK[[ @@ -950,59 +1041,55 @@ for a in a do return end]] { - [1] = { - locals = { - [1] = 2, - }, - type = "in", - start = 1, - finish = 28, - keys = { - [1] = 2, - }, - [1] = 7, - }, - [2] = { - type = "local", - start = 5, + type = "in", + start = 0, + bstart = 13, + finish = 20003, + keyword = { + [1] = 0, + [2] = 3, + [3] = 6, + [4] = 8, + [5] = 11, + [6] = 13, + [7] = 20000, + [8] = 20003, + }, + keys = { + type = "list", + start = 4, finish = 5, - effect = 14, - parent = 1, - value = 3, - [1] = "a", - }, - [3] = { - type = "select", - parent = 2, - vararg = 4, - index = 1, - }, - [4] = { - type = "call", - start = 11, - finish = 10, - parent = 3, - node = 5, - args = 6, - }, - [5] = { - type = "getglobal", - start = 10, - finish = 10, - parent = 4, - [1] = "a", + range = 8, + parent = "", + [1] = { + type = "local", + start = 4, + finish = 5, + effect = 10, + parent = "", + [1] = "a", + }, }, - [6] = { - type = "callargs", - start = 11, + exps = { + type = "list", + start = 9, finish = 10, - parent = 4, + parent = "", + [1] = { + type = "getglobal", + start = 9, + finish = 10, + parent = "", + [1] = "a", + }, }, - [7] = { + locals = "", + bfinish = 20000, + [1] = { type = "return", - start = 19, - finish = 25, - parent = 1, + start = 10004, + finish = 10010, + parent = "", }, } CHECK[[ @@ -1010,113 +1097,85 @@ for a, b, c in a, b, c do return end]] { - [01] = { - type = "in", - start = 1, - finish = 40, - keys = { - [1] = 2, - [2] = 9, - [3] = 11, + type = "in", + start = 0, + bstart = 25, + finish = 20003, + keyword = { + [1] = 0, + [2] = 3, + [3] = 12, + [4] = 14, + [5] = 23, + [6] = 25, + [7] = 20000, + [8] = 20003, + }, + keys = { + type = "list", + start = 4, + finish = 11, + range = 14, + parent = "", + [1] = { + type = "local", + start = 4, + finish = 5, + effect = 22, + parent = "", + [1] = "a", }, - locals = { - [1] = 2, - [2] = 9, - [3] = 11, + [2] = { + type = "local", + start = 7, + finish = 8, + effect = 22, + parent = "", + [1] = "b", }, - [1] = 13, - }, - [02] = { - type = "local", - start = 5, - finish = 5, - effect = 26, - parent = 1, - value = 3, - [1] = "a", - }, - [03] = { - type = "select", - parent = 2, - vararg = 4, - index = 1, - }, - [04] = { - type = "call", - start = 17, - finish = 22, - parent = 3, - extParent = { - [1] = 10, - [2] = 12, + [3] = { + type = "local", + start = 10, + finish = 11, + effect = 22, + parent = "", + [1] = "c", }, - node = 5, - args = 6, - }, - [05] = { - type = "getglobal", - start = 16, - finish = 16, - parent = 4, - [1] = "a", - }, - [06] = { - type = "callargs", - start = 17, - finish = 22, - parent = 4, - [1] = 7, - [2] = 8, - }, - [07] = { - type = "getglobal", - start = 19, - finish = 19, - parent = 6, - [1] = "b", }, - [08] = { - type = "getglobal", - start = 22, + exps = { + type = "list", + start = 15, finish = 22, - parent = 6, - [1] = "c", - }, - [09] = { - type = "local", - start = 8, - finish = 8, - effect = 26, - parent = 1, - value = 10, - [1] = "b", - }, - [10] = { - type = "select", - parent = 9, - vararg = 4, - index = 2, - }, - [11] = { - type = "local", - start = 11, - finish = 11, - effect = 26, - parent = 1, - value = 12, - [1] = "c", - }, - [12] = { - type = "select", - parent = 11, - vararg = 4, - index = 3, + parent = "", + [1] = { + type = "getglobal", + start = 15, + finish = 16, + parent = "", + [1] = "a", + }, + [2] = { + type = "getglobal", + start = 18, + finish = 19, + parent = "", + [1] = "b", + }, + [3] = { + type = "getglobal", + start = 21, + finish = 22, + parent = "", + [1] = "c", + }, }, - [13] = { + locals = "", + bfinish = 20000, + [1] = { type = "return", - start = 31, - finish = 37, - parent = 1, + start = 10004, + finish = 10010, + parent = "", }, } CHECK[[ @@ -1124,25 +1183,31 @@ while true do return end]] { - [1] = { - type = "while", - start = 1, - finish = 28, - filter = 2, - [1] = 3, - }, - [2] = { + type = "while", + start = 0, + bstart = 13, + finish = 20003, + keyword = { + [1] = 0, + [2] = 5, + [3] = 11, + [4] = 13, + [5] = 20000, + [6] = 20003, + }, + filter = { type = "boolean", - start = 7, + start = 6, finish = 10, - parent = 1, + parent = "", [1] = true, }, - [3] = { + bfinish = 20000, + [1] = { type = "return", - start = 19, - finish = 25, - parent = 1, + start = 10004, + finish = 10010, + parent = "", }, } CHECK[[ @@ -1150,25 +1215,37 @@ repeat break until 1]] { - [1] = { - type = "repeat", - start = 1, - finish = 25, - filter = 3, - [1] = 2, + type = "repeat", + start = 0, + bstart = 6, + finish = 20007, + keyword = { + [1] = 0, + [2] = 6, + [3] = 20000, + [4] = 20005, + }, + filter = { + type = "integer", + start = 20006, + finish = 20007, + parent = "", + [1] = 1, }, - [2] = { - type = "break", - start = 12, - finish = 16, - parent = 1, + breaks = { + [1] = { + type = "break", + start = 10004, + finish = 10009, + parent = "", + }, }, - [3] = { - type = "number", - start = 24, - finish = 24, - parent = 1, - [1] = 1, + bfinish = 20000, + [1] = { + type = "break", + start = 10004, + finish = 10009, + parent = "", }, } CHECK[[ @@ -1176,172 +1253,351 @@ function test() return end]] { - [1] = { - type = "setglobal", - start = 10, - finish = 13, - value = 2, - [1] = "test", - }, - [2] = { - type = "function", - start = 1, - finish = 30, - parent = 1, - [1] = 3, - }, - [3] = { - type = "return", - start = 21, - finish = 27, - parent = 2, + type = "setglobal", + start = 9, + vstart = 0, + finish = 13, + range = 20003, + value = { + type = "function", + start = 0, + bstart = 15, + finish = 20003, + keyword = { + [1] = 0, + [2] = 8, + [3] = 20000, + [4] = 20003, + }, + parent = "", + args = { + type = "funcargs", + start = 13, + finish = 15, + parent = "", + }, + returns = "", + bfinish = 20000, + hasReturn = true, + [1] = { + type = "return", + start = 10004, + finish = 10010, + parent = "", + }, }, + [1] = "test", } CHECK[[ function test(a) return end]] { - [1] = { - type = "setglobal", - start = 10, - finish = 13, - value = 2, - [1] = "test", - }, - [2] = { - type = "function", - start = 1, - finish = 31, - parent = 1, - args = 3, - locals = { - [1] = 4, + type = "setglobal", + start = 9, + vstart = 0, + finish = 13, + range = 20003, + value = { + type = "function", + start = 0, + bstart = 16, + finish = 20003, + keyword = { + [1] = 0, + [2] = 8, + [3] = 20000, + [4] = 20003, + }, + parent = "", + args = { + type = "funcargs", + start = 13, + finish = 16, + parent = "", + [1] = { + type = "local", + start = 14, + finish = 15, + effect = 15, + parent = "", + [1] = "a", + }, + }, + locals = "", + returns = "", + bfinish = 20000, + hasReturn = true, + [1] = { + type = "return", + start = 10004, + finish = 10010, + parent = "", }, - [1] = 5, - }, - [3] = { - type = "funcargs", - start = 14, - finish = 16, - parent = 2, - [1] = 4, - }, - [4] = { - type = "local", - start = 15, - finish = 15, - effect = 15, - parent = 3, - [1] = "a", - }, - [5] = { - type = "return", - start = 22, - finish = 28, - parent = 2, }, + [1] = "test", } CHECK[[ function a.b:c(a, b, c) return end]] { - [01] = { - type = "setmethod", - start = 10, + type = "setmethod", + start = 9, + vstart = 0, + finish = 14, + range = 20003, + node = "", + colon = { + type = ":", + start = 12, + finish = 13, + }, + method = { + type = "method", + start = 13, finish = 14, - node = 2, - colon = { - type = ":", - start = 13, - finish = 13, - }, - method = 4, - value = 5, + parent = "", + [1] = "c", }, - [02] = { - type = "getfield", - start = 10, - finish = 12, - parent = 1, - node = 3, - dot = { - type = ".", - start = 11, - finish = 11, + value = { + type = "function", + start = 0, + bstart = 23, + finish = 20003, + keyword = { + [1] = 0, + [2] = 8, + [3] = 20000, + [4] = 20003, }, - field = { - type = "field", - start = 12, - finish = 12, - [1] = "b", + parent = "", + args = { + type = "funcargs", + start = 14, + finish = 23, + parent = "", + [1] = { + type = "self", + start = 8, + finish = 8, + effect = 8, + parent = "", + [1] = "self", + }, + [2] = { + type = "local", + start = 15, + finish = 16, + effect = 16, + parent = "", + [1] = "a", + }, + [3] = { + type = "local", + start = 18, + finish = 19, + effect = 19, + parent = "", + [1] = "b", + }, + [4] = { + type = "local", + start = 21, + finish = 22, + effect = 22, + parent = "", + [1] = "c", + }, + }, + locals = "", + returns = "", + bfinish = 20000, + hasReturn = true, + [1] = { + type = "return", + start = 10004, + finish = 10010, + parent = "", }, }, - [03] = { - type = "getglobal", +} +CHECK[[ +function m:f() + return self +end]] +{ + type = "setmethod", + start = 9, + vstart = 0, + finish = 12, + range = 20003, + node = "", + colon = { + type = ":", start = 10, - finish = 10, - parent = 2, - [1] = "a", + finish = 11, }, - [04] = { + method = { type = "method", - start = 14, - finish = 14, - parent = 1, - [1] = "c", - }, - [05] = { - type = "function", - start = 1, - finish = 38, - parent = 1, - args = 6, - locals = { - [1] = 7, + start = 11, + finish = 12, + parent = "", + [1] = "f", + }, + value = { + type = "function", + start = 0, + bstart = 14, + finish = 20003, + keyword = { + [1] = 0, [2] = 8, - [3] = 9, + [3] = 20000, + [4] = 20003, + }, + parent = "", + args = { + type = "funcargs", + start = 12, + finish = 14, + parent = "", + [1] = { + type = "self", + start = 8, + finish = 8, + effect = 8, + parent = "", + ref = "", + [1] = "self", + }, + }, + locals = "", + returns = "", + bfinish = 20000, + hasReturn = true, + [1] = { + type = "return", + start = 10004, + finish = 10015, + parent = "", + [1] = { + type = "getlocal", + start = 10011, + finish = 10015, + parent = "", + node = "", + [1] = "self", + }, }, - [1] = 10, - }, - [06] = { - type = "funcargs", - start = 15, - finish = 23, - parent = 5, - [1] = 7, - [2] = 8, - [3] = 9, - }, - [07] = { - type = "local", - start = 16, - finish = 16, - effect = 16, - parent = 6, - [1] = "a", }, - [08] = { - type = "local", - start = 19, - finish = 19, - effect = 19, - parent = 6, - [1] = "b", +} + +CHECK [[ +if true then + X = print +end +]] +{ + type = "if", + start = 0, + finish = 20003, + [1] = { + type = "ifblock", + start = 0, + bstart = 12, + finish = 20000, + keyword = { + [1] = 0, + [2] = 2, + [3] = 8, + [4] = 12, + }, + parent = "", + filter = { + type = "boolean", + start = 3, + finish = 7, + parent = "", + [1] = true, + }, + bfinish = 20000, + [1] = { + type = "setglobal", + start = 10004, + finish = 10005, + range = 10013, + parent = "", + value = { + type = "getglobal", + start = 10008, + finish = 10013, + parent = "", + [1] = "print", + }, + [1] = "X", + }, }, - [09] = { - type = "local", - start = 22, - finish = 22, - effect = 22, - parent = 6, - [1] = "c", +} + +CHECK [[ +f:read('a') +]] +{ + type = "call", + start = 0, + finish = 11, + node = "", + args = { + type = "callargs", + start = 6, + finish = 11, + parent = "", + [1] = { + type = "self", + start = 1, + finish = 2, + parent = "", + [1] = "self", + }, + [2] = { + type = "string", + start = 7, + finish = 10, + parent = "", + [1] = "a", + [2] = "'", + }, }, - [10] = { - type = "return", - start = 29, - finish = 35, - parent = 5, +} + +CHECK [[ +f:read 'a' +]] +{ + type = "call", + start = 0, + finish = 10, + node = "", + args = { + type = "callargs", + start = 7, + finish = 10, + parent = "", + [1] = { + type = "self", + start = 1, + finish = 2, + parent = "", + [1] = "self", + }, + [2] = { + type = "string", + start = 7, + finish = 10, + parent = "", + [1] = "a", + [2] = "'", + }, }, } diff --git a/test/ast/Boolean.lua b/test/ast/Boolean.lua index 827af65..d3b292e 100644 --- a/test/ast/Boolean.lua +++ b/test/ast/Boolean.lua @@ -1,18 +1,14 @@ CHECK [[true]] { - [1] = { - type = 'boolean', - start = 1, - finish = 4, - [1] = true, - } + type = "boolean", + start = 0, + finish = 4, + [1] = true, } CHECK [[false]] { - [1] = { - type = 'boolean', - start = 1, - finish = 5, - [1] = false, - } + type = "boolean", + start = 0, + finish = 5, + [1] = false, } diff --git a/test/ast/Comment.lua b/test/ast/Comment.lua new file mode 100644 index 0000000..81c1c92 --- /dev/null +++ b/test/ast/Comment.lua @@ -0,0 +1,43 @@ +Comment [[--AAA]] +{ + [1] = { + type = "comment.short", + start = 0, + finish = 5, + text = "AAA", + }, +} + +Comment [[ +--AAA +]] +{ + [1] = { + type = "comment.short", + start = 0, + finish = 5, + text = "AAA", + }, +} + +Comment [[//AAA]] +{ + [1] = { + type = "comment.cshort", + start = 0, + finish = 5, + text = "AAA", + }, +} + +Comment [[ +//AAA +]] +{ + [1] = { + type = "comment.cshort", + start = 0, + finish = 5, + text = "AAA", + }, +} diff --git a/test/ast/Dirty.lua b/test/ast/Dirty.lua index 32b490b..3745c16 100644 --- a/test/ast/Dirty.lua +++ b/test/ast/Dirty.lua @@ -1,55 +1,45 @@ CHECK'a.' { - [1] = { - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 2, + locals = "", + bfinish = 2, + [1] = { type = "getfield", - start = 1, + start = 0, finish = 2, - parent = 1, - node = 3, + parent = "", + node = "", dot = { type = ".", - start = 2, + start = 1, finish = 2, }, }, - [3] = { - type = "getglobal", - start = 1, - finish = 1, - parent = 2, - [1] = "a", - }, } CHECK'a:' { - [1] = { - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 2, + locals = "", + bfinish = 2, + [1] = { type = "getmethod", - start = 1, + start = 0, finish = 2, - parent = 1, - node = 3, + parent = "", + node = "", colon = { type = ":", - start = 2, + start = 1, finish = 2, }, }, - [3] = { - type = "getglobal", - start = 1, - finish = 1, - parent = 2, - [1] = "a", - }, } CHECK [[ @@ -57,38 +47,44 @@ if true a ]] { - [1] = { - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 20000, + locals = "", + bfinish = 20000, + [1] = { type = "if", - start = 1, - finish = 9, - parent = 1, - [1] = 3, - }, - [3] = { - type = "ifblock", - start = 1, - finish = 9, - parent = 2, - filter = 4, - [1] = 5, - }, - [4] = { - type = "boolean", - start = 4, - finish = 7, - parent = 3, - [1] = true, - }, - [5] = { - type = "getglobal", - start = 9, - finish = 9, - parent = 3, - [1] = "a", + start = 0, + finish = 20000, + parent = "", + [1] = { + type = "ifblock", + start = 0, + bstart = 7, + finish = 20000, + keyword = { + [1] = 0, + [2] = 2, + }, + parent = "", + filter = { + type = "boolean", + start = 3, + finish = 7, + parent = "", + [1] = true, + }, + bfinish = 20000, + [1] = { + type = "getglobal", + start = 10000, + finish = 10001, + parent = "", + node = "", + [1] = "a", + }, + }, }, } @@ -97,38 +93,46 @@ if true then a ]] { - [1] = { - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 20000, + locals = "", + bfinish = 20000, + [1] = { type = "if", - start = 1, - finish = 14, - parent = 1, - [1] = 3, - }, - [3] = { - type = "ifblock", - start = 1, - finish = 14, - parent = 2, - filter = 4, - [1] = 5, - }, - [4] = { - type = "boolean", - start = 4, - finish = 7, - parent = 3, - [1] = true, - }, - [5] = { - type = "getglobal", - start = 14, - finish = 14, - parent = 3, - [1] = "a", + start = 0, + finish = 20000, + parent = "", + [1] = { + type = "ifblock", + start = 0, + bstart = 12, + finish = 20000, + keyword = { + [1] = 0, + [2] = 2, + [3] = 8, + [4] = 12, + }, + parent = "", + filter = { + type = "boolean", + start = 3, + finish = 7, + parent = "", + [1] = true, + }, + bfinish = 20000, + [1] = { + type = "getglobal", + start = 10000, + finish = 10001, + parent = "", + node = "", + [1] = "a", + }, + }, }, } @@ -136,297 +140,1317 @@ CHECK [[ x = ]] { - [1] = { - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 10000, + locals = "", + bfinish = 10000, + [1] = { type = "setglobal", - start = 1, + start = 0, finish = 1, - parent = 1, + parent = "", + node = "", [1] = "x", }, } CHECK'1 == 2' { - [1] = { - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 6, + locals = "", + bfinish = 6, + [1] = { type = "binary", - start = 1, + start = 0, finish = 6, - parent = 1, + parent = "", op = { type = "==", - start = 3, + start = 2, finish = 4, }, - [1] = 3, - [2] = 4, - }, - [3] = { - type = "number", - start = 1, - finish = 1, - parent = 2, - [1] = 1, - }, - [4] = { - type = "number", - start = 6, - finish = 6, - parent = 2, - [1] = 2, + [1] = { + type = "integer", + start = 0, + finish = 1, + parent = "", + [1] = 1, + }, + [2] = { + type = "integer", + start = 5, + finish = 6, + parent = "", + [1] = 2, + }, }, } CHECK 'local function a' { - [1] = { - locals = { - [1] = 2, - }, - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 16, + locals = "", + bfinish = 16, + [1] = { type = "local", - start = 16, + start = 15, + vstart = 6, finish = 16, - effect = 1, - parent = 1, - value = 3, + effect = 16, + range = 16, + parent = "", + locPos = 0, + value = { + type = "function", + start = 6, + bstart = 16, + finish = 16, + keyword = { + [1] = 6, + [2] = 14, + }, + parent = "", + bfinish = 16, + }, [1] = "a", }, - [3] = { - type = "function", - start = 1, - finish = 16, - parent = 2, - }, } CHECK 'local function' { - [1] = { - type = "main", + type = "main", + start = 0, + bstart = 0, + finish = 14, + locals = "", + bfinish = 14, + [1] = { + type = "function", + start = 6, + bstart = 14, + finish = 14, + keyword = { + [1] = 6, + [2] = 14, + }, + parent = "", + bfinish = 14, }, } -CHECK 'local function a(v' +CHECK 'local function f(' { - [1] = { - locals = { - [1] = 2, - }, - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 17, + locals = "", + bfinish = 17, + [1] = { type = "local", - start = 16, + start = 15, + vstart = 6, finish = 16, - effect = 1, - parent = 1, - value = 3, - [1] = "a", - }, - [3] = { - locals = { - [1] = 5, + effect = 16, + range = 17, + parent = "", + locPos = 0, + value = { + type = "function", + start = 6, + bstart = 17, + finish = 17, + keyword = { + [1] = 6, + [2] = 14, + }, + parent = "", + args = { + type = "funcargs", + start = 16, + finish = 17, + parent = "", + }, + bfinish = 17, }, - type = "function", - start = 1, - finish = 18, - parent = 2, - args = 4, + [1] = "f", }, - [4] = { - type = "funcargs", - start = 17, - finish = 18, - parent = 3, - [1] = 5, - }, - [5] = { +} + +CHECK 'local function a(v' +{ + type = "main", + start = 0, + bstart = 0, + finish = 18, + locals = "", + bfinish = 18, + [1] = { type = "local", - start = 18, - finish = 18, - effect = 18, - parent = 4, - [1] = "v", + start = 15, + vstart = 6, + finish = 16, + effect = 16, + range = 18, + parent = "", + locPos = 0, + value = { + type = "function", + start = 6, + bstart = 18, + finish = 18, + keyword = { + [1] = 6, + [2] = 14, + }, + parent = "", + args = { + type = "funcargs", + start = 16, + finish = 18, + parent = "", + [1] = { + type = "local", + start = 17, + finish = 18, + effect = 18, + parent = "", + [1] = "v", + }, + }, + locals = "", + bfinish = 18, + }, + [1] = "a", }, } CHECK 'function a' { - [1] = { - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 10, + locals = "", + bfinish = 10, + [1] = { type = "setglobal", - start = 10, + start = 9, + vstart = 0, finish = 10, - parent = 1, - value = 3, + range = 10, + parent = "", + node = "", + value = { + type = "function", + start = 0, + bstart = 10, + finish = 10, + keyword = { + [1] = 0, + [2] = 8, + }, + parent = "", + bfinish = 10, + }, [1] = "a", }, - [3] = { - type = "function", - start = 1, - finish = 10, - parent = 2, - }, } CHECK 'function a:' { - [1] = { - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 11, + locals = "", + bfinish = 11, + [1] = { type = "setmethod", - start = 10, + start = 9, + vstart = 0, finish = 11, - parent = 1, - node = 3, + range = 11, + parent = "", + node = "", colon = { type = ":", - start = 11, + start = 10, finish = 11, }, - value = 4, - }, - [3] = { - type = "getglobal", - start = 10, - finish = 10, - parent = 2, - [1] = "a", - }, - [4] = { - type = "function", - start = 1, - finish = 11, - parent = 2, + value = { + type = "function", + start = 0, + bstart = 11, + finish = 11, + keyword = { + [1] = 0, + [2] = 8, + }, + parent = "", + locals = "", + bfinish = 11, + }, }, } CHECK 'function a:b(v' { - [1] = { - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 14, + locals = "", + bfinish = 14, + [1] = { type = "setmethod", - start = 10, + start = 9, + vstart = 0, finish = 12, - parent = 1, - node = 3, + range = 14, + parent = "", + node = "", colon = { type = ":", - start = 11, + start = 10, finish = 11, }, - method = 4, - value = 5, + method = { + type = "method", + start = 11, + finish = 12, + parent = "", + [1] = "b", + }, + value = { + type = "function", + start = 0, + bstart = 14, + finish = 14, + keyword = { + [1] = 0, + [2] = 8, + }, + parent = "", + args = { + type = "funcargs", + start = 12, + finish = 14, + parent = "", + [1] = { + type = "self", + start = 8, + finish = 8, + effect = 8, + parent = "", + [1] = "self", + }, + [2] = { + type = "local", + start = 13, + finish = 14, + effect = 14, + parent = "", + [1] = "v", + }, + }, + locals = "", + bfinish = 14, + }, }, - [3] = { - type = "getglobal", - start = 10, - finish = 10, - parent = 2, +} + +CHECK 'return local a' +{ + type = "main", + start = 0, + bstart = 0, + finish = 14, + locals = "", + returns = "", + bfinish = 14, + [1] = { + type = "return", + start = 0, + finish = 6, + parent = "", + }, + [2] = { + type = "local", + start = 13, + finish = 14, + effect = 14, + parent = "", + locPos = 7, [1] = "a", }, - [4] = { - type = "method", - start = 12, - finish = 12, - parent = 2, - [1] = "b", +} + +CHECK 'end' +{ + type = "main", + start = 0, + bstart = 0, + finish = 3, + locals = "", + bfinish = 3, +} + +CHECK 'local x = ,' +{ + type = "main", + start = 0, + bstart = 0, + finish = 11, + locals = "", + bfinish = 11, + [1] = { + type = "local", + start = 6, + finish = 7, + effect = 7, + parent = "", + locPos = 0, + [1] = "x", }, - [5] = { - type = "function", - start = 1, - finish = 14, - parent = 2, - args = 6, - locals = { - [1] = 7, +} + +CHECK 'local x = (a && b)' +{ + type = "main", + start = 0, + bstart = 0, + finish = 18, + locals = "", + bfinish = 18, + [1] = { + type = "local", + start = 6, + finish = 7, + effect = 18, + range = 18, + parent = "", + locPos = 0, + value = { + type = "paren", + start = 10, + finish = 18, + parent = "", + exp = { + type = "binary", + start = 11, + finish = 17, + parent = "", + op = { + type = "and", + start = 13, + finish = 15, + }, + [1] = { + type = "getglobal", + start = 11, + finish = 12, + parent = "", + node = "", + [1] = "a", + }, + [2] = { + type = "getglobal", + start = 16, + finish = 17, + parent = "", + node = "", + [1] = "b", + }, + }, }, + [1] = "x", }, - [6] = { - type = "funcargs", - start = 13, +} + +CHECK 'return 1 + + 1' +{ + type = "main", + start = 0, + bstart = 0, + finish = 14, + locals = "", + returns = "", + bfinish = 14, + [1] = { + type = "return", + start = 0, finish = 14, - parent = 5, - [1] = 7, + parent = "", + [1] = { + type = "binary", + start = 7, + finish = 14, + parent = "", + op = { + type = "+", + start = 9, + finish = 10, + }, + [1] = { + type = "integer", + start = 7, + finish = 8, + parent = "", + [1] = 1, + }, + [2] = { + type = "integer", + start = 13, + finish = 14, + parent = "", + [1] = 1, + }, + }, }, - [7] = { - type = "local", - start = 14, - finish = 14, - effect = 14, - parent = 6, - [1] = "v", +} + +CHECK 'return 1 + # + 2' +{ + type = "main", + start = 0, + bstart = 0, + finish = 16, + locals = "", + returns = "", + bfinish = 16, + [1] = { + type = "return", + start = 0, + finish = 16, + parent = "", + [1] = { + type = "binary", + start = 7, + finish = 16, + parent = "", + op = { + type = "+", + start = 13, + finish = 14, + }, + [1] = { + type = "binary", + start = 7, + finish = 12, + parent = "", + op = { + type = "+", + start = 9, + finish = 10, + }, + [1] = { + type = "integer", + start = 7, + finish = 8, + parent = "", + [1] = 1, + }, + [2] = { + type = "unary", + start = 11, + finish = 12, + parent = "", + op = { + type = "#", + start = 11, + finish = 12, + }, + }, + }, + [2] = { + type = "integer", + start = 15, + finish = 16, + parent = "", + [1] = 2, + }, + }, }, } -CHECK 'return local a' +CHECK 'return 1 + 2 + # + 3' { - [1] = { - type = "main", - locals = { - [1] = 3, + type = "main", + start = 0, + bstart = 0, + finish = 20, + locals = "", + returns = "", + bfinish = 20, + [1] = { + type = "return", + start = 0, + finish = 20, + parent = "", + [1] = { + type = "binary", + start = 7, + finish = 20, + parent = "", + op = { + type = "+", + start = 17, + finish = 18, + }, + [1] = { + type = "binary", + start = 7, + finish = 16, + parent = "", + op = { + type = "+", + start = 13, + finish = 14, + }, + [1] = { + type = "binary", + start = 7, + finish = 12, + parent = "", + op = { + type = "+", + start = 9, + finish = 10, + }, + [1] = { + type = "integer", + start = 7, + finish = 8, + parent = "", + [1] = 1, + }, + [2] = { + type = "integer", + start = 11, + finish = 12, + parent = "", + [1] = 2, + }, + }, + [2] = { + type = "unary", + start = 15, + finish = 16, + parent = "", + op = { + type = "#", + start = 15, + finish = 16, + }, + }, + }, + [2] = { + type = "integer", + start = 19, + finish = 20, + parent = "", + [1] = 3, + }, }, - [1] = 2, - [2] = 3, }, - [2] = { +} + +CHECK [[ +- +return +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 20000, + locals = "", + bfinish = 20000, + [1] = { + type = "unary", + start = 0, + finish = 10006, + parent = "", + op = { + type = "-", + start = 0, + finish = 1, + }, + [1] = { + type = "getglobal", + start = 10000, + finish = 10006, + parent = "", + node = "", + [1] = "return", + }, + }, +} + +CHECK [[ +return; +:::: +return; +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 30000, + locals = "", + returns = "", + bfinish = 30000, + [1] = { type = "return", - start = 1, - finish = 7, - parent = 1, + start = 0, + finish = 6, + parent = "", }, - [3] = { - type = "local", - start = 14, + [2] = { + type = "return", + start = 20000, + finish = 20006, + parent = "", + }, +} + +CHECK [[ +return; +goto; +return; +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 30000, + locals = "", + returns = "", + bfinish = 30000, + [1] = { + type = "return", + start = 0, + finish = 6, + parent = "", + }, + [2] = { + type = "return", + start = 20000, + finish = 20006, + parent = "", + }, +} + +CHECK [[ +call(,-,not,1) +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 10000, + locals = "", + bfinish = 10000, + [1] = { + type = "call", + start = 0, finish = 14, - effect = 15, - parent = 1, + parent = "", + node = "", + args = { + type = "callargs", + start = 4, + finish = 14, + parent = "", + [1] = { + type = "unary", + start = 6, + finish = 7, + parent = "", + op = { + type = "-", + start = 6, + finish = 7, + }, + }, + [2] = { + type = "unary", + start = 8, + finish = 11, + parent = "", + op = { + type = "not", + start = 8, + finish = 11, + }, + }, + [3] = { + type = "integer", + start = 12, + finish = 13, + parent = "", + [1] = 1, + }, + }, + }, +} + +CHECK [[ +{ + ;,-;,1 +} +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 30000, + locals = "", + bfinish = 30000, + [1] = { + type = "table", + start = 0, + bstart = 1, + finish = 20001, + parent = "", + bfinish = 20000, + [1] = { + type = "tableexp", + start = 10006, + finish = 10007, + tindex = 1, + parent = "", + value = { + type = "unary", + start = 10006, + finish = 10007, + parent = "", + op = { + type = "-", + start = 10006, + finish = 10007, + }, + }, + }, + [2] = { + type = "tableexp", + start = 10009, + finish = 10010, + tindex = 2, + parent = "", + value = { + type = "integer", + start = 10009, + finish = 10010, + parent = "", + [1] = 1, + }, + }, + }, +} + +CHECK [[ +local a,b,,d +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 10000, + locals = "", + bfinish = 10000, + [1] = { + type = "local", + start = 6, + finish = 7, + effect = 12, + parent = "", + locPos = 0, [1] = "a", }, + [2] = { + type = "local", + start = 8, + finish = 9, + effect = 12, + parent = "", + [1] = "b", + }, + [3] = { + type = "local", + start = 11, + finish = 12, + effect = 12, + parent = "", + [1] = "d", + }, } -CHECK 'end' +CHECK [[ +if /**/ then +end +]] { - [1] = { - type = "main", + type = "main", + start = 0, + bstart = 0, + finish = 20000, + locals = "", + bfinish = 20000, + [1] = { + type = "if", + start = 0, + finish = 10003, + parent = "", + [1] = { + type = "ifblock", + start = 0, + bstart = 12, + finish = 10000, + keyword = { + [1] = 0, + [2] = 2, + [3] = 8, + [4] = 12, + }, + parent = "", + bfinish = 10000, + }, }, } -CHECK 'local x = ,' +CHECK [[ +f(break) +]] { - [1] = { - type = "main", - locals = { - [1] = 2, + type = "main", + start = 0, + bstart = 0, + finish = 10000, + locals = "", + bfinish = 10000, + [1] = { + type = "call", + start = 0, + finish = 8, + parent = "", + node = "", + args = { + type = "callargs", + start = 1, + finish = 8, + parent = "", + [1] = { + type = "getglobal", + start = 2, + finish = 7, + parent = "", + node = "", + [1] = "break", + }, }, - [1] = 2, }, - [2] = { +} + +CHECK [[ +print(x == ) +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 10000, + locals = "", + bfinish = 10000, + [1] = { + type = "call", + start = 0, + finish = 12, + parent = "", + node = "", + args = { + type = "callargs", + start = 5, + finish = 12, + parent = "", + [1] = { + type = "binary", + start = 6, + finish = 10, + parent = "", + op = { + type = "==", + start = 8, + finish = 10, + }, + [1] = { + type = "getglobal", + start = 6, + finish = 7, + parent = "", + node = "", + [1] = "x", + }, + }, + }, + }, +} +CHECK [[ +local t = { + a = 1, +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 20000, + locals = "", + bfinish = 20000, + [1] = { type = "local", - start = 7, + start = 6, finish = 7, - effect = 12, - parent = 1, + effect = 10010, + range = 10010, + parent = "", + locPos = 0, + value = { + type = "table", + start = 10, + bstart = 11, + finish = 10010, + parent = "", + bfinish = 20000, + [1] = { + type = "tablefield", + start = 10004, + finish = 10005, + range = 10009, + parent = "", + node = "", + field = { + type = "field", + start = 10004, + finish = 10005, + parent = "", + [1] = "a", + }, + value = { + type = "integer", + start = 10008, + finish = 10009, + parent = "", + [1] = 1, + }, + }, + }, + [1] = "t", + }, +} + +CHECK [[ +local t = function f() end +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 10000, + locals = "", + bfinish = 10000, + [1] = { + type = "local", + start = 6, + finish = 7, + effect = 26, + range = 26, + parent = "", + locPos = 0, + value = { + type = "function", + start = 10, + bstart = 22, + finish = 26, + keyword = { + [1] = 10, + [2] = 18, + [3] = 23, + [4] = 26, + }, + parent = "", + args = { + type = "funcargs", + start = 20, + finish = 22, + parent = "", + }, + bfinish = 23, + name = { + type = "getglobal", + start = 19, + finish = 20, + parent = "", + node = "", + [1] = "f", + }, + }, + [1] = "t", + }, +} + +CHECK [[ +function F() + in +end +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 30000, + locals = "", + bfinish = 30000, + [1] = { + type = "setglobal", + start = 9, + vstart = 0, + finish = 10, + range = 20003, + parent = "", + node = "", + value = { + type = "function", + start = 0, + bstart = 12, + finish = 20003, + keyword = { + [1] = 0, + [2] = 8, + [3] = 20000, + [4] = 20003, + }, + parent = "", + args = { + type = "funcargs", + start = 10, + finish = 12, + parent = "", + }, + bfinish = 20000, + }, + [1] = "F", + }, +} + +CHECK [[ +if true then + 1 +end +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 30000, + locals = "", + bfinish = 30000, + [1] = { + type = "if", + start = 0, + finish = 20003, + parent = "", + [1] = { + type = "ifblock", + start = 0, + bstart = 12, + finish = 20000, + keyword = { + [1] = 0, + [2] = 2, + [3] = 8, + [4] = 12, + }, + parent = "", + filter = { + type = "boolean", + start = 3, + finish = 7, + parent = "", + [1] = true, + }, + bfinish = 20000, + [1] = { + type = "integer", + start = 10004, + finish = 10005, + parent = "", + [1] = 1, + }, + }, + }, +} +CHECK [[ +local +local x = 1 +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 20000, + locals = "", + bfinish = 20000, + [1] = { + type = "local", + start = 10006, + finish = 10007, + effect = 10011, + range = 10011, + parent = "", + locPos = 10000, + value = { + type = "integer", + start = 10010, + finish = 10011, + parent = "", + [1] = 1, + }, [1] = "x", }, } +CHECK [[ +return function () + local function fff(xxx) + for f in xx + end +end +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 50000, + locals = "", + returns = "", + bfinish = 50000, + [1] = { + type = "return", + start = 0, + finish = 40003, + parent = "", + [1] = { + type = "function", + start = 7, + bstart = 18, + finish = 40003, + keyword = { + [1] = 7, + [2] = 15, + }, + parent = "", + args = { + type = "funcargs", + start = 16, + finish = 18, + parent = "", + }, + locals = "", + bfinish = 50000, + [1] = { + type = "local", + start = 10019, + vstart = 10010, + finish = 10022, + effect = 10022, + range = 40003, + parent = "", + locPos = 10004, + value = { + type = "function", + start = 10010, + bstart = 10027, + finish = 40003, + keyword = { + [1] = 10010, + [2] = 10018, + [3] = 40000, + [4] = 40003, + }, + parent = "", + args = { + type = "funcargs", + start = 10022, + finish = 10027, + parent = "", + [1] = { + type = "local", + start = 10023, + finish = 10026, + effect = 10026, + parent = "", + [1] = "xxx", + }, + }, + locals = "", + bfinish = 40000, + [1] = { + type = "in", + start = 20008, + bstart = 20019, + finish = 30007, + keyword = { + [1] = 20008, + [2] = 20011, + [3] = 20014, + [4] = 20016, + [5] = 30004, + [6] = 30007, + }, + parent = "", + keys = { + type = "list", + start = 20012, + finish = 20013, + range = 20016, + parent = "", + [1] = { + type = "local", + start = 20012, + finish = 20013, + effect = 20019, + parent = "", + [1] = "f", + }, + }, + exps = { + type = "list", + start = 20017, + finish = 20019, + parent = "", + [1] = { + type = "getglobal", + start = 20017, + finish = 20019, + parent = "", + node = "", + [1] = "xx", + }, + }, + locals = "", + bfinish = 30004, + }, + }, + [1] = "fff", + }, + }, + }, +} diff --git a/test/ast/Emmy.lua b/test/ast/Emmy.lua deleted file mode 100644 index 873544d..0000000 --- a/test/ast/Emmy.lua +++ /dev/null @@ -1,1134 +0,0 @@ -EMMY [[ ----@class Class -local x = 1 -]] -{ - [1] = { - type = 'class', - start = 1, - finish = 15, - class = { - type = 'name', - start = 11, - finish = 15, - [1] = 'Class', - }, - }, -} - -EMMY [[ ----@class Class : SuperClass -local x = 1 -]] -{ - [1] = { - type = 'class', - start = 1, - finish = 28, - class = { - type = 'name', - start = 11, - finish = 15, - [1] = 'Class', - }, - extends= { - type = 'name', - start = 19, - finish = 28, - [1] = 'SuperClass', - }, - }, -} - -EMMY [[ ----@class Class -x = 1 -]] -{ - [1] = { - type = 'class', - start = 1, - finish = 15, - class = { - type = 'name', - start = 11, - finish = 15, - [1] = 'Class', - }, - }, -} - -EMMY [[ ----@type Type -x = 1 -]] -{ - [1] = { - type = 'type', - start = 1, - finish = 13, - types = { - { - type = 'common', - start = 10, - finish = 13, - name = { - type = 'name', - start = 10, - finish = 13, - [1] = 'Type', - } - } - }, - enums = {}, - }, -} - -EMMY [[ ----@type Type1|Type2|Type3 -x = 1 -]] -{ - [1] = { - type = 'type', - start = 1, - finish = 26, - types = { - { - type = 'common', - start = 10, - finish = 14, - name = { - type = 'name', - start = 10, - finish = 14, - [1] = 'Type1', - } - }, - { - type = 'common', - start = 16, - finish = 20, - name = { - type = 'name', - start = 16, - finish = 20, - [1] = 'Type2', - } - }, - { - type = 'common', - start = 22, - finish = 26, - name = { - type = 'name', - start = 22, - finish = 26, - [1] = 'Type3', - } - }, - }, - enums = {}, - }, -} - -EMMY [[ ----@type x | "'a'" | "'b'" -]] -{ - [1] = { - type = 'type', - start = 1, - finish = 26, - types = { - { - type = 'common', - start = 10, - finish = 10, - name = { - type = 'name', - start = 10, - finish = 10, - [1] = 'x', - } - } - }, - enums = { - { - type = 'enum', - start = 14, - finish = 18, - [1] = [['a']], - [2] = '"', - }, - { - type = 'enum', - start = 22, - finish = 26, - [1] = [['b']], - [2] = '"', - }, - } - } -} - -EMMY [[ ----@type "'a'" | "'b'" | "'c'" -]] -{ - [1] = { - type = 'type', - start = 1, - finish = 30, - types = {}, - enums = { - { - type = 'enum', - start = 10, - finish = 14, - [1] = [['a']], - [2] = '"', - }, - { - type = 'enum', - start = 18, - finish = 22, - [1] = [['b']], - [2] = '"', - }, - { - type = 'enum', - start = 26, - finish = 30, - [1] = [['c']], - [2] = '"', - }, - } - } -} - -do return end -EMMY [[ ----@alias Handler LongType -x = 1 -]] -{ - [1] = { - type = 'emmyAlias', - start = 11, - finish = 26, - [1] = { - type = 'emmyName', - start = 11, - finish = 17, - [1] = 'Handler', - }, - [2] = { - type = 'emmyType', - start = 19, - finish = 26, - [1] = { - type = 'emmyName', - start = 19, - finish = 26, - [1] = 'LongType' - }, - }, - }, - [2] = { - type = 'set', - [1] = { - type = 'name', - start = 28, - finish = 28, - [1] = 'x', - }, - [2] = { - type = 'number', - start = 32, - finish = 32, - [1] = 1, - } - } -} - -do return end -EMMY [[ ----@param a1 t1 ----@param a2 t2 ----@param a3 t3 -]] -{ - [1] = { - type = 'emmyParam', - start = 11, - finish = 15, - [1] = { - type = 'emmyName', - start = 11, - finish = 12, - [1] = 'a1', - }, - [2] = { - type = 'emmyType', - start = 14, - finish = 15, - [1] = { - type = 'emmyName', - start = 14, - finish = 15, - [1] = 't1', - } - } - }, - [2] = { - type = 'emmyParam', - start = 27, - finish = 31, - [1] = { - type = 'emmyName', - start = 27, - finish = 28, - [1] = 'a2', - }, - [2] = { - type = 'emmyType', - start = 30, - finish = 31, - [1] = { - type = 'emmyName', - start = 30, - finish = 31, - [1] = 't2', - } - } - }, - [3] = { - type = 'emmyParam', - start = 43, - finish = 47, - [1] = { - type = 'emmyName', - start = 43, - finish = 44, - [1] = 'a3', - }, - [2] = { - type = 'emmyType', - start = 46, - finish = 47, - [1] = { - type = 'emmyName', - start = 46, - finish = 47, - [1] = 't3', - } - } - }, -} - -EMMY [[ ----@return Type1|Type2|Type3 -]] -{ - [1] = { - type = 'emmyReturn', - start = 12, - finish = 28, - [1] = { - type = 'emmyType', - start = 12, - finish = 28, - [1] = { - type = 'emmyName', - start = 12, - finish = 16, - [1] = 'Type1', - }, - [2] = { - type = 'emmyName', - start = 18, - finish = 22, - [1] = 'Type2', - }, - [3] = { - type = 'emmyName', - start = 24, - finish = 28, - [1] = 'Type3', - }, - } - }, -} - -EMMY [[ ----@field open function -]] -{ - [1] = { - type = 'emmyField', - start = 11, - finish = 23, - [1] = 'public', - [2] = { - type = 'emmyName', - start = 11, - finish = 14, - [1] = 'open', - }, - [3] = { - type = 'emmyType', - start = 16, - finish = 23, - [1] = { - type = 'emmyName', - start = 16, - finish = 23, - [1] = 'function', - } - } - }, -} - -EMMY [[ ----@field private open function|string -]] -{ - [1] = { - type = 'emmyField', - start = 19, - finish = 38, - [1] = 'private', - [2] = { - type = 'emmyName', - start = 19, - finish = 22, - [1] = 'open', - }, - [3] = { - type = 'emmyType', - start = 24, - finish = 38, - [1] = { - type = 'emmyName', - start = 24, - finish = 31, - [1] = 'function', - }, - [2] = { - type = 'emmyName', - start = 33, - finish = 38, - [1] = 'string', - } - } - }, -} - -EMMY [[ ----@generic T -]] -{ - [1] = { - type = 'emmyGeneric', - start = 13, - finish = 13, - [1] = { - start = 13, - finish = 13, - [1] = { - type = 'emmyName', - start = 13, - finish = 13, - [1] = 'T', - } - } - } -} - -EMMY [[ ----@generic T : handle -]] -{ - [1] = { - type = 'emmyGeneric', - start = 13, - finish = 22, - [1] = { - start = 13, - finish = 22, - [1] = { - type = 'emmyName', - start = 13, - finish = 13, - [1] = 'T', - }, - [2] = { - type = 'emmyType', - start = 17, - finish = 22, - [1] = { - type = 'emmyName', - start = 17, - finish = 22, - [1] = 'handle', - } - } - } - } -} - -EMMY [[ ----@generic T : handle, K : handle -]] -{ - [1] = { - type = 'emmyGeneric', - start = 13, - finish = 34, - [1] = { - start = 13, - finish = 22, - [1] = { - type = 'emmyName', - start = 13, - finish = 13, - [1] = 'T', - }, - [2] = { - type = 'emmyType', - start = 17, - finish = 22, - [1] = { - type = 'emmyName', - start = 17, - finish = 22, - [1] = 'handle', - } - } - }, - [2] = { - start = 25, - finish = 34, - [1] = { - type = 'emmyName', - start = 25, - finish = 25, - [1] = 'K', - }, - [2] = { - type = 'emmyType', - start = 29, - finish = 34, - [1] = { - type = 'emmyName', - start = 29, - finish = 34, - [1] = 'handle', - } - } - }, - } -} - -EMMY [[ ----@vararg string -]] -{ - [1] = { - type = 'emmyVararg', - start = 12, - finish = 17, - [1] = { - type = 'emmyType', - start = 12, - finish = 17, - [1] = { - type = 'emmyName', - start = 12, - finish = 17, - [1] = 'string', - } - } - } -} - -EMMY [[ ----@language JSON -]] -{ - [1] = { - type = 'emmyLanguage', - start = 14, - finish = 17, - [1] = { - type = 'emmyName', - start = 14, - finish = 17, - [1] = 'JSON', - } - } -} - -EMMY [[ ----@type Type[] -]] -{ - [1] = { - type = 'emmyArrayType', - start = 10, - finish = 15, - [1] = { - type = 'emmyName', - start = 10, - finish = 13, - [1] = 'Type' - }, - } -} - -EMMY [[ ----@type (Type1|Type2)[] -]] -{ - [1] = { - type = 'emmyArrayType', - start = 10, - finish = 24, - [1] = { - type = 'emmyName', - start = 11, - finish = 15, - [1] = 'Type1' - }, - [2] = { - type = 'emmyName', - start = 17, - finish = 21, - [1] = 'Type2' - }, - } -} - -EMMY [[ ----@type table -]] -{ - [1] = { - type = 'emmyTableType', - start = 10, - finish = 26, - [1] = { - type = 'emmyType', - start = 16, - finish = 18, - [1] = { - type = 'emmyName', - start = 16, - finish = 18, - [1] = 'key', - } - }, - [2] = { - type = 'emmyType', - start = 21, - finish = 25, - [1] = { - type = 'emmyName', - start = 21, - finish = 25, - [1] = 'value', - } - } - } -} - -EMMY [[ ----@type fun(key1:t1|t2[], key2:t3):table -]] -{ - [1] = { - type = 'emmyFunctionType', - start = 10, - finish = 49, - args = { - [1] = { - type = 'emmyName', - start = 14, - finish = 17, - [1] = 'key1', - }, - [2] = { - type = 'emmyType', - start = 19, - finish = 25, - [1] = { - type = 'emmyName', - start = 19, - finish = 20, - [1] = 't1', - }, - [2] = { - type = 'emmyArrayType', - start = 22, - finish = 25, - [1] = { - type = 'emmyName', - start = 22, - finish = 23, - [1] = 't2' - }, - } - }, - [3] = { - type = 'emmyName', - start = 28, - finish = 31, - [1] = 'key2', - }, - [4] = { - type = 'emmyType', - start = 33, - finish = 34, - [1] = { - type = 'emmyName', - start = 33, - finish = 34, - [1] = 't3', - } - }, - }, - returns = { - [1] = { - type = 'emmyTableType', - start = 37, - finish = 49, - [1] = { - type = 'emmyType', - start = 43, - finish = 44, - [1] = { - type = 'emmyName', - start = 43, - finish = 44, - [1] = 't5', - }, - }, - [2] = { - type = 'emmyType', - start = 47, - finish = 48, - [1] = { - type = 'emmyName', - start = 47, - finish = 48, - [1] = 't6', - } - } - } - } - } -} - -EMMY [[ ----@param event string | "'onClosed'" | "'onData'" -]] -{ - [1] = { - type = 'emmyParam', - start = 11, - finish = 50, - [1] = { - type = 'emmyName', - start = 11, - finish = 15, - [1] = 'event', - }, - [2] = { - type = 'emmyType', - start = 17, - finish = 22, - [1] = { - type = 'emmyName', - start = 17, - finish = 22, - [1] = 'string', - }, - }, - [3] = { - type = 'emmyEnum', - start = 26, - finish = 37, - [1] = "'onClosed'", - [2] = '"', - }, - [4] = { - type = 'emmyEnum', - start = 41, - finish = 50, - [1] = "'onData'", - [2] = '"', - }, - }, -} - -EMMY [[ ----@see loli#pants -]] -{ - [1] = { - type = 'emmySee', - start = 9, - finish = 18, - [1] = { - type = 'emmyName', - start = 9, - finish = 12, - [1] = 'loli', - }, - [2] = { - type = 'emmyName', - start = 14, - finish = 18, - [1] = 'pants', - } - } -} - -EMMY [[ - ----@class Class -]] -{ - [1] = { - type = 'emmyClass', - start = 12, - finish = 16, - [1] = { - type = 'emmyName', - start = 12, - finish = 16, - [1] = 'Class', - }, - }, -} - -EMMY [[ ----@ ----@cl -]] -{ - [1] = { - type = 'emmyIncomplete', - start = 4, - finish = 4, - [1] = '', - }, - [2] = { - type = 'emmyIncomplete', - start = 10, - finish = 11, - [1] = 'cl', - }, -} - -EMMY [[ -local t = { - ---@type string - x = 1, -} -]] -{ - [1] = { - type = "local", - [1] = { - type = "name", - start = 7, - finish = 7, - [1] = "t", - }, - [2] = { - type = "table", - start = 11, - finish = 44, - [1] = { - type = "emmyType", - start = 26, - finish = 31, - [1] = { - type = "emmyName", - start = 26, - finish = 31, - [1] = "string", - }, - }, - [2] = { - type = "pair", - start = 37, - finish = 41, - [1] = { - type = "name", - start = 37, - finish = 37, - [1] = "x", - }, - [2] = { - type = "number", - start = 41, - finish = 41, - [1] = 1, - }, - }, - }, - }, -} - -EMMY [[ -local function f() - ---@ -end -]] -{ - [1] = { - type = "localfunction", - start = 1, - finish = 31, - argStart = 17, - argFinish = 18, - name = { - [1] = "f", - finish = 16, - start = 16, - type = "name", - }, - [1] = { - type = "emmyIncomplete", - start = 27, - finish = 27, - [1] = "", - }, - }, -} - -EMMY '---@type fun' -{ - [1] = { - type = "emmyFunctionType", - start = 10, - finish = 12, - }, -} - -EMMY [[ ----123 ---- 456 ---- 789 -]] -{ - [1] = { - type = 'emmyComment', - [1] = '123 456 789', - } -} - -EMMY [[ ----@overload fun(a:number):number -]] -{ - [1] = { - type = "emmyOverLoad", - start = 14, - finish = 33, - args = { - [1] = { - type = "emmyName", - start = 18, - finish = 18, - [1] = "a", - }, - [2] = { - type = "emmyType", - start = 20, - finish = 25, - [1] = { - type = "emmyName", - start = 20, - finish = 25, - [1] = "number", - }, - }, - }, - returns = { - [1] = { - type = "emmyType", - start = 28, - finish = 33, - [1] = { - type = "emmyName", - start = 28, - finish = 33, - [1] = "number", - }, - }, - } - }, -} - -EMMY [[ ----@param x string {xx = 1, yy = "zz", zz = false} | "fff" -]] -{ - [1] = { - type = "emmyParam", - start = 11, - finish = 58, - option = { - xx = 1, - yy = 'zz', - zz = false, - }, - [1] = { - type = "emmyName", - start = 11, - finish = 11, - [1] = "x", - }, - [2] = { - type = "emmyType", - start = 13, - finish = 18, - [1] = { - type = "emmyName", - start = 13, - finish = 18, - [1] = "string", - }, - }, - [3] = { - type = 'emmyEnum', - start = 54, - finish = 58, - [1] = "fff", - [2] = '"', - }, - }, -} - -EMMY [[ ----@param event string ----| "'onClosed'" # 12345 ----| > "'onData'" # 22222 -]] -{ - [1] = { - type = 'emmyParam', - start = 11, - finish = 68, - [1] = { - type = 'emmyName', - start = 11, - finish = 15, - [1] = 'event', - }, - [2] = { - type = 'emmyType', - start = 17, - finish = 22, - [1] = { - type = 'emmyName', - start = 17, - finish = 22, - [1] = 'string', - }, - }, - [3] = { - type = 'emmyEnum', - start = 31, - finish = 42, - comment = "12345", - [1] = "'onClosed'", - [2] = '"', - }, - [4] = { - type = 'emmyEnum', - start = 59, - finish = 68, - comment = "22222", - default = true, - [1] = "'onData'", - [2] = '"', - }, - }, -} - -EMMY [[ ----123 ----| 456 ----| 789 -]] -{ - [1] = { - type = 'emmyComment', - [1] = '123\n 456\n 789', - } -} - -EMMY [[ ----@return string {name = 'key'} -]] -{ - [1] = { - type = 'emmyReturn', - start = 12, - finish = 17, - option = { - name = 'key' - }, - [1] = { - type = 'emmyType', - start = 12, - finish = 17, - [1] = { - type = 'emmyName', - start = 12, - finish = 17, - [1] = 'string', - }, - } - } -} - -EMMY [[ ----@overload fun():number,boolean -]] -{ - [1] = { - type = "emmyOverLoad", - start = 14, - finish = 33, - returns = { - [1] = { - type = "emmyType", - start = 20, - finish = 25, - [1] = { - type = "emmyName", - start = 20, - finish = 25, - [1] = "number", - }, - }, - [2] = { - type = "emmyType", - start = 27, - finish = 33, - [1] = { - type = "emmyName", - start = 27, - finish = 33, - [1] = "boolean", - }, - }, - } - }, -} diff --git a/test/ast/Exp.lua b/test/ast/Exp.lua index 362b2fe..aaa9838 100644 --- a/test/ast/Exp.lua +++ b/test/ast/Exp.lua @@ -1,1457 +1,1631 @@ CHECK'nil' { - [1] = { - type = 'nil', - start = 1, - finish = 3, - } + type = "nil", + start = 0, + finish = 3, } CHECK'a' { - [1] = { - type = "getglobal", - start = 1, - finish = 1, - [1] = 'a', - }, + type = "getglobal", + start = 0, + finish = 1, + [1] = "a", } CHECK'a.b' { - [1] = { - type = "getfield", + type = "getfield", + start = 0, + finish = 3, + node = "", + dot = { + type = ".", start = 1, - finish = 3, - node = 2, - dot = { - type = ".", - start = 2, - finish = 2, - }, - field = { - type = "field", - start = 3, - finish = 3, - [1] = "b", - }, + finish = 2, }, - [2] = { - type = "getglobal", - start = 1, - finish = 1, - parent = 1, - [1] = "a", + field = { + type = "field", + start = 2, + finish = 3, + parent = "", + [1] = "b", }, } CHECK'a.b.c' { - [1] = { - type = "getfield", - start = 1, - finish = 5, - node = 2, - dot = { - type = ".", - start = 4, - finish = 4, - }, - field = { - type = "field", - start = 5, - finish = 5, - [1] = "c", - }, - }, - [2] = { - type = "getfield", - start = 1, - finish = 3, - parent = 1, - node = 3, - dot = { - type = ".", - start = 2, - finish = 2, - }, - field = { - type = "field", - start = 3, - finish = 3, - [1] = "b", - }, + type = "getfield", + start = 0, + finish = 5, + node = "", + dot = { + type = ".", + start = 3, + finish = 4, }, - [3] = { - type = "getglobal", - start = 1, - finish = 1, - parent = 2, - [1] = "a", + field = { + type = "field", + start = 4, + finish = 5, + parent = "", + [1] = "c", }, } CHECK'func()' { - [1] = { - type = "call", - start = 1, - finish = 6, - node = 2, - }, - [2] = { - type = "getglobal", - start = 1, - finish = 4, - parent = 1, - [1] = "func", - }, + type = "call", + start = 0, + finish = 6, + node = "", } CHECK'a.b.c()' { - [1] = { - type = "call", - start = 1, - finish = 7, - node = 2, - }, - [2] = { - type = "getfield", - start = 1, - finish = 5, - parent = 1, - node = 3, - dot = { - type = ".", - start = 4, - finish = 4, - }, - field = { - type = "field", - start = 5, - finish = 5, - [1] = "c", - }, - }, - [3] = { - type = "getfield", - start = 1, - finish = 3, - parent = 2, - node = 4, - dot = { - type = ".", - start = 2, - finish = 2, - }, - field = { - type = "field", - start = 3, - finish = 3, - [1] = "b", - }, - }, - [4] = { - type = "getglobal", - start = 1, - finish = 1, - parent = 3, - [1] = "a", - }, + type = "call", + start = 0, + finish = 7, + node = "", } CHECK'1 or 2' { - [1] = { - type = "binary", - start = 1, - finish = 6, - op = { - type = "or", - start = 3, - finish = 4, - }, - [1] = 2, - [2] = 3, + type = "binary", + start = 0, + finish = 6, + op = { + type = "or", + start = 2, + finish = 4, }, - [2] = { - type = "number", - start = 1, + [1] = { + type = "integer", + start = 0, finish = 1, - parent = 1, + parent = "", [1] = 1, }, - [3] = { - type = "number", - start = 6, + [2] = { + type = "integer", + start = 5, finish = 6, - parent = 1, + parent = "", [1] = 2, }, } CHECK'1 < 2' { - [1] = { - type = "binary", - start = 1, - finish = 5, - op = { - type = "<", - start = 3, - finish = 3, - }, - [1] = 2, - [2] = 3, + type = "binary", + start = 0, + finish = 5, + op = { + type = "<", + start = 2, + finish = 3, }, - [2] = { - type = "number", - start = 1, + [1] = { + type = "integer", + start = 0, finish = 1, - parent = 1, + parent = "", [1] = 1, }, - [3] = { - type = "number", - start = 5, + [2] = { + type = "integer", + start = 4, finish = 5, - parent = 1, + parent = "", [1] = 2, }, } CHECK'- 1' { - [1] = { - type = "unary", - start = 1, - finish = 3, - op = { - type = "-", - start = 1, - finish = 1, - }, - [1] = 2, - }, - [2] = { - type = "number", - start = 3, - finish = 3, - parent = 1, - [1] = 1, - }, + type = "integer", + start = 0, + finish = 3, + [1] = -1, } CHECK'not not true' { - [1] = { - type = "unary", - start = 1, - finish = 12, - op = { - type = "not", - start = 1, - finish = 3, - }, - [1] = 2, + type = "unary", + start = 0, + finish = 12, + op = { + type = "not", + start = 0, + finish = 3, }, - [2] = { + [1] = { type = "unary", - start = 5, + start = 4, finish = 12, - parent = 1, + parent = "", op = { type = "not", - start = 5, + start = 4, finish = 7, }, - [1] = 3, - }, - [3] = { - type = "boolean", - start = 9, - finish = 12, - parent = 2, - [1] = true, + [1] = { + type = "boolean", + start = 8, + finish = 12, + parent = "", + [1] = true, + }, }, } CHECK'1 ^ 2' { - [1] = { - type = "binary", - start = 1, - finish = 5, - op = { - type = "^", - start = 3, - finish = 3, - }, - [1] = 2, - [2] = 3, + type = "binary", + start = 0, + finish = 5, + op = { + type = "^", + start = 2, + finish = 3, }, - [2] = { - type = "number", - start = 1, + [1] = { + type = "integer", + start = 0, finish = 1, - parent = 1, + parent = "", [1] = 1, }, - [3] = { - type = "number", - start = 5, + [2] = { + type = "integer", + start = 4, finish = 5, - parent = 1, + parent = "", [1] = 2, }, } CHECK'1 ^ -2' { - [1] = { + type = "binary", + start = 0, + finish = 6, + op = { + type = "^", + start = 2, + finish = 3, + }, + [1] = { + type = "integer", + start = 0, + finish = 1, + parent = "", + [1] = 1, + }, + [2] = { + type = "integer", + start = 4, + finish = 6, + parent = "", + [1] = -2, + }, +} +CHECK'-1 ^ 2' +{ + type = "unary", + start = 0, + finish = 6, + op = { + type = "-", + start = 0, + finish = 1, + }, + [1] = { type = "binary", start = 1, finish = 6, + parent = "", op = { type = "^", start = 3, - finish = 3, + finish = 4, }, - [1] = 2, - [2] = 3, - }, - [2] = { - type = "number", - start = 1, - finish = 1, - parent = 1, - [1] = 1, - }, - [3] = { - type = "unary", - start = 5, - finish = 6, - parent = 1, - op = { - type = "-", + [1] = { + type = 'integer', + start = 1, + finish = 2, + parent = "", + [1] = 1, + }, + [2] = { + type = 'integer', start = 5, - finish = 5, + finish = 6, + parent = "", + [1] = 2, }, - [1] = 4, - }, - [4] = { - type = "number", - start = 6, - finish = 6, - parent = 3, - [1] = 2, }, } CHECK'...' { - [1] = { - type = 'varargs', - start = 1, - finish = 3, - } + type = "varargs", + start = 0, + finish = 3, } CHECK'1 + 2 + 3' { - [1] = { - type = "binary", - start = 1, - finish = 9, - op = { - type = "+", - start = 7, - finish = 7, - }, - [1] = 2, - [2] = 5, + type = "binary", + start = 0, + finish = 9, + op = { + type = "+", + start = 6, + finish = 7, }, - [2] = { + [1] = { type = "binary", - start = 1, + start = 0, finish = 5, - parent = 1, + parent = "", op = { type = "+", - start = 3, + start = 2, finish = 3, }, - [1] = 3, - [2] = 4, - }, - [3] = { - type = "number", - start = 1, - finish = 1, - parent = 2, - [1] = 1, - }, - [4] = { - type = "number", - start = 5, - finish = 5, - parent = 2, - [1] = 2, + [1] = { + type = "integer", + start = 0, + finish = 1, + parent = "", + [1] = 1, + }, + [2] = { + type = "integer", + start = 4, + finish = 5, + parent = "", + [1] = 2, + }, }, - [5] = { - type = "number", - start = 9, + [2] = { + type = "integer", + start = 8, finish = 9, - parent = 1, + parent = "", [1] = 3, }, } CHECK'1 + 2 * 3' { - [1] = { - type = "binary", - start = 1, - finish = 9, - op = { - type = "+", - start = 3, - finish = 3, - }, - [1] = 2, - [2] = 3, + type = "binary", + start = 0, + finish = 9, + op = { + type = "+", + start = 2, + finish = 3, }, - [2] = { - type = "number", - start = 1, + [1] = { + type = "integer", + start = 0, finish = 1, - parent = 1, + parent = "", [1] = 1, }, - [3] = { + [2] = { type = "binary", - start = 5, + start = 4, finish = 9, - parent = 1, + parent = "", op = { type = "*", - start = 7, + start = 6, finish = 7, }, - [1] = 4, - [2] = 5, - }, - [4] = { - type = "number", - start = 5, - finish = 5, - parent = 3, - [1] = 2, - }, - [5] = { - type = "number", - start = 9, - finish = 9, - parent = 3, - [1] = 3, + [1] = { + type = "integer", + start = 4, + finish = 5, + parent = "", + [1] = 2, + }, + [2] = { + type = "integer", + start = 8, + finish = 9, + parent = "", + [1] = 3, + }, }, } CHECK'- 1 + 2 * 3' { - [1] = { - type = "binary", - start = 1, - finish = 11, - op = { - type = "+", - start = 5, - finish = 5, - }, - [1] = 2, - [2] = 4, + type = "binary", + start = 0, + finish = 11, + op = { + type = "+", + start = 4, + finish = 5, }, - [2] = { - type = "unary", - start = 1, + [1] = { + type = "integer", + start = 0, finish = 3, - parent = 1, + parent = "", + [1] = -1, + }, + [2] = { + type = "binary", + start = 6, + finish = 11, + parent = "", op = { - type = "-", - start = 1, - finish = 1, + type = "*", + start = 8, + finish = 9, + }, + [1] = { + type = "integer", + start = 6, + finish = 7, + parent = "", + [1] = 2, + }, + [2] = { + type = "integer", + start = 10, + finish = 11, + parent = "", + [1] = 3, }, - [1] = 3, }, - [3] = { - type = "number", +} +CHECK'-1 + 2 * 3' +{ + type = "binary", + start = 0, + finish = 10, + op = { + type = "+", start = 3, - finish = 3, - parent = 2, - [1] = 1, + finish = 4, + }, + [1] = { + type = "integer", + start = 0, + finish = 2, + parent = "", + [1] = -1, }, - [4] = { + [2] = { type = "binary", - start = 7, - finish = 11, - parent = 1, + start = 5, + finish = 10, + parent = "", op = { type = "*", + start = 7, + finish = 8, + }, + [1] = { + type = "integer", + start = 5, + finish = 6, + parent = "", + [1] = 2, + }, + [2] = { + type = "integer", start = 9, - finish = 9, + finish = 10, + parent = "", + [1] = 3, }, - [1] = 5, - [2] = 6, }, - [5] = { - type = "number", - start = 7, +} +CHECK"x and y == 'unary' and z" +{ + type = "binary", + start = 0, + finish = 24, + op = { + type = "and", + start = 19, + finish = 22, + }, + [1] = { + type = "binary", + start = 0, + finish = 18, + parent = "", + op = { + type = "and", + start = 2, + finish = 5, + }, + [1] = { + type = "getglobal", + start = 0, + finish = 1, + parent = "", + [1] = "x", + }, + [2] = { + type = "binary", + start = 6, + finish = 18, + parent = "", + op = { + type = "==", + start = 8, + finish = 10, + }, + [1] = { + type = "getglobal", + start = 6, + finish = 7, + parent = "", + [1] = "y", + }, + [2] = { + type = "string", + start = 11, + finish = 18, + parent = "", + [1] = "unary", + [2] = "'", + }, + }, + }, + [2] = { + type = "getglobal", + start = 23, + finish = 24, + parent = "", + [1] = "z", + }, +} +CHECK"x and y or '' .. z" +{ + type = "binary", + start = 0, + finish = 18, + op = { + type = "or", + start = 8, + finish = 10, + }, + [1] = { + type = "binary", + start = 0, finish = 7, - parent = 4, - [1] = 2, + parent = "", + op = { + type = "and", + start = 2, + finish = 5, + }, + [1] = { + type = "getglobal", + start = 0, + finish = 1, + parent = "", + [1] = "x", + }, + [2] = { + type = "getglobal", + start = 6, + finish = 7, + parent = "", + [1] = "y", + }, }, - [6] = { - type = "number", + [2] = { + type = "binary", start = 11, - finish = 11, - parent = 4, - [1] = 3, + finish = 18, + parent = "", + op = { + type = "..", + start = 14, + finish = 16, + }, + [1] = { + type = "string", + start = 11, + finish = 13, + parent = "", + [1] = "", + [2] = "'", + }, + [2] = { + type = "getglobal", + start = 17, + finish = 18, + parent = "", + [1] = "z", + }, }, } -- 幂运算从右向左连接 CHECK'1 ^ 2 ^ 3' { - [1] = { - type = "binary", - start = 1, - finish = 9, - op = { - type = "^", - start = 3, - finish = 3, - }, - [1] = 2, - [2] = 3, + type = "binary", + start = 0, + finish = 9, + op = { + type = "^", + start = 2, + finish = 3, }, - [2] = { - type = "number", - start = 1, + [1] = { + type = "integer", + start = 0, finish = 1, - parent = 1, + parent = "", [1] = 1, }, - [3] = { + [2] = { type = "binary", - start = 5, + start = 4, finish = 9, - parent = 1, + parent = "", op = { type = "^", - start = 7, + start = 6, finish = 7, }, - [1] = 4, - [2] = 5, - }, - [4] = { - type = "number", - start = 5, - finish = 5, - parent = 3, - [1] = 2, - }, - [5] = { - type = "number", - start = 9, - finish = 9, - parent = 3, - [1] = 3, + [1] = { + type = "integer", + start = 4, + finish = 5, + parent = "", + [1] = 2, + }, + [2] = { + type = "integer", + start = 8, + finish = 9, + parent = "", + [1] = 3, + }, }, } -- 连接运算从右向左连接 CHECK'1 .. 2 .. 3' { - [1] = { - type = "binary", - start = 1, - finish = 11, - op = { - type = "..", - start = 3, - finish = 4, - }, - [1] = 2, - [2] = 3, + type = "binary", + start = 0, + finish = 11, + op = { + type = "..", + start = 2, + finish = 4, }, - [2] = { - type = "number", - start = 1, + [1] = { + type = "integer", + start = 0, finish = 1, - parent = 1, + parent = "", [1] = 1, }, - [3] = { + [2] = { type = "binary", - start = 6, + start = 5, finish = 11, - parent = 1, + parent = "", op = { type = "..", - start = 8, + start = 7, finish = 9, }, - [1] = 4, - [2] = 5, + [1] = { + type = "integer", + start = 5, + finish = 6, + parent = "", + [1] = 2, + }, + [2] = { + type = "integer", + start = 10, + finish = 11, + parent = "", + [1] = 3, + }, }, - [4] = { - type = "number", - start = 6, - finish = 6, - parent = 3, - [1] = 2, +} +CHECK'1 + - - - - - - - 1' +{ + type = "binary", + start = 0, + finish = 19, + op = { + type = "+", + start = 2, + finish = 3, }, - [5] = { - type = "number", - start = 11, - finish = 11, - parent = 3, - [1] = 3, + [1] = { + type = "integer", + start = 0, + finish = 1, + parent = "", + [1] = 1, + }, + [2] = { + type = "integer", + start = 4, + finish = 19, + parent = "", + [1] = -1, }, } CHECK'(1)' { - [1] = { - exp = 2, - type = "paren", + type = "paren", + start = 0, + finish = 3, + exp = { + type = "integer", start = 1, - finish = 3, - }, - [2] = { - type = "number", - start = 2, finish = 2, - parent = 1, + parent = "", [1] = 1, }, } CHECK'(1 + 2)' { - [1] = { - type = "paren", - start = 1, - finish = 7, - exp = 2, - }, - [2] = { + type = "paren", + start = 0, + finish = 7, + exp = { type = "binary", - start = 2, + start = 1, finish = 6, - parent = 1, + parent = "", op = { type = "+", - start = 4, + start = 3, finish = 4, }, - [1] = 3, - [2] = 4, - }, - [3] = { - type = "number", - start = 2, - finish = 2, - parent = 2, - [1] = 1, - }, - [4] = { - type = "number", - start = 6, - finish = 6, - parent = 2, - [1] = 2, + [1] = { + type = "integer", + start = 1, + finish = 2, + parent = "", + [1] = 1, + }, + [2] = { + type = "integer", + start = 5, + finish = 6, + parent = "", + [1] = 2, + }, }, } CHECK'func(1)' { - [1] = { - type = "call", - start = 1, - finish = 7, - node = 2, - args = 3, - }, - [2] = { - type = "getglobal", - start = 1, - finish = 4, - parent = 1, - [1] = "func", - }, - [3] = { + type = "call", + start = 0, + finish = 7, + node = "", + args = { type = "callargs", - start = 5, + start = 4, finish = 7, - parent = 1, - [1] = 4, - }, - [4] = { - type = "number", - start = 6, - finish = 6, - parent = 3, - [1] = 1, + parent = "", + [1] = { + type = "integer", + start = 5, + finish = 6, + parent = "", + [1] = 1, + }, }, } CHECK'func(1, 2)' { - [1] = { - type = "call", - start = 1, - finish = 10, - node = 2, - args = 3, - }, - [2] = { - type = "getglobal", - start = 1, - finish = 4, - parent = 1, - [1] = "func", - }, - [3] = { + type = "call", + start = 0, + finish = 10, + node = "", + args = { type = "callargs", - start = 5, + start = 4, finish = 10, - parent = 1, - [1] = 4, - [2] = 5, - }, - [4] = { - type = "number", - start = 6, - finish = 6, - parent = 3, - [1] = 1, - }, - [5] = { - type = "number", - start = 9, - finish = 9, - parent = 3, - [1] = 2, + parent = "", + [1] = { + type = "integer", + start = 5, + finish = 6, + parent = "", + [1] = 1, + }, + [2] = { + type = "integer", + start = 8, + finish = 9, + parent = "", + [1] = 2, + }, }, } CHECK'func(...)' { - [1] = { - type = "call", - start = 1, - finish = 9, - node = 2, - args = 3, - }, - [2] = { - type = "getglobal", - start = 1, - finish = 4, - parent = 1, - [1] = "func", - }, - [3] = { + type = "call", + start = 0, + finish = 9, + node = "", + args = { type = "callargs", - start = 5, + start = 4, finish = 9, - parent = 1, - [1] = 4, - }, - [4] = { - type = "varargs", - start = 6, - finish = 8, - parent = 3, + parent = "", + [1] = { + type = "varargs", + start = 5, + finish = 8, + parent = "", + }, }, } CHECK'func(1, ...)' { - [1] = { - type = "call", - start = 1, - finish = 12, - node = 2, - args = 3, - }, - [2] = { - type = "getglobal", - start = 1, - finish = 4, - parent = 1, - [1] = "func", - }, - [3] = { + type = "call", + start = 0, + finish = 12, + node = "", + args = { type = "callargs", - start = 5, + start = 4, finish = 12, - parent = 1, - [1] = 4, - [2] = 5, - }, - [4] = { - type = "number", - start = 6, - finish = 6, - parent = 3, - [1] = 1, - }, - [5] = { - type = "varargs", - start = 9, - finish = 11, - parent = 3, + parent = "", + [1] = { + type = "integer", + start = 5, + finish = 6, + parent = "", + [1] = 1, + }, + [2] = { + type = "varargs", + start = 8, + finish = 11, + parent = "", + }, }, } CHECK'func ""' { - [1] = { - type = "call", - start = 1, - finish = 7, - node = 2, - args = 3, - }, - [2] = { - type = "getglobal", - start = 1, - finish = 4, - parent = 1, - [1] = "func", - }, - [3] = { + type = "call", + start = 0, + finish = 7, + node = "", + args = { type = "callargs", - start = 6, - finish = 7, - parent = 1, - [1] = 4, - }, - [4] = { - type = "string", - start = 6, + start = 5, finish = 7, - parent = 3, - [1] = "", - [2] = "\"", + parent = "", + [1] = { + type = "string", + start = 5, + finish = 7, + parent = "", + [1] = "", + [2] = "\"", + }, }, } CHECK'func {}' { - [1] = { - type = "call", - start = 1, - finish = 7, - node = 2, - args = 3, - }, - [2] = { - type = "getglobal", - start = 1, - finish = 4, - parent = 1, - [1] = "func", - }, - [3] = { + type = "call", + start = 0, + finish = 7, + node = "", + args = { type = "callargs", - start = 6, - finish = 7, - parent = 1, - [1] = 4, - }, - [4] = { - type = "table", - start = 6, + start = 5, finish = 7, - parent = 3, + parent = "", + [1] = { + type = "table", + start = 5, + bstart = 6, + finish = 7, + parent = "", + bfinish = 6, + }, }, } CHECK'table[1]' { - [1] = { - type = "getindex", - start = 1, - finish = 8, - node = 2, - index = 3, - }, - [2] = { - type = "getglobal", - start = 1, - finish = 5, - parent = 1, - [1] = "table", - }, - [3] = { - type = "number", - start = 7, + type = "getindex", + start = 0, + finish = 8, + node = "", + index = { + type = "integer", + start = 6, finish = 7, - parent = 1, + parent = "", [1] = 1, }, } -CHECK'get_point().x' +CHECK'table[[1]]' { - [1] = { - type = "getfield", - start = 1, - finish = 13, - node = 2, - dot = { - type = ".", - start = 12, - finish = 12, - }, - field = { - type = "field", - start = 13, - finish = 13, - [1] = "x", + type = "call", + start = 0, + finish = 10, + node = "", + args = { + type = "callargs", + start = 5, + finish = 10, + parent = "", + [1] = { + type = "string", + start = 5, + finish = 10, + parent = "", + [1] = '1', + [2] = '[[' }, }, - [2] = { - type = "call", - start = 1, - finish = 11, - parent = 1, - node = 3, +} +CHECK'get_point().x' +{ + type = "getfield", + start = 0, + finish = 13, + node = "", + dot = { + type = ".", + start = 11, + finish = 12, }, - [3] = { - type = "getglobal", - start = 1, - finish = 9, - parent = 2, - [1] = "get_point", + field = { + type = "field", + start = 12, + finish = 13, + parent = "", + [1] = "x", }, } CHECK'obj:remove()' { - [1] = { - type = "call", - start = 1, + type = "call", + start = 0, + finish = 12, + node = "", + args = { + type = "callargs", + start = 0, finish = 12, - node = 2, - }, - [2] = { - type = "getmethod", - start = 1, - finish = 10, - parent = 1, - node = 3, - colon = { - type = ":", - start = 4, + parent = "", + [1] = { + type = "self", + start = 3, finish = 4, + parent = "", + [1] = "self", }, - method = 4, - }, - [3] = { - type = "getglobal", - start = 1, - finish = 3, - parent = 2, - [1] = "obj", - }, - [4] = { - type = "method", - start = 5, - finish = 10, - parent = 2, - [1] = "remove", }, } CHECK'(...)[1]' { - [1] = { - type = "getindex", - start = 1, - finish = 8, - node = 2, - index = 4, - }, - [2] = { - type = "paren", - start = 1, - finish = 5, - parent = 1, - exp = 3, - }, - [3] = { - type = "varargs", - start = 2, - finish = 4, - parent = 2, - }, - [4] = { - type = "number", - start = 7, + type = "getindex", + start = 0, + finish = 8, + node = "", + index = { + type = "integer", + start = 6, finish = 7, - parent = 1, + parent = "", [1] = 1, }, } CHECK'function () end' { - [1] = { - type = "function", - start = 1, - finish = 15, + type = "function", + start = 0, + bstart = 11, + finish = 15, + keyword = { + [1] = 0, + [2] = 8, + [3] = 12, + [4] = 15, + }, + args = { + type = "funcargs", + start = 9, + finish = 11, + parent = "", }, + bfinish = 12, } CHECK'function (...) end' { - [1] = { - type = "function", - start = 1, - finish = 18, - args = 2, - }, - [2] = { + type = "function", + start = 0, + bstart = 14, + finish = 18, + keyword = { + [1] = 0, + [2] = 8, + [3] = 15, + [4] = 18, + }, + vararg = "", + args = { type = "funcargs", - start = 10, + start = 9, finish = 14, - parent = 1, - [1] = 3, - }, - [3] = { - type = "...", - start = 11, - finish = 13, - parent = 2, + parent = "", + [1] = { + type = "...", + start = 10, + finish = 13, + parent = "", + [1] = "...", + }, }, + bfinish = 15, } CHECK'function (a, ...) end' { - [1] = { - type = "function", - start = 1, - finish = 21, - args = 2, - locals = { - [1] = 3, - }, - }, - [2] = { + type = "function", + start = 0, + bstart = 17, + finish = 21, + keyword = { + [1] = 0, + [2] = 8, + [3] = 18, + [4] = 21, + }, + vararg = "", + args = { type = "funcargs", - start = 10, + start = 9, finish = 17, - parent = 1, - [1] = 3, - [2] = 4, - }, - [3] = { - type = "local", - start = 11, - finish = 11, - effect = 11, - parent = 2, - [1] = "a", - }, - [4] = { - type = "...", - start = 14, - finish = 16, - parent = 2, + parent = "", + [1] = { + type = "local", + start = 10, + finish = 11, + effect = 11, + parent = "", + [1] = "a", + }, + [2] = { + type = "...", + start = 13, + finish = 16, + parent = "", + [1] = "...", + }, }, + locals = "", + bfinish = 18, } CHECK'{}' { - [1] = { - type = "table", - start = 1, - finish = 2, - }, + type = "table", + start = 0, + bstart = 1, + finish = 2, + bfinish = 1, } CHECK'{...}' { - [1] = { - type = "table", - start = 1, - finish = 5, - [1] = 2, - }, - [2] = { + type = "table", + start = 0, + bstart = 1, + finish = 5, + bfinish = 4, + [1] = { type = "varargs", - start = 2, + start = 1, finish = 4, - parent = 1, + parent = "", }, } CHECK'{1, 2, 3}' { - [1] = { - type = "table", + type = "table", + start = 0, + bstart = 1, + finish = 9, + bfinish = 8, + [1] = { + type = "tableexp", start = 1, - finish = 9, - [1] = 2, - [2] = 3, - [3] = 4, - }, - [2] = { - type = "number", - start = 2, finish = 2, - parent = 1, - [1] = 1, + tindex = 1, + parent = "", + value = { + type = "integer", + start = 1, + finish = 2, + parent = "", + [1] = 1, + }, }, - [3] = { - type = "number", - start = 5, + [2] = { + type = "tableexp", + start = 4, finish = 5, - parent = 1, - [1] = 2, + tindex = 2, + parent = "", + value = { + type = "integer", + start = 4, + finish = 5, + parent = "", + [1] = 2, + }, }, - [4] = { - type = "number", - start = 8, + [3] = { + type = "tableexp", + start = 7, finish = 8, - parent = 1, - [1] = 3, + tindex = 3, + parent = "", + value = { + type = "integer", + start = 7, + finish = 8, + parent = "", + [1] = 3, + }, }, } CHECK'{x = 1, y = 2}' { - [1] = { - type = "table", - start = 1, - finish = 14, - [1] = 2, - [2] = 4, - }, - [2] = { + type = "table", + start = 0, + bstart = 1, + finish = 14, + bfinish = 13, + [1] = { type = "tablefield", - start = 2, - finish = 6, - parent = 1, + start = 1, + finish = 2, + range = 6, + parent = "", + node = "", field = { type = "field", - start = 2, + start = 1, finish = 2, + parent = "", [1] = "x", }, - value = 3, - }, - [3] = { - type = "number", - start = 6, - finish = 6, - parent = 2, - [1] = 1, + value = { + type = "integer", + start = 5, + finish = 6, + parent = "", + [1] = 1, + }, }, - [4] = { + [2] = { type = "tablefield", - start = 9, - finish = 13, - parent = 1, + start = 8, + finish = 9, + range = 13, + parent = "", + node = "", field = { type = "field", - start = 9, + start = 8, finish = 9, + parent = "", [1] = "y", }, - value = 5, - }, - [5] = { - type = "number", - start = 13, - finish = 13, - parent = 4, - [1] = 2, + value = { + type = "integer", + start = 12, + finish = 13, + parent = "", + [1] = 2, + }, }, } CHECK'{["x"] = 1, ["y"] = 2}' { - [1] = { - type = "table", - start = 1, - finish = 22, - [1] = 2, - [2] = 6, - }, - [2] = { + type = "table", + start = 0, + bstart = 1, + finish = 22, + bfinish = 21, + [1] = { type = "tableindex", - start = 2, - finish = 10, - parent = 1, - index = 3, - value = 5, - }, - [3] = { - type = "index", - start = 2, + start = 1, finish = 6, - parent = 2, - index = 4, - }, - [4] = { - type = "string", - start = 3, - finish = 5, - parent = 3, - [1] = "x", - [2] = "\"", - }, - [5] = { - type = "number", - start = 10, - finish = 10, - parent = 2, - [1] = 1, + range = 10, + parent = "", + node = "", + index = { + type = "string", + start = 2, + finish = 5, + parent = "", + [1] = "x", + [2] = "\"", + }, + value = { + type = "integer", + start = 9, + finish = 10, + parent = "", + [1] = 1, + }, }, - [6] = { + [2] = { type = "tableindex", - start = 13, - finish = 21, - parent = 1, - index = 7, - value = 9, - }, - [7] = { - type = "index", - start = 13, + start = 12, finish = 17, - parent = 6, - index = 8, - }, - [8] = { - type = "string", - start = 14, - finish = 16, - parent = 7, - [1] = "y", - [2] = "\"", - }, - [9] = { - type = "number", - start = 21, - finish = 21, - parent = 6, - [1] = 2, + range = 21, + parent = "", + node = "", + index = { + type = "string", + start = 13, + finish = 16, + parent = "", + [1] = "y", + [2] = "\"", + }, + value = { + type = "integer", + start = 20, + finish = 21, + parent = "", + [1] = 2, + }, }, } CHECK'{[x] = 1, [y] = 2}' { - [1] = { - type = "table", - start = 1, - finish = 18, - [1] = 2, - [2] = 6, - }, - [2] = { + type = "table", + start = 0, + bstart = 1, + finish = 18, + bfinish = 17, + [1] = { type = "tableindex", - start = 2, - finish = 8, - parent = 1, - index = 3, - value = 5, - }, - [3] = { - type = "index", - start = 2, + start = 1, finish = 4, - parent = 2, - index = 4, - }, - [4] = { - type = "getglobal", - start = 3, - finish = 3, - parent = 3, - [1] = "x", - }, - [5] = { - type = "number", - start = 8, - finish = 8, - parent = 2, - [1] = 1, + range = 8, + parent = "", + node = "", + index = { + type = "getglobal", + start = 2, + finish = 3, + parent = "", + [1] = "x", + }, + value = { + type = "integer", + start = 7, + finish = 8, + parent = "", + [1] = 1, + }, }, - [6] = { + [2] = { type = "tableindex", - start = 11, - finish = 17, - parent = 1, - index = 7, - value = 9, - }, - [7] = { - type = "index", - start = 11, + start = 10, finish = 13, - parent = 6, - index = 8, - }, - [8] = { - type = "getglobal", - start = 12, - finish = 12, - parent = 7, - [1] = "y", - }, - [9] = { - type = "number", - start = 17, - finish = 17, - parent = 6, - [1] = 2, - }, -} -CHECK'{{}}' -{ - [1] = { - type = "table", - start = 1, - finish = 4, - [1] = 2, - }, - [2] = { - type = "table", - start = 2, - finish = 3, - parent = 1, + range = 17, + parent = "", + node = "", + index = { + type = "getglobal", + start = 11, + finish = 12, + parent = "", + [1] = "y", + }, + value = { + type = "integer", + start = 16, + finish = 17, + parent = "", + [1] = 2, + }, }, } -CHECK'{ a = { b = { c = {} } } }' +CHECK'{x = 1, y = 2, 3}' { - [1] = { - type = "table", - start = 1, - finish = 26, - [1] = 2, - }, - [2] = { + type = "table", + start = 0, + bstart = 1, + finish = 17, + bfinish = 16, + [1] = { type = "tablefield", - start = 3, - finish = 24, - parent = 1, + start = 1, + finish = 2, + range = 6, + parent = "", + node = "", field = { type = "field", - start = 3, - finish = 3, - [1] = "a", + start = 1, + finish = 2, + parent = "", + [1] = "x", + }, + value = { + type = "integer", + start = 5, + finish = 6, + parent = "", + [1] = 1, }, - value = 3, - }, - [3] = { - type = "table", - start = 7, - finish = 24, - parent = 2, - [1] = 4, }, - [4] = { + [2] = { type = "tablefield", - start = 9, - finish = 22, - parent = 3, + start = 8, + finish = 9, + range = 13, + parent = "", + node = "", field = { type = "field", - start = 9, + start = 8, finish = 9, - [1] = "b", + parent = "", + [1] = "y", + }, + value = { + type = "integer", + start = 12, + finish = 13, + parent = "", + [1] = 2, }, - value = 5, }, - [5] = { - type = "table", - start = 13, - finish = 22, - parent = 4, - [1] = 6, + [3] = { + type = "tableexp", + start = 15, + finish = 16, + tindex = 1, + parent = "", + value = { + type = "integer", + start = 15, + finish = 16, + parent = "", + [1] = 3, + }, + }, +} +CHECK'{{}}' +{ + type = "table", + start = 0, + bstart = 1, + finish = 4, + bfinish = 3, + [1] = { + type = "tableexp", + start = 1, + finish = 3, + tindex = 1, + parent = "", + value = { + type = "table", + start = 1, + bstart = 2, + finish = 3, + parent = "", + bfinish = 2, + }, }, - [6] = { +} +CHECK'{ a = { b = { c = {} } } }' +{ + type = "table", + start = 0, + bstart = 1, + finish = 26, + bfinish = 25, + [1] = { type = "tablefield", - start = 15, - finish = 20, - parent = 5, + start = 2, + finish = 3, + range = 24, + parent = "", + node = "", field = { type = "field", - start = 15, - finish = 15, - [1] = "c", + start = 2, + finish = 3, + parent = "", + [1] = "a", + }, + value = { + type = "table", + start = 6, + bstart = 7, + finish = 24, + parent = "", + bfinish = 23, + [1] = { + type = "tablefield", + start = 8, + finish = 9, + range = 22, + parent = "", + node = "", + field = { + type = "field", + start = 8, + finish = 9, + parent = "", + [1] = "b", + }, + value = { + type = "table", + start = 12, + bstart = 13, + finish = 22, + parent = "", + bfinish = 21, + [1] = { + type = "tablefield", + start = 14, + finish = 15, + range = 20, + parent = "", + node = "", + field = { + type = "field", + start = 14, + finish = 15, + parent = "", + [1] = "c", + }, + value = { + type = "table", + start = 18, + bstart = 19, + finish = 20, + parent = "", + bfinish = 19, + }, + }, + }, + }, }, - value = 7, - }, - [7] = { - type = "table", - start = 19, - finish = 20, - parent = 6, }, } CHECK'{{}, {}, {{}, {}}}' { - [1] = { - type = "table", + type = "table", + start = 0, + bstart = 1, + finish = 18, + bfinish = 17, + [1] = { + type = "tableexp", start = 1, - finish = 18, - [1] = 2, - [2] = 3, - [3] = 4, - }, - [2] = { - type = "table", - start = 2, finish = 3, - parent = 1, + tindex = 1, + parent = "", + value = { + type = "table", + start = 1, + bstart = 2, + finish = 3, + parent = "", + bfinish = 2, + }, }, - [3] = { - type = "table", - start = 6, + [2] = { + type = "tableexp", + start = 5, finish = 7, - parent = 1, + tindex = 2, + parent = "", + value = { + type = "table", + start = 5, + bstart = 6, + finish = 7, + parent = "", + bfinish = 6, + }, }, - [4] = { - type = "table", - start = 10, + [3] = { + type = "tableexp", + start = 9, finish = 17, - parent = 1, - [1] = 5, - [2] = 6, - }, - [5] = { - type = "table", - start = 11, - finish = 12, - parent = 4, - }, - [6] = { - type = "table", - start = 15, - finish = 16, - parent = 4, + tindex = 3, + parent = "", + value = { + type = "table", + start = 9, + bstart = 10, + finish = 17, + parent = "", + bfinish = 16, + [1] = { + type = "tableexp", + start = 10, + finish = 12, + tindex = 1, + parent = "", + value = { + type = "table", + start = 10, + bstart = 11, + finish = 12, + parent = "", + bfinish = 11, + }, + }, + [2] = { + type = "tableexp", + start = 14, + finish = 16, + tindex = 2, + parent = "", + value = { + type = "table", + start = 14, + bstart = 15, + finish = 16, + parent = "", + bfinish = 15, + }, + }, + }, }, } CHECK'{1, 2, 3,}' { - [1] = { - type = "table", + type = "table", + start = 0, + bstart = 1, + finish = 10, + bfinish = 9, + [1] = { + type = "tableexp", start = 1, - finish = 10, - [1] = 2, - [2] = 3, - [3] = 4, - }, - [2] = { - type = "number", - start = 2, finish = 2, - parent = 1, - [1] = 1, + tindex = 1, + parent = "", + value = { + type = "integer", + start = 1, + finish = 2, + parent = "", + [1] = 1, + }, }, - [3] = { - type = "number", - start = 5, + [2] = { + type = "tableexp", + start = 4, finish = 5, - parent = 1, - [1] = 2, + tindex = 2, + parent = "", + value = { + type = "integer", + start = 4, + finish = 5, + parent = "", + [1] = 2, + }, }, - [4] = { - type = "number", - start = 8, + [3] = { + type = "tableexp", + start = 7, finish = 8, - parent = 1, - [1] = 3, + tindex = 3, + parent = "", + value = { + type = "integer", + start = 7, + finish = 8, + parent = "", + [1] = 3, + }, }, } CHECK 'notify' { - [1] = { + type = "getglobal", + start = 0, + finish = 6, + [1] = "notify", +} + +CHECK 'a ^ - b' +{ + type = "binary", + start = 0, + finish = 7, + op = { + type = "^", + start = 2, + finish = 3, + }, + [1] = { type = "getglobal", - start = 1, - finish = 6, - [1] = "notify", + start = 0, + finish = 1, + parent = "", + [1] = "a", + }, + [2] = { + type = "unary", + start = 4, + finish = 7, + parent = "", + op = { + type = "-", + start = 4, + finish = 5, + }, + [1] = { + type = "getglobal", + start = 6, + finish = 7, + parent = "", + [1] = "b", + }, + }, +} + +CHECK [=[ +{ +[[]] +}]=] +{ + type = "table", + start = 0, + bstart = 1, + finish = 20001, + bfinish = 20000, + [1] = { + type = "tableexp", + start = 10000, + finish = 10004, + tindex = 1, + parent = "", + value = { + type = "string", + start = 10000, + finish = 10004, + parent = "", + [1] = "", + [2] = "[[", + }, + }, +} + +CHECK [[ +{ + [xxx] +} +]] +{ + type = "table", + start = 0, + bstart = 1, + finish = 20001, + bfinish = 20000, + [1] = { + type = "tableindex", + start = 10004, + finish = 10009, + parent = "", + node = "", + index = { + type = "getglobal", + start = 10005, + finish = 10008, + parent = "", + [1] = "xxx", + }, }, } diff --git a/test/ast/Lua.lua b/test/ast/Lua.lua index 03a07c8..a1605af 100644 --- a/test/ast/Lua.lua +++ b/test/ast/Lua.lua @@ -1,766 +1,743 @@ CHECK'' { - [1] = { - type = "main", - }, + type = "main", + start = 0, + bstart = 0, + finish = 0, + locals = "", + bfinish = 0, } CHECK';;;' { - [1] = { - type = "main", - }, + type = "main", + start = 0, + bstart = 0, + finish = 3, + locals = "", + bfinish = 3, } CHECK';;;x = 1' { - [1] = { - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 8, + locals = "", + bfinish = 8, + [1] = { type = "setglobal", - start = 4, + start = 3, finish = 4, - parent = 1, - value = 3, + range = 8, + parent = "", + node = "", + value = { + type = "integer", + start = 7, + finish = 8, + parent = "", + [1] = 1, + }, [1] = "x", }, - [3] = { - type = "number", - start = 8, - finish = 8, - parent = 2, - [1] = 1, - }, } CHECK'x, y, z = 1, 2, 3' { - [1] = { - type = "main", - [1] = 2, - [2] = 4, - [3] = 6, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 17, + locals = "", + bfinish = 17, + [1] = { type = "setglobal", - start = 1, + start = 0, finish = 1, - parent = 1, - value = 3, + range = 11, + parent = "", + node = "", + value = { + type = "integer", + start = 10, + finish = 11, + parent = "", + [1] = 1, + }, [1] = "x", }, - [3] = { - type = "number", - start = 11, - finish = 11, - parent = 2, - [1] = 1, - }, - [4] = { + [2] = { type = "setglobal", - start = 4, + start = 3, finish = 4, - parent = 1, - value = 5, + range = 14, + parent = "", + node = "", + value = { + type = "integer", + start = 13, + finish = 14, + parent = "", + [1] = 2, + }, [1] = "y", }, - [5] = { - type = "number", - start = 14, - finish = 14, - parent = 4, - [1] = 2, - }, - [6] = { + [3] = { type = "setglobal", - start = 7, + start = 6, finish = 7, - parent = 1, - value = 7, + range = 17, + parent = "", + node = "", + value = { + type = "integer", + start = 16, + finish = 17, + parent = "", + [1] = 3, + }, [1] = "z", }, - [7] = { - type = "number", - start = 17, - finish = 17, - parent = 6, - [1] = 3, - }, } CHECK'local x, y, z' { - [1] = { - type = "main", - locals = { - [1] = 2, - [2] = 3, - [3] = 4, - }, - [1] = 2, - [2] = 3, - [3] = 4, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 13, + locals = "", + bfinish = 13, + [1] = { type = "local", - start = 7, + start = 6, finish = 7, - effect = 14, - parent = 1, + effect = 13, + parent = "", + locPos = 0, [1] = "x", }, - [3] = { + [2] = { type = "local", - start = 10, + start = 9, finish = 10, - effect = 14, - parent = 1, + effect = 13, + parent = "", [1] = "y", }, - [4] = { + [3] = { type = "local", - start = 13, + start = 12, finish = 13, - effect = 14, - parent = 1, + effect = 13, + parent = "", [1] = "z", }, } CHECK'local x, y, z = 1, 2, 3' { - [1] = { - type = "main", - locals = { - [1] = 2, - [2] = 4, - [3] = 6, - }, - [1] = 2, - [2] = 4, - [3] = 6, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 23, + locals = "", + bfinish = 23, + [1] = { type = "local", - start = 7, + start = 6, finish = 7, - effect = 24, - parent = 1, - value = 3, + effect = 23, + range = 17, + parent = "", + locPos = 0, + value = { + type = "integer", + start = 16, + finish = 17, + parent = "", + [1] = 1, + }, [1] = "x", }, - [3] = { - type = "number", - start = 17, - finish = 17, - parent = 2, - [1] = 1, - }, - [4] = { + [2] = { type = "local", - start = 10, + start = 9, finish = 10, - effect = 24, - parent = 1, - value = 5, + effect = 23, + range = 20, + parent = "", + value = { + type = "integer", + start = 19, + finish = 20, + parent = "", + [1] = 2, + }, [1] = "y", }, - [5] = { - type = "number", - start = 20, - finish = 20, - parent = 4, - [1] = 2, - }, - [6] = { + [3] = { type = "local", - start = 13, + start = 12, finish = 13, - effect = 24, - parent = 1, - value = 7, + effect = 23, + range = 23, + parent = "", + value = { + type = "integer", + start = 22, + finish = 23, + parent = "", + [1] = 3, + }, [1] = "z", }, - [7] = { - type = "number", - start = 23, - finish = 23, - parent = 6, - [1] = 3, - }, } CHECK'local x, y = y, x' { - [1] = { - locals = { - [1] = 2, - [2] = 4, - }, - type = "main", - [1] = 2, - [2] = 4, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 17, + locals = "", + bfinish = 17, + [1] = { type = "local", - start = 7, + start = 6, finish = 7, - effect = 18, - parent = 1, - value = 3, + effect = 17, + range = 14, + parent = "", + locPos = 0, + value = { + type = "getglobal", + start = 13, + finish = 14, + parent = "", + node = "", + [1] = "y", + }, [1] = "x", }, - [3] = { - type = "getglobal", - start = 14, - finish = 14, - parent = 2, - [1] = "y", - }, - [4] = { + [2] = { type = "local", - start = 10, + start = 9, finish = 10, - effect = 18, - parent = 1, - value = 5, + effect = 17, + range = 17, + parent = "", + value = { + type = "getglobal", + start = 16, + finish = 17, + parent = "", + node = "", + [1] = "x", + }, [1] = "y", }, - [5] = { - type = "getglobal", - start = 17, - finish = 17, - parent = 4, - [1] = "x", - }, } CHECK'local x, y = f()' { - [1] = { - locals = { - [1] = 2, - [2] = 6, - }, - type = "main", - [1] = 2, - [2] = 6, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 16, + locals = "", + bfinish = 16, + [1] = { type = "local", - start = 7, + start = 6, finish = 7, - effect = 17, - parent = 1, - value = 3, + effect = 16, + range = 16, + parent = "", + locPos = 0, + value = { + type = "select", + start = 13, + finish = 16, + parent = "", + vararg = "", + sindex = 1, + }, [1] = "x", }, - [3] = { - type = "select", - parent = 2, - vararg = 4, - index = 1, - }, - [4] = { - type = "call", - start = 14, - finish = 16, - parent = 3, - extParent = { - [1] = 7, - }, - node = 5, - }, - [5] = { - type = "getglobal", - start = 14, - finish = 14, - parent = 4, - [1] = "f", - }, - [6] = { + [2] = { type = "local", - start = 10, + start = 9, finish = 10, - effect = 17, - parent = 1, - value = 7, + effect = 16, + range = 16, + parent = "", + value = { + type = "select", + start = 13, + finish = 16, + parent = "", + vararg = "", + sindex = 2, + }, [1] = "y", }, - [7] = { - type = "select", - parent = 6, - vararg = 4, - index = 2, - }, } CHECK'local x, y = (f())' { - [1] = { - locals = { - [1] = 2, - [2] = 6, - }, - type = "main", - [1] = 2, - [2] = 6, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 18, + locals = "", + bfinish = 18, + [1] = { type = "local", - start = 7, + start = 6, finish = 7, - effect = 19, - parent = 1, - value = 3, + effect = 18, + range = 18, + parent = "", + locPos = 0, + value = { + type = "paren", + start = 13, + finish = 18, + parent = "", + exp = { + type = "call", + start = 14, + finish = 17, + parent = "", + node = "", + }, + }, [1] = "x", }, - [3] = { - type = "paren", - start = 14, - finish = 18, - parent = 2, - exp = 4, - }, - [4] = { - type = "call", - start = 15, - finish = 17, - parent = 3, - node = 5, - }, - [5] = { - type = "getglobal", - start = 15, - finish = 15, - parent = 4, - [1] = "f", - }, - [6] = { + [2] = { type = "local", - start = 10, + start = 9, finish = 10, - effect = 19, - parent = 1, + effect = 18, + parent = "", [1] = "y", }, } CHECK'local x, y = f(), nil' { - [1] = { - locals = { - [1] = 2, - [2] = 6, - }, - type = "main", - [1] = 2, - [2] = 6, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 21, + locals = "", + bfinish = 21, + [1] = { type = "local", - start = 7, + start = 6, finish = 7, - effect = 22, - parent = 1, - value = 3, + effect = 21, + range = 16, + parent = "", + locPos = 0, + value = { + type = "select", + start = 13, + finish = 16, + parent = "", + vararg = "", + sindex = 1, + }, [1] = "x", }, - [3] = { - type = "select", - parent = 2, - vararg = 4, - index = 1, - }, - [4] = { - type = "call", - start = 14, - finish = 16, - parent = 3, - node = 5, - }, - [5] = { - type = "getglobal", - start = 14, - finish = 14, - parent = 4, - [1] = "f", - }, - [6] = { + [2] = { type = "local", - start = 10, + start = 9, finish = 10, - effect = 22, - parent = 1, - value = 7, + effect = 21, + range = 21, + parent = "", + value = { + type = "nil", + start = 18, + finish = 21, + parent = "", + }, [1] = "y", }, - [7] = { - type = "nil", - start = 19, - finish = 21, - parent = 6, - }, } CHECK'local x, y = ...' { - [1] = { - locals = { - [1] = 2, - [2] = 5, - }, - type = "main", - [1] = 2, - [2] = 5, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 16, + locals = "", + bfinish = 16, + [1] = { type = "local", - start = 7, + start = 6, finish = 7, - effect = 17, - parent = 1, - value = 3, - [1] = "x", - }, - [3] = { - type = "select", - parent = 2, - vararg = 4, - index = 1, - }, - [4] = { - type = "varargs", - start = 14, - finish = 16, - parent = 3, - extParent = { - [1] = 6, + effect = 16, + range = 16, + parent = "", + locPos = 0, + value = { + type = "select", + start = 13, + finish = 16, + parent = "", + vararg = "", + sindex = 1, }, + [1] = "x", }, - [5] = { + [2] = { type = "local", - start = 10, + start = 9, finish = 10, - effect = 17, - parent = 1, - value = 6, + effect = 16, + range = 16, + parent = "", + value = { + type = "select", + start = 13, + finish = 16, + parent = "", + vararg = "", + sindex = 2, + }, [1] = "y", }, - [6] = { - type = "select", - parent = 5, - vararg = 4, - index = 2, - }, } CHECK'local x, y = (...)' { - [1] = { - locals = { - [1] = 2, - [2] = 5, - }, - type = "main", - [1] = 2, - [2] = 5, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 18, + locals = "", + bfinish = 18, + [1] = { type = "local", - start = 7, + start = 6, finish = 7, - effect = 19, - parent = 1, - value = 3, + effect = 18, + range = 18, + parent = "", + locPos = 0, + value = { + type = "paren", + start = 13, + finish = 18, + parent = "", + exp = { + type = "varargs", + start = 14, + finish = 17, + parent = "", + }, + }, [1] = "x", }, - [3] = { - type = "paren", - start = 14, - finish = 18, - parent = 2, - exp = 4, - }, - [4] = { - type = "varargs", - start = 15, - finish = 17, - parent = 3, - }, - [5] = { + [2] = { type = "local", - start = 10, + start = 9, finish = 10, - effect = 19, - parent = 1, + effect = 18, + parent = "", [1] = "y", }, } CHECK'local x, y = ..., nil' { - [1] = { - locals = { - [1] = 2, - [2] = 5, - }, - type = "main", - [1] = 2, - [2] = 5, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 21, + locals = "", + bfinish = 21, + [1] = { type = "local", - start = 7, + start = 6, finish = 7, - effect = 22, - parent = 1, - value = 3, + effect = 21, + range = 16, + parent = "", + locPos = 0, + value = { + type = "select", + start = 13, + finish = 16, + parent = "", + vararg = "", + sindex = 1, + }, [1] = "x", }, - [3] = { - type = "select", - parent = 2, - vararg = 4, - index = 1, - }, - [4] = { - type = "varargs", - start = 14, - finish = 16, - parent = 3, - }, - [5] = { + [2] = { type = "local", - start = 10, + start = 9, finish = 10, - effect = 22, - parent = 1, - value = 6, + effect = 21, + range = 21, + parent = "", + value = { + type = "nil", + start = 18, + finish = 21, + parent = "", + }, [1] = "y", }, - [6] = { - type = "nil", - start = 19, - finish = 21, - parent = 5, - }, } CHECK'local x , y = 1' { - [1] = { - locals = { - [1] = 2, - [2] = 5, - }, - type = "main", - [1] = 2, - [2] = 5, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 30, + locals = "", + bfinish = 30, + [1] = { type = "local", - start = 7, + start = 6, finish = 7, - effect = 31, - parent = 1, - value = 4, + effect = 30, + range = 30, + parent = "", + locPos = 0, + value = { + type = "integer", + start = 29, + finish = 30, + parent = "", + [1] = 1, + }, attrs = { - [1] = 3, + type = "localattrs", + parent = "", + [1] = { + type = "localattr", + start = 8, + finish = 15, + parent = "", + [1] = "const", + }, }, [1] = "x", }, - [3] = { - type = "localattr", - start = 10, - finish = 14, - parent = 2, - [1] = "const", - }, - [4] = { - type = "number", - start = 30, - finish = 30, - parent = 2, - [1] = 1, - }, - [5] = { + [2] = { type = "local", - start = 18, + start = 17, finish = 18, - effect = 31, - parent = 1, + effect = 30, + parent = "", attrs = { - [1] = 6, + type = "localattrs", + parent = "", + [1] = { + type = "localattr", + start = 19, + finish = 26, + parent = "", + [1] = "close", + }, }, [1] = "y", }, - [6] = { - type = "localattr", - start = 21, - finish = 25, - parent = 5, - [1] = "close", - }, } CHECK[[ x = 1 y = 2 ]] { - [1] = { - type = "main", - [1] = 2, - [2] = 4, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 20000, + locals = "", + bfinish = 20000, + [1] = { type = "setglobal", - start = 1, + start = 0, finish = 1, - parent = 1, - value = 3, + range = 5, + parent = "", + node = "", + value = { + type = "integer", + start = 4, + finish = 5, + parent = "", + [1] = 1, + }, [1] = "x", }, - [3] = { - type = "number", - start = 5, - finish = 5, - parent = 2, - [1] = 1, - }, - [4] = { + [2] = { type = "setglobal", - start = 7, - finish = 7, - parent = 1, - value = 5, + start = 10000, + finish = 10001, + range = 10005, + parent = "", + node = "", + value = { + type = "integer", + start = 10004, + finish = 10005, + parent = "", + [1] = 2, + }, [1] = "y", }, - [5] = { - type = "number", - start = 11, - finish = 11, - parent = 4, - [1] = 2, - }, } CHECK[[ x, y = 1, 2 ]] { - [1] = { - type = "main", - [1] = 2, - [2] = 4, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 10000, + locals = "", + bfinish = 10000, + [1] = { type = "setglobal", - start = 1, + start = 0, finish = 1, - parent = 1, - value = 3, + range = 8, + parent = "", + node = "", + value = { + type = "integer", + start = 7, + finish = 8, + parent = "", + [1] = 1, + }, [1] = "x", }, - [3] = { - type = "number", - start = 8, - finish = 8, - parent = 2, - [1] = 1, - }, - [4] = { + [2] = { type = "setglobal", - start = 4, + start = 3, finish = 4, - parent = 1, - value = 5, + range = 11, + parent = "", + node = "", + value = { + type = "integer", + start = 10, + finish = 11, + parent = "", + [1] = 2, + }, [1] = "y", }, - [5] = { - type = "number", - start = 11, - finish = 11, - parent = 4, - [1] = 2, - }, } CHECK[[ local function a() return end]] { - [1] = { - locals = { - [1] = 2, - }, - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 20003, + locals = "", + bfinish = 20003, + [1] = { type = "local", - start = 16, + start = 15, + vstart = 6, finish = 16, - effect = 1, - parent = 1, - value = 3, + effect = 16, + range = 20003, + parent = "", + locPos = 0, + value = { + type = "function", + start = 6, + bstart = 18, + finish = 20003, + keyword = { + [1] = 6, + [2] = 14, + [3] = 20000, + [4] = 20003, + }, + parent = "", + args = { + type = "funcargs", + start = 16, + finish = 18, + parent = "", + }, + returns = "", + bfinish = 20000, + hasReturn = true, + [1] = { + type = "return", + start = 10004, + finish = 10010, + parent = "", + }, + }, [1] = "a", }, - [3] = { - type = "function", - start = 1, - finish = 33, - parent = 2, - [1] = 4, - }, - [4] = { - type = "return", - start = 24, - finish = 30, - parent = 3, - }, } CHECK[[ local function f() return f() end]] { - [1] = { - locals = { - [1] = 2, - }, - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 20003, + locals = "", + bfinish = 20003, + [1] = { type = "local", - start = 16, + start = 15, + vstart = 6, finish = 16, - effect = 1, - parent = 1, - value = 3, - ref = { - [1] = 6, + effect = 16, + range = 20003, + parent = "", + locPos = 0, + value = { + type = "function", + start = 6, + bstart = 18, + finish = 20003, + keyword = { + [1] = 6, + [2] = 14, + [3] = 20000, + [4] = 20003, + }, + parent = "", + args = { + type = "funcargs", + start = 16, + finish = 18, + parent = "", + }, + returns = "", + bfinish = 20000, + hasReturn = true, + [1] = { + type = "return", + start = 10004, + finish = 10014, + parent = "", + [1] = { + type = "call", + start = 10011, + finish = 10014, + parent = "", + node = "", + }, + }, }, - [1] = "f", - }, - [3] = { - type = "function", - start = 1, - finish = 37, - parent = 2, - [1] = 4, - }, - [4] = { - type = "return", - start = 24, - finish = 33, - parent = 3, - [1] = 5, - }, - [5] = { - type = "call", - start = 31, - finish = 33, - parent = 4, - node = 6, - }, - [6] = { - type = "getlocal", - start = 31, - finish = 31, - parent = 5, + ref = "", [1] = "f", }, } @@ -769,63 +746,67 @@ local function a(b, c) return end]] { - [1] = { - locals = { - [1] = 2, - }, - type = "main", - [1] = 2, - }, - [2] = { + type = "main", + start = 0, + bstart = 0, + finish = 20003, + locals = "", + bfinish = 20003, + [1] = { type = "local", - start = 16, + start = 15, + vstart = 6, finish = 16, - effect = 1, - parent = 1, - value = 3, - [1] = "a", - }, - [3] = { - locals = { - [1] = 5, - [2] = 6, + effect = 16, + range = 20003, + parent = "", + locPos = 0, + value = { + type = "function", + start = 6, + bstart = 22, + finish = 20003, + keyword = { + [1] = 6, + [2] = 14, + [3] = 20000, + [4] = 20003, + }, + parent = "", + args = { + type = "funcargs", + start = 16, + finish = 22, + parent = "", + [1] = { + type = "local", + start = 17, + finish = 18, + effect = 18, + parent = "", + [1] = "b", + }, + [2] = { + type = "local", + start = 20, + finish = 21, + effect = 21, + parent = "", + [1] = "c", + }, + }, + locals = "", + returns = "", + bfinish = 20000, + hasReturn = true, + [1] = { + type = "return", + start = 10004, + finish = 10010, + parent = "", + }, }, - type = "function", - start = 1, - finish = 37, - parent = 2, - args = 4, - [1] = 7, - }, - [4] = { - type = "funcargs", - start = 17, - finish = 22, - parent = 3, - [1] = 5, - [2] = 6, - }, - [5] = { - type = "local", - start = 18, - finish = 18, - effect = 18, - parent = 4, - [1] = "b", - }, - [6] = { - type = "local", - start = 21, - finish = 21, - effect = 21, - parent = 4, - [1] = "c", - }, - [7] = { - type = "return", - start = 28, - finish = 34, - parent = 3, + [1] = "a", }, } @@ -836,112 +817,635 @@ end y, z = 3, 4 ]] { - [01] = { - locals = { - [1] = 2, - [2] = 4, - [3] = 6, - [4] = 7, - }, - type = "main", - [1] = 2, - [2] = 4, - [3] = 6, - [4] = 7, - [5] = 9, - [6] = 11, - }, - [02] = { - type = "local", - start = 7, + type = "main", + start = 0, + bstart = 0, + finish = 40000, + locals = "", + bfinish = 40000, + [1] = { + type = "local", + start = 6, finish = 7, - effect = 21, - parent = 1, - value = 3, + effect = 20, + range = 17, + parent = "", + locPos = 0, + value = { + type = "integer", + start = 16, + finish = 17, + parent = "", + [1] = 1, + }, [1] = "x", }, - [03] = { - type = "number", - start = 17, - finish = 17, - parent = 2, - [1] = 1, - }, - [04] = { + [2] = { type = "local", - start = 10, + start = 9, finish = 10, - effect = 21, - parent = 1, - value = 5, - ref = { - [1] = 9, + effect = 20, + range = 20, + parent = "", + value = { + type = "integer", + start = 19, + finish = 20, + parent = "", + [1] = 2, }, + ref = "", [1] = "y", }, - [05] = { - type = "number", - start = 20, - finish = 20, - parent = 4, - [1] = 2, - }, - [06] = { + [3] = { type = "local", - start = 13, + start = 12, finish = 13, - effect = 21, - parent = 1, - ref = { - [1] = 11, - }, + effect = 20, + parent = "", + ref = "", [1] = "z", }, - [07] = { + [4] = { type = "local", - start = 37, - finish = 37, - effect = 22, - parent = 1, - value = 8, + start = 10015, + vstart = 10006, + finish = 10016, + effect = 10016, + range = 20003, + parent = "", + locPos = 10000, + value = { + type = "function", + start = 10006, + bstart = 10018, + finish = 20003, + keyword = { + [1] = 10006, + [2] = 10014, + [3] = 20000, + [4] = 20003, + }, + parent = "", + args = { + type = "funcargs", + start = 10016, + finish = 10018, + parent = "", + }, + bfinish = 20000, + }, [1] = "f", }, - [08] = { - type = "function", - start = 22, - finish = 43, - parent = 7, - }, - [09] = { + [5] = { type = "setlocal", - start = 45, - finish = 45, - parent = 1, - loc = 4, - value = 10, + start = 30000, + finish = 30001, + range = 30008, + parent = "", + node = "", + value = { + type = "integer", + start = 30007, + finish = 30008, + parent = "", + [1] = 3, + }, [1] = "y", }, - [10] = { - type = "number", - start = 52, - finish = 52, - parent = 9, - [1] = 3, - }, - [11] = { + [6] = { type = "setlocal", - start = 48, - finish = 48, - parent = 1, - loc = 6, - value = 12, + start = 30003, + finish = 30004, + range = 30011, + parent = "", + node = "", + value = { + type = "integer", + start = 30010, + finish = 30011, + parent = "", + [1] = 4, + }, + [1] = "z", + }, +} +CHECK[[ +local f = require +local z = f +z'xxx' +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 30000, + locals = "", + bfinish = 30000, + [1] = { + type = "local", + start = 6, + finish = 7, + effect = 17, + range = 17, + special = "require", + parent = "", + locPos = 0, + value = { + type = "getglobal", + start = 10, + finish = 17, + special = "require", + parent = "", + node = "", + [1] = "require", + }, + ref = "", + [1] = "f", + }, + [2] = { + type = "local", + start = 10006, + finish = 10007, + effect = 10011, + range = 10011, + special = "require", + parent = "", + locPos = 10000, + value = { + type = "getlocal", + start = 10010, + finish = 10011, + special = "require", + parent = "", + node = "", + [1] = "f", + }, + ref = "", + [1] = "z", + }, + [3] = { + type = "call", + start = 20000, + finish = 20006, + parent = "", + node = "", + args = { + type = "callargs", + start = 20001, + finish = 20006, + parent = "", + [1] = { + type = "string", + start = 20001, + finish = 20006, + parent = "", + [1] = "xxx", + [2] = "'", + }, + }, + }, +} +CHECK[[ +A:B(1):C(2) +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 10000, + locals = "", + bfinish = 10000, + [1] = { + type = "call", + start = 0, + finish = 11, + parent = "", + node = "", + args = { + type = "callargs", + start = 8, + finish = 11, + parent = "", + [1] = { + type = "self", + start = 6, + finish = 7, + parent = "", + [1] = "self", + }, + [2] = { + type = "integer", + start = 9, + finish = 10, + parent = "", + [1] = 2, + }, + }, + }, +} + +CHECK [[ + +local s = [ [=[111]=] ] +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 20000, + locals = "", + bfinish = 20000, + [1] = { + type = "local", + start = 10006, + finish = 10007, + effect = 10007, + parent = "", + locPos = 10000, + [1] = "s", + }, + [2] = { + type = "string", + start = 10012, + finish = 10021, + parent = "", + [1] = "111", + [2] = "[=[", + }, +} +CHECK([[ +local x = 1 // 2 +]], { + nonstandardSymbol = { ['//'] = true } +}) +{ + type = "main", + start = 0, + bstart = 0, + finish = 10000, + locals = "", + bfinish = 10000, + [1] = { + type = "local", + start = 6, + finish = 7, + effect = 11, + range = 11, + parent = "", + locPos = 0, + value = { + type = "integer", + start = 10, + finish = 11, + parent = "", + [1] = 1, + }, + [1] = "x", + }, +} + +CHECK([[ +local x = { + 1, // BAD + 2, // GOOD + 3, // GOOD +} +]], { + nonstandardSymbol = { ['//'] = true } +}) +{ + type = "main", + start = 0, + bstart = 0, + finish = 50000, + locals = "", + bfinish = 50000, + [1] = { + type = "local", + start = 6, + finish = 7, + effect = 40001, + range = 40001, + parent = "", + locPos = 0, + value = { + type = "table", + start = 10, + bstart = 11, + finish = 40001, + parent = "", + bfinish = 40000, + [1] = { + type = "tableexp", + start = 10004, + finish = 10005, + tindex = 1, + parent = "", + value = { + type = "integer", + start = 10004, + finish = 10005, + parent = "", + [1] = 1, + }, + }, + [2] = { + type = "tableexp", + start = 20004, + finish = 20005, + tindex = 2, + parent = "", + value = { + type = "integer", + start = 20004, + finish = 20005, + parent = "", + [1] = 2, + }, + }, + [3] = { + type = "tableexp", + start = 30004, + finish = 30005, + tindex = 3, + parent = "", + value = { + type = "integer", + start = 30004, + finish = 30005, + parent = "", + [1] = 3, + }, + }, + }, + [1] = "x", + }, +} + +CHECK [[ +local x +return { + x = 1, +} +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 40000, + locals = "", + returns = "", + bfinish = 40000, + [1] = { + type = "local", + start = 6, + finish = 7, + effect = 7, + parent = "", + locPos = 0, + [1] = "x", + }, + [2] = { + type = "return", + start = 10000, + finish = 30001, + parent = "", + [1] = { + type = "table", + start = 10007, + bstart = 10008, + finish = 30001, + parent = "", + bfinish = 30000, + [1] = { + type = "tablefield", + start = 20004, + finish = 20005, + range = 20009, + parent = "", + node = "", + field = { + type = "field", + start = 20004, + finish = 20005, + parent = "", + [1] = "x", + }, + value = { + type = "integer", + start = 20008, + finish = 20009, + parent = "", + [1] = 1, + }, + }, + }, + }, +} + +CHECK [[ +local x +a = { + x +} +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 40000, + locals = "", + bfinish = 40000, + [1] = { + type = "local", + start = 6, + finish = 7, + effect = 7, + parent = "", + locPos = 0, + ref = "", + [1] = "x", + }, + [2] = { + type = "setglobal", + start = 10000, + finish = 10001, + range = 30001, + parent = "", + node = "", + value = { + type = "table", + start = 10004, + bstart = 10005, + finish = 30001, + parent = "", + bfinish = 30000, + [1] = { + type = "tableexp", + start = 20004, + finish = 20005, + tindex = 1, + parent = "", + value = { + type = "getlocal", + start = 20004, + finish = 20005, + parent = "", + node = "", + [1] = "x", + }, + }, + }, + [1] = "a", + }, +} + +CHECK [[ +x, y, z = 1, func() +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 10000, + locals = "", + bfinish = 10000, + [1] = { + type = "setglobal", + start = 0, + finish = 1, + range = 11, + parent = "", + node = "", + value = { + type = "integer", + start = 10, + finish = 11, + parent = "", + [1] = 1, + }, + [1] = "x", + }, + [2] = { + type = "setglobal", + start = 3, + finish = 4, + range = 19, + parent = "", + node = "", + value = { + type = "select", + start = 13, + finish = 19, + parent = "", + vararg = "", + sindex = 1, + }, + [1] = "y", + }, + [3] = { + type = "setglobal", + start = 6, + finish = 7, + range = 19, + parent = "", + node = "", + value = { + type = "select", + start = 13, + finish = 19, + parent = "", + vararg = "", + sindex = 2, + }, [1] = "z", }, - [12] = { - type = "number", - start = 55, - finish = 55, - parent = 11, - [1] = 4, +} + +CHECK [[ +local x, y +-- comments +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 20000, + locals = "", + bfinish = 20000, + [1] = { + type = "local", + start = 6, + finish = 7, + effect = 10, + parent = "", + locPos = 0, + [1] = "x", + }, + [2] = { + type = "local", + start = 9, + finish = 10, + effect = 10, + parent = "", + [1] = "y", + }, +} + +CHECK [[ +local _ENV = nil +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 10000, + locals = "", + bfinish = 10000, + [1] = { + type = "local", + start = 6, + finish = 10, + effect = 16, + range = 16, + parent = "", + locPos = 0, + value = { + type = "nil", + start = 13, + finish = 16, + parent = "", + }, + [1] = "_ENV", + }, +} + +CHECK [[ +_ENV = nil +]] +{ + type = "main", + start = 0, + bstart = 0, + finish = 10000, + locals = "", + bfinish = 10000, + [1] = { + type = "local", + start = 0, + finish = 4, + effect = 10, + range = 10, + parent = "", + node = "", + locPos = 0, + value = { + type = "nil", + start = 7, + finish = 10, + parent = "", + }, + [1] = "_ENV", }, } diff --git a/test/ast/LuaDoc.lua b/test/ast/LuaDoc.lua new file mode 100644 index 0000000..6b79443 --- /dev/null +++ b/test/ast/LuaDoc.lua @@ -0,0 +1,1048 @@ +LuaDoc [[ +---@class Class +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.class", + start = 10, + finish = 15, + range = 15, + parent = "", + bindComments = { + }, + calls = { + }, + class = { + type = "doc.class.name", + start = 10, + finish = 15, + parent = "", + [1] = "Class", + }, + fields = { + }, + operators = { + }, + originalComment = "", + }, +} + +LuaDoc [[ +---@class Class : SuperClass +local x = 1 +]] +{ + type = "doc", + start = 0, + finish = 20000, + parent = "", + [1] = { + type = "doc.class", + start = 10, + finish = 28, + range = 28, + parent = "", + bindComments = { + }, + bindSource = "", + calls = { + }, + class = { + type = "doc.class.name", + start = 10, + finish = 15, + parent = "", + [1] = "Class", + }, + extends = { + [1] = { + type = "doc.extends.name", + start = 18, + finish = 28, + parent = "", + [1] = "SuperClass", + }, + }, + fields = { + }, + operators = { + }, + originalComment = "", + }, +} + +LuaDoc [[ +---@type Type +x = 1 +]] +{ + type = "doc", + start = 0, + finish = 20000, + parent = "", + [1] = { + type = "doc.type", + start = 9, + finish = 13, + range = 13, + parent = "", + bindSource = "", + firstFinish = 13, + originalComment = "", + types = { + [1] = { + type = "doc.type.name", + start = 9, + finish = 13, + parent = "", + [1] = "Type", + }, + }, + }, +} + +LuaDoc [[ +---@type Type1|Type2|Type3 +x = 1 +]] +{ + type = "doc", + start = 0, + finish = 20000, + parent = "", + [1] = { + type = "doc.type", + start = 9, + finish = 26, + range = 26, + parent = "", + bindSource = "", + firstFinish = 26, + originalComment = "", + types = { + [1] = { + type = "doc.type.name", + start = 9, + finish = 14, + parent = "", + [1] = "Type1", + }, + [2] = { + type = "doc.type.name", + start = 15, + finish = 20, + parent = "", + [1] = "Type2", + }, + [3] = { + type = "doc.type.name", + start = 21, + finish = 26, + parent = "", + [1] = "Type3", + }, + }, + }, +} + +LuaDoc [[ +---@type x | "'a'" | "'b'" +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.type", + start = 9, + finish = 26, + range = 26, + parent = "", + firstFinish = 26, + originalComment = "", + types = { + [1] = { + type = "doc.type.name", + start = 9, + finish = 10, + parent = "", + [1] = "x", + }, + [2] = { + type = "doc.type.string", + start = 13, + finish = 18, + parent = "", + [1] = "a", + [2] = "'", + }, + [3] = { + type = "doc.type.string", + start = 21, + finish = 26, + parent = "", + [1] = "b", + [2] = "'", + }, + }, + }, +} + +LuaDoc [[ +---@type "'a'" | "'b'" | "'c'" +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.type", + start = 9, + finish = 30, + range = 30, + parent = "", + firstFinish = 30, + originalComment = "", + types = { + [1] = { + type = "doc.type.string", + start = 9, + finish = 14, + parent = "", + [1] = "a", + [2] = "'", + }, + [2] = { + type = "doc.type.string", + start = 17, + finish = 22, + parent = "", + [1] = "b", + [2] = "'", + }, + [3] = { + type = "doc.type.string", + start = 25, + finish = 30, + parent = "", + [1] = "c", + [2] = "'", + }, + }, + }, +} + +LuaDoc [[ +---@alias Handler LongType +x = 1 +]] +{ + type = "doc", + start = 0, + finish = 20000, + parent = "", + [1] = { + type = "doc.alias", + start = 10, + finish = 26, + range = 26, + parent = "", + alias = { + type = "doc.alias.name", + start = 10, + finish = 17, + parent = "", + [1] = "Handler", + }, + bindComments = { + }, + extends = { + type = "doc.type", + start = 18, + finish = 26, + parent = "", + firstFinish = 26, + types = { + [1] = { + type = "doc.type.name", + start = 18, + finish = 26, + parent = "", + [1] = "LongType", + }, + }, + }, + originalComment = "", + }, +} + +LuaDoc [[ +---@param a1 t1 +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.param", + start = 10, + finish = 15, + range = 15, + parent = "", + extends = { + type = "doc.type", + start = 13, + finish = 15, + parent = "", + firstFinish = 15, + types = { + [1] = { + type = "doc.type.name", + start = 13, + finish = 15, + parent = "", + [1] = "t1", + }, + }, + }, + firstFinish = 15, + originalComment = "", + param = { + type = "doc.param.name", + start = 10, + finish = 12, + parent = "", + [1] = "a1", + }, + }, +} + +LuaDoc [[ +---@return Type1|Type2|Type3 +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.return", + start = 11, + finish = 28, + range = 28, + parent = "", + returns = "", + originalComment = "", + }, +} + +LuaDoc [[ +---@return Type1,Type2,Type3 +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.return", + start = 11, + finish = 28, + range = 28, + parent = "", + returns = "", + originalComment = "", + }, +} + +LuaDoc [[ +---@field open function +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.field", + start = 10, + finish = 23, + range = 23, + parent = "", + field = { + type = "doc.field.name", + start = 10, + finish = 14, + parent = "", + [1] = "open", + }, + bindComments = { + }, + extends = { + type = "doc.type", + start = 15, + finish = 23, + parent = "", + firstFinish = 23, + types = { + [1] = { + type = "doc.type.name", + start = 15, + finish = 23, + parent = "", + [1] = "function", + }, + }, + }, + originalComment = "", + }, +} + +LuaDoc [[ +---@field private open function|string +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.field", + start = 10, + finish = 38, + range = 38, + parent = "", + field = { + type = "doc.field.name", + start = 18, + finish = 22, + parent = "", + [1] = "open", + }, + bindComments = { + }, + extends = { + type = "doc.type", + start = 23, + finish = 38, + parent = "", + firstFinish = 38, + types = { + [1] = { + type = "doc.type.name", + start = 23, + finish = 31, + parent = "", + [1] = "function", + }, + [2] = { + type = "doc.type.name", + start = 32, + finish = 38, + parent = "", + [1] = "string", + }, + }, + }, + originalComment = "", + visible = "private", + }, +} + +LuaDoc [[ +---@generic T +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.generic", + start = 12, + finish = 13, + range = 13, + parent = "", + generics = { + [1] = { + type = "doc.generic.object", + start = 12, + finish = 13, + parent = "", + generic = { + type = "doc.generic.name", + start = 12, + finish = 13, + parent = "", + [1] = "T", + }, + }, + }, + originalComment = "", + }, +} + +LuaDoc [[ +---@generic T : handle +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.generic", + start = 12, + finish = 22, + range = 22, + parent = "", + generics = { + [1] = { + type = "doc.generic.object", + start = 12, + finish = 22, + parent = "", + extends = { + type = "doc.type", + start = 16, + finish = 22, + parent = "", + firstFinish = 22, + types = { + [1] = { + type = "doc.type.name", + start = 16, + finish = 22, + parent = "", + [1] = "handle", + }, + }, + }, + generic = { + type = "doc.generic.name", + start = 12, + finish = 13, + parent = "", + [1] = "T", + }, + }, + }, + originalComment = "", + }, +} + +LuaDoc [[ +---@generic T : handle, K : handle +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.generic", + start = 12, + finish = 34, + range = 34, + parent = "", + generics = { + [1] = { + type = "doc.generic.object", + start = 12, + finish = 22, + parent = "", + extends = { + type = "doc.type", + start = 16, + finish = 22, + parent = "", + firstFinish = 22, + types = { + [1] = { + type = "doc.type.name", + start = 16, + finish = 22, + parent = "", + [1] = "handle", + }, + }, + }, + generic = { + type = "doc.generic.name", + start = 12, + finish = 13, + parent = "", + [1] = "T", + }, + }, + [2] = { + type = "doc.generic.object", + start = 24, + finish = 34, + parent = "", + extends = { + type = "doc.type", + start = 28, + finish = 34, + parent = "", + firstFinish = 34, + types = { + [1] = { + type = "doc.type.name", + start = 28, + finish = 34, + parent = "", + [1] = "handle", + }, + }, + }, + generic = { + type = "doc.generic.name", + start = 24, + finish = 25, + parent = "", + [1] = "K", + }, + }, + }, + originalComment = "", + }, +} + +LuaDoc [[ +---@vararg string +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.vararg", + start = 11, + finish = 17, + range = 17, + parent = "", + vararg = "", + originalComment = "", + }, +} + +LuaDoc [[ +---@type Type[] +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.type", + start = 9, + finish = 15, + range = 15, + parent = "", + firstFinish = 15, + originalComment = "", + types = { + [1] = { + type = "doc.type.array", + start = 9, + finish = 15, + parent = "", + node = "", + }, + }, + }, +} + +LuaDoc [[ +---@type table +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.type", + start = 9, + finish = 26, + range = 26, + parent = "", + firstFinish = 26, + originalComment = "", + types = { + [1] = { + type = "doc.type.sign", + start = 9, + finish = 26, + parent = "", + node = "", + signs = { + [1] = { + type = "doc.type", + start = 15, + finish = 18, + parent = "", + firstFinish = 18, + types = { + [1] = { + type = "doc.type.name", + start = 15, + finish = 18, + parent = "", + [1] = "key", + }, + }, + }, + [2] = { + type = "doc.type", + start = 20, + finish = 25, + parent = "", + firstFinish = 25, + types = { + [1] = { + type = "doc.type.name", + start = 20, + finish = 25, + parent = "", + [1] = "value", + }, + }, + }, + }, + }, + }, + }, +} + +IGNORE_MAP['returns'] = nil +IGNORE_MAP['extends'] = true +LuaDoc [[ +---@type fun(key1:t1, key2:t2):t3 +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.type", + start = 9, + finish = 33, + range = 33, + parent = "", + firstFinish = 33, + originalComment = "", + types = { + [1] = { + type = "doc.type.function", + start = 9, + finish = 33, + parent = "", + args = { + [1] = { + type = "doc.type.arg", + start = 13, + finish = 20, + parent = "", + extends = "", + name = { + type = "doc.type.arg.name", + start = 13, + finish = 17, + parent = "", + [1] = "key1", + }, + }, + [2] = { + type = "doc.type.arg", + start = 22, + finish = 29, + parent = "", + extends = "", + name = { + type = "doc.type.arg.name", + start = 22, + finish = 26, + parent = "", + [1] = "key2", + }, + }, + }, + returns = { + [1] = { + type = "doc.type", + start = 31, + finish = 33, + parent = "", + firstFinish = 33, + types = { + [1] = { + type = "doc.type.name", + start = 31, + finish = 33, + parent = "", + [1] = "t3", + }, + }, + }, + }, + }, + }, + }, +} + +IGNORE_MAP['returns'] = true +IGNORE_MAP['extends'] = nil +LuaDoc [[ +---@param event string | "'onClosed'" | "'onData'" +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.param", + start = 10, + finish = 50, + range = 50, + parent = "", + extends = { + type = "doc.type", + start = 16, + finish = 50, + parent = "", + firstFinish = 50, + types = { + [1] = { + type = "doc.type.name", + start = 16, + finish = 22, + parent = "", + [1] = "string", + }, + [2] = { + type = "doc.type.string", + start = 25, + finish = 37, + parent = "", + [1] = "onClosed", + [2] = "'", + }, + [3] = { + type = "doc.type.string", + start = 40, + finish = 50, + parent = "", + [1] = "onData", + [2] = "'", + }, + }, + }, + firstFinish = 50, + originalComment = "", + param = { + type = "doc.param.name", + start = 10, + finish = 15, + parent = "", + [1] = "event", + }, + }, +} + +LuaDoc [[ +---@overload fun(a:number):number +]] +{ + type = "doc", + start = 0, + finish = 10000, + parent = "", + [1] = { + type = "doc.overload", + start = 13, + finish = 33, + range = 33, + parent = "", + originalComment = "", + overload = { + type = "doc.type.function", + start = 13, + finish = 33, + parent = "", + args = { + [1] = { + type = "doc.type.arg", + start = 17, + finish = 25, + parent = "", + extends = { + type = "doc.type", + start = 19, + finish = 25, + parent = "", + firstFinish = 25, + types = { + [1] = { + type = "doc.type.name", + start = 19, + finish = 25, + parent = "", + [1] = "number", + }, + }, + }, + name = { + type = "doc.type.arg.name", + start = 17, + finish = 18, + parent = "", + [1] = "a", + }, + }, + }, + returns = "", + }, + }, +} + +LuaDoc [[ +---@type { +--- x: number, +--- y: number, +--- [number]: boolean, +---} +]] +{ + type = "doc", + start = 0, + finish = 50000, + parent = "", + [1] = { + type = "doc.type", + start = 9, + finish = 40004, + range = 40004, + parent = "", + firstFinish = 40004, + originalComment = "", + types = { + [1] = { + type = "doc.type.table", + start = 9, + finish = 40004, + parent = "", + fields = { + [1] = { + type = "doc.type.field", + start = 10004, + finish = 10013, + parent = "", + extends = { + type = "doc.type", + start = 10007, + finish = 10013, + parent = "", + firstFinish = 10013, + types = { + [1] = { + type = "doc.type.name", + start = 10007, + finish = 10013, + parent = "", + [1] = "number", + }, + }, + }, + name = { + type = "doc.field.name", + start = 10004, + finish = 10005, + parent = "", + [1] = "x", + }, + }, + [2] = { + type = "doc.type.field", + start = 20004, + finish = 20013, + parent = "", + extends = { + type = "doc.type", + start = 20007, + finish = 20013, + parent = "", + firstFinish = 20013, + types = { + [1] = { + type = "doc.type.name", + start = 20007, + finish = 20013, + parent = "", + [1] = "number", + }, + }, + }, + name = { + type = "doc.field.name", + start = 20004, + finish = 20005, + parent = "", + [1] = "y", + }, + }, + [3] = { + type = "doc.type.field", + start = 30005, + finish = 30021, + parent = "", + extends = { + type = "doc.type", + start = 30014, + finish = 30021, + parent = "", + firstFinish = 30021, + types = { + [1] = { + type = "doc.type.name", + start = 30014, + finish = 30021, + parent = "", + [1] = "boolean", + }, + }, + }, + name = { + type = "doc.type", + start = 30005, + finish = 30011, + parent = "", + firstFinish = 30011, + types = { + [1] = { + type = "doc.type.name", + start = 30005, + finish = 30011, + parent = "", + [1] = "number", + }, + }, + }, + }, + }, + }, + }, + }, +} diff --git a/test/ast/Nil.lua b/test/ast/Nil.lua index 96f8b8b..70744e2 100644 --- a/test/ast/Nil.lua +++ b/test/ast/Nil.lua @@ -1,16 +1,12 @@ CHECK [[nil]] { - [1] = { - type = 'nil', - start = 1, - finish = 3, - } + type = "nil", + start = 0, + finish = 3, } CHECK [[ nil]] { - [1] = { - type = 'nil', - start = 4, - finish = 6, - } + type = "nil", + start = 3, + finish = 6, } diff --git a/test/ast/Number.lua b/test/ast/Number.lua index 90d117b..c956850 100644 --- a/test/ast/Number.lua +++ b/test/ast/Number.lua @@ -1,81 +1,84 @@ CHECK'345' { - [1] = { - type = 'number', - start = 1, - finish = 3, - [1] = 345, - } + type = "integer", + start = 0, + finish = 3, + [1] = 345, } CHECK'345.0' { - [1] = { - type = 'number', - start = 1, - finish = 5, - [1] = 345.0, - } + type = "number", + start = 0, + finish = 5, + [1] = 0x1.59p+8, } CHECK'0xff' { - [1] = { - type = 'number', - start = 1, - finish = 4, - [1] = 0xff, - } + type = "integer", + start = 0, + finish = 4, + [1] = 255, } CHECK'314.16e-2' { - [1] = { - type = 'number', - start = 1, - finish = 9, - [1] = 314.16e-2, - } + type = "number", + start = 0, + finish = 9, + [1] = 3.1416, } CHECK'0.31416E1' { - [1] = { - type = 'number', - start = 1, - finish = 9, - [1] = 0.31416E1, - } + type = "number", + start = 0, + finish = 9, + [1] = 0x1.921ff2e48e8a7p+1, +} +CHECK'.31416E1' +{ + type = "number", + start = 0, + finish = 8, + [1] = 0x1.921ff2e48e8a7p+1, } CHECK'34e1' { - [1] = { - type = 'number', - start = 1, - finish = 4, - [1] = 34e1, - } + type = "number", + start = 0, + finish = 4, + [1] = 0x1.54p+8, } CHECK'0x0.1E' { - [1] = { - type = 'number', - start = 1, - finish = 6, - [1] = 0x0.1E, - } + type = "number", + start = 0, + finish = 6, + [1] = 0x1.ep-4, } CHECK'0xA23p-4' { - [1] = { - type = 'number', - start = 1, - finish = 8, - [1] = 0xA23p-4, - } + type = "number", + start = 0, + finish = 8, + [1] = 0x1.446p+7, } CHECK'0X1.921FB54442D18P+1' { - [1] = { - type = 'number', - start = 1, - finish = 20, - [1] = 0X1.921FB54442D18P+1, - } + type = "number", + start = 0, + finish = 20, + [1] = 0x1.921fb54442d18p+1, +} +CHECK'-345' +{ + type = "integer", + start = 0, + finish = 4, + [1] = -345, +} +CHECK'0b110110' +{ + type = "integer", + start = 0, + finish = 8, + [1] = 54, } diff --git a/test/ast/String.lua b/test/ast/String.lua index 9f9dd03..ac32030 100644 --- a/test/ast/String.lua +++ b/test/ast/String.lua @@ -1,146 +1,215 @@ CHECK[['123']] { - [1] = { - type = 'string', - start = 1, - finish = 5, - [1] = [[123]], - [2] = [[']], - } + type = "string", + start = 0, + finish = 5, + [1] = "123", + [2] = "'", } CHECK[['123\'']] { - [1] = { - type = 'string', - start = 1, - finish = 7, - [1] = [[123']], - [2] = [[']], - } + type = "string", + start = 0, + finish = 7, + escs = { + [1] = 4, + [2] = 6, + [3] = "normal", + }, + [1] = "123'", + [2] = "'", } CHECK[['123\z 345']] { - [1] = { - type = 'string', - start = 1, - finish = 15, - [1] = [[123345]], - [2] = [[']], - } + type = "string", + start = 0, + finish = 10008, + escs = { + [1] = 4, + [2] = 6, + [3] = "normal", + }, + [1] = "123345", + [2] = "'", +} + +CHECK[['123\ +345']] +{ + type = "string", + start = 0, + finish = 10004, + escs = { + [1] = 4, + [2] = 5, + [3] = "normal", + }, + [1] = "123\ +345", + [2] = "'", } CHECK[===[[[123]]]===] { - [1] = { - type = 'string', - start = 1, - finish = 7, - [1] = [[123]], - [2] = [=[[[]=], - } + type = "string", + start = 0, + finish = 7, + [1] = "123", + [2] = "[[", } CHECK[===[[[123 345]]]===] { - [1] = { - type = 'string', - start = 1, - finish = 11, - [1] = [[123 -345]], - [2] = [=[[[]=], - } + type = "string", + start = 0, + finish = 10005, + [1] = "123\ +345", + [2] = "[[", } CHECK[['alo\n123"']] { - [1] = { - type = 'string', - start = 1, - finish = 11, - [1] = [[alo -123"]], - [2] = [=[']=], - } + type = "string", + start = 0, + finish = 11, + escs = { + [1] = 4, + [2] = 6, + [3] = "normal", + }, + [1] = "alo\ +123\"", + [2] = "'", } CHECK[['\97lo\10\04923"']] { - [1] = { - type = 'string', - start = 1, - finish = 17, - [1] = [[alo -123"]], - [2] = [=[']=], - } + type = "string", + start = 0, + finish = 17, + escs = { + [1] = 1, + [2] = 4, + [3] = "byte", + [4] = 6, + [5] = 9, + [6] = "byte", + [7] = 9, + [8] = 13, + [9] = "byte", + }, + [1] = "alo\ +123\"", + [2] = "'", } CHECK[['\xff']] { - [1] = { - type = 'string', - start = 1, - finish = 6, - [1] = '\xff', - [2] = [=[']=], - } + type = "string", + start = 0, + finish = 6, + escs = { + [1] = 1, + [2] = 6, + [3] = "byte", + }, + [1] = "\xff", + [2] = "'", } CHECK[['\x1A']] { - [1] = { - type = 'string', - start = 1, - finish = 6, - [1] = '\x1A', - [2] = [=[']=], - } + type = "string", + start = 0, + finish = 6, + escs = { + [1] = 1, + [2] = 6, + [3] = "byte", + }, + [1] = "\26", + [2] = "'", } CHECK[['\492']] { - [1] = { - type = 'string', - start = 1, - finish = 6, - [1] = '', - [2] = [=[']=], - } + type = "string", + start = 0, + finish = 6, + escs = { + [1] = 1, + [2] = 5, + [3] = "byte", + }, + [1] = "", + [2] = "'", } CHECK[['\u{3b1}']] { - [1] = { - type = 'string', - start = 1, - finish = 9, - [1] = '\u{3b1}', - [2] = [=[']=], - } + type = "string", + start = 0, + finish = 9, + escs = { + [1] = 1, + [2] = 9, + [3] = "unicode", + }, + [1] = "α", + [2] = "'", } CHECK[['\u{0}']] { - [1] = { - type = 'string', - start = 1, - finish = 7, - [1] = '\0', - [2] = [=[']=], - } + type = "string", + start = 0, + finish = 7, + escs = { + [1] = 1, + [2] = 7, + [3] = "unicode", + }, + [1] = "\0", + [2] = "'", } CHECK[['\u{ffffff}']] { - [1] = { - type = 'string', - start = 1, - finish = 12, - [1] = '', - [2] = [=[']=], - } + type = "string", + start = 0, + finish = 12, + escs = { + [1] = 1, + [2] = 12, + [3] = "unicode", + }, + [1] = "", + [2] = "'", } CHECK[=[[[ abcdef]]]=] { - [1] = { - type = 'string', - start = 1, - finish = 11, - [1] = 'abcdef', - [2] = '[[', - } + type = "string", + start = 0, + finish = 10008, + [1] = "abcdef", + [2] = "[[", +} +CHECK[['aaa]] +{ + type = "string", + start = 0, + finish = 4, + [1] = "aaa", + [2] = "'", +} +CHECK[['aaa +]] +{ + type = "string", + start = 0, + finish = 4, + [1] = "aaa", + [2] = "'", +} +CHECK[[`12345`]] +{ + type = "string", + start = 0, + finish = 7, + [1] = "12345", + [2] = "`", } diff --git a/test/ast/init.lua b/test/ast/init.lua index 19eafb6..f8c5f2b 100644 --- a/test/ast/init.lua +++ b/test/ast/init.lua @@ -2,11 +2,19 @@ local parser = require 'parser' local fs = require 'bee.filesystem' local utility = require 'utility' +EXISTS = {} + local function eq(a, b) local tp1, tp2 = type(a), type(b) if tp1 ~= tp2 then return false end + if a == '' and tp1 == 'table' then + return true + end + if b == '' and tp2 == 'table' then + return true + end if tp1 == 'table' then local checked = {} for k in pairs(a) do @@ -28,25 +36,37 @@ local function eq(a, b) return a == b end +---@type {[string]: integer, [integer]: string} local sortList = { - 'type', 'start', 'finish', 'effect', + 'specials', + 'type', 'start', 'vstart', 'bstart', 'finish', 'effect', 'range', 'tindex', + 'tag', 'special', 'keyword', 'parent', 'extParent', 'child', 'filter', - 'node', + 'vararg', + 'node', 'locPos', 'op', 'args', - 'loc', 'max', 'step', 'keys', + 'loc', 'init', 'max', 'step', 'keys', 'exps', 'call', 'func', 'dot', 'colon', - 'vararg', 'field', 'index', 'method', - 'exp', 'value', - 'attrs', - 'locals', 'ref', + 'exp', 'value', 'vref', + 'attrs', 'escs', + 'locals', 'ref', 'returns', 'breaks', } for i, v in ipairs(sortList) do sortList[v] = i end +local ignoreList = { + 'specials', 'locals', 'ref', 'node', 'parent', 'extParent', 'returns', 'state', 'mirror', 'next', 'vararg', 'originalComment', 'typeCache', 'eachCache', 'bindSource' +} +local ignoreMap = {} +for i, v in ipairs(ignoreList) do + ignoreMap[v] = true +end -local option = { +IGNORE_MAP = ignoreMap + +local myOption = { alignment = true, sorter = function (keys, keymap) table.sort(keys, function (a, b) @@ -61,47 +81,136 @@ local option = { if tp1 == 'number' and tp2 == 'number' then return a < b end - local s1 = sortList[a] - local s2 = sortList[b] - if s1 and not s2 then + local s1 = sortList[a] or 9999 + local s2 = sortList[b] or 9999 + if s1 == s2 then + return a < b + else + return s1 < s2 + end + end) + end, + loop = ('%q'):format(''), + number = function (n) + return ('%q'):format(n) + end, + format = setmetatable({}, { __index = function (_, key) + return function (value, _, _, _) + if ignoreMap[key] then + return '' + end + if type(key) == 'string' and key:sub(1, 1) == '_' then + return nil + end + return value + end + end}), +} + +local targetOption = { + alignment = true, + sorter = function (keys, keymap) + table.sort(keys, function (a, b) + local tp1 = type(a) + local tp2 = type(b) + if tp1 == 'number' and tp2 ~= 'number' then return false end - if s2 and not s1 then + if tp1 ~= 'number' and tp2 == 'number' then return true end - if s1 and s2 then + if tp1 == 'number' and tp2 == 'number' then + return a < b + end + local s1 = sortList[a] or 9999 + local s2 = sortList[b] or 9999 + if s1 == s2 then + return a < b + else return s1 < s2 end - return a < b end) end, + loop = ('%q'):format(''), + number = function (n) + return ('%q'):format(n) + end, } +local function autoFix(myBuf, targetBuf) + local info = debug.getinfo(3, 'Sl') + local filename = info.source:sub(2) + local fileBuf = utility.loadFile(filename) + assert(fileBuf) + local pos = fileBuf:find(targetBuf, 1, true) + local newFileBuf = fileBuf:sub(1, pos-1) .. myBuf .. fileBuf:sub(pos + #targetBuf) + utility.saveFile(filename, newFileBuf) +end + local function test(type) - CHECK = function (buf) + local mode = type + if mode == 'Dirty' then + mode = 'Lua' + end + CHECK = function (buf, opt) return function (target_ast) - local state, err = parser:compile(buf, type, 'Lua 5.4') + local state, err = parser.compile(buf, mode, 'Lua 5.4', opt) if not state then error(('语法树生成失败:%s'):format(err)) end - if not eq(state.root, target_ast) then + state.ast.state = nil + local result = utility.dump(state.ast, myOption) + local expect = utility.dump(target_ast, targetOption) + if result ~= expect then fs.create_directory(ROOT / 'test' / 'log') - utility.saveFile((ROOT / 'test' / 'log' / 'my_ast.ast'):string(), utility.dump(state.root, option)) - utility.saveFile((ROOT / 'test' / 'log' / 'target_ast.ast'):string(), utility.dump(target_ast, option)) + utility.saveFile((ROOT / 'test' / 'log' / 'my_ast.ast'):string(), result) + utility.saveFile((ROOT / 'test' / 'log' / 'target_ast.ast'):string(), expect) + autoFix(result, expect) error(('语法树不相等:%s\n%s'):format(type, buf)) end end end - EMMY = function (buf) - return function (target_ast) - local _, err, emmy = parser:parse(buf, type, 'Lua 5.4') - if not emmy then + LuaDoc = function (buf) + return function (target_doc) + local state, err = parser.compile(buf, 'Lua', 'Lua 5.4') + if not state then + error(('语法树生成失败:%s'):format(err)) + end + parser.luadoc.luadoc(state) + for _, doc in ipairs(state.ast.docs) do + doc.bindGroup = nil + doc.bindSources = nil + end + state.ast.docs.groups = nil + local result = utility.dump(state.ast.docs, myOption) + local expect = utility.dump(target_doc, targetOption) + if result ~= expect then + fs.create_directory(ROOT / 'test' / 'log') + utility.saveFile((ROOT / 'test' / 'log' / 'my_doc.ast'):string(), result) + utility.saveFile((ROOT / 'test' / 'log' / 'target_doc.ast'):string(), expect) + autoFix(result, expect) + error(('语法树不相等:%s\n%s'):format(type, buf)) + end + end + end + Comment = function (buf) + return function (target_comment) + local state, err = parser.compile(buf, mode, 'Lua 5.4', { + ['nonstandardSymbol'] = { + ['//'] = true, + }, + }) + if not state then error(('语法树生成失败:%s'):format(err)) end - if not eq(emmy, target_ast) then + state.ast.state = nil + local result = utility.dump(state.comms, myOption) + local expect = utility.dump(target_comment, targetOption) + if result ~= expect then fs.create_directory(ROOT / 'test' / 'log') - io.save(ROOT / 'test' / 'log' / 'my_emmy.ast', table.dump(emmy)) - io.save(ROOT / 'test' / 'log' / 'target_emmy.ast', table.dump(target_ast)) + utility.saveFile((ROOT / 'test' / 'log' / 'my_ast.ast'):string(), result) + utility.saveFile((ROOT / 'test' / 'log' / 'target_ast.ast'):string(), expect) + --autoFix(result, expect) error(('语法树不相等:%s\n%s'):format(type, buf)) end end @@ -117,5 +226,5 @@ test 'Exp' test 'Action' test 'Lua' test 'Dirty' -do return end -test 'Emmy' +test 'LuaDoc' +test 'Comment' diff --git a/test/grammar.lua b/test/grammar.lua index d639a54..a044c77 100644 --- a/test/grammar.lua +++ b/test/grammar.lua @@ -1,9 +1,9 @@ local parser = require 'parser' local function check_str(str, name, mode) - local ast, err = parser:compile(str, mode, 'Lua 5.3') + local ast = parser.compile(str, mode, 'Lua 5.3') assert(ast) - if #err > 0 and mode ~= 'Dirty' then + if #ast.errs > 0 and mode ~= 'Dirty' then error(([[ [%s]测试失败: %s @@ -37,6 +37,7 @@ check 'Comment' [===[--[[123 123 123]]]===], +[===[-- [[Abc]]a]===], } check 'Sp' @@ -90,7 +91,7 @@ check 'String' [['\xff']], [['\x1A']], -[['\492']], +[['\192']], [['\0']], [['\049']], [['\0492']], @@ -328,7 +329,23 @@ return { ---@type a b = 2, } -]] +]], +[[ +local x = ... +]], +[[ +return not true +]], +[[ +for _ in _ do + do return end +end +]], +[[ +if true then + print(...) +end +]], } -- Dirty diff --git a/test/grammar_check.lua b/test/grammar_check.lua index fcfabb5..cb9a7ca 100644 --- a/test/grammar_check.lua +++ b/test/grammar_check.lua @@ -28,21 +28,62 @@ local function eq(a, b) return a == b end +local function getLine(offset, lns) + for i = 0, #lns do + if offset >= lns[i] + and offset < lns[i+1] then + return i + end + end +end + +local function getPosition(offset, lns) + for i = 0, #lns do + if offset >= lns[i] + and offset < lns[i+1] then + return 10000 * i + offset - lns[i] + end + end +end + +---@param script string +---@param sep string local function catchTarget(script, sep) + local pattern = ('()<%%%s.-%%%s>()'):format(sep, sep) + local lns = {} + lns[0] = 0 + for pos in script:gmatch '()\n' do + lns[#lns+1] = pos + end + lns[#lns+1] = math.maxinteger + local codes = {} + local pos = 1 + ---@type [integer, integer][] local list = {} - local cur = 1 - local cut = 0 - while true do - local start, finish = script:find(('<%%%s.-%%%s>'):format(sep, sep), cur) - if not start then - break + local cuted = 0 + local lastLine = 0 + for a, b in script:gmatch(pattern) do + codes[#codes+1] = script:sub(pos, a - 1) + codes[#codes+1] = script:sub(a + 2, b - 3) + pos = b + local line1 = getLine(a + 1, lns) + if line1 ~= lastLine then + cuted = 0 + lastLine = line1 end - list[#list+1] = { start - cut, math.max(start - cut, finish - 4 - cut) } - cur = finish + 1 - cut = cut + 4 + cuted = cuted + 2 + local left = getPosition(a + 1, lns) - cuted + local line2 = getLine(b - 3, lns) + if line2 ~= lastLine then + cuted = 0 + lastLine = line2 + end + local right = getPosition(b - 3, lns) - cuted + cuted = cuted + 2 + list[#list+1] = { left, right } end - local new_script = script:gsub(('<%%%s(.-)%%%s>'):format(sep, sep), '%1') - return new_script, list + codes[#codes+1] = script:sub(pos) + return table.concat(codes), list end local Version @@ -50,9 +91,9 @@ local Version local function TEST(script) return function (expect) local newScript, list = catchTarget(script, '!') - local ast, errs = parser:parse(newScript, 'lua', Version) + local ast = parser.compile(newScript, 'Lua', Version) assert(ast) - assert(errs) + local errs = ast.errs local first = errs[1] local target = list[1] if not expect then @@ -69,15 +110,15 @@ local function TEST(script) assert(first.type == expect.type) assert(first.start == target[1]) assert(first.finish == target[2]) - assert(eq(first.version, expect.version)) - assert(eq(first.info, expect.info)) + assert(eq(expect.version, first.version)) + assert(eq(expect.info, first.info)) end end Version = 'Lua 5.3' TEST[[ -local +local ]] { type = 'MISS_NAME', @@ -87,20 +128,14 @@ TEST[[ ]] { - type = 'UNKNOWN_SYMBOL', - info = { - symbol = '???', - } + type = 'UNICODE_NAME', } TEST[[ n = 1 ]] { - type = 'UNKNOWN_SYMBOL', - info = { - symbol = 'a', - } + type = 'MALFORMED_NUMBER', } TEST[[ @@ -142,17 +177,16 @@ s = [===[a]======] } TEST[======[ -s = [===[a]======] +s = [===[a]=]]======] { - multi = 1, - type = 'ERR_LSTRING_END', + type = 'MISS_SYMBOL', info = { symbol = ']===]', } } TEST[[ -s = 'zzz' +s = '\xzzz' ]] { type = 'MISS_ESC_X', @@ -169,14 +203,14 @@ s = '\u' } TEST[[ -s = '' +s = '\u' ]] { type = 'UTF8_SMALL', } TEST[[ -s = '' +s = '\u' ]] { type = 'UTF8_MAX', @@ -187,11 +221,11 @@ s = '' } TEST[[ -s = '' +s = '\u' ]] { type = 'UTF8_MAX', - version = 'Lua 5.4', + version = {'Lua 5.4', 'Lua 5.5'}, info = { min = '000000', max = '10FFFF', @@ -199,7 +233,7 @@ s = '' } TEST[[ -s = '\u{aaa +s = '\u{aaa' ]] { type = 'MISS_SYMBOL', @@ -222,16 +256,29 @@ s = '' type = 'ERR_ESC', } +TEST[[ +s = '' +]] +{ + type = 'ERR_ESC', +} + +TEST[[ +s = '' +]] +{ + type = 'ERR_ESC', +} + TEST[[ n = 0x ]] { type = 'MUST_X16', - multi = 1, } TEST[[ -n = 0x +n = 0xzzzz ]] { type = 'MUST_X16', @@ -240,7 +287,7 @@ n = 0x TEST[[ -n = 0x. +n = 0x.zzzz ]] { type = 'MUST_X16', @@ -343,13 +390,10 @@ f(1) } TEST[[ -f(1) +f(1,) ]] { - type = 'UNEXPECT_SYMBOL', - info = { - symbol = ',', - } + type = 'MISS_EXP', } TEST[[ @@ -497,6 +541,7 @@ TEST[[ function f( ]] { + multi = 1, type = 'MISS_SYMBOL', info = { symbol = ')', @@ -517,6 +562,7 @@ TEST[[ f = function ( ]] { + multi = 1, type = 'MISS_SYMBOL', info = { symbol = ')', @@ -534,13 +580,10 @@ f = function end } TEST[[ -f = function f() end +f = function () end ]] { - type = 'MISS_SYMBOL', - info = { - symbol = '(', - } + type = 'UNEXPECT_EFUNC_NAME', } TEST[[ @@ -551,7 +594,12 @@ function f() type = 'MISS_SYMBOL', info = { symbol = 'end', - related = {1, 8}, + related = { + { + start = 0, + finish = 8, + } + }, } } @@ -571,7 +619,7 @@ function f:() end } TEST[[ -function f:xy() end +function f:x.y() end ]] { type = 'MISS_SYMBOL', @@ -595,20 +643,20 @@ function f(,a) end } TEST[[ -function f(...) end +function f(..., ) end ]] { type = 'ARGS_AFTER_DOTS', } TEST[[ -local x = ]] +local x = ]] { type = 'MISS_EXP', } TEST[[ -x = ]] +x = ]] { type = 'MISS_EXP', } @@ -659,7 +707,12 @@ for k, v in next do type = 'MISS_SYMBOL', info = { symbol = 'end', - related = {1, 3}, + related = { + { + start = 0, + finish = 3, + }, + } } } @@ -704,8 +757,8 @@ for i = 1, do end ]] { - multi = 2, - type = 'MISS_LOOP_MAX' + --multi = 2, + type = 'MISS_EXP' } TEST[[ @@ -717,30 +770,24 @@ end } TEST[[ -for i = 1 do +for i = 1, do end ]] { - multi = 1, - type = 'UNEXPECT_SYMBOL', - info = { - symbol = ',', - } + --multi = 1, + type = 'MISS_EXP', } TEST[[ -for i = 1, 2 do +for i = 1, 2, do end ]] { - type = 'UNEXPECT_SYMBOL', - info = { - symbol = ',', - } + type = 'MISS_EXP', } TEST[[ -for i = 1, 2 3 do +for i = 1, 23 do end ]] { @@ -769,7 +816,12 @@ for i = 1, 2 do type = 'MISS_SYMBOL', info = { symbol = 'end', - related = {1, 3}, + related = { + { + start = 0, + finish = 3, + }, + } } } @@ -808,7 +860,12 @@ while true do type = 'MISS_SYMBOL', info = { symbol = 'end', - related = {1, 5}, + related = { + { + start = 0, + finish = 5, + }, + } } } @@ -865,7 +922,12 @@ if true then type = 'MISS_SYMBOL', info = { symbol = 'end', - related = {1, 2}, + related = { + { + start = 0, + finish = 2, + }, + } } } @@ -886,7 +948,12 @@ else type = 'MISS_SYMBOL', info = { symbol = 'end', - related = {1, 2}, + related = { + { + start = 0, + finish = 2, + }, + } } } @@ -964,8 +1031,8 @@ end TEST[[ if true then else - -end +end ]] { type = 'BLOCK_AFTER_ELSE', @@ -1017,8 +1084,11 @@ TEST[[ return a b ]] { - type = 'ERR_UEQ', + type = 'ERR_NONSTANDARD_SYMBOL', fix = EXISTS, + info = { + symbol = '~=', + }, } TEST[[ @@ -1053,39 +1123,35 @@ return { -- 以下测试来自 https://github.com/andremm/lua-parser/blob/master/test.lua TEST[[ -f = 9 +f = 9e ]] { type = 'MISS_EXPONENT', - multi = 1, } TEST[[ -f = 5. +f = 5.e ]] { type = 'MISS_EXPONENT', - multi = 1, } TEST[[ -f = .9 +f = .9e- ]] { type = 'MISS_EXPONENT', - multi = 1, } TEST[[ -f = 5.9 +f = 5.9e+ ]] { type = 'MISS_EXPONENT', - multi = 1, } TEST[[ -hex = 0x +hex = 0xG ]] { type = 'MUST_X16', @@ -1108,7 +1174,6 @@ long string ]==] ]=============] { - multi = 2, type = 'MISS_SYMBOL', info = { symbol = ']===]', @@ -1159,11 +1224,10 @@ comment } TEST[[ ---[=[xxx -]] +--[=[xxx]==] +]] { - multi = 1, - type = 'ERR_LCOMMENT_END', + type = 'MISS_SYMBOL', info = { symbol = ']=]', }, @@ -1178,21 +1242,20 @@ a = function (a,b,) end } TEST[[ -a = function (...) end +a = function (...,) end ]] { type = 'ARGS_AFTER_DOTS', } TEST[[ -local a = function (1) end +local a = function () end ]] { - type = 'MISS_SYMBOL', + type = 'UNKNOWN_SYMBOL', info = { - symbol = ')', + symbol = '1', }, - multi = 1, } TEST[[ @@ -1203,7 +1266,12 @@ local test = function ( a , b , c , ... ) type = 'MISS_SYMBOL', info = { symbol = 'end', - related = {14, 21}, + related = { + { + start = 13, + finish = 21, + }, + } } } @@ -1216,35 +1284,47 @@ local test = ( a , b , c , ... ) } TEST[[ -a = 3 / / 2 +a = 3 / 2 ]] { - type = 'MISS_EXP', + type = 'UNKNOWN_SYMBOL', + info = { + symbol = '/' + } } TEST[[ -b = 1 && 1 +b = 1 1 ]] { - type = 'MISS_EXP', + type = 'ERR_NONSTANDARD_SYMBOL', + info = { + symbol = 'and', + } } TEST[[ -b = 1 <> 0 +b = 1 <!> 0 ]] { - type = 'MISS_EXP', + type = 'UNKNOWN_SYMBOL', + info = { + symbol = '>', + } } TEST[[ -b = 1 < < 0 +b = 1 < 0 ]] { - type = 'MISS_EXP', + type = 'UNKNOWN_SYMBOL', + info = { + symbol = '<', + } } TEST[[ -concat2 = 2^ +concat2 = 2^3. ]] { type = 'MALFORMED_NUMBER', @@ -1272,13 +1352,10 @@ for k,v in pairs(t:any) do end } TEST[[ -for i=1,10 do end +for i=1,10, do end ]] { - type = 'UNEXPECT_SYMBOL', - info = { - symbol = ',' - }, + type = 'MISS_EXP', } TEST[[ @@ -1299,7 +1376,7 @@ function func(a,b,c,) end } TEST[[ -function func(...) end +function func(...,) end ]] { type = 'ARGS_AFTER_DOTS' @@ -1323,7 +1400,12 @@ if a then type = 'MISS_SYMBOL', info = { symbol = 'end', - related = {1, 2}, + related = { + { + start = 0, + finish = 2, + }, + } } } @@ -1343,7 +1425,12 @@ if a then else type = 'MISS_SYMBOL', info = { symbol = 'end', - related = {1, 2}, + related = { + { + start = 0, + finish = 2, + }, + } } } @@ -1386,19 +1473,6 @@ TEST[[ type = 'KEYWORD', } ---TEST[[ ---::label:: ---::other_label:: ---:::: ---]] ---{ --- type = 'REDEFINE_LABEL', --- info = { --- label = 'label', --- related = {3, 7}, --- } ---} - TEST[[ local function () end @@ -1407,13 +1481,24 @@ end type = 'UNEXPECT_LFUNC_NAME' } +TEST [[ +f() 1 +]] +{ + multi = 1, + type = 'UNKNOWN_SYMBOL', + info = { + symbol = '=', + } +} + Version = 'Lua 5.1' TEST[[ ]] { type = 'UNSUPPORT_SYMBOL', - version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'}, + version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'Lua 5.5', 'LuaJIT'}, info = { version = 'Lua 5.1', } @@ -1425,22 +1510,22 @@ local goto = 1 (nil) TEST[[ -local x = '{1000}' +local x = '' ]] { type = 'ERR_ESC', - version = {'Lua 5.3', 'Lua 5.4', 'LuaJIT'}, + version = {'Lua 5.3', 'Lua 5.4', 'Lua 5.5', 'LuaJIT'}, info = { version ='Lua 5.1', } } TEST[[ -local x = 'ff' +local x = '' ]] { type = 'ERR_ESC', - version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'}, + version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'Lua 5.5', 'LuaJIT'}, info = { version = 'Lua 5.1', } @@ -1452,7 +1537,7 @@ local x = 1 2 ]] { type = 'UNSUPPORT_SYMBOL', - version = {'Lua 5.3', 'Lua 5.4'}, + version = {'Lua 5.3', 'Lua 5.4', 'Lua 5.5'}, info = { version = 'Lua 5.2', } @@ -1463,18 +1548,18 @@ local x = 1 2 ]] { type = 'UNSUPPORT_SYMBOL', - version = {'Lua 5.3', 'Lua 5.4'}, + version = {'Lua 5.3', 'Lua 5.4', 'Lua 5.5'}, info = { version = 'Lua 5.2', } } TEST[[ -local x = '{1000}' +local x = '' ]] { type = 'ERR_ESC', - version = {'Lua 5.3', 'Lua 5.4', 'LuaJIT'}, + version = {'Lua 5.3', 'Lua 5.4', 'Lua 5.5', 'LuaJIT'}, info = { version = 'Lua 5.2', } @@ -1491,11 +1576,11 @@ end Version = 'Lua 5.3' TEST[[ -local x <> = 1 +local x !> = 1 ]] { type = 'UNSUPPORT_SYMBOL', - version = 'Lua 5.4', + version = {'Lua 5.4', 'Lua 5.5'}, info = { version = 'Lua 5.3', } @@ -1509,6 +1594,20 @@ local x <> = 1 ]] (nil) +TEST[[ +local x , y !> = 1 +]] +{ + type = 'MULTI_CLOSE', +} + +TEST[[ +local x , y , z !> = 1 +]] +{ + type = 'MULTI_CLOSE', +} + TEST[[ s = '' ]] @@ -1516,7 +1615,7 @@ s = '' TEST[[ -s = '' +s = '\u' ]] { type = 'UTF8_MAX', @@ -1532,9 +1631,16 @@ x = 42 { type = 'UNSUPPORT_SYMBOL', version = 'LuaJIT', - info = { - version = 'Lua 5.4', - } + info = EXISTS, +} + +TEST[[ +x = +]] +{ + type = 'UNSUPPORT_SYMBOL', + version = 'LuaJIT', + info = EXISTS, } TEST[[ @@ -1543,19 +1649,14 @@ x = 12.5 { type = 'UNSUPPORT_SYMBOL', version = 'LuaJIT', - info = { - version = 'Lua 5.4', - } + info = EXISTS, } TEST[[ x = 1.23 ]] { - type = 'UNKNOWN_SYMBOL', - info = { - symbol = 'LL' - } + type = 'MALFORMED_NUMBER', } Version = 'LuaJIT' @@ -1567,6 +1668,8 @@ x = 0x2aLL x = 0x2All x = 12.5i x = 1I +x = 18446744073709551615ULL +x = 0b11011 ]] (nil) @@ -1574,8 +1677,12 @@ TEST[[ x = 1.23 ]] { - type = 'UNKNOWN_SYMBOL', - info = { - symbol = 'LL' - } + type = 'MALFORMED_NUMBER', +} + +TEST[[ +x = 0b1 +]] +{ + type = 'MALFORMED_NUMBER', } diff --git a/test/lines.lua b/test/lines.lua deleted file mode 100644 index a004e8d..0000000 --- a/test/lines.lua +++ /dev/null @@ -1,123 +0,0 @@ - -local utility = require 'utility' - -local parser = require 'parser' -local buf = utility.loadFile((ROOT / 'test' / 'perform' / 'lines.txt'):string()) -assert(buf) -buf = buf:gsub('\r\n', '\n'):gsub('[\r\n]', '\r\n') -local lines = parser:lines(buf) - -assert(#lines == 365) -assert(lines[2].start == 58) -assert(lines[3].start == 60) -assert(lines[358].sp == 2) -assert(lines[358].tab == 0) -assert(lines[359].sp == 0) -assert(lines[359].tab == 1) - -local row, col = lines:rowcol(618) -assert(row == 22) -assert(col == 19) - -local pos = lines:position(22, 19) -assert(pos == 618) - -local row, col = lines:rowcol(9999999) -assert(row == 365) -assert(col == 1) - -local row, col = lines:rowcol(-100) -assert(row == 1) -assert(col == 1) - -local pos = lines:position(8, 999) -assert(pos == 293) - -local pos = lines:position(8, -100) -assert(pos == 270) - -local pos = lines:position(-100, 1) -assert(pos == 1) - -local pos = lines:position(9999, 1) -assert(pos == 10373) - -local pos = lines:position(365, 1) -assert(pos == 10373) - -local row, col = lines:rowcol(10373) -assert(row == 365) -assert(col == 1) - -local pos = lines:position(9999, 1, 'utf8') -assert(pos == 10337) - -local pos = lines:position(191, 16, 'utf8') -assert(pos == 4863) - -local row, col = lines:rowcol(4863, 'utf8') -assert(row == 191) -assert(col == 16) - -local row, col = lines:rowcol(4863) -assert(col ~= 16) - -lines:set_code 'utf8' -local row, col = lines:rowcol(4863) -assert(row == 191) -assert(col == 16) - - -local buf = [[ -local xx]] -local lines = parser:lines(buf) - -local row, col = lines:rowcol(7) -assert(row == 1) -assert(col == 7) - -local row, col = lines:rowcol(8) -assert(row == 1) -assert(col == 8) - -local row, col = lines:rowcol(9) -assert(row == 1) -assert(col == 9) - -local row, col = lines:rowcol(10) -assert(row == 1) -assert(col == 9) - -local buf = [[ -local xx -local xx]] - -local lines = parser:lines(buf) - -local row, col = lines:rowcol(14) -assert(row == 2) -assert(col == 5) - -local row, col = lines:rowcol(15) -assert(row == 2) -assert(col == 6) - -local row, col = lines:rowcol(16) -assert(row == 2) -assert(col == 7) - -local row, col = lines:rowcol(17) -assert(row == 2) -assert(col == 8) - -local row, col = lines:rowcol(18) -assert(row == 2) -assert(col == 9) - -local row, col = lines:rowcol(19) -assert(row == 2) -assert(col == 9) - -local row, col = lines:rowcol(20) -assert(row == 2) -assert(col == 9) diff --git a/test/main.lua b/test/main.lua index ff63072..feecb3f 100644 --- a/test/main.lua +++ b/test/main.lua @@ -4,7 +4,6 @@ package.path = package.path .. ';' .. root .. '\\src\\?.lua' .. ';' .. root .. '\\test\\?.lua' .. ';' .. root .. '\\test\\?\\init.lua' -local parser = require 'parser' local fs = require 'bee.filesystem' rawset(_G, 'ROOT', fs.path(root)) @@ -20,9 +19,10 @@ local function main() --collectgarbage 'stop' unitTest 'ast' unitTest 'grammar' - unitTest 'lines' + --unitTest 'lines' unitTest 'grammar_check' unitTest 'syntax_check' + --unitTest 'guide' unitTest 'perform' print('测试完成') diff --git a/test/perform/3.txt b/test/perform/3.txt index 61e5abc..650c495 100644 --- a/test/perform/3.txt +++ b/test/perform/3.txt @@ -1 +1 @@ -return {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +return {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} diff --git a/test/perform/6.txt b/test/perform/6.txt new file mode 100644 index 0000000..7492872 --- /dev/null +++ b/test/perform/6.txt @@ -0,0 +1,1001 @@ +--------------------------------------------------------------------------------------------- +-- Copyright (c) Microsoft Corporation. All rights reserved. +-- Licensed under the MIT License. See License.txt in the project root for license information. +---------------------------------------------------------------------------------------------- + +--#region util +-- Copying/Replacing +-- https://github.com/Microsoft/vscode/blob/master/src/vs/base/common/platform.ts +-- https://github.com/Microsoft/vscode/blob/master/src/vs/base/common/charCode.ts + +local platform = require 'bee.platform' +local ll = require 'lpeglabel' +local isWindows = platform.OS == 'Windows' + +local CharCode = { + Null = 0, + --- The `\t` character. + Tab = 9, + --- The `\n` character. + LineFeed = 10, + --- The `\r` character. + CarriageReturn = 13, + Space = 32, + --- The `!` character. + ExclamationMark = 33, + --- The `"` character. + DoubleQuote = 34, + --- The `#` character. + Hash = 35, + --- The `$` character. + DollarSign = 36, + --- The `%` character. + PercentSign = 37, + --- The `&` character. + Ampersand = 38, + --- The `'` character. + SingleQuote = 39, + --- The `(` character. + OpenParen = 40, + --- The `)` character. + CloseParen = 41, + --- The `*` character. + Asterisk = 42, + --- The `+` character. + Plus = 43, + --- The `,` character. + Comma = 44, + --- The `-` character. + Dash = 45, + --- The `.` character. + Period = 46, + --- The `/` character. + Slash = 47, + + Digit0 = 48, + Digit1 = 49, + Digit2 = 50, + Digit3 = 51, + Digit4 = 52, + Digit5 = 53, + Digit6 = 54, + Digit7 = 55, + Digit8 = 56, + Digit9 = 57, + + --- The `:` character. + Colon = 58, + --- The `;` character. + Semicolon = 59, + --- The `<` character. + LessThan = 60, + --- The `=` character. + Equals = 61, + --- The `>` character. + GreaterThan = 62, + --- The `?` character. + QuestionMark = 63, + --- The `@` character. + AtSign = 64, + + A = 65, + B = 66, + C = 67, + D = 68, + E = 69, + F = 70, + G = 71, + H = 72, + I = 73, + J = 74, + K = 75, + L = 76, + M = 77, + N = 78, + O = 79, + P = 80, + Q = 81, + R = 82, + S = 83, + T = 84, + U = 85, + V = 86, + W = 87, + X = 88, + Y = 89, + Z = 90, + + --- The `[` character. + OpenSquareBracket = 91, + --- The `\` character. + Backslash = 92, + --- The `]` character. + CloseSquareBracket = 93, + --- The `^` character. + Caret = 94, + --- The `_` character. + Underline = 95, + --- The ``(`)`` character. + BackTick = 96, + + a = 97, + b = 98, + c = 99, + d = 100, + e = 101, + f = 102, + g = 103, + h = 104, + i = 105, + j = 106, + k = 107, + l = 108, + m = 109, + n = 110, + o = 111, + p = 112, + q = 113, + r = 114, + s = 115, + t = 116, + u = 117, + v = 118, + w = 119, + x = 120, + y = 121, + z = 122, + + --- The `{` character. + OpenCurlyBrace = 123, + --- The `|` character. + Pipe = 124, + --- The `}` character. + CloseCurlyBrace = 125, + --- The `~` character. + Tilde = 126, + + U_Combining_Grave_Accent = 0x0300, -- U+0300 Combining Grave Accent + U_Combining_Acute_Accent = 0x0301, -- U+0301 Combining Acute Accent + U_Combining_Circumflex_Accent = 0x0302, -- U+0302 Combining Circumflex Accent + U_Combining_Tilde = 0x0303, -- U+0303 Combining Tilde + U_Combining_Macron = 0x0304, -- U+0304 Combining Macron + U_Combining_Overline = 0x0305, -- U+0305 Combining Overline + U_Combining_Breve = 0x0306, -- U+0306 Combining Breve + U_Combining_Dot_Above = 0x0307, -- U+0307 Combining Dot Above + U_Combining_Diaeresis = 0x0308, -- U+0308 Combining Diaeresis + U_Combining_Hook_Above = 0x0309, -- U+0309 Combining Hook Above + U_Combining_Ring_Above = 0x030A, -- U+030A Combining Ring Above + U_Combining_Double_Acute_Accent = 0x030B, -- U+030B Combining Double Acute Accent + U_Combining_Caron = 0x030C, -- U+030C Combining Caron + U_Combining_Vertical_Line_Above = 0x030D, -- U+030D Combining Vertical Line Above + U_Combining_Double_Vertical_Line_Above = 0x030E, -- U+030E Combining Double Vertical Line Above + U_Combining_Double_Grave_Accent = 0x030F, -- U+030F Combining Double Grave Accent + U_Combining_Candrabindu = 0x0310, -- U+0310 Combining Candrabindu + U_Combining_Inverted_Breve = 0x0311, -- U+0311 Combining Inverted Breve + U_Combining_Turned_Comma_Above = 0x0312, -- U+0312 Combining Turned Comma Above + U_Combining_Comma_Above = 0x0313, -- U+0313 Combining Comma Above + U_Combining_Reversed_Comma_Above = 0x0314, -- U+0314 Combining Reversed Comma Above + U_Combining_Comma_Above_Right = 0x0315, -- U+0315 Combining Comma Above Right + U_Combining_Grave_Accent_Below = 0x0316, -- U+0316 Combining Grave Accent Below + U_Combining_Acute_Accent_Below = 0x0317, -- U+0317 Combining Acute Accent Below + U_Combining_Left_Tack_Below = 0x0318, -- U+0318 Combining Left Tack Below + U_Combining_Right_Tack_Below = 0x0319, -- U+0319 Combining Right Tack Below + U_Combining_Left_Angle_Above = 0x031A, -- U+031A Combining Left Angle Above + U_Combining_Horn = 0x031B, -- U+031B Combining Horn + U_Combining_Left_Half_Ring_Below = 0x031C, -- U+031C Combining Left Half Ring Below + U_Combining_Up_Tack_Below = 0x031D, -- U+031D Combining Up Tack Below + U_Combining_Down_Tack_Below = 0x031E, -- U+031E Combining Down Tack Below + U_Combining_Plus_Sign_Below = 0x031F, -- U+031F Combining Plus Sign Below + U_Combining_Minus_Sign_Below = 0x0320, -- U+0320 Combining Minus Sign Below + U_Combining_Palatalized_Hook_Below = 0x0321, -- U+0321 Combining Palatalized Hook Below + U_Combining_Retroflex_Hook_Below = 0x0322, -- U+0322 Combining Retroflex Hook Below + U_Combining_Dot_Below = 0x0323, -- U+0323 Combining Dot Below + U_Combining_Diaeresis_Below = 0x0324, -- U+0324 Combining Diaeresis Below + U_Combining_Ring_Below = 0x0325, -- U+0325 Combining Ring Below + U_Combining_Comma_Below = 0x0326, -- U+0326 Combining Comma Below + U_Combining_Cedilla = 0x0327, -- U+0327 Combining Cedilla + U_Combining_Ogonek = 0x0328, -- U+0328 Combining Ogonek + U_Combining_Vertical_Line_Below = 0x0329, -- U+0329 Combining Vertical Line Below + U_Combining_Bridge_Below = 0x032A, -- U+032A Combining Bridge Below + U_Combining_Inverted_Double_Arch_Below = 0x032B, -- U+032B Combining Inverted Double Arch Below + U_Combining_Caron_Below = 0x032C, -- U+032C Combining Caron Below + U_Combining_Circumflex_Accent_Below = 0x032D, -- U+032D Combining Circumflex Accent Below + U_Combining_Breve_Below = 0x032E, -- U+032E Combining Breve Below + U_Combining_Inverted_Breve_Below = 0x032F, -- U+032F Combining Inverted Breve Below + U_Combining_Tilde_Below = 0x0330, -- U+0330 Combining Tilde Below + U_Combining_Macron_Below = 0x0331, -- U+0331 Combining Macron Below + U_Combining_Low_Line = 0x0332, -- U+0332 Combining Low Line + U_Combining_Double_Low_Line = 0x0333, -- U+0333 Combining Double Low Line + U_Combining_Tilde_Overlay = 0x0334, -- U+0334 Combining Tilde Overlay + U_Combining_Short_Stroke_Overlay = 0x0335, -- U+0335 Combining Short Stroke Overlay + U_Combining_Long_Stroke_Overlay = 0x0336, -- U+0336 Combining Long Stroke Overlay + U_Combining_Short_Solidus_Overlay = 0x0337, -- U+0337 Combining Short Solidus Overlay + U_Combining_Long_Solidus_Overlay = 0x0338, -- U+0338 Combining Long Solidus Overlay + U_Combining_Right_Half_Ring_Below = 0x0339, -- U+0339 Combining Right Half Ring Below + U_Combining_Inverted_Bridge_Below = 0x033A, -- U+033A Combining Inverted Bridge Below + U_Combining_Square_Below = 0x033B, -- U+033B Combining Square Below + U_Combining_Seagull_Below = 0x033C, -- U+033C Combining Seagull Below + U_Combining_X_Above = 0x033D, -- U+033D Combining X Above + U_Combining_Vertical_Tilde = 0x033E, -- U+033E Combining Vertical Tilde + U_Combining_Double_Overline = 0x033F, -- U+033F Combining Double Overline + U_Combining_Grave_Tone_Mark = 0x0340, -- U+0340 Combining Grave Tone Mark + U_Combining_Acute_Tone_Mark = 0x0341, -- U+0341 Combining Acute Tone Mark + U_Combining_Greek_Perispomeni = 0x0342, -- U+0342 Combining Greek Perispomeni + U_Combining_Greek_Koronis = 0x0343, -- U+0343 Combining Greek Koronis + U_Combining_Greek_Dialytika_Tonos = 0x0344, -- U+0344 Combining Greek Dialytika Tonos + U_Combining_Greek_Ypogegrammeni = 0x0345, -- U+0345 Combining Greek Ypogegrammeni + U_Combining_Bridge_Above = 0x0346, -- U+0346 Combining Bridge Above + U_Combining_Equals_Sign_Below = 0x0347, -- U+0347 Combining Equals Sign Below + U_Combining_Double_Vertical_Line_Below = 0x0348, -- U+0348 Combining Double Vertical Line Below + U_Combining_Left_Angle_Below = 0x0349, -- U+0349 Combining Left Angle Below + U_Combining_Not_Tilde_Above = 0x034A, -- U+034A Combining Not Tilde Above + U_Combining_Homothetic_Above = 0x034B, -- U+034B Combining Homothetic Above + U_Combining_Almost_Equal_To_Above = 0x034C, -- U+034C Combining Almost Equal To Above + U_Combining_Left_Right_Arrow_Below = 0x034D, -- U+034D Combining Left Right Arrow Below + U_Combining_Upwards_Arrow_Below = 0x034E, -- U+034E Combining Upwards Arrow Below + U_Combining_Grapheme_Joiner = 0x034F, -- U+034F Combining Grapheme Joiner + U_Combining_Right_Arrowhead_Above = 0x0350, -- U+0350 Combining Right Arrowhead Above + U_Combining_Left_Half_Ring_Above = 0x0351, -- U+0351 Combining Left Half Ring Above + U_Combining_Fermata = 0x0352, -- U+0352 Combining Fermata + U_Combining_X_Below = 0x0353, -- U+0353 Combining X Below + U_Combining_Left_Arrowhead_Below = 0x0354, -- U+0354 Combining Left Arrowhead Below + U_Combining_Right_Arrowhead_Below = 0x0355, -- U+0355 Combining Right Arrowhead Below + U_Combining_Right_Arrowhead_And_Up_Arrowhead_Below = 0x0356, -- U+0356 Combining Right Arrowhead And Up Arrowhead Below + U_Combining_Right_Half_Ring_Above = 0x0357, -- U+0357 Combining Right Half Ring Above + U_Combining_Dot_Above_Right = 0x0358, -- U+0358 Combining Dot Above Right + U_Combining_Asterisk_Below = 0x0359, -- U+0359 Combining Asterisk Below + U_Combining_Double_Ring_Below = 0x035A, -- U+035A Combining Double Ring Below + U_Combining_Zigzag_Above = 0x035B, -- U+035B Combining Zigzag Above + U_Combining_Double_Breve_Below = 0x035C, -- U+035C Combining Double Breve Below + U_Combining_Double_Breve = 0x035D, -- U+035D Combining Double Breve + U_Combining_Double_Macron = 0x035E, -- U+035E Combining Double Macron + U_Combining_Double_Macron_Below = 0x035F, -- U+035F Combining Double Macron Below + U_Combining_Double_Tilde = 0x0360, -- U+0360 Combining Double Tilde + U_Combining_Double_Inverted_Breve = 0x0361, -- U+0361 Combining Double Inverted Breve + U_Combining_Double_Rightwards_Arrow_Below = 0x0362, -- U+0362 Combining Double Rightwards Arrow Below + U_Combining_Latin_Small_Letter_A = 0x0363, -- U+0363 Combining Latin Small Letter A + U_Combining_Latin_Small_Letter_E = 0x0364, -- U+0364 Combining Latin Small Letter E + U_Combining_Latin_Small_Letter_I = 0x0365, -- U+0365 Combining Latin Small Letter I + U_Combining_Latin_Small_Letter_O = 0x0366, -- U+0366 Combining Latin Small Letter O + U_Combining_Latin_Small_Letter_U = 0x0367, -- U+0367 Combining Latin Small Letter U + U_Combining_Latin_Small_Letter_C = 0x0368, -- U+0368 Combining Latin Small Letter C + U_Combining_Latin_Small_Letter_D = 0x0369, -- U+0369 Combining Latin Small Letter D + U_Combining_Latin_Small_Letter_H = 0x036A, -- U+036A Combining Latin Small Letter H + U_Combining_Latin_Small_Letter_M = 0x036B, -- U+036B Combining Latin Small Letter M + U_Combining_Latin_Small_Letter_R = 0x036C, -- U+036C Combining Latin Small Letter R + U_Combining_Latin_Small_Letter_T = 0x036D, -- U+036D Combining Latin Small Letter T + U_Combining_Latin_Small_Letter_V = 0x036E, -- U+036E Combining Latin Small Letter V + U_Combining_Latin_Small_Letter_X = 0x036F, -- U+036F Combining Latin Small Letter X + + -- Unicode Character 'LINE SEPARATOR' (U+2028) + -- http://www.fileformat.info/info/unicode/char/2028/index.htm + LINE_SEPARATOR_2028 = 8232, + + -- http://www.fileformat.info/info/unicode/category/Sk/list.htm + U_CIRCUMFLEX = 0x005E, -- U+005E CIRCUMFLEX + U_GRAVE_ACCENT = 0x0060, -- U+0060 GRAVE ACCENT + U_DIAERESIS = 0x00A8, -- U+00A8 DIAERESIS + U_MACRON = 0x00AF, -- U+00AF MACRON + U_ACUTE_ACCENT = 0x00B4, -- U+00B4 ACUTE ACCENT + U_CEDILLA = 0x00B8, -- U+00B8 CEDILLA + U_MODIFIER_LETTER_LEFT_ARROWHEAD = 0x02C2, -- U+02C2 MODIFIER LETTER LEFT ARROWHEAD + U_MODIFIER_LETTER_RIGHT_ARROWHEAD = 0x02C3, -- U+02C3 MODIFIER LETTER RIGHT ARROWHEAD + U_MODIFIER_LETTER_UP_ARROWHEAD = 0x02C4, -- U+02C4 MODIFIER LETTER UP ARROWHEAD + U_MODIFIER_LETTER_DOWN_ARROWHEAD = 0x02C5, -- U+02C5 MODIFIER LETTER DOWN ARROWHEAD + U_MODIFIER_LETTER_CENTRED_RIGHT_HALF_RING = 0x02D2, -- U+02D2 MODIFIER LETTER CENTRED RIGHT HALF RING + U_MODIFIER_LETTER_CENTRED_LEFT_HALF_RING = 0x02D3, -- U+02D3 MODIFIER LETTER CENTRED LEFT HALF RING + U_MODIFIER_LETTER_UP_TACK = 0x02D4, -- U+02D4 MODIFIER LETTER UP TACK + U_MODIFIER_LETTER_DOWN_TACK = 0x02D5, -- U+02D5 MODIFIER LETTER DOWN TACK + U_MODIFIER_LETTER_PLUS_SIGN = 0x02D6, -- U+02D6 MODIFIER LETTER PLUS SIGN + U_MODIFIER_LETTER_MINUS_SIGN = 0x02D7, -- U+02D7 MODIFIER LETTER MINUS SIGN + U_BREVE = 0x02D8, -- U+02D8 BREVE + U_DOT_ABOVE = 0x02D9, -- U+02D9 DOT ABOVE + U_RING_ABOVE = 0x02DA, -- U+02DA RING ABOVE + U_OGONEK = 0x02DB, -- U+02DB OGONEK + U_SMALL_TILDE = 0x02DC, -- U+02DC SMALL TILDE + U_DOUBLE_ACUTE_ACCENT = 0x02DD, -- U+02DD DOUBLE ACUTE ACCENT + U_MODIFIER_LETTER_RHOTIC_HOOK = 0x02DE, -- U+02DE MODIFIER LETTER RHOTIC HOOK + U_MODIFIER_LETTER_CROSS_ACCENT = 0x02DF, -- U+02DF MODIFIER LETTER CROSS ACCENT + U_MODIFIER_LETTER_EXTRA_HIGH_TONE_BAR = 0x02E5, -- U+02E5 MODIFIER LETTER EXTRA-HIGH TONE BAR + U_MODIFIER_LETTER_HIGH_TONE_BAR = 0x02E6, -- U+02E6 MODIFIER LETTER HIGH TONE BAR + U_MODIFIER_LETTER_MID_TONE_BAR = 0x02E7, -- U+02E7 MODIFIER LETTER MID TONE BAR + U_MODIFIER_LETTER_LOW_TONE_BAR = 0x02E8, -- U+02E8 MODIFIER LETTER LOW TONE BAR + U_MODIFIER_LETTER_EXTRA_LOW_TONE_BAR = 0x02E9, -- U+02E9 MODIFIER LETTER EXTRA-LOW TONE BAR + U_MODIFIER_LETTER_YIN_DEPARTING_TONE_MARK = 0x02EA, -- U+02EA MODIFIER LETTER YIN DEPARTING TONE MARK + U_MODIFIER_LETTER_YANG_DEPARTING_TONE_MARK = 0x02EB, -- U+02EB MODIFIER LETTER YANG DEPARTING TONE MARK + U_MODIFIER_LETTER_UNASPIRATED = 0x02ED, -- U+02ED MODIFIER LETTER UNASPIRATED + U_MODIFIER_LETTER_LOW_DOWN_ARROWHEAD = 0x02EF, -- U+02EF MODIFIER LETTER LOW DOWN ARROWHEAD + U_MODIFIER_LETTER_LOW_UP_ARROWHEAD = 0x02F0, -- U+02F0 MODIFIER LETTER LOW UP ARROWHEAD + U_MODIFIER_LETTER_LOW_LEFT_ARROWHEAD = 0x02F1, -- U+02F1 MODIFIER LETTER LOW LEFT ARROWHEAD + U_MODIFIER_LETTER_LOW_RIGHT_ARROWHEAD = 0x02F2, -- U+02F2 MODIFIER LETTER LOW RIGHT ARROWHEAD + U_MODIFIER_LETTER_LOW_RING = 0x02F3, -- U+02F3 MODIFIER LETTER LOW RING + U_MODIFIER_LETTER_MIDDLE_GRAVE_ACCENT = 0x02F4, -- U+02F4 MODIFIER LETTER MIDDLE GRAVE ACCENT + U_MODIFIER_LETTER_MIDDLE_DOUBLE_GRAVE_ACCENT = 0x02F5, -- U+02F5 MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT + U_MODIFIER_LETTER_MIDDLE_DOUBLE_ACUTE_ACCENT = 0x02F6, -- U+02F6 MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT + U_MODIFIER_LETTER_LOW_TILDE = 0x02F7, -- U+02F7 MODIFIER LETTER LOW TILDE + U_MODIFIER_LETTER_RAISED_COLON = 0x02F8, -- U+02F8 MODIFIER LETTER RAISED COLON + U_MODIFIER_LETTER_BEGIN_HIGH_TONE = 0x02F9, -- U+02F9 MODIFIER LETTER BEGIN HIGH TONE + U_MODIFIER_LETTER_END_HIGH_TONE = 0x02FA, -- U+02FA MODIFIER LETTER END HIGH TONE + U_MODIFIER_LETTER_BEGIN_LOW_TONE = 0x02FB, -- U+02FB MODIFIER LETTER BEGIN LOW TONE + U_MODIFIER_LETTER_END_LOW_TONE = 0x02FC, -- U+02FC MODIFIER LETTER END LOW TONE + U_MODIFIER_LETTER_SHELF = 0x02FD, -- U+02FD MODIFIER LETTER SHELF + U_MODIFIER_LETTER_OPEN_SHELF = 0x02FE, -- U+02FE MODIFIER LETTER OPEN SHELF + U_MODIFIER_LETTER_LOW_LEFT_ARROW = 0x02FF, -- U+02FF MODIFIER LETTER LOW LEFT ARROW + U_GREEK_LOWER_NUMERAL_SIGN = 0x0375, -- U+0375 GREEK LOWER NUMERAL SIGN + U_GREEK_TONOS = 0x0384, -- U+0384 GREEK TONOS + U_GREEK_DIALYTIKA_TONOS = 0x0385, -- U+0385 GREEK DIALYTIKA TONOS + U_GREEK_KORONIS = 0x1FBD, -- U+1FBD GREEK KORONIS + U_GREEK_PSILI = 0x1FBF, -- U+1FBF GREEK PSILI + U_GREEK_PERISPOMENI = 0x1FC0, -- U+1FC0 GREEK PERISPOMENI + U_GREEK_DIALYTIKA_AND_PERISPOMENI = 0x1FC1, -- U+1FC1 GREEK DIALYTIKA AND PERISPOMENI + U_GREEK_PSILI_AND_VARIA = 0x1FCD, -- U+1FCD GREEK PSILI AND VARIA + U_GREEK_PSILI_AND_OXIA = 0x1FCE, -- U+1FCE GREEK PSILI AND OXIA + U_GREEK_PSILI_AND_PERISPOMENI = 0x1FCF, -- U+1FCF GREEK PSILI AND PERISPOMENI + U_GREEK_DASIA_AND_VARIA = 0x1FDD, -- U+1FDD GREEK DASIA AND VARIA + U_GREEK_DASIA_AND_OXIA = 0x1FDE, -- U+1FDE GREEK DASIA AND OXIA + U_GREEK_DASIA_AND_PERISPOMENI = 0x1FDF, -- U+1FDF GREEK DASIA AND PERISPOMENI + U_GREEK_DIALYTIKA_AND_VARIA = 0x1FED, -- U+1FED GREEK DIALYTIKA AND VARIA + U_GREEK_DIALYTIKA_AND_OXIA = 0x1FEE, -- U+1FEE GREEK DIALYTIKA AND OXIA + U_GREEK_VARIA = 0x1FEF, -- U+1FEF GREEK VARIA + U_GREEK_OXIA = 0x1FFD, -- U+1FFD GREEK OXIA + U_GREEK_DASIA = 0x1FFE, -- U+1FFE GREEK DASIA + + + U_OVERLINE = 0x203E, -- Unicode Character 'OVERLINE' + + -- UTF-8 BOM + -- Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF) + -- http://www.fileformat.info/info/unicode/char/feff/index.htm + UTF8_BOM = 65279, +} + + +local _schemePattern = '^%w[%w%d+.-]*$' +local _singleSlashStart = '^/' +local _doubleSlashStart = '^//' + +local _throwOnMissingSchema = true + +local function setUriThrowOnMissingScheme(value) + local old = _throwOnMissingSchema + _throwOnMissingSchema = value + return old +end + +local function _validateUri(ret, _strict) + + -- scheme, must be set + if not ret.scheme then + if _strict or _throwOnMissingSchema then + error('[UriError]: Scheme is missing'); + else + -- console.warn(`[UriError]: Scheme is missing: {scheme: "", authority: "${ret.authority}", path: "${ret.path}", query: "${ret.query}", fragment: "${ret.fragment}"}`); + end + end + + -- scheme, https://tools.ietf.org/html/rfc3986#section-3.1 + -- ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + if ret.scheme and not ret.scheme:match(_schemePattern) then + error('[UriError]: Scheme contains illegal characters.') + end + + -- path, http://tools.ietf.org/html/rfc3986#section-3.3 + -- If a URI contains an authority component, then the path component + -- must either be empty or begin with a slash ("/") character. If a URI + -- does not contain an authority component, then the path cannot begin + -- with two slash characters ("//"). + if ret.path then + if ret.authority then + if not ret.path:match(_singleSlashStart) then + error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character') + end + else + if ret.path:match(_doubleSlashStart) then + error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")') + end + end + end +end + +-- for a while we allowed uris *without* schemes and this is the migration +-- for them, e.g. an uri without scheme and without strict-mode warns and falls +-- back to the file-scheme. that should cause the least carnage and still be a +-- clear warning +local function _schemeFix(scheme, _strict) + if _strict or _throwOnMissingSchema then + return scheme or _empty + end + if not scheme then + -- console.trace('BAD uri lacks scheme, falling back to file-scheme.'); + scheme = 'file' + end + return scheme +end + +-- implements a bit of https://tools.ietf.org/html/rfc3986#section-5 +local function _referenceResolution(scheme, path) + + -- the slash-character is our 'default base' as we don't + -- support constructing URIs relative to other URIs. This + -- also means that we alter and potentially break paths. + -- see https://tools.ietf.org/html/rfc3986#section-5.1.4 + if scheme == 'file' then + if not path then + path = _slash + elseif path:sub(1, 1) ~= _slash then + path = _slash .. path + end + end + return path +end + +local _empty = '' +local _slash = '/' +-- /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; +local _regexp = ll.P { + ll.Ct( + ll.Cg(ll.V'scheme' ^-1, 'scheme') * + ll.Cg(ll.V'authority'^-1, 'authority') * + ll.Cg(ll.V'path' , 'path') * + ll.Cg(ll.V'query' ^-1, 'query') * + ll.Cg(ll.V'fragment' ^-1, 'fragment') + ), + scheme = ll.C((ll.P(1) - ll.S':/?#')^1) * ll.P':', + authority = ll.P'//' * ll.C((ll.P(1) - ll.S'/?#')^0), + path = ll.C((ll.P(1) - ll.S'?#')^0), + query = ll.P'?' * ll.C((ll.P(1) - ll.P'#')^0), + fragment = ll.P'#' * ll.C(ll.P(1)^0) +} + +--Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986. +--This class is a simple parser which creates the basic component parts +--(http://tools.ietf.org/html/rfc3986#section-3) with minimal validation +--and encoding. +-- +-- foo://example.com:8042/over/there?name=ferret#nose +-- \_/ \______________/\_________/ \_________/ \__/ +-- | | | | | +-- scheme authority path query fragment +-- | _____________________|__ +-- / \ / \ +-- urn:example:animal:ferret:nose +-- + +local URI = {} +local _URI = {} + +do + ---@class URI + URI.__index = mt + URI.type = 'URI' + setmetatable(URI, URI) + + function URI:isUri(thing) + if getmetatable(thing) == URI then + return true + end + if not thing then + return false + end + return type(thing.authority) == 'string' + and type(thing.fragment) == 'string' + and type(thing.path) == 'string' + and type(thing.query) == 'string' + and type(thing.scheme) == 'string' + and type(thing.fsPath) == 'function' + and type(thing.with) == 'function' + and type(thing.toString) == 'function' + end + + --- scheme is the 'http' part of 'http://www.msft.com/some/path?query#fragment'. + --- The part before the first colon. + URI.scheme = nil + + --- authority is the 'www.msft.com' part of 'http://www.msft.com/some/path?query#fragment'. + --- The part between the first double slashes and the next slash. + URI.authority = nil + + --- path is the '/some/path' part of 'http://www.msft.com/some/path?query#fragment'. + URI.path = nil + + --- query is the 'query' part of 'http://www.msft.com/some/path?query#fragment'. + URI.query = nil + + --- fragment is the 'fragment' part of 'http://www.msft.com/some/path?query#fragment'. + URI.fragment = nil + + function URI:__call(schemeOrData, authority, path, query, fragment, _strict) + local self = setmetatable({}, self) + if type(schemeOrData) == 'table' then + self.scheme = schemeOrData.scheme or _empty + self.authority = schemeOrData.authority or _empty + self.path = schemeOrData.path or _empty + self.query = schemeOrData.query or _empty + self.fragment = schemeOrData.fragment or _empty + -- no validation because it's this URI + -- that creates uri components. + -- _validateUri(this); + else + self.scheme = _schemeFix(schemeOrData, _strict) + self.authority = authority or _empty + self.path = _referenceResolution(self.scheme, path or _empty) + self.query = query or _empty + self.fragment = fragment or _empty + + _validateUri(this, _strict) + end + return self + end + + -- ---- filesystem path ----------------------- + + --- Returns a string representing the corresponding file system path of this URI. + ---|Will handle UNC paths, normalizes windows drive letters to lower-case, and uses the + ---|platform specific path separator. + ---| + ---|Will *not* validate the path for invalid characters and semantics. + ---|Will *not* look at the scheme of this URI. + ---|The result shall *not* be used for display purposes but for accessing a file on disk. + ---| + ---| + ---|The *difference* to `URI#path` is the use of the platform specific separator and the handling + ---|of UNC paths. See the below sample of a file-uri with an authority (UNC path). + ---| + ---|```ts + ---| const u = URI.parse('file://server/c$/folder/file.txt') + ---| u.authority === 'server' + ---| u.path === '/shares/c$/file.txt' + ---| u.fsPath === '\\server\c$\folder\file.txt' + ---|``` + ---| + ---|Using `URI#path` to read a file (using fs-apis) would not be enough because parts of the path, + ---|namely the server name, would be missing. Therefore `URI#fsPath` exists - it's sugar to ease working + ---|with URIs that represent files on disk (`file` scheme). + + function URI:fsPath() + -- if (this.scheme !== 'file') { + -- console.warn(`[UriError] calling fsPath with scheme ${this.scheme}`); + -- } + return _makeFsPath(self) + end + + ------ modify to new ------------------------- + + --with(change: { scheme?: string; authority?: string | null; path?: string | null; query?: string | null; fragment?: string | null }): URI { + + -- if (!change) { + -- return this; + -- } + + -- let { scheme, authority, path, query, fragment } = change; + -- if (scheme === undefined) { + -- scheme = this.scheme; + -- } else if (scheme === null) { + -- scheme = _empty; + -- } + -- if (authority === undefined) { + -- authority = this.authority; + -- } else if (authority === null) { + -- authority = _empty; + -- } + -- if (path === undefined) { + -- path = this.path; + -- } else if (path === null) { + -- path = _empty; + -- } + -- if (query === undefined) { + -- query = this.query; + -- } else if (query === null) { + -- query = _empty; + -- } + -- if (fragment === undefined) { + -- fragment = this.fragment; + -- } else if (fragment === null) { + -- fragment = _empty; + -- } + + -- if (scheme === this.scheme + -- && authority === this.authority + -- && path === this.path + -- && query === this.query + -- && fragment === this.fragment) { + + -- return this; + -- } + + -- return new _URI(scheme, authority, path, query, fragment); + --} + + ------ parse & validate ------------------------ + + --- Creates a new URI from a string, e.g. `http://www.msft.com/some/path`, + ---|`file:///usr/home`, or `scheme:with/path`. + ---| + ---|@param value A string which represents an URI (see `URI#toString`). + --function URI:parse(value, _strict) + -- local match = _regexp:match(value) + -- if not match then + -- return _URI(_empty, _empty, _empty, _empty, _empty) + -- end + -- return _URI( + -- match.scheme or _empty, + -- decodeURIComponent(match[4] || _empty), + -- decodeURIComponent(match[5] || _empty), + -- decodeURIComponent(match[7] || _empty), + -- decodeURIComponent(match[9] || _empty), + -- _strict + -- ); + --end + + --- Creates a new URI from a file system path, e.g. `c:\my\files`, + ---|`/usr/home`, or `\\server\share\some\path`. + ---| + ---|The *difference* between `URI#parse` and `URI#file` is that the latter treats the argument + ---|as path, not as stringified-uri. E.g. `URI.file(path)` is **not the same as** + ---|`URI.parse('file://' + path)` because the path might contain characters that are + ---|interpreted (# and ?). See the following sample: + ---|```ts + ---|const good = URI.file('/coding/c#/project1'); + ---|good.scheme === 'file'; + ---|good.path === '/coding/c#/project1'; + ---|good.fragment === ''; + ---|const bad = URI.parse('file://' + '/coding/c#/project1'); + ---|bad.scheme === 'file'; + ---|bad.path === '/coding/c'; // path is now broken + ---|bad.fragment === '/project1'; + ---|``` + ---| * + ---| * @param path A file system path (see `URI#fsPath`) + ---| */ + function mt:file(path) + + local authority = _empty + + -- normalize to fwd-slashes on windows, + -- on other systems bwd-slashes are valid + -- filename character, eg /f\oo/ba\r.txt + if isWindows then + path = path:gsub('[\\/]', _slash) + end + + -- check for authority as used in UNC shares + -- or use the path as given + if path:sub(1, 1) == _slash and path:sub(2, 2) == _slash then + local idx = path:find(_slash, 3) + if not idx then + authority = path:sub(3) + path = _slash + else + authority = path:sub(3, idx-1) + path = path:sub(idx) or _slash; + end + end + + return _URI('file', authority, path, _empty, _empty) + } + + static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): URI { + return new _URI( + components.scheme, + components.authority, + components.path, + components.query, + components.fragment, + ); + } + + // ---- printing/externalize --------------------------- + + /** + * Creates a string representation for this URI. It's guaranteed that calling + * `URI.parse` with the result of this function creates an URI which is equal + * to this URI. + * + * * The result shall *not* be used for display purposes but for externalization or transport. + * * The result will be encoded using the percentage encoding and encoding happens mostly + * ignore the scheme-specific encoding rules. + * + * @param skipEncoding Do not encode the result, default is `false` + */ + toString(skipEncoding: boolean = false): string { + return _asFormatted(this, skipEncoding); + } + + toJSON(): UriComponents { + return this; + } + + static revive(data: UriComponents | URI): URI; + static revive(data: UriComponents | URI | undefined): URI | undefined; + static revive(data: UriComponents | URI | null): URI | null; + static revive(data: UriComponents | URI | undefined | null): URI | undefined | null; + static revive(data: UriComponents | URI | undefined | null): URI | undefined | null { + if (!data) { + return data; + } else if (data instanceof URI) { + return data; + } else { + const result = new _URI(data); + result._formatted = (data).external; + result._fsPath = (data)._sep === _pathSepMarker ? (data).fsPath : null; + return result; + } + } +} + +export interface UriComponents { + scheme: string; + authority: string; + path: string; + query: string; + fragment: string; +} + +interface UriState extends UriComponents { + $mid: number; + external: string; + fsPath: string; + _sep: 1 | undefined; +} + +const _pathSepMarker = isWindows ? 1 : undefined; + +// tslint:disable-next-line:class-name +class _URI extends URI { + + _formatted: string | null = null; + _fsPath: string | null = null; + + get fsPath(): string { + if (!this._fsPath) { + this._fsPath = _makeFsPath(this); + } + return this._fsPath; + } + + toString(skipEncoding: boolean = false): string { + if (!skipEncoding) { + if (!this._formatted) { + this._formatted = _asFormatted(this, false); + } + return this._formatted; + } else { + // we don't cache that + return _asFormatted(this, true); + } + } + + toJSON(): UriComponents { + const res = { + $mid: 1 + }; + // cached state + if (this._fsPath) { + res.fsPath = this._fsPath; + res._sep = _pathSepMarker; + } + if (this._formatted) { + res.external = this._formatted; + } + // uri components + if (this.path) { + res.path = this.path; + } + if (this.scheme) { + res.scheme = this.scheme; + } + if (this.authority) { + res.authority = this.authority; + } + if (this.query) { + res.query = this.query; + } + if (this.fragment) { + res.fragment = this.fragment; + } + return res; + } +} + +// reserved characters: https://tools.ietf.org/html/rfc3986#section-2.2 +const encodeTable: { [ch: number]: string } = { + [CharCode.Colon]: '%3A', // gen-delims + [CharCode.Slash]: '%2F', + [CharCode.QuestionMark]: '%3F', + [CharCode.Hash]: '%23', + [CharCode.OpenSquareBracket]: '%5B', + [CharCode.CloseSquareBracket]: '%5D', + [CharCode.AtSign]: '%40', + + [CharCode.ExclamationMark]: '%21', // sub-delims + [CharCode.DollarSign]: '%24', + [CharCode.Ampersand]: '%26', + [CharCode.SingleQuote]: '%27', + [CharCode.OpenParen]: '%28', + [CharCode.CloseParen]: '%29', + [CharCode.Asterisk]: '%2A', + [CharCode.Plus]: '%2B', + [CharCode.Comma]: '%2C', + [CharCode.Semicolon]: '%3B', + [CharCode.Equals]: '%3D', + + [CharCode.Space]: '%20', +}; + +function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): string { + let res: string | undefined = undefined; + let nativeEncodePos = -1; + + for (let pos = 0; pos < uriComponent.length; pos++) { + const code = uriComponent.charCodeAt(pos); + + // unreserved characters: https://tools.ietf.org/html/rfc3986#section-2.3 + if ( + (code >= CharCode.a && code <= CharCode.z) + || (code >= CharCode.A && code <= CharCode.Z) + || (code >= CharCode.Digit0 && code <= CharCode.Digit9) + || code === CharCode.Dash + || code === CharCode.Period + || code === CharCode.Underline + || code === CharCode.Tilde + || (allowSlash && code === CharCode.Slash) + ) { + // check if we are delaying native encode + if (nativeEncodePos !== -1) { + res += encodeURIComponent(uriComponent.substring(nativeEncodePos, pos)); + nativeEncodePos = -1; + } + // check if we write into a new string (by default we try to return the param) + if (res !== undefined) { + res += uriComponent.charAt(pos); + } + + } else { + // encoding needed, we need to allocate a new string + if (res === undefined) { + res = uriComponent.substr(0, pos); + } + + // check with default table first + const escaped = encodeTable[code]; + if (escaped !== undefined) { + + // check if we are delaying native encode + if (nativeEncodePos !== -1) { + res += encodeURIComponent(uriComponent.substring(nativeEncodePos, pos)); + nativeEncodePos = -1; + } + + // append escaped variant to result + res += escaped; + + } else if (nativeEncodePos === -1) { + // use native encode only when needed + nativeEncodePos = pos; + } + } + } + + if (nativeEncodePos !== -1) { + res += encodeURIComponent(uriComponent.substring(nativeEncodePos)); + } + + return res !== undefined ? res : uriComponent; +} + +function encodeURIComponentMinimal(path: string): string { + let res: string | undefined = undefined; + for (let pos = 0; pos < path.length; pos++) { + const code = path.charCodeAt(pos); + if (code === CharCode.Hash || code === CharCode.QuestionMark) { + if (res === undefined) { + res = path.substr(0, pos); + } + res += encodeTable[code]; + } else { + if (res !== undefined) { + res += path[pos]; + } + } + } + return res !== undefined ? res : path; +} + +/** + * Compute `fsPath` for the given uri + */ +function _makeFsPath(uri: URI): string { + + let value: string; + if (uri.authority && uri.path.length > 1 && uri.scheme === 'file') { + // unc path: file://shares/c$/far/boo + value = `//${uri.authority}${uri.path}`; + } else if ( + uri.path.charCodeAt(0) === CharCode.Slash + && (uri.path.charCodeAt(1) >= CharCode.A && uri.path.charCodeAt(1) <= CharCode.Z || uri.path.charCodeAt(1) >= CharCode.a && uri.path.charCodeAt(1) <= CharCode.z) + && uri.path.charCodeAt(2) === CharCode.Colon + ) { + // windows drive letter: file:///c:/far/boo + value = uri.path[1].toLowerCase() + uri.path.substr(2); + } else { + // other path + value = uri.path; + } + if (isWindows) { + value = value.replace(/\//g, '\\'); + } + return value; +} + +/** + * Create the external version of a uri + */ +function _asFormatted(uri: URI, skipEncoding: boolean): string { + + const encoder = !skipEncoding + ? encodeURIComponentFast + : encodeURIComponentMinimal; + + let res = ''; + let { scheme, authority, path, query, fragment } = uri; + if (scheme) { + res += scheme; + res += ':'; + } + if (authority || scheme === 'file') { + res += _slash; + res += _slash; + } + if (authority) { + let idx = authority.indexOf('@'); + if (idx !== -1) { + // @ + const userinfo = authority.substr(0, idx); + authority = authority.substr(idx + 1); + idx = userinfo.indexOf(':'); + if (idx === -1) { + res += encoder(userinfo, false); + } else { + // :@ + res += encoder(userinfo.substr(0, idx), false); + res += ':'; + res += encoder(userinfo.substr(idx + 1), false); + } + res += '@'; + } + authority = authority.toLowerCase(); + idx = authority.indexOf(':'); + if (idx === -1) { + res += encoder(authority, false); + } else { + // : + res += encoder(authority.substr(0, idx), false); + res += authority.substr(idx); + } + } + if (path) { + // lower-case windows drive letters in /C:/fff or C:/fff + if (path.length >= 3 && path.charCodeAt(0) === CharCode.Slash && path.charCodeAt(2) === CharCode.Colon) { + const code = path.charCodeAt(1); + if (code >= CharCode.A && code <= CharCode.Z) { + path = `/${String.fromCharCode(code + 32)}:${path.substr(3)}`; // "/c:".length === 3 + } + } else if (path.length >= 2 && path.charCodeAt(1) === CharCode.Colon) { + const code = path.charCodeAt(0); + if (code >= CharCode.A && code <= CharCode.Z) { + path = `${String.fromCharCode(code + 32)}:${path.substr(2)}`; // "/c:".length === 3 + } + } + // encode the rest of the path + res += encoder(path, true); + } + if (query) { + res += '?'; + res += encoder(query, false); + } + if (fragment) { + res += '#'; + res += !skipEncoding ? encodeURIComponentFast(fragment, false) : fragment; + } + return res; +} diff --git a/test/perform/7.txt b/test/perform/7.txt new file mode 100644 index 0000000..71c0e98 --- /dev/null +++ b/test/perform/7.txt @@ -0,0 +1,2410 @@ + yes = { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.vomit + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.stimtrap + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = { + [name] = "BIG GAME HUNTER" + [reward] = "HunterS" + [desc] = "DEAL 4000 MELEE DAMAGE TOTAL" + [reqamount] = 4000 + [req] = "DamageDealt" + }; + [3] = game + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + [3] = "baseframe" + [4] = "name" + [5] = "Text" + [6] = "desc" + [7] = "ReplicatedStorage" + [8] = "skins" + [9] = "firstperson" + [10] = "reward" + [11] = "FindFirstChild" + [12] = "rewardframe" + [13] = "handname" + [14] = "Value" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.tank + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "airdroppage" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "BadBat" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = workspace + [2] = "HunterS" + [3] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [4] = game + }; + [Constants] = { + [1] = "ServerStuff" + [2] = "adjustStats" + [3] = "changehands" + [4] = "FireServer" + [5] = "equippedhands" + [6] = "CURRENTLY WEARING: " + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "ReplicatedStorage" + [11] = "skins" + [12] = "firstperson" + [13] = "FindFirstChild" + [14] = "handname" + [15] = "Value" + [16] = "Text" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = function: 0xbfea5de53c6214a9, is lua closure: true + }; + [Constants] = { + [1] = "airdroppage" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = Instance + }; + [Constants] = { + [1] = "challengehover" + [2] = "UDim2" + [3] = "new" + [4] = new + [5] = "X" + [6] = "Y" + [7] = "Position" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.cowboy + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = { + [name] = "DEUS VULT" + [reward] = "TGarment" + [desc] = "DECAPITATE 80 PLAYERS WITH THE GREAT SWORD" + [reqamount] = 80 + [req] = "GreatswordDecaps" + }; + [3] = game + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + [3] = "baseframe" + [4] = "name" + [5] = "Text" + [6] = "desc" + [7] = "ReplicatedStorage" + [8] = "skins" + [9] = "firstperson" + [10] = "reward" + [11] = "FindFirstChild" + [12] = "rewardframe" + [13] = "handname" + [14] = "Value" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.trapper + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.AmyGotANewJob + [2] = workspace + [3] = Players.AmyGotANewJob + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "PBaton" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "TJavelin" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "CHammer" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.ByDracoton + [2] = workspace + [3] = Players.ByDracoton + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = { + [name] = "SPECIAL OPERATIVE" + [reward] = "GSuit" + [desc] = "GET 4 HEADSHOTS WITH RIFLE" + [reqamount] = 4 + [req] = "RifleHeadshots" + }; + [3] = game + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + [3] = "baseframe" + [4] = "name" + [5] = "Text" + [6] = "desc" + [7] = "ReplicatedStorage" + [8] = "skins" + [9] = "firstperson" + [10] = "reward" + [11] = "FindFirstChild" + [12] = "rewardframe" + [13] = "handname" + [14] = "Value" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = { + [name] = "HAZARDOUS SURVIVOR" + [reward] = "HevS" + [desc] = "LAND 33 CHARGED BACKSTABS WITH THE CROWBAR" + [reqamount] = 33 + [req] = "LandedCrowbars" + }; + [3] = game + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + [3] = "baseframe" + [4] = "name" + [5] = "Text" + [6] = "desc" + [7] = "ReplicatedStorage" + [8] = "skins" + [9] = "firstperson" + [10] = "reward" + [11] = "FindFirstChild" + [12] = "rewardframe" + [13] = "handname" + [14] = "Value" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.1buggyfresh + [2] = workspace + [3] = Players.1buggyfresh + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "SAxe" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x797771a78f38bb51, is lua closure: true + [2] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [3] = Players.ViniDalvino.PlayerGui.mainGui.spectateframe + [4] = Workspace.Camera.Blur + [5] = workspace + [6] = 1 + }; + [Constants] = { + [1] = "spawn" + [2] = spawn + [3] = "Visible" + [4] = "Enabled" + [5] = "CurrentCamera" + [6] = "Crossroads" + [7] = "Cameras" + [8] = "cam" + [9] = "look" + [10] = "CFrame" + [11] = "FieldOfView" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "PFork" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = workspace + [2] = "PilotG" + [3] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [4] = game + }; + [Constants] = { + [1] = "ServerStuff" + [2] = "adjustStats" + [3] = "changehands" + [4] = "FireServer" + [5] = "equippedhands" + [6] = "CURRENTLY WEARING: " + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "ReplicatedStorage" + [11] = "skins" + [12] = "firstperson" + [13] = "FindFirstChild" + [14] = "handname" + [15] = "Value" + [16] = "Text" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = workspace + [2] = "Hotline" + [3] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [4] = game + }; + [Constants] = { + [1] = "ServerStuff" + [2] = "adjustStats" + [3] = "changehands" + [4] = "FireServer" + [5] = "equippedhands" + [6] = "CURRENTLY WEARING: " + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "ReplicatedStorage" + [11] = "skins" + [12] = "firstperson" + [13] = "FindFirstChild" + [14] = "handname" + [15] = "Value" + [16] = "Text" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.pistol + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = Instance + }; + [Constants] = { + [1] = "challengehover" + [2] = "UDim2" + [3] = "new" + [4] = new + [5] = "X" + [6] = "Y" + [7] = "Position" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x797771a78f38bb51, is lua closure: true + [2] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [3] = Players.ViniDalvino.PlayerGui.mainGui.spectateframe + [4] = Workspace.Camera.Blur + [5] = workspace + }; + [Constants] = { + [1] = "spawn" + [2] = spawn + [3] = "Visible" + [4] = "Enabled" + [5] = "CurrentCamera" + [6] = "cpart" + [7] = "CFrame" + [8] = "FieldOfView" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x888c30bb001129f9, is lua closure: true + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = { + [Trackers] = { + [DBPellets] = 79 + [ChargedFists] = 58 + [LandedCharges] = 875 + [DamageDealt] = 38653.76475 + [GreatswordDecaps] = 50 + [Headshots] = 41 + [LandedCrowbars] = 2 + [LandedSMG] = 23 + [LandedBludgeons] = 234 + [Lockers] = 1032 + [Backstabs] = 330 + [JumpHits] = 26 + }; + [ChosenAirdrop] = "default" + [HandInventory] = { + [SOutfit] = true + [PraeS] = true + [Hotline] = true + [POveralls] = true + [PilotG] = true + [PSuit] = true + [HunterS] = true + }; + [Deaths] = 20 + [LastWep] = "Shovel" + [SpecialRoles] = { + [Shamed] = false + }; + [Hands] = "POveralls" + [WeaponsFound] = { + [Fist] = true + [PAxe] = true + [CHammer] = true + [BBat] = true + [PFork] = true + [RPier] = true + [IPick] = true + [LPipe] = true + [CBar] = true + [THawk] = true + [GSword] = true + [SAxe] = true + [TSpear] = true + [BKnuckles] = true + [CCleaver] = true + [BadBat] = true + [FAxe] = true + [HKnife] = true + [SHammer] = true + [Shovel] = true + [HBerd] = true + [MMachete] = true + [NSword] = true + [CKnife] = true + [PBaton] = true + [KaramB] = true + [BSpear] = true + [TJavelin] = true + }; + [Kills] = 5 + }; + }; + [Constants] = { + [1] = "stats" + [2] = "Position" + [3] = "UDim2" + [4] = "new" + [5] = new + [6] = 0.75 + [7] = "HIDE PLAYER STATS" + [8] = "Text" + [9] = "base" + [10] = "kills" + [11] = "TOTAL KILLS: " + [12] = "Kills" + [13] = "deaths" + [14] = "TOTAL DEATHS: " + [15] = "Deaths" + [16] = "Trackers" + [17] = "Backstabs" + [18] = "backstabs" + [19] = "TOTAL BACKSTABS: " + [20] = "Headshots" + [21] = "headshots" + [22] = "TOTAL HEADSHOTS: " + [23] = "LandedCharges" + [24] = "chargelanded" + [25] = "CHARGED HITS LANDED: " + [26] = "Lockers" + [27] = "lockersopen" + [28] = "TOTAL LOCKERS OPENED: " + [29] = "DamageDealt" + [30] = "damage" + [31] = "TOTAL MELEE DAMAGE DEALT: " + [32] = "math" + [33] = "ceil" + [34] = ceil + [35] = "handslist" + [36] = "Visible" + [37] = "equippedhands" + [38] = "ipairs" + [39] = ipairs + [40] = "GetChildren" + [41] = "Name" + [42] = "example" + [43] = "Destroy" + [44] = "SHOW PLAYER STATS" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = Instance + }; + [Constants] = { + [1] = "challengehover" + [2] = "UDim2" + [3] = "new" + [4] = new + [5] = "X" + [6] = "Y" + [7] = "Position" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = Instance + }; + [Constants] = { + [1] = "challengehover" + [2] = "UDim2" + [3] = "new" + [4] = new + [5] = "X" + [6] = "Y" + [7] = "Position" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "TSpear" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.ninja + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = Instance + }; + [Constants] = { + [1] = "challengehover" + [2] = "UDim2" + [3] = "new" + [4] = new + [5] = "X" + [6] = "Y" + [7] = "Position" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = workspace + [2] = "PSuit" + [3] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [4] = game + }; + [Constants] = { + [1] = "ServerStuff" + [2] = "adjustStats" + [3] = "changehands" + [4] = "FireServer" + [5] = "equippedhands" + [6] = "CURRENTLY WEARING: " + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "ReplicatedStorage" + [11] = "skins" + [12] = "firstperson" + [13] = "FindFirstChild" + [14] = "handname" + [15] = "Value" + [16] = "Text" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.Captain_Chezeon + [2] = workspace + [3] = Players.Captain_Chezeon + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.trolo97685 + [2] = workspace + [3] = Players.trolo97685 + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = { + [name] = "KEEPING IT STYLISH" + [reward] = "PSuit" + [desc] = "PERFORM 50 BACKSTABS" + [reqamount] = 50 + [req] = "Backstabs" + }; + [3] = game + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + [3] = "baseframe" + [4] = "name" + [5] = "Text" + [6] = "desc" + [7] = "ReplicatedStorage" + [8] = "skins" + [9] = "firstperson" + [10] = "reward" + [11] = "FindFirstChild" + [12] = "rewardframe" + [13] = "handname" + [14] = "Value" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.starDestroyer1015 + [2] = workspace + [3] = Players.starDestroyer1015 + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = { + [name] = "DISTINGUISHED CONTESTANT" + [reward] = "GOveralls" + [desc] = "DEAL 350000 GRAND TOTAL MELEE DAMAGE" + [reqamount] = 350000 + [req] = "DamageDealt" + }; + [3] = game + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + [3] = "baseframe" + [4] = "name" + [5] = "Text" + [6] = "desc" + [7] = "ReplicatedStorage" + [8] = "skins" + [9] = "firstperson" + [10] = "reward" + [11] = "FindFirstChild" + [12] = "rewardframe" + [13] = "handname" + [14] = "Value" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.swat + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.rainbow + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = { + [name] = "SPECIAL RIOT OPERATIVE" + [reward] = "RGear" + [desc] = "LAND 40 HITS WITH THE SUBMACHINE GUN" + [reqamount] = 40 + [req] = "LandedSMG" + }; + [3] = game + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + [3] = "baseframe" + [4] = "name" + [5] = "Text" + [6] = "desc" + [7] = "ReplicatedStorage" + [8] = "skins" + [9] = "firstperson" + [10] = "reward" + [11] = "FindFirstChild" + [12] = "rewardframe" + [13] = "handname" + [14] = "Value" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.default + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.bait + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.survivor + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.SilverShrimp73 + [2] = workspace + [3] = Players.SilverShrimp73 + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "hideduels" + [2] = "Text" + [3] = "HIDE DUEL LIST" + [4] = "SHOW DUEL LIST" + [5] = "duellist" + [6] = "Visible" + [7] = "dueltitle" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.ZxphyrEdit + [2] = workspace + [3] = ZxphyrEdit + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = workspace + [2] = "normal" + [3] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "ServerStuff" + [2] = "spawnPlayer" + [3] = "FireServer" + [4] = "ipairs" + [5] = ipairs + [6] = "CurrentCamera" + [7] = "GetChildren" + [8] = "Destroy" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.1321DAVID1231 + [2] = workspace + [3] = 1321DAVID1231 + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "NSword" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = { + [name] = "AVIATOR MERCENARY" + [reward] = "PilotG" + [desc] = "LAND 20 CHARGED HITS WHILE IN THE AIR" + [reqamount] = 20 + [req] = "JumpHits" + }; + [3] = game + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + [3] = "baseframe" + [4] = "name" + [5] = "Text" + [6] = "desc" + [7] = "ReplicatedStorage" + [8] = "skins" + [9] = "firstperson" + [10] = "reward" + [11] = "FindFirstChild" + [12] = "rewardframe" + [13] = "handname" + [14] = "Value" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "RPier" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.ZxphyrEdit + [2] = workspace + [3] = ZxphyrEdit + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "Shovel" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x888c30bb001129f9, is lua closure: true + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.doomguy + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = { + [name] = "PUGILIST" + [reward] = "BGloves" + [desc] = "LAND 80 FULLY CHARGED FIST HITS" + [reqamount] = 80 + [req] = "ChargedFists" + }; + [3] = game + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + [3] = "baseframe" + [4] = "name" + [5] = "Text" + [6] = "desc" + [7] = "ReplicatedStorage" + [8] = "skins" + [9] = "firstperson" + [10] = "reward" + [11] = "FindFirstChild" + [12] = "rewardframe" + [13] = "handname" + [14] = "Value" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = { + [name] = "1-800-HOTLINE" + [reward] = "Hotline" + [desc] = "LAND 100 CHARGED HITS WITH BLUDGEON WEAPONS" + [reqamount] = 100 + [req] = "LandedBludgeons" + }; + [3] = game + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + [3] = "baseframe" + [4] = "name" + [5] = "Text" + [6] = "desc" + [7] = "ReplicatedStorage" + [8] = "skins" + [9] = "firstperson" + [10] = "reward" + [11] = "FindFirstChild" + [12] = "rewardframe" + [13] = "handname" + [14] = "Value" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "CBar" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.1321DAVID1231 + [2] = workspace + [3] = 1321DAVID1231 + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.jumbledflower24 + [2] = workspace + [3] = Players.jumbledflower24 + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.1buggyfresh + [2] = workspace + [3] = 1buggyfresh + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "BBat" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.templar + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.Zulu_B + [2] = workspace + [3] = Zulu_B + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "SHammer" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "IPick" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = { + [name] = "LOCKER SCAVENGER" + [reward] = "SOutfit" + [desc] = "OPEN 800 LOCKERS" + [reqamount] = 800 + [req] = "Lockers" + }; + [3] = game + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + [3] = "baseframe" + [4] = "name" + [5] = "Text" + [6] = "desc" + [7] = "ReplicatedStorage" + [8] = "skins" + [9] = "firstperson" + [10] = "reward" + [11] = "FindFirstChild" + [12] = "rewardframe" + [13] = "handname" + [14] = "Value" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "FAxe" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = { + [name] = "DOUBLE BARREL SLAYER" + [reward] = "PraeS" + [desc] = "HIT 30 DOUBLE BARREL SHOTGUN PELLETS" + [reqamount] = 30 + [req] = "DBPellets" + }; + [3] = game + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + [3] = "baseframe" + [4] = "name" + [5] = "Text" + [6] = "desc" + [7] = "ReplicatedStorage" + [8] = "skins" + [9] = "firstperson" + [10] = "reward" + [11] = "FindFirstChild" + [12] = "rewardframe" + [13] = "handname" + [14] = "Value" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "Fist" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "duellist" + [2] = "Name" + [3] = "FindFirstChild" + [4] = "button" + [5] = "BackgroundColor3" + [6] = "Color3" + [7] = "new" + [8] = new + [9] = 0.1921568627451 + [10] = 0.65098039215686 + [11] = 0.090196078431373 + [12] = "delay" + [13] = delay + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "GSword" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = Instance + }; + [Constants] = { + [1] = "challengehover" + [2] = "UDim2" + [3] = "new" + [4] = new + [5] = "X" + [6] = "Y" + [7] = "Position" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = function: 0xe62662cc30449ff9, is lua closure: true + [3] = workspace + [4] = "normal" + }; + [Constants] = { + [1] = "weaponlist" + [2] = "example" + [3] = "Destroy" + [4] = "GetChildren" + [5] = "math" + [6] = "random" + [7] = random + [8] = "Name" + [9] = "ServerStuff" + [10] = "spawnPlayer" + [11] = "FireServer" + [12] = "ipairs" + [13] = ipairs + [14] = "CurrentCamera" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = workspace + [2] = "SOutfit" + [3] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [4] = game + }; + [Constants] = { + [1] = "ServerStuff" + [2] = "adjustStats" + [3] = "changehands" + [4] = "FireServer" + [5] = "equippedhands" + [6] = "CURRENTLY WEARING: " + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "ReplicatedStorage" + [11] = "skins" + [12] = "firstperson" + [13] = "FindFirstChild" + [14] = "handname" + [15] = "Value" + [16] = "Text" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = workspace + [2] = "PraeS" + [3] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [4] = game + }; + [Constants] = { + [1] = "ServerStuff" + [2] = "adjustStats" + [3] = "changehands" + [4] = "FireServer" + [5] = "equippedhands" + [6] = "CURRENTLY WEARING: " + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "ReplicatedStorage" + [11] = "skins" + [12] = "firstperson" + [13] = "FindFirstChild" + [14] = "handname" + [15] = "Value" + [16] = "Text" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "KaramB" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "CCleaver" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = Instance + }; + [Constants] = { + [1] = "challengehover" + [2] = "UDim2" + [3] = "new" + [4] = new + [5] = "X" + [6] = "Y" + [7] = "Position" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = Instance + }; + [Constants] = { + [1] = "challengehover" + [2] = "UDim2" + [3] = "new" + [4] = new + [5] = "X" + [6] = "Y" + [7] = "Position" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = workspace + [2] = "POveralls" + [3] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [4] = game + }; + [Constants] = { + [1] = "ServerStuff" + [2] = "adjustStats" + [3] = "changehands" + [4] = "FireServer" + [5] = "equippedhands" + [6] = "CURRENTLY WEARING: " + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "ReplicatedStorage" + [11] = "skins" + [12] = "firstperson" + [13] = "FindFirstChild" + [14] = "handname" + [15] = "Value" + [16] = "Text" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = Instance + }; + [Constants] = { + [1] = "challengehover" + [2] = "UDim2" + [3] = "new" + [4] = new + [5] = "X" + [6] = "Y" + [7] = "Position" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "LPipe" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.Conoxr + [2] = workspace + [3] = Players.Conoxr + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "THawk" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.hunter + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.stimmer + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.Sovietkrieg + [2] = workspace + [3] = Players.Sovietkrieg + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.knife + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "BKnuckles" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "challengehover" + [2] = "Visible" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "HKnife" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "CKnife" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe.duellist.plsunbansugingmelon + [2] = workspace + [3] = Players.plsunbansugingmelon + }; + [Constants] = { + [1] = "button" + [2] = "BackgroundColor3" + [3] = "Color3" + [4] = "new" + [5] = new + [6] = 0.1921568627451 + [7] = "string" + [8] = "upper" + [9] = upper + [10] = "INVITE SENT" + [11] = "Text" + [12] = 0.78039215686275 + [13] = 0.17254901960784 + [14] = "ServerStuff" + [15] = "duel" + [16] = "invite" + [17] = "FireServer" + [18] = "delay" + [19] = delay + [20] = 0.65098039215686 + [21] = 0.090196078431373 + [22] = "ACCEPTED DUEL" + [23] = 0.75294117647059 + [24] = 0.074509803921569 + [25] = "accept" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "MMachete" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "PAxe" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.defense + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "BSpear" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xe62662cc30449ff9, is lua closure: true + [2] = "HBerd" + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0x76d5d365ab363181, is lua closure: true + [2] = ReplicatedStorage.airdrops.teamer + }; + [Constants] = { + [1] = "Name" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = Instance + }; + [Constants] = { + [1] = "challengehover" + [2] = "UDim2" + [3] = "new" + [4] = new + [5] = "X" + [6] = "Y" + [7] = "Position" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = function: 0xa98a318fcb7fce11, is lua closure: true + }; + [Constants] = nil + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = Instance + }; + [Constants] = { + [1] = "challengehover" + [2] = "UDim2" + [3] = "new" + [4] = new + [5] = "X" + [6] = "Y" + [7] = "Position" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + [2] = Instance + }; + [Constants] = { + [1] = "challengehover" + [2] = "UDim2" + [3] = "new" + [4] = new + [5] = "X" + [6] = "Y" + [7] = "Position" + }; + }; + { + [Variables] = { + [script] = Players.ViniDalvino.PlayerGui.mainGui.main + }; + [Upvalues] = { + [1] = Players.ViniDalvino.PlayerGui.mainGui.mainframe + }; + [Constants] = { + [1] = "challenges" + [2] = "Position" + [3] = "UDim2" + [4] = "new" + [5] = new + [6] = 0.3 + [7] = "challengelist" + [8] = "Visible" + [9] = 0.72 + [10] = "HIDE CHALLENGES" + [11] = "Text" + [12] = "VIEW CHALLENGES" + }; + }; diff --git a/test/perform/init.lua b/test/perform/init.lua index 8d7c555..1ce2a16 100644 --- a/test/perform/init.lua +++ b/test/perform/init.lua @@ -1,13 +1,17 @@ local fs = require 'bee.filesystem' +local thread = require 'bee.thread' local parser = require 'parser' local utility = require 'utility' +thread.newchannel 'TEST' +local ch = thread.channel 'TEST' + local function scanDirectory(path) local files = {} local function scan(path) if fs.is_directory(path) then - for path in path:list_directory() do + for path in fs.pairs(path) do scan(path) end else @@ -37,14 +41,16 @@ local function performTest() end local clock = os.clock() for path, buf in pairs(files) do - local suc, err = parser:parse(buf, 'lua', 'Lua 5.4') - if not suc then + local state = parser.compile(buf, 'Lua', 'Lua 5.4') + if not state then error(('文件解析失败:%s'):format(path:string())) end - local lines, err = parser:lines(buf) - if not suc then - error(('行号解析失败:%s'):format(path:string())) - end + parser.luadoc.luadoc(state) + --local dump = utility.unpack(state.root) + --utility.pack(dump) + + --ch:push(dump) + --ch:pop() end local passed = os.clock() - clock print(('综合性能测试完成,总大小[%.3f]kb,速度[%.3f]mb/s,用时[%.3f]秒'):format(size / 1000, size / passed / 1000 / 1000, passed)) @@ -56,10 +62,11 @@ local function test(path) return end local testTimes = 10 + local state local clock = os.clock() for i = 1, testTimes do - local suc, err = parser:compile(buf, 'lua', 'Lua 5.4') - if not suc then + state = parser.compile(buf, 'Lua', 'Lua 5.4') + if not state then error(('文件解析失败:%s'):format(path:string())) end if os.clock() - clock > 1.0 then @@ -68,8 +75,19 @@ local function test(path) end end local passed = os.clock() - clock + + --local clock = os.clock() + --local dump = utility.unpack(state.ast) + --utility.pack(dump) + --local unpackPassed = os.clock() - clock + + --local clock = os.clock() + --ch:push(dump) + --ch:pop() + --local channelPassed = os.clock() - clock + local size = #buf * testTimes - print(('[%s]测试完成,大小[%.3f]kb,速度[%.3f]mb/s,用时[%.3f]秒'):format(path, size / 1000, size / passed / 1000 / 1000, passed)) + print(('[%s]测试完成,大小[%.3f]kb,速度[%.3f]mb/s,平均用时[%.3f]毫秒'):format(path, size / testTimes / 1000, size / passed / 1000 / 1000, passed / testTimes * 1000)) end collectgarbage 'stop' @@ -78,5 +96,7 @@ test[[test\perform\2.txt]] test[[test\perform\3.txt]] test[[test\perform\4.txt]] test[[test\perform\5.txt]] +test[[test\perform\6.txt]] +test[[test\perform\7.txt]] performTest() collectgarbage 'restart' diff --git a/test/performance.lua b/test/performance.lua index 5ab21d0..1bf3b06 100644 --- a/test/performance.lua +++ b/test/performance.lua @@ -1,65 +1,56 @@ -local function test(lpeg, buf) - local P = lpeg.P - local V = lpeg.V - local S = lpeg.S - local R = lpeg.R - local paser = P - { - V'Value', - Nl = P'\r\n' + S'\r\n', - Sp = S' \t', - Spnl = (V'Sp' + V'Nl')^0, - Bool = P'true' + P'false', - Int = '0' + (P'-'^-1 * R'09'^1), - Float = P'-'^-1 * ('0' + R'09'^1) * '.' * R'09'^0 * V'FloatSuffix'^-1, - Bad = (R'09' + R'az' + R'AZ')^1, - FloatSuffix = S'eE' * P'-'^-1 * R'09'^1, - Null = P'null', - String = '"' - * ('\\' * P(1) + (P(1) - '"'))^0 - * '"', - Table = V'Spnl' - * S'[{' * V'Spnl' - * ( - V'Object' - + V'Value' - + P',' * V'Spnl' - )^0 * V'Spnl' - * S']}' * V'Spnl', - Object = V'Spnl' * V'Key' * V'Spnl' * V'Value' * V'Spnl', - Key = V'String' * V'Spnl' * ':', - Value = V'Table' - + V'Bool' - + V'Null' - + V'String' - + V'Float' - + V'Int' - + V'Bad' - } - local clock = os.clock() - local res - for _ = 1, 1000 do - res = paser:match(buf) - end - local passed = os.clock() - clock - assert(res == #buf + 1) - print(passed) +local path = arg[1] +if not path then + print('No path!') + return end -local f = io.open [[test\perform\test.json]] -local buf = f:read 'a' -f:close() +print('Input path = ' .. path) + +local root = arg[0] .. '\\..\\..' +package.path = package.path .. ';' .. root .. '\\src\\?.lua' + .. ';' .. root .. '\\src\\?\\init.lua' + .. ';' .. root .. '\\test\\?.lua' + .. ';' .. root .. '\\test\\?\\init.lua' -collectgarbage 'stop' +local fs = require 'bee.filesystem' +local util = require 'utility' +local parser = require 'parser' + +local function scanFiles(fspath, callback) + if fs.is_directory(fspath) then + for subpath in fs.pairs(fspath) do + scanFiles(subpath, callback) + end + else + callback(fspath) + end +end -print('===========test lpeg-1.0.1-DEBUG====================') -test(package.loadlib('bin/lpeg-1.0.1-DEBUG.dll', 'luaopen_lpeg')(), buf) +print('Scanning files...') +local fileNames = {} +scanFiles(fs.path(path), function (fullPath) + if fullPath:extension():string() ~= '.lua' then + return + end + fileNames[#fileNames+1] = fullPath:string() +end) -print('===========test lpeg-1.0.2-DEBUG====================') -test(package.loadlib('bin/lpeg-1.0.2-DEBUG.dll', 'luaopen_lpeg')(), buf) +print('Loading files...') +local files = {} +local size = 0 +for _, fileName in ipairs(fileNames) do + local file = util.loadFile(fileName) + files[#files+1] = file + size = size + #file +end -print('===========test lpeg-1.0.1-NDEBUG====================') -test(package.loadlib('bin/lpeg-1.0.1-NDEBUG.dll', 'luaopen_lpeg')(), buf) +print(('Loaded %d files, total size = %.3f KB'):format(#files, size / 1000)) +print('Start parsing...') +local clock = os.clock() +for _, file in ipairs(files) do + local state = parser.compile(file, 'Lua', 'Lua 5.4') + parser.luadoc(state) +end +local passed = os.clock() - clock -print('===========test lpeg-1.0.2-NDEBUG====================') -test(package.loadlib('bin/lpeg-1.0.2-NDEBUG.dll', 'luaopen_lpeg')(), buf) +print(('Total time = %.3f s, speed = %.3f MB/s'):format(passed, size / passed / 1000 / 1000)) diff --git a/test/syntax_check.lua b/test/syntax_check.lua index 47d00bf..26fb115 100644 --- a/test/syntax_check.lua +++ b/test/syntax_check.lua @@ -28,21 +28,61 @@ local function eq(a, b) return a == b end +local function getLine(offset, lns) + for i = 0, #lns do + if offset >= lns[i] + and offset < lns[i+1] then + return i + end + end +end + +local function getPosition(offset, lns) + for i = 0, #lns do + if offset >= lns[i] + and offset < lns[i+1] then + return 10000 * i + offset - lns[i] + end + end +end + +---@param script string +---@param sep string local function catchTarget(script, sep) + local pattern = ('()<%%%s.-%%%s>()'):format(sep, sep) + local lns = {} + lns[0] = 0 + for pos in script:gmatch '()\n' do + lns[#lns+1] = pos + end + lns[#lns+1] = math.maxinteger + local codes = {} + local pos = 1 local list = {} - local cur = 1 - local cut = 0 - while true do - local start, finish = script:find(('<%%%s.-%%%s>'):format(sep, sep), cur) - if not start then - break + local cuted = 0 + local lastLine = 0 + for a, b in script:gmatch(pattern) do + codes[#codes+1] = script:sub(pos, a - 1) + codes[#codes+1] = script:sub(a + 2, b - 3) + pos = b + local line1 = getLine(a + 1, lns) + if line1 ~= lastLine then + cuted = 0 + lastLine = line1 + end + cuted = cuted + 2 + local left = getPosition(a + 1, lns) - cuted + local line2 = getLine(b - 3, lns) + if line2 ~= lastLine then + cuted = 0 + lastLine = line2 end - list[#list+1] = { start - cut, math.max(start - cut, finish - 4 - cut) } - cur = finish + 1 - cut = cut + 4 + local right = getPosition(b - 3, lns) - cuted + cuted = cuted + 2 + list[#list+1] = { left, right } end - local new_script = script:gsub(('<%%%s(.-)%%%s>'):format(sep, sep), '%1') - return new_script, list + codes[#codes+1] = script:sub(pos) + return table.concat(codes), list end local Version @@ -50,9 +90,9 @@ local Version local function TEST(script) return function (expect) local newScript, list = catchTarget(script, '!') - local ast, errs = parser:compile(newScript, 'lua', Version) + local ast = parser.compile(newScript, 'Lua', Version) assert(ast) - assert(errs) + local errs = ast.errs local first = errs[1] local target = list[1] if not expect then @@ -74,6 +114,31 @@ local function TEST(script) end end +function TestWith(version) + return function (script) + return function (expect) + Version = version + TEST(script)(expect) + Version = 'Lua 5.4' + end + end +end + +TEST [[ +local = 1 +]] +{ + type = 'KEYWORD' +} + +TEST [[ +local function f() +end +]] +{ + type = 'KEYWORD' +} + TEST[[ function f() return @@ -166,12 +231,11 @@ end } TEST[[ -:: label :: label +:: label :: +goto label ]] { type = 'ACTION_AFTER_RETURN', - multi = 3, } TEST[[ @@ -310,30 +374,438 @@ return x TEST[[ ::label:: +::other_label:: :::: ]] { type = 'REDEFINED_LABEL', - relative = { + related = { { - start = 3, + start = 3, finish = 7, }, } } +Version = 'Lua 5.4' TEST[[ ::label:: -do +::other_label:: +if true then :::: end ]] { type = 'REDEFINED_LABEL', - relative = { + related = { { - start = 3, + start = 3, finish = 7, }, } } + +TEST[[ +if true then + ::label:: +end +::label:: +]] +(nil) + +Version = 'Lua 5.3' +TEST[[ +::label:: +::other_label:: +if true then + ::label:: +end +]] +(nil) + +TEST[[ +if true then + ::label:: +end +::label:: +]] +(nil) + +Version = 'Lua 5.4' +TEST[[ +local x = 1 + = 2 +]] +{ + type = 'SET_CONST', +} + +TEST[[ +local x = 1 +function () end +]] +{ + type = 'SET_CONST', +} + +TEST[[ +local x = 1 + = 2 +]] +{ + type = 'SET_CONST', +} + +TEST[[ +local x <> = 1 +]] +{ + type = 'UNKNOWN_ATTRIBUTE', +} + +TEST [[ +return function () local l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, l188, l189, l190, l191, l192, l193, l194, l195, l196, l197, l198, l199, l200, , l202 end +]] +{ + type = 'LOCAL_LIMIT', +} + +TEST [[ +local l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, l188, l189, l190, l191, l192, l193, l194, l195, l196, l197, l198, l199, l200, , l202 +]] +{ + type = 'LOCAL_LIMIT', +} + +TEST [[ +return function (l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, l188, l189, l190, l191, l192, l193, l194, l195, l196, l197, l198, l199, l200, , l202) end +]] +{ + type = 'LOCAL_LIMIT', +} + +TEST [[ +return function (l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, l188, l189, l190, l191, l192, l193, l194, l195, l196, l197, l198, l199) + do + local x + end + local x + do + local + end +end +]] +{ + type = 'LOCAL_LIMIT', +} + +TEST [[ +return function (l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, l188, l189, l190, l191, l192, l193, l194, l195, l196, l197, l198, l199) + local function F() end + local +end +]] +{ + type = 'LOCAL_LIMIT', +} + +TEST [[ +local l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, l188, l189, l190, l191, l192, l193, l194, l195, l196, l197 + +for = 1, 10 do end -- use 4 local variables +]] +{ + type = 'LOCAL_LIMIT', +} + +TEST [[ +local l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, l188, l189, l190, l191, l192, l193, l194, l195, l196 + +for i = 1, 10 do end +]] +(nil) + +TEST [[ +local l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, l188, l189, l190, l191, l192, l193, l194, l195, l196 + +for in _ do end +]] +{ + type = 'LOCAL_LIMIT', +} + +TEST [[ +local l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, l188, l189, l190, l191, l192, l193, l194, l195 + +for x in _ do end +]] +(nil) + +Version = 'Lua 5.1' + +TEST [[ +local l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, l188, l189, l190, l191, l192, l193, l194, l195, l196, l197 + +for in _ do end +]] +{ + type = 'LOCAL_LIMIT', +} + +TEST [[ +local l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, l188, l189, l190, l191, l192, l193, l194, l195, l196 + +for x in _ do end +]] +(nil) + +TEST [[ +local l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, l188, l189, l190, l191, l192, l193, l194, l195, l196, l197, l198, l199, l200 + +_ENV = nil +]] +(nil) + +TEST [[ +local l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, l188, l189, l190, l191, l192, l193, l194, l195, l196, l197, l198, l199, l200 + +local = nil +]] +{ + type = 'LOCAL_LIMIT', +} + +Version = 'Lua 5.4' + +TEST [[ +local x =!> 1 +]] +{ + multi = 1, + type = 'MISS_SPACE_BETWEEN', +} + +TEST [[ +function mt() end +]] +{ + type = 'INDEX_IN_FUNC_NAME' +} + +TEST [[ +function mt() end +]] +{ + multi = 2, + type = 'INDEX_IN_FUNC_NAME' +} + +Version = 'Lua 5.4' +TEST [[ +goto = 1 +]] +{ + multi = 1, + type = 'MISS_NAME' +} + +Version = 'Lua 5.1' +TEST [[ +goto = 1 +]] +(nil) + +TEST [[ +return { + function () end +} +]] +(nil) + +TEST [[ + +print(1) +]] +{ + type = 'ACTION_AFTER_RETURN', +} + +TEST [[ + +return 1 +]] +{ + type = 'ACTION_AFTER_RETURN', +} + +TEST [[ +f + +]] +{ + type = 'AMBIGUOUS_SYNTAX', +} + +TEST [[ +f:xx + +]] +{ + type = 'AMBIGUOUS_SYNTAX', +} + +TEST [[ +f + +.x = 1 +]] +{ + type = 'AMBIGUOUS_SYNTAX', +} + +TEST [===[ +print [[ + +]] +]===] +{ + type = 'NESTING_LONG_MARK', +} + +TEST [===[ +print [[ + +]] +]===] +{ + type = 'NESTING_LONG_MARK', +} + +TEST [===[ +print [=[ +[=[ +]=] +]===] +(nil) + +TEST [===[ +print [=[ +[=[ +]=] +]===] +(nil) + +TEST [===[ +print [[]] +print [[]] +]===] +(nil) + +Version = 'Lua 5.4' +TEST [===[ +print [[ +[[ +]] +]===] +(nil) + +TEST [===[ +print [[ +[[ +]] +]===] +(nil) + +Version = 'Lua 5.4' + +TEST [[ +f +'' +]] +(nil) + +TEST [[ +f +{} +]] +(nil) + +TEST '\v\f' +(nil) + +TEST [=[ +print(:gsub()) +]=] +{ + type = 'NEED_PAREN', +} + +TEST [=[ +print(:gsub()) +]=] +{ + type = 'NEED_PAREN', +} + +TEST [=[ +print(:gsub()) +]=] +{ + type = 'NEED_PAREN', +} + +TEST [=[ +print(:gsub()) +]=] +{ + type = 'NEED_PAREN', +} + +TEST [[ +local t = '' +(function () end)() +]] +(nil) + +TEST [[ +local t = "" +(function () end)() +]] +(nil) + +TEST [[ +local t = {} +(function () end)() +]] +(nil) + +TEST [=[ +local t = [[]] +(function () end)() +]=] +(nil) + +TestWith 'LuaJIT' [[ +goto LABEL +::LABEL:: +]] +(nil) + +TestWith 'LuaJIT' [[ +local goto = 1 +]] +(nil) + +TestWith 'LuaJIT' [[ +local goto]] +(nil) + +TestWith 'LuaJIT' [[ +f(1, goto, 2) +]] +(nil) + +TestWith 'LuaJIT' [[ +local function f(x, goto, y) end +]] +(nil)