diff options
Diffstat (limited to 'script/parser/ast.lua')
-rw-r--r-- | script/parser/ast.lua | 1751 |
1 files changed, 1751 insertions, 0 deletions
diff --git a/script/parser/ast.lua b/script/parser/ast.lua new file mode 100644 index 00000000..d8614eae --- /dev/null +++ b/script/parser/ast.lua @@ -0,0 +1,1751 @@ +local tonumber = tonumber +local stringChar = string.char +local utf8Char = utf8.char +local tableUnpack = table.unpack +local mathType = math.type +local tableRemove = table.remove +local pairs = pairs +local tableSort = table.sort + +_ENV = nil + +local State +local PushError +local PushDiag +local PushComment + +-- goto 单独处理 +local RESERVED = { + ['and'] = true, + ['break'] = true, + ['do'] = true, + ['else'] = true, + ['elseif'] = true, + ['end'] = true, + ['false'] = true, + ['for'] = true, + ['function'] = true, + ['if'] = true, + ['in'] = true, + ['local'] = true, + ['nil'] = true, + ['not'] = true, + ['or'] = true, + ['repeat'] = true, + ['return'] = true, + ['then'] = true, + ['true'] = true, + ['until'] = true, + ['while'] = true, +} + +local VersionOp = { + ['&'] = {'Lua 5.3', 'Lua 5.4'}, + ['~'] = {'Lua 5.3', 'Lua 5.4'}, + ['|'] = {'Lua 5.3', 'Lua 5.4'}, + ['<<'] = {'Lua 5.3', 'Lua 5.4'}, + ['>>'] = {'Lua 5.3', 'Lua 5.4'}, + ['//'] = {'Lua 5.3', 'Lua 5.4'}, +} + +local function checkOpVersion(op) + local versions = VersionOp[op.type] + if not versions then + return + end + for i = 1, #versions do + if versions[i] == State.version then + return + end + end + PushError { + type = 'UNSUPPORT_SYMBOL', + start = op.start, + finish = op.finish, + version = versions, + info = { + version = State.version, + } + } +end + +local function checkMissEnd(start) + if not State.MissEndErr then + return + end + local err = State.MissEndErr + State.MissEndErr = nil + local _, finish = State.lua:find('[%w_]+', start) + if not finish then + return + end + err.info.related = { + { + start = start, + finish = finish, + } + } + PushError { + type = 'MISS_END', + start = start, + finish = finish, + } +end + +local function getSelect(vararg, index) + return { + type = 'select', + start = vararg.start, + finish = vararg.finish, + vararg = vararg, + index = index, + } +end + +local function getValue(values, i) + if not values then + return nil, nil + end + local value = values[i] + if not value then + local last = values[#values] + if not last then + return nil, nil + end + if last.type == 'call' or last.type == 'varargs' then + return getSelect(last, i - #values + 1) + end + return nil, nil + end + if value.type == 'call' or value.type == 'varargs' then + value = getSelect(value, 1) + end + return value +end + +local function createLocal(key, effect, value, attrs) + if not key then + return nil + end + key.type = 'local' + key.effect = effect + key.value = value + key.attrs = attrs + if value then + key.range = value.finish + end + return key +end + +local function createCall(args, start, finish) + if args then + args.type = 'callargs' + args.start = start + args.finish = finish + end + return { + type = 'call', + start = start, + finish = finish, + args = args, + } +end + +local function packList(start, list, finish) + local lastFinish = start + local wantName = true + local count = 0 + for i = 1, #list do + local ast = list[i] + if ast.type == ',' then + if wantName or i == #list then + PushError { + type = 'UNEXPECT_SYMBOL', + start = ast.start, + finish = ast.finish, + info = { + symbol = ',', + } + } + end + wantName = true + else + if not wantName then + PushError { + type = 'MISS_SYMBOL', + start = lastFinish, + finish = ast.start - 1, + info = { + symbol = ',', + } + } + end + wantName = false + count = count + 1 + list[count] = list[i] + end + lastFinish = ast.finish + 1 + end + for i = count + 1, #list do + list[i] = nil + end + list.type = 'list' + list.start = start + list.finish = finish - 1 + return list +end + +local BinaryLevel = { + ['or'] = 1, + ['and'] = 2, + ['<='] = 3, + ['>='] = 3, + ['<'] = 3, + ['>'] = 3, + ['~='] = 3, + ['=='] = 3, + ['|'] = 4, + ['~'] = 5, + ['&'] = 6, + ['<<'] = 7, + ['>>'] = 7, + ['..'] = 8, + ['+'] = 9, + ['-'] = 9, + ['*'] = 10, + ['//'] = 10, + ['/'] = 10, + ['%'] = 10, + ['^'] = 11, +} + +local BinaryForward = { + [01] = true, + [02] = true, + [03] = true, + [04] = true, + [05] = true, + [06] = true, + [07] = true, + [08] = false, + [09] = true, + [10] = true, + [11] = false, +} + +local Defs = { + Nil = function (pos) + return { + type = 'nil', + start = pos, + finish = pos + 2, + } + end, + True = function (pos) + return { + type = 'boolean', + start = pos, + finish = pos + 3, + [1] = true, + } + end, + False = function (pos) + return { + type = 'boolean', + start = pos, + finish = pos + 4, + [1] = false, + } + end, + ShortComment = function (start, text, finish) + PushComment { + start = start, + finish = finish - 1, + text = text, + } + end, + LongComment = function (beforeEq, afterEq, str, missPos) + if missPos then + local endSymbol = ']' .. ('='):rep(afterEq-beforeEq) .. ']' + local s, _, w = str:find('(%][%=]*%])[%c%s]*$') + if s then + PushError { + type = 'ERR_LCOMMENT_END', + start = missPos - #str + s - 1, + finish = missPos - #str + s + #w - 2, + info = { + symbol = endSymbol, + }, + fix = { + title = 'FIX_LCOMMENT_END', + { + start = missPos - #str + s - 1, + finish = missPos - #str + s + #w - 2, + text = endSymbol, + } + }, + } + end + PushError { + type = 'MISS_SYMBOL', + start = missPos, + finish = missPos, + info = { + symbol = endSymbol, + }, + fix = { + title = 'ADD_LCOMMENT_END', + { + start = missPos, + finish = missPos, + text = endSymbol, + } + }, + } + end + end, + CLongComment = function (start1, finish1, start2, finish2) + PushError { + type = 'ERR_C_LONG_COMMENT', + start = start1, + finish = finish2 - 1, + fix = { + title = 'FIX_C_LONG_COMMENT', + { + start = start1, + finish = finish1 - 1, + text = '--[[', + }, + { + start = start2, + finish = finish2 - 1, + text = '--]]' + }, + } + } + end, + CCommentPrefix = function (start, finish) + PushError { + type = 'ERR_COMMENT_PREFIX', + start = start, + finish = finish - 1, + fix = { + title = 'FIX_COMMENT_PREFIX', + { + start = start, + finish = finish - 1, + text = '--', + }, + } + } + end, + String = function (start, quote, str, finish) + return { + type = 'string', + start = start, + finish = finish - 1, + [1] = str, + [2] = quote, + } + end, + LongString = function (beforeEq, afterEq, str, missPos) + if missPos then + local endSymbol = ']' .. ('='):rep(afterEq-beforeEq) .. ']' + local s, _, w = str:find('(%][%=]*%])[%c%s]*$') + if s then + PushError { + type = 'ERR_LSTRING_END', + start = missPos - #str + s - 1, + finish = missPos - #str + s + #w - 2, + info = { + symbol = endSymbol, + }, + fix = { + title = 'FIX_LSTRING_END', + { + start = missPos - #str + s - 1, + finish = missPos - #str + s + #w - 2, + text = endSymbol, + } + }, + } + end + PushError { + type = 'MISS_SYMBOL', + start = missPos, + finish = missPos, + info = { + symbol = endSymbol, + }, + fix = { + title = 'ADD_LSTRING_END', + { + start = missPos, + finish = missPos, + text = endSymbol, + } + }, + } + end + return '[' .. ('='):rep(afterEq-beforeEq) .. '[', str + end, + Char10 = function (char) + char = tonumber(char) + if not char or char < 0 or char > 255 then + return '' + end + return stringChar(char) + end, + Char16 = function (pos, char) + if State.version == 'Lua 5.1' then + PushError { + type = 'ERR_ESC', + start = pos-1, + finish = pos, + version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'}, + info = { + version = State.version, + } + } + return char + end + return stringChar(tonumber(char, 16)) + end, + CharUtf8 = function (pos, char) + if State.version ~= 'Lua 5.3' + and State.version ~= 'Lua 5.4' + and State.version ~= 'LuaJIT' + then + PushError { + type = 'ERR_ESC', + start = pos-3, + finish = pos-2, + version = {'Lua 5.3', 'Lua 5.4', 'LuaJIT'}, + info = { + version = State.version, + } + } + return char + end + if #char == 0 then + PushError { + type = 'UTF8_SMALL', + start = pos-3, + finish = pos, + } + return '' + end + local v = tonumber(char, 16) + if not v then + for i = 1, #char do + if not tonumber(char:sub(i, i), 16) then + PushError { + type = 'MUST_X16', + start = pos + i - 1, + finish = pos + i - 1, + } + end + end + return '' + end + if State.version == 'Lua 5.4' then + if v < 0 or v > 0x7FFFFFFF then + PushError { + type = 'UTF8_MAX', + start = pos-3, + finish = pos+#char, + info = { + min = '00000000', + max = '7FFFFFFF', + } + } + end + else + if v < 0 or v > 0x10FFFF then + PushError { + type = 'UTF8_MAX', + start = pos-3, + finish = pos+#char, + version = v <= 0x7FFFFFFF and 'Lua 5.4' or nil, + info = { + min = '000000', + max = '10FFFF', + } + } + end + end + if v >= 0 and v <= 0x10FFFF then + return utf8Char(v) + end + return '' + end, + Number = function (start, number, finish) + local n = tonumber(number) + if n then + State.LastNumber = { + type = 'number', + start = start, + finish = finish - 1, + [1] = n, + } + return State.LastNumber + else + PushError { + type = 'MALFORMED_NUMBER', + start = start, + finish = finish - 1, + } + State.LastNumber = { + type = 'number', + start = start, + finish = finish - 1, + [1] = 0, + } + return State.LastNumber + end + end, + FFINumber = function (start, symbol) + local lastNumber = State.LastNumber + if mathType(lastNumber[1]) == 'float' then + PushError { + type = 'UNKNOWN_SYMBOL', + start = start, + finish = start + #symbol - 1, + info = { + symbol = symbol, + } + } + lastNumber[1] = 0 + return + end + if State.version ~= 'LuaJIT' then + PushError { + type = 'UNSUPPORT_SYMBOL', + start = start, + finish = start + #symbol - 1, + version = 'LuaJIT', + info = { + version = State.version, + } + } + lastNumber[1] = 0 + end + end, + ImaginaryNumber = function (start, symbol) + local lastNumber = State.LastNumber + if State.version ~= 'LuaJIT' then + PushError { + type = 'UNSUPPORT_SYMBOL', + start = start, + finish = start + #symbol - 1, + version = 'LuaJIT', + info = { + version = State.version, + } + } + end + lastNumber[1] = 0 + end, + Name = function (start, str, finish) + local isKeyWord + if RESERVED[str] then + isKeyWord = true + elseif str == 'goto' then + if State.version ~= 'Lua 5.1' and State.version ~= 'LuaJIT' then + isKeyWord = true + end + end + if isKeyWord then + PushError { + type = 'KEYWORD', + start = start, + finish = finish - 1, + } + end + return { + type = 'name', + start = start, + finish = finish - 1, + [1] = str, + } + end, + GetField = function (dot, field) + local obj = { + type = 'getfield', + field = field, + dot = dot, + start = dot.start, + finish = (field or dot).finish, + } + if field then + field.type = 'field' + field.parent = obj + end + return obj + end, + GetIndex = function (start, index, finish) + local obj = { + type = 'getindex', + start = start, + finish = finish - 1, + index = index, + } + if index then + index.parent = obj + end + return obj + end, + GetMethod = function (colon, method) + local obj = { + type = 'getmethod', + method = method, + colon = colon, + start = colon.start, + finish = (method or colon).finish, + } + if method then + method.type = 'method' + method.parent = obj + end + return obj + end, + Single = function (unit) + unit.type = 'getname' + return unit + end, + Simple = function (units) + local last = units[1] + for i = 2, #units do + local current = units[i] + current.node = last + current.start = last.start + last.next = current + last = units[i] + end + return last + end, + SimpleCall = function (call) + if call.type ~= 'call' and call.type ~= 'getmethod' then + PushError { + type = 'EXP_IN_ACTION', + start = call.start, + finish = call.finish, + } + end + return call + end, + BinaryOp = function (start, op) + return { + type = op, + start = start, + finish = start + #op - 1, + } + end, + UnaryOp = function (start, op) + return { + type = op, + start = start, + finish = start + #op - 1, + } + end, + Unary = function (first, ...) + if not ... then + return nil + end + local list = {first, ...} + local e = list[#list] + for i = #list - 1, 1, -1 do + local op = list[i] + checkOpVersion(op) + e = { + type = 'unary', + op = op, + start = op.start, + finish = e.finish, + [1] = e, + } + end + return e + end, + SubBinary = function (op, symb) + if symb then + return op, symb + end + PushError { + type = 'MISS_EXP', + start = op.start, + finish = op.finish, + } + end, + Binary = function (first, op, second, ...) + if not first then + return second + end + if not op then + return first + end + if not ... then + checkOpVersion(op) + return { + type = 'binary', + op = op, + start = first.start, + finish = second.finish, + [1] = first, + [2] = second, + } + end + local list = {first, op, second, ...} + local ops = {} + for i = 2, #list, 2 do + ops[#ops+1] = i + end + tableSort(ops, function (a, b) + local op1 = list[a] + local op2 = list[b] + local lv1 = BinaryLevel[op1.type] + local lv2 = BinaryLevel[op2.type] + if lv1 == lv2 then + local forward = BinaryForward[lv1] + if forward then + return op1.start > op2.start + else + return op1.start < op2.start + end + else + return lv1 < lv2 + end + end) + local final + for i = #ops, 1, -1 do + local n = ops[i] + local op = list[n] + local left = list[n-1] + local right = list[n+1] + local exp = { + type = 'binary', + op = op, + start = left.start, + finish = right and right.finish or op.finish, + [1] = left, + [2] = right, + } + local leftIndex, rightIndex + if list[left] then + leftIndex = list[left[1]] + else + leftIndex = n - 1 + end + if list[right] then + rightIndex = list[right[2]] + else + rightIndex = n + 1 + end + + list[leftIndex] = exp + list[rightIndex] = exp + list[left] = leftIndex + list[right] = rightIndex + list[exp] = n + final = exp + + checkOpVersion(op) + end + return final + end, + Paren = function (start, exp, finish) + if exp and exp.type == 'paren' then + exp.start = start + exp.finish = finish - 1 + return exp + end + return { + type = 'paren', + start = start, + finish = finish - 1, + exp = exp + } + end, + VarArgs = function (dots) + dots.type = 'varargs' + return dots + end, + PackLoopArgs = function (start, list, finish) + local list = packList(start, list, finish) + if #list == 0 then + PushError { + type = 'MISS_LOOP_MIN', + start = finish, + finish = finish, + } + elseif #list == 1 then + PushError { + type = 'MISS_LOOP_MAX', + start = finish, + finish = finish, + } + end + return list + end, + PackInNameList = function (start, list, finish) + local list = packList(start, list, finish) + if #list == 0 then + PushError { + type = 'MISS_NAME', + start = start, + finish = finish, + } + end + return list + end, + PackInExpList = function (start, list, finish) + local list = packList(start, list, finish) + if #list == 0 then + PushError { + type = 'MISS_EXP', + start = start, + finish = finish, + } + end + return list + end, + PackExpList = function (start, list, finish) + local list = packList(start, list, finish) + return list + end, + PackNameList = function (start, list, finish) + local list = packList(start, list, finish) + return list + end, + Call = function (start, args, finish) + return createCall(args, start, finish-1) + end, + COMMA = function (start) + return { + type = ',', + start = start, + finish = start, + } + end, + SEMICOLON = function (start) + return { + type = ';', + start = start, + finish = start, + } + end, + DOTS = function (start) + return { + type = '...', + start = start, + finish = start + 2, + } + end, + COLON = function (start) + return { + type = ':', + start = start, + finish = start, + } + end, + DOT = function (start) + return { + type = '.', + start = start, + finish = start, + } + end, + Function = function (functionStart, functionFinish, args, actions, endStart, endFinish) + actions.type = 'function' + actions.start = functionStart + actions.finish = endFinish - 1 + actions.args = args + actions.keyword= { + functionStart, functionFinish - 1, + endStart, endFinish - 1, + } + checkMissEnd(functionStart) + return actions + end, + NamedFunction = function (functionStart, functionFinish, name, args, actions, endStart, endFinish) + actions.type = 'function' + actions.start = functionStart + actions.finish = endFinish - 1 + actions.args = args + actions.keyword= { + functionStart, functionFinish - 1, + endStart, endFinish - 1, + } + checkMissEnd(functionStart) + if not name then + return + end + if name.type == 'getname' then + name.type = 'setname' + name.value = actions + elseif name.type == 'getfield' then + name.type = 'setfield' + name.value = actions + elseif name.type == 'getmethod' then + name.type = 'setmethod' + name.value = actions + end + name.range = actions.finish + name.vstart = functionStart + return name + end, + LocalFunction = function (start, functionStart, functionFinish, name, args, actions, endStart, endFinish) + actions.type = 'function' + actions.start = start + actions.finish = endFinish - 1 + actions.args = args + actions.keyword= { + functionStart, functionFinish - 1, + endStart, endFinish - 1, + } + checkMissEnd(start) + + if not name then + return + end + + if name.type ~= 'getname' then + PushError { + type = 'UNEXPECT_LFUNC_NAME', + start = name.start, + finish = name.finish, + } + return + end + + local loc = createLocal(name, name.start, actions) + loc.localfunction = true + loc.vstart = functionStart + + return loc + end, + Table = function (start, tbl, finish) + tbl.type = 'table' + tbl.start = start + tbl.finish = finish - 1 + local wantField = true + local lastStart = start + 1 + local fieldCount = 0 + for i = 1, #tbl do + local field = tbl[i] + if field.type == ',' or field.type == ';' then + if wantField then + PushError { + type = 'MISS_EXP', + start = lastStart, + finish = field.start - 1, + } + end + wantField = true + lastStart = field.finish + 1 + else + if not wantField then + PushError { + type = 'MISS_SEP_IN_TABLE', + start = lastStart, + finish = field.start - 1, + } + end + wantField = false + lastStart = field.finish + 1 + fieldCount = fieldCount + 1 + tbl[fieldCount] = field + end + end + for i = fieldCount + 1, #tbl do + tbl[i] = nil + end + return tbl + end, + NewField = function (start, field, value, finish) + local obj = { + type = 'tablefield', + start = start, + finish = finish-1, + field = field, + value = value, + } + if field then + field.type = 'field' + field.parent = obj + end + return obj + end, + NewIndex = function (start, index, value, finish) + local obj = { + type = 'tableindex', + start = start, + finish = finish-1, + index = index, + value = value, + } + if index then + index.parent = obj + end + return obj + end, + FuncArgs = function (start, args, finish) + args.type = 'funcargs' + args.start = start + args.finish = finish - 1 + local lastStart = start + 1 + local wantName = true + local argCount = 0 + for i = 1, #args do + local arg = args[i] + local argAst = arg + if argAst.type == ',' then + if wantName then + PushError { + type = 'MISS_NAME', + start = lastStart, + finish = argAst.start-1, + } + end + wantName = true + else + if not wantName then + PushError { + type = 'MISS_SYMBOL', + start = lastStart-1, + finish = argAst.start-1, + info = { + symbol = ',', + } + } + end + wantName = false + argCount = argCount + 1 + + if argAst.type == '...' then + args[argCount] = arg + if i < #args then + local a = args[i+1] + local b = args[#args] + PushError { + type = 'ARGS_AFTER_DOTS', + start = a.start, + finish = b.finish, + } + end + break + else + args[argCount] = createLocal(arg, arg.start) + end + end + lastStart = argAst.finish + 1 + end + for i = argCount + 1, #args do + args[i] = nil + end + if wantName and argCount > 0 then + PushError { + type = 'MISS_NAME', + start = lastStart, + finish = finish - 1, + } + end + return args + end, + Set = function (start, keys, values, finish) + for i = 1, #keys do + local key = keys[i] + if key.type == 'getname' then + key.type = 'setname' + key.value = getValue(values, i) + elseif key.type == 'getfield' then + key.type = 'setfield' + key.value = getValue(values, i) + elseif key.type == 'getindex' then + key.type = 'setindex' + key.value = getValue(values, i) + end + if key.value then + key.range = key.value.finish + end + end + if values then + for i = #keys+1, #values do + local value = values[i] + PushDiag('redundant-value', { + start = value.start, + finish = value.finish, + max = #keys, + passed = #values, + }) + end + end + return tableUnpack(keys) + end, + LocalAttr = function (attrs) + if #attrs == 0 then + return nil + end + 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 + attrs.start = attrs[1].start + attrs.finish = attrs[#attrs].finish + return attrs + end, + LocalName = function (name, attrs) + if not name then + return name + end + name.attrs = attrs + return name + end, + Local = function (start, keys, values, finish) + for i = 1, #keys do + local key = keys[i] + local attrs = key.attrs + key.attrs = nil + local value = getValue(values, i) + createLocal(key, finish, value, attrs) + end + if values then + for i = #keys+1, #values do + local value = values[i] + PushDiag('redundant-value', { + start = value.start, + finish = value.finish, + max = #keys, + passed = #values, + }) + end + end + return tableUnpack(keys) + end, + Do = function (start, actions, endA, endB) + actions.type = 'do' + actions.start = start + actions.finish = endB - 1 + actions.keyword= { + start, start + #'do' - 1, + endA , endB - 1, + } + checkMissEnd(start) + return actions + end, + Break = function (start, finish) + return { + type = 'break', + start = start, + finish = finish - 1, + } + end, + Return = function (start, exps, finish) + exps.type = 'return' + exps.start = start + exps.finish = finish - 1 + return exps + end, + Label = function (start, name, finish) + if State.version == 'Lua 5.1' then + PushError { + type = 'UNSUPPORT_SYMBOL', + start = start, + finish = finish - 1, + version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'}, + info = { + version = State.version, + } + } + return + end + if not name then + return nil + end + name.type = 'label' + return name + end, + GoTo = function (start, name, finish) + if State.version == 'Lua 5.1' then + PushError { + type = 'UNSUPPORT_SYMBOL', + start = start, + finish = finish - 1, + version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'}, + info = { + version = State.version, + } + } + return + end + if not name then + return nil + end + name.type = 'goto' + return name + end, + IfBlock = function (ifStart, ifFinish, exp, thenStart, thenFinish, actions, finish) + actions.type = 'ifblock' + actions.start = ifStart + actions.finish = finish - 1 + actions.filter = exp + actions.keyword= { + ifStart, ifFinish - 1, + thenStart, thenFinish - 1, + } + return actions + end, + ElseIfBlock = function (elseifStart, elseifFinish, exp, thenStart, thenFinish, actions, finish) + actions.type = 'elseifblock' + actions.start = elseifStart + actions.finish = finish - 1 + actions.filter = exp + actions.keyword= { + elseifStart, elseifFinish - 1, + thenStart, thenFinish - 1, + } + return actions + end, + ElseBlock = function (elseStart, elseFinish, actions, finish) + actions.type = 'elseblock' + actions.start = elseStart + actions.finish = finish - 1 + actions.keyword= { + elseStart, elseFinish - 1, + } + return actions + end, + If = function (start, blocks, endStart, endFinish) + blocks.type = 'if' + blocks.start = start + blocks.finish = endFinish - 1 + local hasElse + for i = 1, #blocks do + local block = blocks[i] + if i == 1 and block.type ~= 'ifblock' then + PushError { + type = 'MISS_SYMBOL', + start = block.start, + finish = block.start, + info = { + symbol = 'if', + } + } + end + if hasElse then + PushError { + type = 'BLOCK_AFTER_ELSE', + start = block.start, + finish = block.finish, + } + end + if block.type == 'elseblock' then + hasElse = true + end + end + checkMissEnd(start) + return blocks + end, + Loop = function (forA, forB, arg, steps, doA, doB, blockStart, block, endA, endB) + local loc = createLocal(arg, blockStart, steps[1]) + block.type = 'loop' + block.start = forA + block.finish = endB - 1 + block.loc = loc + block.max = steps[2] + block.step = steps[3] + block.keyword= { + forA, forB - 1, + doA , doB - 1, + endA, endB - 1, + } + checkMissEnd(forA) + return block + end, + In = function (forA, forB, keys, inA, inB, exp, doA, doB, blockStart, block, endA, endB) + local func = tableRemove(exp, 1) + block.type = 'in' + block.start = forA + block.finish = endB - 1 + block.keys = keys + block.keyword= { + forA, forB - 1, + inA , inB - 1, + doA , doB - 1, + endA, endB - 1, + } + + local values + if func then + local call = createCall(exp, func.finish + 1, exp.finish) + call.node = func + call.start = func.start + func.next = call + values = { call } + keys.range = call.finish + end + for i = 1, #keys do + local loc = keys[i] + if values then + createLocal(loc, blockStart, getValue(values, i)) + else + createLocal(loc, blockStart) + end + end + checkMissEnd(forA) + return block + end, + While = function (whileA, whileB, filter, doA, doB, block, endA, endB) + block.type = 'while' + block.start = whileA + block.finish = endB - 1 + block.filter = filter + block.keyword= { + whileA, whileB - 1, + doA , doB - 1, + endA , endB - 1, + } + checkMissEnd(whileA) + return block + end, + Repeat = function (repeatA, repeatB, block, untilA, untilB, filter, finish) + block.type = 'repeat' + block.start = repeatA + block.finish = finish + block.filter = filter + block.keyword= { + repeatA, repeatB - 1, + untilA , untilB - 1, + } + return block + end, + Lua = function (start, actions, finish) + actions.type = 'main' + actions.start = start + actions.finish = finish - 1 + return actions + end, + + -- 捕获错误 + UnknownSymbol = function (start, symbol) + PushError { + type = 'UNKNOWN_SYMBOL', + start = start, + finish = start + #symbol - 1, + info = { + symbol = symbol, + } + } + return + end, + UnknownAction = function (start, symbol) + PushError { + type = 'UNKNOWN_SYMBOL', + start = start, + finish = start + #symbol - 1, + info = { + symbol = symbol, + } + } + end, + DirtyName = function (pos) + PushError { + type = 'MISS_NAME', + start = pos, + finish = pos, + } + return nil + end, + DirtyExp = function (pos) + PushError { + type = 'MISS_EXP', + start = pos, + finish = pos, + } + return nil + end, + MissExp = function (pos) + PushError { + type = 'MISS_EXP', + start = pos, + finish = pos, + } + end, + MissExponent = function (start, finish) + PushError { + type = 'MISS_EXPONENT', + start = start, + finish = finish - 1, + } + end, + MissQuote1 = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = '"' + } + } + end, + MissQuote2 = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = "'" + } + } + end, + MissEscX = function (pos) + PushError { + type = 'MISS_ESC_X', + start = pos-2, + finish = pos+1, + } + end, + MissTL = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = '{', + } + } + end, + MissTR = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = '}', + } + } + end, + MissBR = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = ']', + } + } + end, + MissPL = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = '(', + } + } + end, + MissPR = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = ')', + } + } + end, + ErrEsc = function (pos) + PushError { + type = 'ERR_ESC', + start = pos-1, + finish = pos, + } + end, + MustX16 = function (pos, str) + PushError { + type = 'MUST_X16', + start = pos, + finish = pos + #str - 1, + } + end, + MissAssign = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = '=', + } + } + end, + MissTableSep = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = ',' + } + } + end, + MissField = function (pos) + PushError { + type = 'MISS_FIELD', + start = pos, + finish = pos, + } + end, + MissMethod = function (pos) + PushError { + type = 'MISS_METHOD', + start = pos, + finish = pos, + } + end, + MissLabel = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = '::', + } + } + end, + MissEnd = function (pos) + State.MissEndErr = PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = 'end', + } + } + return pos, pos + end, + MissDo = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = 'do', + } + } + return pos, pos + end, + MissComma = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = ',', + } + } + end, + MissIn = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = 'in', + } + } + return pos, pos + end, + MissUntil = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = 'until', + } + } + return pos, pos + end, + MissThen = function (pos) + PushError { + type = 'MISS_SYMBOL', + start = pos, + finish = pos, + info = { + symbol = 'then', + } + } + return pos, pos + end, + MissName = function (pos) + PushError { + type = 'MISS_NAME', + start = pos, + finish = pos, + } + end, + ExpInAction = function (start, exp, finish) + PushError { + type = 'EXP_IN_ACTION', + start = start, + finish = finish - 1, + } + -- 当exp为nil时,不能返回任何值,否则会产生带洞的actionlist + if exp then + return exp + else + return + end + end, + MissIf = function (start, block) + PushError { + type = 'MISS_SYMBOL', + start = start, + finish = start, + info = { + symbol = 'if', + } + } + return block + end, + MissGT = function (start) + PushError { + type = 'MISS_SYMBOL', + start = start, + finish = start, + info = { + symbol = '>' + } + } + end, + ErrAssign = function (start, finish) + PushError { + type = 'ERR_ASSIGN_AS_EQ', + start = start, + finish = finish - 1, + fix = { + title = 'FIX_ASSIGN_AS_EQ', + { + start = start, + finish = finish - 1, + text = '=', + } + } + } + end, + ErrEQ = function (start, finish) + PushError { + type = 'ERR_EQ_AS_ASSIGN', + start = start, + finish = finish - 1, + fix = { + title = 'FIX_EQ_AS_ASSIGN', + { + start = start, + finish = finish - 1, + text = '==', + } + } + } + return '==' + end, + ErrUEQ = function (start, finish) + PushError { + type = 'ERR_UEQ', + start = start, + finish = finish - 1, + fix = { + title = 'FIX_UEQ', + { + start = start, + finish = finish - 1, + text = '~=', + } + } + } + return '==' + end, + ErrThen = function (start, finish) + PushError { + type = 'ERR_THEN_AS_DO', + start = start, + finish = finish - 1, + fix = { + title = 'FIX_THEN_AS_DO', + { + start = start, + finish = finish - 1, + text = 'then', + } + } + } + return start, finish + end, + ErrDo = function (start, finish) + PushError { + type = 'ERR_DO_AS_THEN', + start = start, + finish = finish - 1, + fix = { + title = 'FIX_DO_AS_THEN', + { + start = start, + finish = finish - 1, + text = 'do', + } + } + } + return start, finish + end, +} + +local function init(state) + State = state + PushError = state.pushError + PushDiag = state.pushDiag + PushComment = state.pushComment +end + +local function close() + State = nil + PushError = function (...) end + PushDiag = function (...) end + PushComment = function (...) end +end + +return { + defs = Defs, + init = init, + close = close, +} |