diff options
Diffstat (limited to 'script/parser')
-rw-r--r-- | script/parser/guide.lua | 203 | ||||
-rw-r--r-- | script/parser/luadoc.lua | 457 | ||||
-rw-r--r-- | script/parser/newparser.lua | 10 |
3 files changed, 353 insertions, 317 deletions
diff --git a/script/parser/guide.lua b/script/parser/guide.lua index e7b19bd3..2de1ac22 100644 --- a/script/parser/guide.lua +++ b/script/parser/guide.lua @@ -1,66 +1,59 @@ local error = error local type = type ----@class parser.guide.object ----@field bindDocs parser.guide.object[] ----@field bindGroup parser.guide.object[] ----@field bindSources parser.guide.object[] ----@field value parser.guide.object ----@field parent parser.guide.object +---@class parser.object +---@field bindDocs parser.object[] +---@field bindGroup parser.object[] +---@field bindSources parser.object[] +---@field value parser.object +---@field parent parser.object ---@field type string ---@field special string ---@field tag string ----@field args parser.guide.object[] ----@field locals parser.guide.object[] ----@field returns parser.guide.object[] +---@field args parser.object[] +---@field locals parser.object[] +---@field returns parser.object[] ---@field uri uri ---@field start integer ---@field finish integer ---@field effect integer ---@field attrs string[] ----@field specials parser.guide.object[] ----@field labels parser.guide.object[] ----@field node parser.guide.object +---@field specials parser.object[] +---@field labels parser.object[] +---@field node parser.object ---@field dummy boolean ----@field field parser.guide.object ----@field method parser.guide.object ----@field index parser.guide.object ----@field extends parser.guide.object[] ----@field types parser.guide.object[] ----@field fields parser.guide.object[] ----@field typeGeneric table<integer, parser.guide.object[]> ----@field tkey parser.guide.object ----@field tvalue parser.guide.object +---@field field parser.object +---@field method parser.object +---@field index parser.object +---@field extends parser.object[]|parser.object +---@field types parser.object[] +---@field fields parser.object[] +---@field tkey parser.object +---@field tvalue parser.object ---@field tindex integer ----@field op parser.guide.object ----@field next parser.guide.object ----@field docParam parser.guide.object +---@field op parser.object +---@field next parser.object +---@field docParam parser.object ---@field sindex integer ----@field name parser.guide.object ----@field call parser.guide.object ----@field closure parser.guide.object ----@field proto parser.guide.object ----@field exp parser.guide.object ----@field isGeneric boolean ----@field alias parser.guide.object ----@field class parser.guide.object ----@field vararg parser.guide.object ----@field param parser.guide.object ----@field overload parser.guide.object +---@field name parser.object +---@field call parser.object +---@field closure parser.object +---@field proto parser.object +---@field exp parser.object +---@field alias parser.object +---@field class parser.object +---@field vararg parser.object +---@field param parser.object +---@field overload parser.object ---@field docParamMap table<string, integer> ---@field upvalues table<string, string[]> ----@field ref parser.guide.object[] +---@field ref parser.object[] ---@field returnIndex integer ----@field docs parser.guide.object[] +---@field docs parser.object[] ---@field state table ---@field comment table ---@field optional boolean ----@field _root parser.guide.object ----@field _noders noders ----@field _mnode parser.guide.object ----@field _noded boolean ----@field _initedNoders boolean ----@field _compiledGlobals boolean +---@field _root parser.object ---@class guide ---@field debugMode boolean @@ -125,8 +118,9 @@ local childMap = { ['unary'] = {1}, ['doc'] = {'#'}, - ['doc.class'] = {'class', '#extends', 'comment'}, + ['doc.class'] = {'class', '#extends', '#signs', 'comment'}, ['doc.type'] = {'#types', 'name', 'comment'}, + ['doc.type.name'] = {'#signs'}, ['doc.alias'] = {'alias', 'extends', 'comment'}, ['doc.param'] = {'param', 'extends', 'comment'}, ['doc.return'] = {'#returns', 'comment'}, @@ -135,19 +129,18 @@ local childMap = { ['doc.generic.object'] = {'generic', 'extends', 'comment'}, ['doc.vararg'] = {'vararg', 'comment'}, ['doc.type.array'] = {'node'}, - ['doc.type.table'] = {'tkey', 'tvalue', 'comment'}, ['doc.type.function'] = {'#args', '#returns', 'comment'}, - ['doc.type.ltable'] = {'#fields', 'comment'}, + ['doc.type.table'] = {'#fields', 'comment'}, ['doc.type.literal'] = {'node'}, ['doc.type.arg'] = {'name', 'extends'}, - ['doc.type.field'] = {'extends'}, + ['doc.type.field'] = {'name', 'extends'}, ['doc.overload'] = {'overload', 'comment'}, ['doc.see'] = {'name', 'field'}, ['doc.version'] = {'#versions'}, ['doc.diagnostic'] = {'#names'}, } ----@type table<string, fun(obj: parser.guide.object, list: parser.guide.object[])> +---@type table<string, fun(obj: parser.object, list: parser.object[])> local compiledChildMap = setmetatable({}, {__index = function (self, name) local defs = childMap[name] if not defs then @@ -227,7 +220,7 @@ local function formatNumber(n) end --- 是否是字面量 ----@param obj parser.guide.object +---@param obj parser.object ---@return boolean function m.isLiteral(obj) local tp = obj.type @@ -238,10 +231,14 @@ function m.isLiteral(obj) or tp == 'integer' or tp == 'table' or tp == 'function' + or tp == 'doc.type.function' + or tp == 'doc.type.table' + or tp == 'doc.type.string' + or tp == 'doc.type.integer' end --- 获取字面量 ----@param obj parser.guide.object +---@param obj parser.object ---@return any function m.getLiteral(obj) local tp = obj.type @@ -258,8 +255,8 @@ function m.getLiteral(obj) end --- 寻找父函数 ----@param obj parser.guide.object ----@return parser.guide.object +---@param obj parser.object +---@return parser.object function m.getParentFunction(obj) for _ = 1, 10000 do obj = obj.parent @@ -275,8 +272,8 @@ function m.getParentFunction(obj) end --- 寻找所在区块 ----@param obj parser.guide.object ----@return parser.guide.object +---@param obj parser.object +---@return parser.object function m.getBlock(obj) for _ = 1, 10000 do if not obj then @@ -304,8 +301,8 @@ function m.getBlock(obj) end --- 寻找所在父区块 ----@param obj parser.guide.object ----@return parser.guide.object +---@param obj parser.object +---@return parser.object function m.getParentBlock(obj) for _ = 1, 10000 do obj = obj.parent @@ -321,8 +318,8 @@ function m.getParentBlock(obj) end --- 寻找所在可break的父区块 ----@param obj parser.guide.object ----@return parser.guide.object +---@param obj parser.object +---@return parser.object function m.getBreakBlock(obj) for _ = 1, 10000 do obj = obj.parent @@ -341,8 +338,8 @@ function m.getBreakBlock(obj) end --- 寻找doc的主体 ----@param obj parser.guide.object ----@return parser.guide.object +---@param obj parser.object +---@return parser.object function m.getDocState(obj) for _ = 1, 10000 do local parent = obj.parent @@ -358,8 +355,8 @@ function m.getDocState(obj) end --- 寻找所在父类型 ----@param obj parser.guide.object ----@return parser.guide.object +---@param obj parser.object +---@return parser.object function m.getParentType(obj, want) for _ = 1, 10000 do obj = obj.parent @@ -374,8 +371,8 @@ function m.getParentType(obj, want) end --- 寻找根区块 ----@param obj parser.guide.object ----@return parser.guide.object +---@param obj parser.object +---@return parser.object function m.getRoot(obj) local source = obj if source._root then @@ -399,7 +396,7 @@ function m.getRoot(obj) error('guide.getRoot overstack') end ----@param obj parser.guide.object +---@param obj parser.object ---@return uri function m.getUri(obj) if obj.uri then @@ -653,10 +650,10 @@ function m.eachSourceBetween(ast, start, finish, callback) end local function getSourceTypeCache(ast) - local cache = ast.typeCache + local cache = ast._typeCache if not cache then cache = {} - ast.typeCache = cache + ast._typeCache = cache m.eachSource(ast, function (source) local tp = source.type if not tp then @@ -699,10 +696,10 @@ end --- 遍历所有的source function m.eachSource(ast, callback) - local cache = ast.eachCache + local cache = ast._eachCache if not cache then cache = { ast } - ast.eachCache = cache + ast._eachCache = cache local mark = {} local index = 1 while true do @@ -817,20 +814,23 @@ function m.getLineRange(state, row) end local isSetMap = { - ['setglobal'] = true, - ['local'] = true, - ['setlocal'] = true, - ['setfield'] = true, - ['setmethod'] = true, - ['setindex'] = true, - ['tablefield'] = true, - ['tableindex'] = true, - ['tableexp'] = true, - ['doc.class.name'] = true, - ['doc.alias.name'] = true, - ['doc.field.name'] = true, - ['doc.field'] = true, - ['doc.type.field'] = true, + ['setglobal'] = true, + ['local'] = true, + ['setlocal'] = true, + ['setfield'] = true, + ['setmethod'] = true, + ['setindex'] = true, + ['tablefield'] = true, + ['tableindex'] = true, + ['tableexp'] = true, + ['label'] = true, + ['doc.class'] = true, + ['doc.alias'] = true, + ['doc.field'] = true, + ['doc.class.name'] = true, + ['doc.alias.name'] = true, + ['doc.field.name'] = true, + ['doc.type.field'] = true, } function m.isSet(source) local tp = source.type @@ -890,17 +890,17 @@ function m.getKeyNameOfLiteral(obj) elseif tp == 'number' then local n = obj[1] if n then - return formatNumber(obj[1]) + return obj[1] end elseif tp == 'integer' then local n = obj[1] if n then - return formatNumber(obj[1]) + return obj[1] end elseif tp == 'boolean' then local b = obj[1] if b then - return tostring(b) + return b end end end @@ -933,7 +933,7 @@ function m.getKeyName(obj) or tp == 'tableindex' then return m.getKeyNameOfLiteral(obj.index) elseif tp == 'tableexp' then - return tostring(obj.tindex) + return obj.tindex elseif tp == 'field' or tp == 'method' or tp == 'doc.see.field' then @@ -943,11 +943,11 @@ function m.getKeyName(obj) elseif tp == 'doc.alias' then return obj.alias[1] elseif tp == 'doc.field' then - return tostring(obj.field[1]) + return obj.field[1] elseif tp == 'doc.field.name' then - return tostring(obj[1]) + return obj[1] elseif tp == 'doc.type.field' then - return tostring(obj.name[1]) + return obj.name[1] elseif tp == 'dummy' then return obj[1] end @@ -1109,7 +1109,7 @@ function m.getPath(a, b, sameFunction) end ---是否是全局变量(包括 _G.XXX 形式) ----@param source parser.guide.object +---@param source parser.object ---@return boolean function m.isGlobal(source) if source._isGlobal ~= nil then @@ -1187,4 +1187,25 @@ function m.isOOP(source) return false end +local basicTypeMap = { + ['unknown'] = true, + ['any'] = true, + ['true'] = true, + ['false'] = true, + ['nil'] = true, + ['boolean'] = true, + ['number'] = true, + ['string'] = true, + ['table'] = true, + ['function'] = true, + ['thread'] = true, + ['userdata'] = true, +} + +---@param str string +---@return boolean +function m.isBasicType(str) + return basicTypeMap[str] == true +end + return m diff --git a/script/parser/luadoc.lua b/script/parser/luadoc.lua index d15fd95a..4dbece5a 100644 --- a/script/parser/luadoc.lua +++ b/script/parser/luadoc.lua @@ -120,6 +120,10 @@ Symbol <- ({} { end, }) +---@class parser.object +---@field literal boolean +---@field signs parser.object[] + local function trim(str) return str:match '^%s*(%S+)%s*$' end @@ -189,14 +193,14 @@ local function parseName(tp, parent) return nil end nextToken() - local class = { + local name = { type = tp, start = getStart(), finish = getFinish(), parent = parent, [1] = nameText, } - return class + return name end local function nextSymbolOrError(symbol) @@ -220,25 +224,138 @@ local function parseIndexField(tp, parent) return nil end nextToken() - local class = { - type = tp, - start = getStart(), - finish = getFinish(), - parent = parent, + local start = getFinish() - 1 + local indexTP, index = peekToken() + if indexTP == 'name' then + local field = parseType(parent) + nextSymbolOrError ']' + return field + else + nextToken() + local class = { + type = tp, + start = start, + finish = getFinish(), + parent = parent, + } + class[1] = index + nextSymbolOrError ']' + class.finish = getFinish() + return class + end +end + +local function parseTable(parent) + if not checkToken('symbol', '{', 1) then + return nil + end + nextToken() + local typeUnit = { + type = 'doc.type.table', + start = getStart(), + parent = parent, + fields = {}, } - local indexTP, index = nextToken() - if indexTP ~= 'integer' - and indexTP ~= 'string' then + + while true do + if checkToken('symbol', '}', 1) then + nextToken() + break + end + local field = { + type = 'doc.type.field', + parent = typeUnit, + } + + do + field.name = parseName('doc.field.name', field) + or parseIndexField('doc.field.name', field) + if not field.name then + pushWarning { + type = 'LUADOC_MISS_FIELD_NAME', + start = getFinish(), + finish = getFinish(), + } + break + end + if not field.start then + field.start = field.name.start + end + if checkToken('symbol', '?', 1) then + nextToken() + field.optional = true + end + field.finish = getFinish() + if not nextSymbolOrError(':') then + break + end + field.extends = parseType(field) + if not field.extends then + break + end + field.finish = getFinish() + end + + typeUnit.fields[#typeUnit.fields+1] = field + if checkToken('symbol', ',', 1) then + nextToken() + else + nextSymbolOrError('}') + break + end + end + typeUnit.finish = getFinish() + return typeUnit +end + +local function parseSigns(parent, mode) + if not checkToken('symbol', '<', 1) then + return nil + end + nextToken() + local signs = {} + while true do + local sign + if mode == 'name' then + sign = parseName('doc.generic.name', parent) + if not sign then + pushWarning { + type = 'LUADOC_MISS_SIGN_NAME', + start = getFinish(), + finish = getFinish(), + } + break + end + elseif mode == 'type' then + sign = parseType(parent) + if not sign then + pushWarning { + type = 'LUADOC_MISS_TYPE_NAME', + start = getFinish(), + finish = getFinish(), + } + break + end + end + signs[#signs+1] = sign + if checkToken('symbol', ',', 1) then + nextToken() + else + break + end + end + if not checkToken('symbol', '>', 1) then pushWarning { - type = 'LUADOC_INDEX_MUST_INT', - start = getStart(), + type = 'LUADOC_MISS_SYMBOL', + start = getFinish(), finish = getFinish(), + symbol = { + symbol = '>', + } } end - class[1] = index - nextSymbolOrError ']' - class.finish = getFinish() - return class + nextToken() + return signs end local function parseClass(parent) @@ -258,6 +375,7 @@ local function parseClass(parent) end result.start = getStart() result.finish = getFinish() + result.signs = parseSigns(result, 'name') if not checkToken('symbol', ':', 1) then return result end @@ -267,6 +385,7 @@ local function parseClass(parent) while true do local extend = parseName('doc.extends.name', result) + or parseTable(result) if not extend then pushWarning { type = 'LUADOC_MISS_CLASS_EXTENDS_NAME', @@ -301,39 +420,6 @@ local function parseTypeUnitArray(parent, node) return result end -local function parseTypeUnitTable(parent, node) - if not checkToken('symbol', '<', 1) then - return nil - end - if not nextSymbolOrError('<') then - return nil - end - - local result = { - type = 'doc.type.table', - start = node.start, - node = node, - parent = parent, - } - - local key = parseType(result) - if not key or not nextSymbolOrError(',') then - return nil - end - local value = parseType(result) - if not value then - return nil - end - nextSymbolOrError('>') - - node.parent = result - result.finish = getFinish() - result.tkey = key - result.tvalue = value - - return result -end - local function parseDots(tp, parent) if not checkToken('symbol', '...', 1) then return @@ -349,9 +435,14 @@ local function parseDots(tp, parent) return dots end -local function parseTypeUnitFunction() +local function parseTypeUnitFunction(parent) + if not checkToken('name', 'fun', 1) then + return nil + end + nextToken() local typeUnit = { type = 'doc.type.function', + parent = parent, start = getStart(), args = {}, returns = {}, @@ -422,74 +513,17 @@ local function parseTypeUnitFunction() return typeUnit end -local function parseTypeUnitLiteralTable() - local typeUnit = { - type = 'doc.type.ltable', - start = getStart(), - fields = {}, - } - - while true do - if checkToken('symbol', '}', 1) then - nextToken() - break - end - local field = { - type = 'doc.type.field', - parent = typeUnit, - } - - do - field.name = parseName('doc.field.name', field) - or parseIndexField('doc.field.name', field) - if not field.name then - pushWarning { - type = 'LUADOC_MISS_FIELD_NAME', - start = getFinish(), - finish = getFinish(), - } - break - end - if not field.start then - field.start = field.name.start - end - if checkToken('symbol', '?', 1) then - nextToken() - field.optional = true - end - field.finish = getFinish() - if not nextSymbolOrError(':') then - break - end - field.extends = parseType(field) - if not field.extends then - break - end - field.finish = getFinish() - end - - typeUnit.fields[#typeUnit.fields+1] = field - if checkToken('symbol', ',', 1) then - nextToken() - else - nextSymbolOrError('}') - break - end - end - typeUnit.finish = getFinish() - return typeUnit -end - local parseTypeUnit -local function parseDocFunction(parent, content) +local function parseFunction(parent) + local _, content = peekToken() if content == 'async' then + nextToken() local pos = getStart() local tp, cont = peekToken() if tp == 'name' then if cont == 'fun' then - nextToken() - local func = parseTypeUnit(parent, cont) + local func = parseTypeUnit(parent) if func then func.async = true func.asyncPos = pos @@ -499,32 +533,74 @@ local function parseDocFunction(parent, content) end end if content == 'fun' then - return parseTypeUnitFunction() + return parseTypeUnitFunction(parent) end end -function parseTypeUnit(parent, content) - local result = parseDocFunction(parent, content) - if not result then - if content == '{' then - result = parseTypeUnitLiteralTable() +local function parseString(parent) + local tp, content = peekToken() + if not tp or tp ~= 'string' then + return nil + end + + nextToken() + -- compatibility + if content:sub(1, 1) == '"' + or content:sub(1, 1) == "'" then + if content:sub(1, 1) == content:sub(-1, -1) then + content = content:sub(2, -2) end end - if not result then - result = { - type = 'doc.type.name', - start = getStart(), - finish = getFinish(), - [1] = content, - } + local str = { + type = 'doc.type.string', + start = getStart(), + finish = getFinish(), + parent = parent, + [1] = content, + } + return str +end + +local function parseInteger(parent) + local tp, content = peekToken() + if not tp or tp ~= 'integer' then + return nil end + + nextToken() + local integer = { + type = 'doc.type.integer', + start = getStart(), + finish = getFinish(), + parent = parent, + [1] = content, + } + return integer +end + +function parseTypeUnit(parent) + local result = parseFunction(parent) + or parseTable(parent) + or parseString(parent) + or parseInteger(parent) + or parseDots('doc.type.name', parent) if not result then - return nil + local literal = checkToken('symbol', '`', 1) + if literal then + nextToken() + end + result = parseName('doc.type.name', parent) + if not result then + return nil + end + if literal then + result.literal = true + nextSymbolOrError '`' + end + result.signs = parseSigns(result, 'type') end - result.parent = parent while true do local newResult = parseTypeUnitArray(parent, result) - or parseTypeUnitTable(parent, result) if not newResult then break end @@ -534,34 +610,23 @@ function parseTypeUnit(parent, content) end local function parseResume(parent) - local result = { - type = 'doc.resume', - parent = parent, - } - + local default, additional if checkToken('symbol', '>', 1) then nextToken() - result.default = true + default = true end if checkToken('symbol', '+', 1) then nextToken() - result.additional = true + additional = true end - local tp = peekToken() - if tp ~= 'string' then - pushWarning { - type = 'LUADOC_MISS_STRING', - start = getFinish(), - finish = getFinish(), - } - return nil + local result = parseTypeUnit(parent) + if result then + result.default = default + result.additional = additional end - local _, str = nextToken() - result[1] = str - result.start = getStart() - result.finish = getFinish() + return result end @@ -572,77 +637,16 @@ function parseType(parent) types = {}, } while true do - local tp, content = peekToken() - if not tp then + local typeUnit = parseTypeUnit(result) + if not typeUnit then break end - -- 处理 `T` 的情况 - local typeLiteral = nil - if tp == 'symbol' and content == '`' then - nextToken() - if not checkToken('symbol', '`', 2) then - break - end - tp, content = peekToken() - if not tp then - break - end - -- TypeLiteral,指代类型的字面值。比如,对于类 Cat 来说,它的 TypeLiteral 是 "Cat" - typeLiteral = true + result.types[#result.types+1] = typeUnit + if not result.start then + result.start = typeUnit.start end - if tp == 'name' then - nextToken() - local typeUnit = parseTypeUnit(result, content) - if not typeUnit then - break - end - if typeLiteral then - nextToken() - typeUnit.literal = true - end - result.types[#result.types+1] = typeUnit - if not result.start then - result.start = typeUnit.start - end - elseif tp == 'string' then - nextToken() - local typeEnum = { - type = 'doc.type.enum', - start = getStart(), - finish = getFinish(), - parent = result, - [1] = content, - } - result.types[#result.types+1] = typeEnum - if not result.start then - result.start = typeEnum.start - end - elseif tp == 'symbol' and content == '{' then - nextToken() - local typeUnit = parseTypeUnit(result, content) - if not typeUnit then - break - end - result.types[#result.types+1] = typeUnit - if not result.start then - result.start = typeUnit.start - end - elseif tp == 'symbol' and content == '...' then - nextToken() - local vararg = { - type = 'doc.type.name', - start = getStart(), - finish = getFinish(), - parent = result, - [1] = content, - } - result.types[#result.types+1] = vararg - if not result.start then - result.start = vararg.start - end - end if not checkToken('symbol', '|', 1) then break end @@ -741,6 +745,7 @@ local function parseAlias() return nil end result.start = getStart() + result.signs = parseSigns(result, 'name') result.extends = parseType(result) if not result.extends then pushWarning { @@ -933,11 +938,10 @@ local function parseOverload() } return nil end - nextToken() local result = { type = 'doc.overload', } - result.overload = parseDocFunction(result, name) + result.overload = parseFunction(result) if not result.overload then return nil end @@ -1261,19 +1265,30 @@ end local function bindGeneric(binded) local generics = {} for _, doc in ipairs(binded) do - if doc.type == 'doc.generic' then + if doc.type == 'doc.generic' then for _, obj in ipairs(doc.generics) do local name = obj.generic[1] - generics[name] = {} + generics[name] = true end - elseif doc.type == 'doc.param' - or doc.type == 'doc.return' - or doc.type == 'doc.type' then + end + if doc.type == 'doc.class' + or doc.type == 'doc.alias' then + if doc.signs then + for _, sign in ipairs(doc.signs) do + local name = sign[1] + generics[name] = true + end + end + end + if doc.type == 'doc.param' + or doc.type == 'doc.return' + or doc.type == 'doc.type' + or doc.type == 'doc.class' + or doc.type == 'doc.alias' then guide.eachSourceType(doc, 'doc.type.name', function (src) local name = src[1] if generics[name] then - generics[name][#generics[name]+1] = src - src.typeGeneric = generics + src.type = 'doc.generic.name' end end) end diff --git a/script/parser/newparser.lua b/script/parser/newparser.lua index e4884212..66df7046 100644 --- a/script/parser/newparser.lua +++ b/script/parser/newparser.lua @@ -2588,9 +2588,9 @@ local function skipSeps() end end ----@return parser.guide.object first ----@return parser.guide.object second ----@return parser.guide.object[] rest +---@return parser.object first +---@return parser.object second +---@return parser.object[] rest local function parseSetValues() skipSpace() local first = parseExp() @@ -2645,8 +2645,8 @@ local function pushActionIntoCurrentChunk(action) end end ----@return parser.guide.object second ----@return parser.guide.object[] rest +---@return parser.object second +---@return parser.object[] rest local function parseVarTails(parser, isLocal) if Tokens[Index + 1] ~= ',' then return |