summaryrefslogtreecommitdiff
path: root/server-beta
diff options
context:
space:
mode:
Diffstat (limited to 'server-beta')
-rw-r--r--server-beta/src/parser/ast.lua1644
-rw-r--r--server-beta/src/parser/calcline.lua93
-rw-r--r--server-beta/src/parser/compile.lua574
-rw-r--r--server-beta/src/parser/emmy.lua321
-rw-r--r--server-beta/src/parser/grammar.lua534
-rw-r--r--server-beta/src/parser/guide.lua175
-rw-r--r--server-beta/src/parser/init.lua11
-rw-r--r--server-beta/src/parser/lines.lua187
-rw-r--r--server-beta/src/parser/parse.lua37
-rw-r--r--server-beta/src/parser/relabel.lua361
-rw-r--r--server-beta/src/parser/split.lua9
11 files changed, 3946 insertions, 0 deletions
diff --git a/server-beta/src/parser/ast.lua b/server-beta/src/parser/ast.lua
new file mode 100644
index 00000000..a7cb1d08
--- /dev/null
+++ b/server-beta/src/parser/ast.lua
@@ -0,0 +1,1644 @@
+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/server-beta/src/parser/calcline.lua b/server-beta/src/parser/calcline.lua
new file mode 100644
index 00000000..26f475d9
--- /dev/null
+++ b/server-beta/src/parser/calcline.lua
@@ -0,0 +1,93 @@
+local m = require 'lpeglabel'
+
+local row
+local fl
+local NL = (m.P'\r\n' + m.S'\r\n') * m.Cp() / function (pos)
+ row = row + 1
+ fl = pos
+end
+local ROWCOL = (NL + m.P(1))^0
+local function rowcol(str, n)
+ row = 1
+ fl = 1
+ ROWCOL:match(str:sub(1, n))
+ local col = n - fl + 1
+ return row, col
+end
+
+local function rowcol_utf8(str, n)
+ row = 1
+ fl = 1
+ ROWCOL:match(str:sub(1, n))
+ return row, utf8.len(str, fl, n)
+end
+
+local function position(str, _row, _col)
+ local cur = 1
+ local row = 1
+ while true do
+ if row == _row then
+ return cur + _col - 1
+ elseif row > _row then
+ return cur - 1
+ end
+ local pos = str:find('[\r\n]', cur)
+ if not pos then
+ return #str
+ end
+ row = row + 1
+ if str:sub(pos, pos+1) == '\r\n' then
+ cur = pos + 2
+ else
+ cur = pos + 1
+ end
+ end
+end
+
+local function position_utf8(str, _row, _col)
+ local cur = 1
+ local row = 1
+ while true do
+ if row == _row then
+ return utf8.offset(str, _col, cur)
+ elseif row > _row then
+ return cur - 1
+ end
+ local pos = str:find('[\r\n]', cur)
+ if not pos then
+ return #str
+ end
+ row = row + 1
+ if str:sub(pos, pos+1) == '\r\n' then
+ cur = pos + 2
+ else
+ cur = pos + 1
+ end
+ end
+end
+
+local NL = m.P'\r\n' + m.S'\r\n'
+
+local function line(str, row)
+ local count = 0
+ local res
+ local LINE = m.Cmt((1 - NL)^0, function (_, _, c)
+ count = count + 1
+ if count == row then
+ res = c
+ return false
+ end
+ return true
+ end)
+ local MATCH = (LINE * NL)^0 * LINE
+ MATCH:match(str)
+ return res
+end
+
+return {
+ rowcol = rowcol,
+ rowcol_utf8 = rowcol_utf8,
+ position = position,
+ position_utf8 = position_utf8,
+ line = line,
+}
diff --git a/server-beta/src/parser/compile.lua b/server-beta/src/parser/compile.lua
new file mode 100644
index 00000000..f6efb6e1
--- /dev/null
+++ b/server-beta/src/parser/compile.lua
@@ -0,0 +1,574 @@
+local guide = require 'parser.guide'
+local type = type
+
+_ENV = nil
+
+local pushError, Root, Compile, CompileBlock, Cache, Block, GoToTag
+
+local vmMap = {
+ ['nil'] = function (obj)
+ Root[#Root+1] = obj
+ return #Root
+ end,
+ ['boolean'] = function (obj)
+ Root[#Root+1] = obj
+ return #Root
+ end,
+ ['string'] = function (obj)
+ Root[#Root+1] = obj
+ return #Root
+ end,
+ ['number'] = function (obj)
+ Root[#Root+1] = obj
+ return #Root
+ end,
+ ['...'] = function (obj)
+ Root[#Root+1] = obj
+ return #Root
+ end,
+ ['getname'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ local loc = guide.getLocal(Root, obj, obj[1], obj.start)
+ if loc then
+ obj.type = 'getlocal'
+ obj.loc = Cache[loc]
+ if not loc.ref then
+ loc.ref = {}
+ end
+ loc.ref[#loc.ref+1] = id
+ else
+ obj.type = 'getglobal'
+ end
+ return id
+ end,
+ ['getfield'] = function (obj)
+ local node = obj.node
+ Root[#Root+1] = obj
+ local id = #Root
+ obj.node = Compile(node, id)
+ return id
+ end,
+ ['call'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ local node = obj.node
+ local args = obj.args
+ if node then
+ obj.node = Compile(node, id)
+ end
+ if args then
+ obj.args = Compile(args, id)
+ end
+ return id
+ end,
+ ['callargs'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ for i = 1, #obj do
+ local arg = obj[i]
+ obj[i] = Compile(arg, id)
+ end
+ return id
+ end,
+ ['binary'] = function (obj)
+ local e1 = obj[1]
+ local e2 = obj[2]
+ Root[#Root+1] = obj
+ local id = #Root
+ obj[1] = Compile(e1, id)
+ obj[2] = Compile(e2, id)
+ return id
+ end,
+ ['unary'] = function (obj)
+ local e = obj[1]
+ Root[#Root+1] = obj
+ local id = #Root
+ obj[1] = Compile(e, id)
+ return id
+ end,
+ ['varargs'] = function (obj)
+ local func = guide.getParentFunction(Root, obj)
+ if func then
+ local index = guide.getFunctionVarArgs(Root, func)
+ if not index then
+ pushError {
+ type = 'UNEXPECT_DOTS',
+ start = obj.start,
+ finish = obj.finish,
+ }
+ end
+ end
+ Root[#Root+1] = obj
+ return #Root
+ end,
+ ['paren'] = function (obj)
+ local exp = obj.exp
+ Root[#Root+1] = obj
+ local id = #Root
+ obj.exp = Compile(exp, id)
+ return id
+ end,
+ ['getindex'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ local node = obj.node
+ obj.node = Compile(node, id)
+ local index = obj.index
+ if index then
+ obj.index = Compile(index, id)
+ end
+ return id
+ end,
+ ['getmethod'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ local node = obj.node
+ local method = obj.method
+ obj.node = Compile(node, id)
+ if method then
+ obj.method = Compile(method, id)
+ end
+ return id
+ end,
+ ['setmethod'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ local node = obj.node
+ local method = obj.method
+ local value = obj.value
+ obj.node = Compile(node, id)
+ if method then
+ obj.method = Compile(method, id)
+ end
+ obj.value = Compile(value, id)
+ return id
+ end,
+ ['method'] = function (obj)
+ Root[#Root+1] = obj
+ return #Root
+ end,
+ ['function'] = function (obj)
+ local lastBlock = Block
+ Block = obj
+ Root[#Root+1] = obj
+ local id = #Root
+ local args = obj.args
+ if args then
+ obj.args = Compile(args, id)
+ end
+ for i = 1, #obj do
+ local act = obj[i]
+ obj[i] = Compile(act, id)
+ end
+ Block = lastBlock
+ return id
+ end,
+ ['funcargs'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ for i = 1, #obj do
+ local arg = obj[i]
+ obj[i] = Compile(arg, id)
+ end
+ return id
+ end,
+ ['table'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ for i = 1, #obj do
+ local v = obj[i]
+ obj[i] = Compile(v, id)
+ end
+ return id
+ end,
+ ['tablefield'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ local value = obj.value
+ if value then
+ obj.value = Compile(value, id)
+ end
+ return id
+ end,
+ ['tableindex'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ local index = obj.index
+ local value = obj.value
+ obj.index = Compile(index, id)
+ obj.value = Compile(value, id)
+ return id
+ end,
+ ['index'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ local index = obj.index
+ obj.index = Compile(index, id)
+ return id
+ end,
+ ['select'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ local vararg = obj.vararg
+ if not Cache[vararg] then
+ obj.vararg = Compile(vararg, id)
+ Cache[vararg] = obj.vararg
+ else
+ obj.vararg = Cache[vararg]
+ if not vararg.extParent then
+ vararg.extParent = {}
+ end
+ vararg.extParent[#vararg.extParent+1] = id
+ end
+ return id
+ end,
+ ['setname'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ local value = obj.value
+ if value then
+ obj.value = Compile(value, id)
+ end
+ local loc = guide.getLocal(Root, obj, obj[1], obj.start)
+ if loc then
+ obj.type = 'setlocal'
+ obj.loc = Cache[loc]
+ if not loc.ref then
+ loc.ref = {}
+ end
+ loc.ref[#loc.ref+1] = id
+ else
+ obj.type = 'setglobal'
+ end
+ return id
+ end,
+ ['local'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ local attrs = obj.attrs
+ if attrs then
+ for i = 1, #attrs do
+ local attr = attrs[i]
+ attrs[i] = Compile(attr, id)
+ end
+ end
+ if Block then
+ if not Block.locals then
+ Block.locals = {}
+ end
+ Block.locals[#Block.locals+1] = id
+ end
+ local value = obj.value
+ if value then
+ obj.value = Compile(value, id)
+ end
+ Cache[obj] = id
+ return id
+ end,
+ ['localattr'] = function (obj)
+ Root[#Root+1] = obj
+ return #Root
+ end,
+ ['setfield'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ local node = obj.node
+ local field = obj.field
+ local value = obj.value
+ obj.node = Compile(node, id)
+ obj.field = Compile(field, id)
+ obj.value = Compile(value, id)
+ return id
+ end,
+ ['do'] = function (obj)
+ local lastBlock = Block
+ Block = obj
+ Root[#Root+1] = obj
+ local id = #Root
+ CompileBlock(obj, id)
+ Block = lastBlock
+ return id
+ end,
+ ['return'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ for i = 1, #obj do
+ local act = obj[i]
+ obj[i] = Compile(act, id)
+ end
+ if Block and Block[#Block] ~= obj then
+ pushError {
+ type = 'ACTION_AFTER_RETURN',
+ start = obj.start,
+ finish = obj.finish,
+ }
+ end
+ return id
+ end,
+ ['label'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ local block = guide.getBlock(Root, obj)
+ if block then
+ if not block.labels then
+ block.labels = {}
+ end
+ local name = obj[1]
+ local label = guide.getLabel(Root, block, name)
+ if label then
+ pushError {
+ type = 'REDEFINED_LABEL',
+ start = obj.start,
+ finish = obj.finish,
+ relative = {
+ {
+ label.start,
+ label.finish,
+ }
+ }
+ }
+ end
+ block.labels[name] = id
+ end
+ return id
+ end,
+ ['goto'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ GoToTag[#GoToTag+1] = obj
+ return id
+ end,
+ ['if'] = function (obj)
+ Root[#Root+1] = obj
+ local id = #Root
+ for i = 1, #obj do
+ local block = obj[i]
+ obj[i] = Compile(block, id)
+ end
+ return id
+ end,
+ ['ifblock'] = function (obj)
+ local lastBlock = Block
+ Block = obj
+ Root[#Root+1] = obj
+ local id = #Root
+ local filter = obj.filter
+ obj.filter = Compile(filter, id)
+ CompileBlock(obj, id)
+ Block = lastBlock
+ return id
+ end,
+ ['elseifblock'] = function (obj)
+ local lastBlock = Block
+ Block = obj
+ Root[#Root+1] = obj
+ local id = #Root
+ local filter = obj.filter
+ if filter then
+ obj.filter = Compile(filter, id)
+ end
+ CompileBlock(obj, id)
+ Block = lastBlock
+ return id
+ end,
+ ['elseblock'] = function (obj)
+ local lastBlock = Block
+ Block = obj
+ Root[#Root+1] = obj
+ local id = #Root
+ CompileBlock(obj, id)
+ Block = lastBlock
+ return id
+ end,
+ ['loop'] = function (obj)
+ local lastBlock = Block
+ Block = obj
+ Root[#Root+1] = obj
+ local id = #Root
+ local loc = obj.loc
+ local max = obj.max
+ local step = obj.step
+ if loc then
+ obj.loc = Compile(loc, id)
+ end
+ if max then
+ obj.max = Compile(max, id)
+ end
+ if step then
+ obj.step = Compile(step, id)
+ end
+ CompileBlock(obj, id)
+ Block = lastBlock
+ return id
+ end,
+ ['in'] = function (obj)
+ local lastBlock = Block
+ Block = obj
+ Root[#Root+1] = obj
+ local id = #Root
+ local keys = obj.keys
+ for i = 1, #keys do
+ local loc = keys[i]
+ keys[i] = Compile(loc, id)
+ end
+ CompileBlock(obj, id)
+ Block = lastBlock
+ return id
+ end,
+ ['while'] = function (obj)
+ local lastBlock = Block
+ Block = obj
+ Root[#Root+1] = obj
+ local id = #Root
+ local filter = obj.filter
+ obj.filter = Compile(filter, id)
+ CompileBlock(obj, id)
+ Block = lastBlock
+ return id
+ end,
+ ['repeat'] = function (obj)
+ local lastBlock = Block
+ Block = obj
+ Root[#Root+1] = obj
+ local id = #Root
+ CompileBlock(obj, id)
+ local filter = obj.filter
+ obj.filter = Compile(filter, id)
+ Block = lastBlock
+ return id
+ end,
+ ['break'] = function (obj)
+ if not guide.getBreakBlock(Root, obj) then
+ pushError {
+ type = 'BREAK_OUTSIDE',
+ start = obj.start,
+ finish = obj.finish,
+ }
+ end
+ Root[#Root+1] = obj
+ return #Root
+ end,
+ ['main'] = function (obj)
+ Block = obj
+ Root[#Root+1] = obj
+ local id = #Root
+ CompileBlock(obj, id)
+ Block = nil
+ return id
+ end,
+}
+
+function CompileBlock(obj, parent)
+ for i = 1, #obj do
+ local act = obj[i]
+ local f = vmMap[act.type]
+ if f then
+ act.parent = parent
+ obj[i] = f(act)
+ end
+ end
+end
+
+function Compile(obj, parent)
+ if not obj then
+ return nil
+ end
+ local f = vmMap[obj.type]
+ if not f then
+ return nil
+ end
+ obj.parent = parent
+ return f(obj)
+end
+
+local function compileGoTo(obj)
+ local name = obj[1]
+ local label = guide.getLabel(Root, obj, name)
+ if not label then
+ pushError {
+ type = 'NO_VISIBLE_LABEL',
+ start = obj.start,
+ finish = obj.finish,
+ info = {
+ label = name,
+ }
+ }
+ return
+ end
+ -- 如果有局部变量在 goto 与 label 之间声明,
+ -- 并在 label 之后使用,则算作语法错误
+
+ -- 如果 label 在 goto 之前声明,那么不会有中间声明的局部变量
+ if obj.start > label.start then
+ return
+ end
+
+ local block = guide.getBlock(Root, obj)
+ local locals = block and block.locals
+ if not locals then
+ return
+ end
+
+ for i = 1, #locals do
+ local loc = Root[locals[i]]
+ -- 检查局部变量声明位置为 goto 与 label 之间
+ if loc.start < obj.start or loc.finish > label.finish then
+ goto CONTINUE
+ end
+ -- 检查局部变量的使用位置在 label 之后
+ local refs = loc.ref
+ if not refs then
+ goto CONTINUE
+ end
+ for j = 1, #refs do
+ local ref = Root[refs[j]]
+ if ref.finish > label.finish then
+ pushError {
+ type = 'JUMP_LOCAL_SCOPE',
+ start = obj.start,
+ finish = obj.finish,
+ info = {
+ loc = loc[1],
+ },
+ relative = {
+ {
+ start = label.start,
+ finish = label.finish,
+ },
+ {
+ start = loc.start,
+ finish = loc.finish,
+ }
+ },
+ }
+ return
+ end
+ end
+ ::CONTINUE::
+ end
+end
+
+local function PostCompile()
+ for i = 1, #GoToTag do
+ compileGoTo(GoToTag[i])
+ end
+end
+
+return function (self, lua, mode, version)
+ local state, errs = self:parse(lua, mode, version)
+ if not state then
+ return errs
+ end
+ pushError = state.pushError
+ Root = state.root
+ Cache = {}
+ GoToTag = {}
+ if type(state.ast) == 'table' then
+ Compile(state.ast)
+ end
+ PostCompile()
+ state.ast = nil
+ Cache = nil
+ return state, errs
+end
diff --git a/server-beta/src/parser/emmy.lua b/server-beta/src/parser/emmy.lua
new file mode 100644
index 00000000..4c1e087a
--- /dev/null
+++ b/server-beta/src/parser/emmy.lua
@@ -0,0 +1,321 @@
+local State
+local pushError
+
+local grammar = [[
+EmmyLua <- ({} '---' EmmyBody {} ShortComment)
+ -> EmmyLua
+EmmySp <- (!'---@' !'---' Comment / %s / %nl)*
+EmmyComments <- (EmmyComment (%nl EmmyComMulti / %nl EmmyComSingle)*)
+EmmyComment <- EmmySp %s* {(!%nl .)*}
+EmmyComMulti <- EmmySp '---|' {} -> en {(!%nl .)*}
+EmmyComSingle <- EmmySp '---' !'@' %s* {} -> ' ' {(!%nl .)*}
+EmmyBody <- '@class' %s+ EmmyClass -> EmmyClass
+ / '@type' %s+ EmmyType -> EmmyType
+ / '@alias' %s+ EmmyAlias -> EmmyAlias
+ / '@param' %s+ EmmyParam -> EmmyParam
+ / '@return' %s+ EmmyReturn -> EmmyReturn
+ / '@field' %s+ EmmyField -> EmmyField
+ / '@generic' %s+ EmmyGeneric -> EmmyGeneric
+ / '@vararg' %s+ EmmyVararg -> EmmyVararg
+ / '@language' %s+ EmmyLanguage -> EmmyLanguage
+ / '@see' %s+ EmmySee -> EmmySee
+ / '@overload' %s+ EmmyOverLoad -> EmmyOverLoad
+ / %s* EmmyComments -> EmmyComment
+ / EmmyIncomplete
+
+EmmyName <- ({} {[a-zA-Z_] [a-zA-Z0-9_]*})
+ -> EmmyName
+MustEmmyName <- EmmyName / DirtyEmmyName
+DirtyEmmyName <- {} -> DirtyEmmyName
+EmmyLongName <- ({} {(!%nl .)+})
+ -> EmmyName
+EmmyIncomplete <- MustEmmyName
+ -> EmmyIncomplete
+
+EmmyClass <- (MustEmmyName EmmyParentClass?)
+EmmyParentClass <- %s* {} ':' %s* MustEmmyName
+
+EmmyType <- EmmyTypeUnits EmmyTypeEnums
+EmmyTypeUnits <- {|
+ EmmyTypeUnit?
+ (%s* '|' %s* !String EmmyTypeUnit)*
+ |}
+EmmyTypeEnums <- {| EmmyTypeEnum* |}
+EmmyTypeUnit <- EmmyFunctionType
+ / EmmyTableType
+ / EmmyArrayType
+ / EmmyCommonType
+EmmyCommonType <- EmmyName
+ -> EmmyCommonType
+EmmyTypeEnum <- %s* (%nl %s* '---')? '|'? EmmyEnum
+ -> EmmyTypeEnum
+EmmyEnum <- %s* {'>'?} %s* String (EmmyEnumComment / (!%nl !'|' .)*)
+EmmyEnumComment <- %s* '#' %s* {(!%nl .)*}
+
+EmmyAlias <- MustEmmyName %s* EmmyType EmmyTypeEnum*
+
+EmmyParam <- MustEmmyName %s* EmmyType %s* EmmyOption %s* EmmyTypeEnum*
+EmmyOption <- Table?
+ -> EmmyOption
+
+EmmyReturn <- {} %nil {} Table -> EmmyOption
+ / {} EmmyType {} EmmyOption
+
+EmmyField <- (EmmyFieldAccess MustEmmyName %s* EmmyType)
+EmmyFieldAccess <- ({'public'} Cut %s*)
+ / ({'protected'} Cut %s*)
+ / ({'private'} Cut %s*)
+ / {} -> 'public'
+
+EmmyGeneric <- EmmyGenericBlock
+ (%s* ',' %s* EmmyGenericBlock)*
+EmmyGenericBlock<- (MustEmmyName %s* (':' %s* EmmyType)?)
+ -> EmmyGenericBlock
+
+EmmyVararg <- EmmyType
+
+EmmyLanguage <- MustEmmyName
+
+EmmyArrayType <- ({} MustEmmyName -> EmmyCommonType {} '[' DirtyBR)
+ -> EmmyArrayType
+ / ({} PL EmmyCommonType DirtyPR '[' DirtyBR)
+ -> EmmyArrayType
+
+EmmyTableType <- ({} 'table' Cut '<' %s* EmmyType %s* ',' %s* EmmyType %s* '>' {})
+ -> EmmyTableType
+
+EmmyFunctionType<- ({} 'fun' Cut %s* EmmyFunctionArgs %s* EmmyFunctionRtns {})
+ -> EmmyFunctionType
+EmmyFunctionArgs<- ('(' %s* EmmyFunctionArg %s* (',' %s* EmmyFunctionArg %s*)* DirtyPR)
+ -> EmmyFunctionArgs
+ / '(' %nil DirtyPR -> None
+ / %nil
+EmmyFunctionRtns<- (':' %s* EmmyType (%s* ',' %s* EmmyType)*)
+ -> EmmyFunctionRtns
+ / %nil
+EmmyFunctionArg <- MustEmmyName %s* ':' %s* EmmyType
+
+EmmySee <- {} MustEmmyName %s* '#' %s* MustEmmyName {}
+EmmyOverLoad <- EmmyFunctionType
+]]
+
+local ast = {
+ EmmyLua = function (start, emmy, finish)
+ emmy.start = start
+ emmy.finish = finish - 1
+ State.emmy[#State.emmy+1] = emmy
+ end,
+ EmmyName = function (start, str)
+ return {
+ type = 'name',
+ start = start,
+ finish = start + #str - 1,
+ [1] = str,
+ }
+ end,
+ DirtyEmmyName = function (pos)
+ pushError {
+ type = 'MISS_NAME',
+ level = 'warning',
+ start = pos,
+ finish = pos,
+ }
+ return {
+ type = 'emmyName',
+ start = pos-1,
+ finish = pos-1,
+ [1] = ''
+ }
+ end,
+ EmmyClass = function (class, startPos, extends)
+ if extends and extends[1] == '' then
+ extends.start = startPos
+ end
+ return {
+ type = 'class',
+ class = class,
+ extends = extends,
+ }
+ end,
+ EmmyType = function (types, enums)
+ local result = {
+ type = 'type',
+ types = types,
+ enums = enums,
+ }
+ return result
+ end,
+ EmmyCommonType = function (name)
+ return {
+ type = 'common',
+ start = name.start,
+ finish = name.finish,
+ name = name,
+ }
+ end,
+ EmmyArrayType = function (start, emmy, _, finish)
+ emmy.type = 'emmyArrayType'
+ emmy.start = start
+ emmy.finish = finish - 1
+ return emmy
+ end,
+ EmmyTableType = function (start, keyType, valueType, finish)
+ return {
+ type = 'emmyTableType',
+ start = start,
+ finish = finish - 1,
+ [1] = keyType,
+ [2] = valueType,
+ }
+ end,
+ EmmyFunctionType = function (start, args, returns, finish)
+ local result = {
+ start = start,
+ finish = finish - 1,
+ type = 'emmyFunctionType',
+ args = args,
+ returns = returns,
+ }
+ return result
+ end,
+ EmmyFunctionRtns = function (...)
+ return {...}
+ end,
+ EmmyFunctionArgs = function (...)
+ local args = {...}
+ args[#args] = nil
+ return args
+ end,
+ EmmyAlias = function (name, emmyName, ...)
+ return {
+ type = 'emmyAlias',
+ start = name.start,
+ finish = emmyName.finish,
+ name,
+ emmyName,
+ ...
+ }
+ end,
+ EmmyParam = function (argName, emmyName, option, ...)
+ local emmy = {
+ type = 'emmyParam',
+ option = option,
+ argName,
+ emmyName,
+ ...
+ }
+ emmy.start = emmy[1].start
+ emmy.finish = emmy[#emmy].finish
+ return emmy
+ end,
+ EmmyReturn = function (start, type, finish, option)
+ local emmy = {
+ type = 'emmyReturn',
+ option = option,
+ start = start,
+ finish = finish - 1,
+ [1] = type,
+ }
+ return emmy
+ end,
+ EmmyField = function (access, fieldName, ...)
+ local obj = {
+ type = 'emmyField',
+ access, fieldName,
+ ...
+ }
+ obj.start = obj[2].start
+ obj.finish = obj[3].finish
+ return obj
+ end,
+ EmmyGenericBlock = function (genericName, parentName)
+ return {
+ start = genericName.start,
+ finish = parentName and parentName.finish or genericName.finish,
+ genericName,
+ parentName,
+ }
+ end,
+ EmmyGeneric = function (...)
+ local emmy = {
+ type = 'emmyGeneric',
+ ...
+ }
+ emmy.start = emmy[1].start
+ emmy.finish = emmy[#emmy].finish
+ return emmy
+ end,
+ EmmyVararg = function (typeName)
+ return {
+ type = 'emmyVararg',
+ start = typeName.start,
+ finish = typeName.finish,
+ typeName,
+ }
+ end,
+ EmmyLanguage = function (language)
+ return {
+ type = 'emmyLanguage',
+ start = language.start,
+ finish = language.finish,
+ language,
+ }
+ end,
+ EmmySee = function (start, className, methodName, finish)
+ return {
+ type = 'emmySee',
+ start = start,
+ finish = finish - 1,
+ className, methodName
+ }
+ end,
+ EmmyOverLoad = function (EmmyFunctionType)
+ EmmyFunctionType.type = 'emmyOverLoad'
+ return EmmyFunctionType
+ end,
+ EmmyIncomplete = function (emmyName)
+ emmyName.type = 'emmyIncomplete'
+ return emmyName
+ end,
+ EmmyComment = function (...)
+ return {
+ type = 'emmyComment',
+ [1] = table.concat({...}),
+ }
+ end,
+ EmmyOption = function (options)
+ if not options or options == '' then
+ return nil
+ end
+ local option = {}
+ for _, pair in ipairs(options) do
+ if pair.type == 'pair' then
+ local key = pair[1]
+ local value = pair[2]
+ if key.type == 'name' then
+ option[key[1]] = value[1]
+ end
+ end
+ end
+ return option
+ end,
+ EmmyTypeEnum = function (default, enum, comment)
+ enum.type = 'enum'
+ if default ~= '' then
+ enum.default = true
+ end
+ enum.comment = comment
+ return enum
+ end,
+}
+
+local function init(state)
+ State = state
+ pushError = state.pushError
+end
+
+return {
+ grammar = grammar,
+ ast = ast,
+ init = init,
+}
diff --git a/server-beta/src/parser/grammar.lua b/server-beta/src/parser/grammar.lua
new file mode 100644
index 00000000..dfee8a1b
--- /dev/null
+++ b/server-beta/src/parser/grammar.lua
@@ -0,0 +1,534 @@
+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/server-beta/src/parser/guide.lua b/server-beta/src/parser/guide.lua
new file mode 100644
index 00000000..d7f39652
--- /dev/null
+++ b/server-beta/src/parser/guide.lua
@@ -0,0 +1,175 @@
+local error = error
+
+_ENV = nil
+
+local m = {}
+
+local blockTypes = {
+ ['while'] = true,
+ ['in'] = true,
+ ['loop'] = true,
+ ['repeat'] = true,
+ ['do'] = true,
+ ['function'] = true,
+ ['ifblock'] = true,
+ ['elseblock'] = true,
+ ['elseifblock'] = true,
+ ['main'] = true,
+}
+
+local breakBlockTypes = {
+ ['while'] = true,
+ ['in'] = true,
+ ['loop'] = true,
+ ['repeat'] = true,
+}
+
+--- 寻找所在函数
+function m.getParentFunction(root, obj)
+ for _ = 1, 1000 do
+ obj = root[obj.parent]
+ if not obj then
+ break
+ end
+ local tp = obj.type
+ if tp == 'function' then
+ return obj
+ end
+ end
+ return nil
+end
+
+--- 寻找所在区块
+function m.getBlock(root, obj)
+ for _ = 1, 1000 do
+ if not obj then
+ return nil
+ end
+ local tp = obj.type
+ if blockTypes[tp] then
+ return obj
+ end
+ obj = root[obj.parent]
+ end
+ error('guide.getBlock overstack')
+end
+
+--- 寻找所在父区块
+function m.getParentBlock(root, obj)
+ for _ = 1, 1000 do
+ obj = root[obj.parent]
+ if not obj then
+ return nil
+ end
+ local tp = obj.type
+ if blockTypes[tp] then
+ return obj
+ end
+ end
+ error('guide.getParentBlock overstack')
+end
+
+--- 寻找所在可break的父区块
+function m.getBreakBlock(root, obj)
+ for _ = 1, 1000 do
+ obj = root[obj.parent]
+ if not obj then
+ return nil
+ end
+ local tp = obj.type
+ if breakBlockTypes[tp] then
+ return obj
+ end
+ if tp == 'function' then
+ return nil
+ end
+ end
+ error('guide.getBreakBlock overstack')
+end
+
+--- 寻找函数的不定参数,返回不定参在第几个参数上,以及该参数对象。
+--- 如果函数是主函数,则返回`0, nil`。
+---@return table
+---@return integer
+function m.getFunctionVarArgs(root, func)
+ if func.type == 'main' then
+ return 0, nil
+ end
+ if func.type ~= 'function' then
+ return nil, nil
+ end
+ local args = root[func.args]
+ if not args then
+ return nil, nil
+ end
+ for i = 1, #args do
+ local arg = root[args[i]]
+ if arg.type == '...' then
+ return i, arg
+ end
+ end
+ return nil, nil
+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
+ if not block then
+ return nil
+ end
+ local locals = block.locals
+ local res
+ if not locals then
+ goto CONTINUE
+ end
+ for i = 1, #locals do
+ local loc = 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
+ end
+ end
+ end
+ if res then
+ return res
+ end
+ ::CONTINUE::
+ block = m.getParentBlock(root, block)
+ end
+ error('guide.getLocal overstack')
+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
+ return nil
+ end
+ local labels = block.labels
+ if labels then
+ local label = labels[name]
+ if label then
+ return root[label]
+ end
+ end
+ if block.type == 'function' then
+ return nil
+ end
+ block = m.getParentBlock(root, block)
+ end
+ error('guide.getLocal overstack')
+end
+
+return m
diff --git a/server-beta/src/parser/init.lua b/server-beta/src/parser/init.lua
new file mode 100644
index 00000000..5eeb0da2
--- /dev/null
+++ b/server-beta/src/parser/init.lua
@@ -0,0 +1,11 @@
+local api = {
+ grammar = require 'parser.grammar',
+ parse = require 'parser.parse',
+ compile = require 'parser.compile',
+ split = require 'parser.split',
+ calcline = require 'parser.calcline',
+ lines = require 'parser.lines',
+ guide = require 'parser.guide',
+}
+
+return api
diff --git a/server-beta/src/parser/lines.lua b/server-beta/src/parser/lines.lua
new file mode 100644
index 00000000..f2f076e1
--- /dev/null
+++ b/server-beta/src/parser/lines.lua
@@ -0,0 +1,187 @@
+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
+ -- <utf8>
+ 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
+ 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
+ else
+ return row, 1
+ end
+ 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)
+end
diff --git a/server-beta/src/parser/parse.lua b/server-beta/src/parser/parse.lua
new file mode 100644
index 00000000..cec946c0
--- /dev/null
+++ b/server-beta/src/parser/parse.lua
@@ -0,0 +1,37 @@
+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/server-beta/src/parser/relabel.lua b/server-beta/src/parser/relabel.lua
new file mode 100644
index 00000000..ac902403
--- /dev/null
+++ b/server-beta/src/parser/relabel.lua
@@ -0,0 +1,361 @@
+-- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $
+
+-- imported functions and modules
+local tonumber, type, print, error = tonumber, type, print, error
+local pcall = pcall
+local setmetatable = setmetatable
+local tinsert, concat = table.insert, table.concat
+local rep = string.rep
+local m = require"lpeglabel"
+
+-- 'm' will be used to parse expressions, and 'mm' will be used to
+-- create expressions; that is, 're' runs on 'm', creating patterns
+-- on 'mm'
+local mm = m
+
+-- pattern's metatable
+local mt = getmetatable(mm.P(0))
+
+
+
+-- No more global accesses after this point
+_ENV = nil
+
+
+local any = m.P(1)
+local dummy = mm.P(false)
+
+
+local errinfo = {
+ NoPatt = "no pattern found",
+ ExtraChars = "unexpected characters after the pattern",
+
+ ExpPatt1 = "expected a pattern after '/'",
+
+ ExpPatt2 = "expected a pattern after '&'",
+ ExpPatt3 = "expected a pattern after '!'",
+
+ ExpPatt4 = "expected a pattern after '('",
+ ExpPatt5 = "expected a pattern after ':'",
+ ExpPatt6 = "expected a pattern after '{~'",
+ ExpPatt7 = "expected a pattern after '{|'",
+
+ ExpPatt8 = "expected a pattern after '<-'",
+
+ ExpPattOrClose = "expected a pattern or closing '}' after '{'",
+
+ ExpNumName = "expected a number, '+', '-' or a name (no space) after '^'",
+ ExpCap = "expected a string, number, '{}' or name after '->'",
+
+ ExpName1 = "expected the name of a rule after '=>'",
+ ExpName2 = "expected the name of a rule after '=' (no space)",
+ ExpName3 = "expected the name of a rule after '<' (no space)",
+
+ ExpLab1 = "expected a label after '{'",
+
+ ExpNameOrLab = "expected a name or label after '%' (no space)",
+
+ ExpItem = "expected at least one item after '[' or '^'",
+
+ MisClose1 = "missing closing ')'",
+ MisClose2 = "missing closing ':}'",
+ MisClose3 = "missing closing '~}'",
+ MisClose4 = "missing closing '|}'",
+ MisClose5 = "missing closing '}'", -- for the captures
+
+ MisClose6 = "missing closing '>'",
+ MisClose7 = "missing closing '}'", -- for the labels
+
+ MisClose8 = "missing closing ']'",
+
+ MisTerm1 = "missing terminating single quote",
+ MisTerm2 = "missing terminating double quote",
+}
+
+local function expect (pattern, label)
+ return pattern + m.T(label)
+end
+
+
+-- Pre-defined names
+local Predef = { nl = m.P"\n" }
+
+
+local mem
+local fmem
+local gmem
+
+
+local function updatelocale ()
+ mm.locale(Predef)
+ Predef.a = Predef.alpha
+ Predef.c = Predef.cntrl
+ Predef.d = Predef.digit
+ Predef.g = Predef.graph
+ Predef.l = Predef.lower
+ Predef.p = Predef.punct
+ Predef.s = Predef.space
+ Predef.u = Predef.upper
+ Predef.w = Predef.alnum
+ Predef.x = Predef.xdigit
+ Predef.A = any - Predef.a
+ Predef.C = any - Predef.c
+ Predef.D = any - Predef.d
+ Predef.G = any - Predef.g
+ Predef.L = any - Predef.l
+ Predef.P = any - Predef.p
+ Predef.S = any - Predef.s
+ Predef.U = any - Predef.u
+ Predef.W = any - Predef.w
+ Predef.X = any - Predef.x
+ mem = {} -- restart memoization
+ fmem = {}
+ gmem = {}
+ local mt = {__mode = "v"}
+ setmetatable(mem, mt)
+ setmetatable(fmem, mt)
+ setmetatable(gmem, mt)
+end
+
+
+updatelocale()
+
+
+
+local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end)
+
+
+local function getdef (id, defs)
+ local c = defs and defs[id]
+ if not c then
+ error("undefined name: " .. id)
+ end
+ return c
+end
+
+
+local function mult (p, n)
+ local np = mm.P(true)
+ while n >= 1 do
+ if n%2 >= 1 then np = np * p end
+ p = p * p
+ n = n/2
+ end
+ return np
+end
+
+local function equalcap (s, i, c)
+ if type(c) ~= "string" then return nil end
+ local e = #c + i
+ if s:sub(i, e - 1) == c then return e else return nil end
+end
+
+
+local S = (Predef.space + "--" * (any - Predef.nl)^0)^0
+
+local name = m.C(m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0)
+
+local arrow = S * "<-"
+
+-- a defined name only have meaning in a given environment
+local Def = name * m.Carg(1)
+
+local num = m.C(m.R"09"^1) * S / tonumber
+
+local String = "'" * m.C((any - "'" - m.P"\n")^0) * expect("'", "MisTerm1")
+ + '"' * m.C((any - '"' - m.P"\n")^0) * expect('"', "MisTerm2")
+
+
+local defined = "%" * Def / function (c,Defs)
+ local cat = Defs and Defs[c] or Predef[c]
+ if not cat then
+ error("name '" .. c .. "' undefined")
+ end
+ return cat
+end
+
+local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R
+
+local item = defined + Range + m.C(any - m.P"\n")
+
+local Class =
+ "["
+ * (m.C(m.P"^"^-1)) -- optional complement symbol
+ * m.Cf(expect(item, "ExpItem") * (item - "]")^0, mt.__add)
+ / function (c, p) return c == "^" and any - p or p end
+ * expect("]", "MisClose8")
+
+local function adddef (t, k, exp)
+ if t[k] then
+ -- TODO 改了一下这里的代码,重复定义不会抛错
+ --error("'"..k.."' already defined as a rule")
+ else
+ t[k] = exp
+ end
+ return t
+end
+
+local function firstdef (n, r) return adddef({n}, n, r) end
+
+
+local function NT (n, b)
+ if not b then
+ error("rule '"..n.."' used outside a grammar")
+ else return mm.V(n)
+ end
+end
+
+
+local exp = m.P{ "Exp",
+ Exp = S * ( m.V"Grammar"
+ + m.Cf(m.V"Seq" * (S * "/" * expect(S * m.V"Seq", "ExpPatt1"))^0, mt.__add) );
+ Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix" * (S * m.V"Prefix")^0, mt.__mul);
+ Prefix = "&" * expect(S * m.V"Prefix", "ExpPatt2") / mt.__len
+ + "!" * expect(S * m.V"Prefix", "ExpPatt3") / mt.__unm
+ + m.V"Suffix";
+ Suffix = m.Cf(m.V"Primary" *
+ ( S * ( m.P"+" * m.Cc(1, mt.__pow)
+ + m.P"*" * m.Cc(0, mt.__pow)
+ + m.P"?" * m.Cc(-1, mt.__pow)
+ + "^" * expect( m.Cg(num * m.Cc(mult))
+ + m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow)
+ + name * m.Cc"lab"
+ ),
+ "ExpNumName")
+ + "->" * expect(S * ( m.Cg((String + num) * m.Cc(mt.__div))
+ + m.P"{}" * m.Cc(nil, m.Ct)
+ + m.Cg(Def / getdef * m.Cc(mt.__div))
+ ),
+ "ExpCap")
+ + "=>" * expect(S * m.Cg(Def / getdef * m.Cc(m.Cmt)),
+ "ExpName1")
+ )
+ )^0, function (a,b,f) if f == "lab" then return a + mm.T(b) else return f(a,b) end end );
+ Primary = "(" * expect(m.V"Exp", "ExpPatt4") * expect(S * ")", "MisClose1")
+ + String / mm.P
+ + Class
+ + defined
+ + "%" * expect(m.P"{", "ExpNameOrLab")
+ * expect(S * m.V"Label", "ExpLab1")
+ * expect(S * "}", "MisClose7") / mm.T
+ + "{:" * (name * ":" + m.Cc(nil)) * expect(m.V"Exp", "ExpPatt5")
+ * expect(S * ":}", "MisClose2")
+ / function (n, p) return mm.Cg(p, n) end
+ + "=" * expect(name, "ExpName2")
+ / function (n) return mm.Cmt(mm.Cb(n), equalcap) end
+ + m.P"{}" / mm.Cp
+ + "{~" * expect(m.V"Exp", "ExpPatt6")
+ * expect(S * "~}", "MisClose3") / mm.Cs
+ + "{|" * expect(m.V"Exp", "ExpPatt7")
+ * expect(S * "|}", "MisClose4") / mm.Ct
+ + "{" * expect(m.V"Exp", "ExpPattOrClose")
+ * expect(S * "}", "MisClose5") / mm.C
+ + m.P"." * m.Cc(any)
+ + (name * -arrow + "<" * expect(name, "ExpName3")
+ * expect(">", "MisClose6")) * m.Cb("G") / NT;
+ Label = num + name;
+ Definition = name * arrow * expect(m.V"Exp", "ExpPatt8");
+ Grammar = m.Cg(m.Cc(true), "G")
+ * m.Cf(m.V"Definition" / firstdef * (S * m.Cg(m.V"Definition"))^0,
+ adddef) / mm.P;
+}
+
+local pattern = S * m.Cg(m.Cc(false), "G") * expect(exp, "NoPatt") / mm.P
+ * S * expect(-any, "ExtraChars")
+
+local function lineno (s, i)
+ if i == 1 then return 1, 1 end
+ local adjustment = 0
+ -- report the current line if at end of line, not the next
+ if s:sub(i,i) == '\n' then
+ i = i-1
+ adjustment = 1
+ end
+ local rest, num = s:sub(1,i):gsub("[^\n]*\n", "")
+ local r = #rest
+ return 1 + num, (r ~= 0 and r or 1) + adjustment
+end
+
+local function calcline (s, i)
+ if i == 1 then return 1, 1 end
+ local rest, line = s:sub(1,i):gsub("[^\n]*\n", "")
+ local col = #rest
+ return 1 + line, col ~= 0 and col or 1
+end
+
+
+local function splitlines(str)
+ local t = {}
+ local function helper(line) tinsert(t, line) return "" end
+ helper((str:gsub("(.-)\r?\n", helper)))
+ return t
+end
+
+local function compile (p, defs)
+ if mm.type(p) == "pattern" then return p end -- already compiled
+ p = p .. " " -- for better reporting of column numbers in errors when at EOF
+ local ok, cp, label, poserr = pcall(function() return pattern:match(p, 1, defs) end)
+ if not ok and cp then
+ if type(cp) == "string" then
+ cp = cp:gsub("^[^:]+:[^:]+: ", "")
+ end
+ error(cp, 3)
+ end
+ if not cp then
+ local lines = splitlines(p)
+ local line, col = lineno(p, poserr)
+ local err = {}
+ tinsert(err, "L" .. line .. ":C" .. col .. ": " .. errinfo[label])
+ tinsert(err, lines[line])
+ tinsert(err, rep(" ", col-1) .. "^")
+ error("syntax error(s) in pattern\n" .. concat(err, "\n"), 3)
+ end
+ return cp
+end
+
+local function match (s, p, i)
+ local cp = mem[p]
+ if not cp then
+ cp = compile(p)
+ mem[p] = cp
+ end
+ return cp:match(s, i or 1)
+end
+
+local function find (s, p, i)
+ local cp = fmem[p]
+ if not cp then
+ cp = compile(p) / 0
+ cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) }
+ fmem[p] = cp
+ end
+ local i, e = cp:match(s, i or 1)
+ if i then return i, e - 1
+ else return i
+ end
+end
+
+local function gsub (s, p, rep)
+ local g = gmem[p] or {} -- ensure gmem[p] is not collected while here
+ gmem[p] = g
+ local cp = g[rep]
+ if not cp then
+ cp = compile(p)
+ cp = mm.Cs((cp / rep + 1)^0)
+ g[rep] = cp
+ end
+ return cp:match(s)
+end
+
+
+-- exported names
+local re = {
+ compile = compile,
+ match = match,
+ find = find,
+ gsub = gsub,
+ updatelocale = updatelocale,
+ calcline = calcline
+}
+
+return re
diff --git a/server-beta/src/parser/split.lua b/server-beta/src/parser/split.lua
new file mode 100644
index 00000000..6ce4a4e7
--- /dev/null
+++ b/server-beta/src/parser/split.lua
@@ -0,0 +1,9 @@
+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