summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2020-10-19 20:50:18 +0800
committer最萌小汐 <sumneko@hotmail.com>2020-10-19 20:50:18 +0800
commitbb319039fdf41326f23ef0c7288123889434b0a8 (patch)
tree3fe4677dad3fd571fa216279a5992e4bf09b4bff
parentf90a6366f4a4212e23612f17bffa506ffe25e030 (diff)
downloadlua-language-server-bb319039fdf41326f23ef0c7288123889434b0a8.zip
更新parser
-rw-r--r--script-beta/parser/emmy.lua321
-rw-r--r--script-beta/parser/init.lua15
-rw-r--r--script-beta/parser/luadoc.lua684
3 files changed, 692 insertions, 328 deletions
diff --git a/script-beta/parser/emmy.lua b/script-beta/parser/emmy.lua
deleted file mode 100644
index 4c1e087a..00000000
--- a/script-beta/parser/emmy.lua
+++ /dev/null
@@ -1,321 +0,0 @@
-local State
-local pushError
-
-local grammar = [[
-EmmyLua <- ({} '---' EmmyBody {} ShortComment)
- -> EmmyLua
-EmmySp <- (!'---@' !'---' Comment / %s / %nl)*
-EmmyComments <- (EmmyComment (%nl EmmyComMulti / %nl EmmyComSingle)*)
-EmmyComment <- EmmySp %s* {(!%nl .)*}
-EmmyComMulti <- EmmySp '---|' {} -> en {(!%nl .)*}
-EmmyComSingle <- EmmySp '---' !'@' %s* {} -> ' ' {(!%nl .)*}
-EmmyBody <- '@class' %s+ EmmyClass -> EmmyClass
- / '@type' %s+ EmmyType -> EmmyType
- / '@alias' %s+ EmmyAlias -> EmmyAlias
- / '@param' %s+ EmmyParam -> EmmyParam
- / '@return' %s+ EmmyReturn -> EmmyReturn
- / '@field' %s+ EmmyField -> EmmyField
- / '@generic' %s+ EmmyGeneric -> EmmyGeneric
- / '@vararg' %s+ EmmyVararg -> EmmyVararg
- / '@language' %s+ EmmyLanguage -> EmmyLanguage
- / '@see' %s+ EmmySee -> EmmySee
- / '@overload' %s+ EmmyOverLoad -> EmmyOverLoad
- / %s* EmmyComments -> EmmyComment
- / EmmyIncomplete
-
-EmmyName <- ({} {[a-zA-Z_] [a-zA-Z0-9_]*})
- -> EmmyName
-MustEmmyName <- EmmyName / DirtyEmmyName
-DirtyEmmyName <- {} -> DirtyEmmyName
-EmmyLongName <- ({} {(!%nl .)+})
- -> EmmyName
-EmmyIncomplete <- MustEmmyName
- -> EmmyIncomplete
-
-EmmyClass <- (MustEmmyName EmmyParentClass?)
-EmmyParentClass <- %s* {} ':' %s* MustEmmyName
-
-EmmyType <- EmmyTypeUnits EmmyTypeEnums
-EmmyTypeUnits <- {|
- EmmyTypeUnit?
- (%s* '|' %s* !String EmmyTypeUnit)*
- |}
-EmmyTypeEnums <- {| EmmyTypeEnum* |}
-EmmyTypeUnit <- EmmyFunctionType
- / EmmyTableType
- / EmmyArrayType
- / EmmyCommonType
-EmmyCommonType <- EmmyName
- -> EmmyCommonType
-EmmyTypeEnum <- %s* (%nl %s* '---')? '|'? EmmyEnum
- -> EmmyTypeEnum
-EmmyEnum <- %s* {'>'?} %s* String (EmmyEnumComment / (!%nl !'|' .)*)
-EmmyEnumComment <- %s* '#' %s* {(!%nl .)*}
-
-EmmyAlias <- MustEmmyName %s* EmmyType EmmyTypeEnum*
-
-EmmyParam <- MustEmmyName %s* EmmyType %s* EmmyOption %s* EmmyTypeEnum*
-EmmyOption <- Table?
- -> EmmyOption
-
-EmmyReturn <- {} %nil {} Table -> EmmyOption
- / {} EmmyType {} EmmyOption
-
-EmmyField <- (EmmyFieldAccess MustEmmyName %s* EmmyType)
-EmmyFieldAccess <- ({'public'} Cut %s*)
- / ({'protected'} Cut %s*)
- / ({'private'} Cut %s*)
- / {} -> 'public'
-
-EmmyGeneric <- EmmyGenericBlock
- (%s* ',' %s* EmmyGenericBlock)*
-EmmyGenericBlock<- (MustEmmyName %s* (':' %s* EmmyType)?)
- -> EmmyGenericBlock
-
-EmmyVararg <- EmmyType
-
-EmmyLanguage <- MustEmmyName
-
-EmmyArrayType <- ({} MustEmmyName -> EmmyCommonType {} '[' DirtyBR)
- -> EmmyArrayType
- / ({} PL EmmyCommonType DirtyPR '[' DirtyBR)
- -> EmmyArrayType
-
-EmmyTableType <- ({} 'table' Cut '<' %s* EmmyType %s* ',' %s* EmmyType %s* '>' {})
- -> EmmyTableType
-
-EmmyFunctionType<- ({} 'fun' Cut %s* EmmyFunctionArgs %s* EmmyFunctionRtns {})
- -> EmmyFunctionType
-EmmyFunctionArgs<- ('(' %s* EmmyFunctionArg %s* (',' %s* EmmyFunctionArg %s*)* DirtyPR)
- -> EmmyFunctionArgs
- / '(' %nil DirtyPR -> None
- / %nil
-EmmyFunctionRtns<- (':' %s* EmmyType (%s* ',' %s* EmmyType)*)
- -> EmmyFunctionRtns
- / %nil
-EmmyFunctionArg <- MustEmmyName %s* ':' %s* EmmyType
-
-EmmySee <- {} MustEmmyName %s* '#' %s* MustEmmyName {}
-EmmyOverLoad <- EmmyFunctionType
-]]
-
-local ast = {
- EmmyLua = function (start, emmy, finish)
- emmy.start = start
- emmy.finish = finish - 1
- State.emmy[#State.emmy+1] = emmy
- end,
- EmmyName = function (start, str)
- return {
- type = 'name',
- start = start,
- finish = start + #str - 1,
- [1] = str,
- }
- end,
- DirtyEmmyName = function (pos)
- pushError {
- type = 'MISS_NAME',
- level = 'warning',
- start = pos,
- finish = pos,
- }
- return {
- type = 'emmyName',
- start = pos-1,
- finish = pos-1,
- [1] = ''
- }
- end,
- EmmyClass = function (class, startPos, extends)
- if extends and extends[1] == '' then
- extends.start = startPos
- end
- return {
- type = 'class',
- class = class,
- extends = extends,
- }
- end,
- EmmyType = function (types, enums)
- local result = {
- type = 'type',
- types = types,
- enums = enums,
- }
- return result
- end,
- EmmyCommonType = function (name)
- return {
- type = 'common',
- start = name.start,
- finish = name.finish,
- name = name,
- }
- end,
- EmmyArrayType = function (start, emmy, _, finish)
- emmy.type = 'emmyArrayType'
- emmy.start = start
- emmy.finish = finish - 1
- return emmy
- end,
- EmmyTableType = function (start, keyType, valueType, finish)
- return {
- type = 'emmyTableType',
- start = start,
- finish = finish - 1,
- [1] = keyType,
- [2] = valueType,
- }
- end,
- EmmyFunctionType = function (start, args, returns, finish)
- local result = {
- start = start,
- finish = finish - 1,
- type = 'emmyFunctionType',
- args = args,
- returns = returns,
- }
- return result
- end,
- EmmyFunctionRtns = function (...)
- return {...}
- end,
- EmmyFunctionArgs = function (...)
- local args = {...}
- args[#args] = nil
- return args
- end,
- EmmyAlias = function (name, emmyName, ...)
- return {
- type = 'emmyAlias',
- start = name.start,
- finish = emmyName.finish,
- name,
- emmyName,
- ...
- }
- end,
- EmmyParam = function (argName, emmyName, option, ...)
- local emmy = {
- type = 'emmyParam',
- option = option,
- argName,
- emmyName,
- ...
- }
- emmy.start = emmy[1].start
- emmy.finish = emmy[#emmy].finish
- return emmy
- end,
- EmmyReturn = function (start, type, finish, option)
- local emmy = {
- type = 'emmyReturn',
- option = option,
- start = start,
- finish = finish - 1,
- [1] = type,
- }
- return emmy
- end,
- EmmyField = function (access, fieldName, ...)
- local obj = {
- type = 'emmyField',
- access, fieldName,
- ...
- }
- obj.start = obj[2].start
- obj.finish = obj[3].finish
- return obj
- end,
- EmmyGenericBlock = function (genericName, parentName)
- return {
- start = genericName.start,
- finish = parentName and parentName.finish or genericName.finish,
- genericName,
- parentName,
- }
- end,
- EmmyGeneric = function (...)
- local emmy = {
- type = 'emmyGeneric',
- ...
- }
- emmy.start = emmy[1].start
- emmy.finish = emmy[#emmy].finish
- return emmy
- end,
- EmmyVararg = function (typeName)
- return {
- type = 'emmyVararg',
- start = typeName.start,
- finish = typeName.finish,
- typeName,
- }
- end,
- EmmyLanguage = function (language)
- return {
- type = 'emmyLanguage',
- start = language.start,
- finish = language.finish,
- language,
- }
- end,
- EmmySee = function (start, className, methodName, finish)
- return {
- type = 'emmySee',
- start = start,
- finish = finish - 1,
- className, methodName
- }
- end,
- EmmyOverLoad = function (EmmyFunctionType)
- EmmyFunctionType.type = 'emmyOverLoad'
- return EmmyFunctionType
- end,
- EmmyIncomplete = function (emmyName)
- emmyName.type = 'emmyIncomplete'
- return emmyName
- end,
- EmmyComment = function (...)
- return {
- type = 'emmyComment',
- [1] = table.concat({...}),
- }
- end,
- EmmyOption = function (options)
- if not options or options == '' then
- return nil
- end
- local option = {}
- for _, pair in ipairs(options) do
- if pair.type == 'pair' then
- local key = pair[1]
- local value = pair[2]
- if key.type == 'name' then
- option[key[1]] = value[1]
- end
- end
- end
- return option
- end,
- EmmyTypeEnum = function (default, enum, comment)
- enum.type = 'enum'
- if default ~= '' then
- enum.default = true
- end
- enum.comment = comment
- return enum
- end,
-}
-
-local function init(state)
- State = state
- pushError = state.pushError
-end
-
-return {
- grammar = grammar,
- ast = ast,
- init = init,
-}
diff --git a/script-beta/parser/init.lua b/script-beta/parser/init.lua
index 5eeb0da2..ba40d145 100644
--- a/script-beta/parser/init.lua
+++ b/script-beta/parser/init.lua
@@ -1,11 +1,12 @@
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',
+ 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',
+ luadoc = require 'parser.luadoc',
}
return api
diff --git a/script-beta/parser/luadoc.lua b/script-beta/parser/luadoc.lua
new file mode 100644
index 00000000..ebab1ee9
--- /dev/null
+++ b/script-beta/parser/luadoc.lua
@@ -0,0 +1,684 @@
+local m = require 'lpeglabel'
+local re = require 'parser.relabel'
+
+local TokenTypes, TokenStarts, TokenFinishs, TokenContents
+local Ci, Offset, pushError
+local parseType
+local Parser = re.compile([[
+Main <- (Token / Sp)*
+Sp <- %s+
+X16 <- [a-fA-F0-9]
+Word <- [a-zA-Z0-9_]
+Token <- Name / String / Symbol
+Name <- ({} {[a-zA-Z_] [a-zA-Z0-9_.]*} {})
+ -> Name
+String <- ({} StringDef {})
+ -> String
+StringDef <- '"'
+ {~(Esc / !'"' .)*~} -> 1
+ ('"'?)
+ / "'"
+ {~(Esc / !"'" .)*~} -> 1
+ ("'"?)
+ / ('[' {:eq: '='* :} '['
+ {(!StringClose .)*} -> 1
+ (StringClose?))
+StringClose <- ']' =eq ']'
+Esc <- '\' -> ''
+ EChar
+EChar <- 'a' -> ea
+ / 'b' -> eb
+ / 'f' -> ef
+ / 'n' -> en
+ / 'r' -> er
+ / 't' -> et
+ / 'v' -> ev
+ / '\'
+ / '"'
+ / "'"
+ / %nl
+ / ('z' (%nl / %s)*) -> ''
+ / ('x' {X16 X16}) -> Char16
+ / ([0-9] [0-9]? [0-9]?) -> Char10
+ / ('u{' {Word*} '}') -> CharUtf8
+Symbol <- ({} {
+ ':'
+ / '|'
+ / ','
+ / '[]'
+ / '<'
+ / '>'
+ / '('
+ / ')'
+ } {})
+ -> Symbol
+]], {
+ s = m.S' \t',
+ ea = '\a',
+ eb = '\b',
+ ef = '\f',
+ en = '\n',
+ er = '\r',
+ et = '\t',
+ ev = '\v',
+ Char10 = function (char)
+ char = tonumber(char)
+ if not char or char < 0 or char > 255 then
+ return ''
+ end
+ return string.char(char)
+ end,
+ Char16 = function (char)
+ return string.char(tonumber(char, 16))
+ end,
+ CharUtf8 = function (char)
+ if #char == 0 then
+ return ''
+ end
+ local v = tonumber(char, 16)
+ if not v then
+ return ''
+ end
+ if v >= 0 and v <= 0x10FFFF then
+ return utf8.char(v)
+ end
+ return ''
+ end,
+ Name = function (start, content, finish)
+ Ci = Ci + 1
+ TokenTypes[Ci] = 'name'
+ TokenStarts[Ci] = start
+ TokenFinishs[Ci] = finish - 1
+ TokenContents[Ci] = content
+ end,
+ String = function (start, content, finish)
+ Ci = Ci + 1
+ TokenTypes[Ci] = 'string'
+ TokenStarts[Ci] = start
+ TokenFinishs[Ci] = finish - 1
+ TokenContents[Ci] = content
+ end,
+ Symbol = function (start, content, finish)
+ Ci = Ci + 1
+ TokenTypes[Ci] = 'symbol'
+ TokenStarts[Ci] = start
+ TokenFinishs[Ci] = finish - 1
+ TokenContents[Ci] = content
+ end,
+})
+
+local function parseTokens(text)
+ Ci = 0
+ TokenTypes = {}
+ TokenStarts = {}
+ TokenFinishs = {}
+ TokenContents = {}
+ Parser:match(text)
+ Ci = 0
+end
+
+local function peekToken()
+ return TokenTypes[Ci+1], TokenContents[Ci+1]
+end
+
+local function nextToken()
+ Ci = Ci + 1
+ if not TokenTypes[Ci] then
+ Ci = Ci - 1
+ return nil
+ end
+ return TokenTypes[Ci], TokenContents[Ci]
+end
+
+local function checkToken(tp, content, offset)
+ offset = offset or 0
+ return TokenTypes[Ci + offset] == tp
+ and TokenContents[Ci + offset] == content
+end
+
+local function getStart()
+ return TokenStarts[Ci] + Offset
+end
+
+local function getFinish()
+ return TokenFinishs[Ci] + Offset
+end
+
+local function try(callback)
+ local savePoint = Ci
+ -- rollback
+ local suc = callback()
+ if not suc then
+ Ci = savePoint
+ end
+ return suc
+end
+
+local function parseName(tp, parent)
+ local nameTp, nameText = peekToken()
+ if nameTp ~= 'name' then
+ return nil
+ end
+ nextToken()
+ local class = {
+ type = tp,
+ start = getStart(),
+ finish = getFinish(),
+ parent = parent,
+ [1] = nameText,
+ }
+ return class
+end
+
+local function parseClass(parent)
+ local result = {
+ type = 'doc.class',
+ parent = parent,
+ }
+ result.class = parseName('doc.class.name', result)
+ if not result.class then
+ pushError {
+ type = 'LUADOC_MISS_CLASS_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ result.start = getStart()
+ result.finish = getFinish()
+ if not peekToken() then
+ return result
+ end
+ nextToken()
+ if not checkToken('symbol', ':') then
+ pushError {
+ type = 'LUADOC_MISS_EXTENSION_SYMBOL',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return result
+ end
+ result.extends = parseName('doc.extends.name', result)
+ if not result.extends then
+ pushError {
+ type = 'LUADOC_MISS_EXTENDS_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return result
+ end
+ result.finish = getFinish()
+ return result
+end
+
+local function nextSymbolOrError(symbol)
+ if checkToken('symbol', symbol, 1) then
+ nextToken()
+ return true
+ end
+ pushError {
+ type = 'LUADOC_MISS_SYMBOL',
+ start = getFinish(),
+ finish = getFinish(),
+ info = {
+ symbol = symbol,
+ }
+ }
+ return false
+end
+
+local function parseTypeUnitTable()
+ local typeUnit = {
+ type = 'doc.type.table',
+ start = getStart(),
+ }
+ if not nextSymbolOrError('<') then
+ return nil
+ end
+ local key = parseType(typeUnit)
+ if not key or not nextSymbolOrError(',') then
+ return nil
+ end
+ local value = parseType(typeUnit)
+ if not value then
+ return nil
+ end
+ nextSymbolOrError('>')
+ typeUnit.key = key
+ typeUnit.value = value
+ typeUnit.finish = getFinish()
+ return typeUnit
+end
+
+local function parseTypeUnitFunction()
+ local typeUnit = {
+ type = 'doc.type.function',
+ start = getStart(),
+ args = {},
+ returns = {},
+ }
+ if not nextSymbolOrError('(') then
+ return nil
+ end
+ while true do
+ if checkToken('symbol', ')', 1) then
+ nextToken()
+ break
+ end
+ local arg = {
+ type = 'doc.type.arg',
+ parent = typeUnit,
+ }
+ arg.name = parseName('doc.type.name', arg)
+ if not arg.name then
+ pushError {
+ type = 'LUADOC_MISS_ARG_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ break
+ end
+ if not arg.start then
+ arg.start = arg.name.start
+ end
+ arg.finish = getFinish()
+ if not nextSymbolOrError(':') then
+ break
+ end
+ arg.extends = parseType(arg)
+ if not arg.extends then
+ break
+ end
+ arg.finish = getFinish()
+ typeUnit.args[#typeUnit.args+1] = arg
+ if checkToken('symbol', ',', 1) then
+ nextToken()
+ else
+ nextSymbolOrError(')')
+ break
+ end
+ end
+ if checkToken('symbol', ':', 1) then
+ nextToken()
+ while true do
+ local rtn = parseType(arg)
+ if not rtn then
+ break
+ end
+ typeUnit.returns[#typeUnit.returns+1] = rtn
+ if checkToken('symbol', ',', 1) then
+ nextToken()
+ else
+ break
+ end
+ end
+ end
+ typeUnit.finish = getFinish()
+ return typeUnit
+end
+
+local function parseTypeUnit(parent, content)
+ local typeUnit
+ if content == 'table' then
+ typeUnit = parseTypeUnitTable()
+ elseif content == 'fun' then
+ typeUnit = parseTypeUnitFunction()
+ else
+ typeUnit = {
+ type = 'doc.type.name',
+ start = getStart(),
+ finish = getFinish(),
+ [1] = content,
+ }
+ end
+ if not typeUnit then
+ return nil
+ end
+ typeUnit.parent = parent
+ if checkToken('symbol', '[]', 1) then
+ nextToken()
+ typeUnit.array = true
+ typeUnit.finish = getFinish()
+ end
+ return typeUnit
+end
+
+function parseType(parent)
+ if not peekToken() then
+ pushError {
+ type = 'LUADOC_MISS_TYPE_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ local result = {
+ type = 'doc.type',
+ parent = parent,
+ types = {},
+ enums = {},
+ }
+ while true do
+ local tp, content = peekToken()
+ if not tp then
+ break
+ end
+ if tp == 'name' 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 == 'string' then
+ nextToken()
+ local typeEnum = {
+ type = 'doc.type.enum',
+ start = getStart(),
+ finish = getFinish(),
+ parent = result,
+ [1] = content,
+ }
+ result.enums[#result.enums+1] = typeEnum
+ if not result.start then
+ result.start = typeEnum.start
+ end
+ end
+ if not checkToken('symbol', '|', 1) then
+ break
+ end
+ nextToken()
+ end
+ result.finish = getFinish()
+ if #result.types == 0 and #result.enums == 0 then
+ return nil
+ end
+ return result
+end
+
+local function parseAlias()
+ local result = {
+ type = 'doc.alias',
+ }
+ result.alias = parseName('doc.alias.name', result)
+ if not result.alias then
+ pushError {
+ type = 'LUADOC_MISS_ALIAS_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ result.start = getStart()
+ result.extends = parseType(result)
+ if not result.extends then
+ pushError {
+ type = 'LUADOC_MISS_ALIAS_EXTENDS',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ result.finish = getFinish()
+ return result
+end
+
+local function parseParam()
+ local result = {
+ type = 'doc.param',
+ }
+ result.param = parseName('doc.param.name', result)
+ if not result.param then
+ pushError {
+ type = 'LUADOC_MISS_PARAM_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ result.start = getStart()
+ result.extends = parseType(result)
+ if not result.extends then
+ pushError {
+ type = 'LUADOC_MISS_PARAM_EXTENDS',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ result.finish = getFinish()
+ return result
+end
+
+local function parseReturn()
+ local result = {
+ type = 'doc.return',
+ returns = {},
+ }
+ while true do
+ local docType = parseType(result)
+ if not docType then
+ break
+ end
+ if not result.start then
+ result.start = docType.start
+ end
+ result.returns[#result.returns+1] = docType
+ if not checkToken('symbol', ',', 1) then
+ break
+ end
+ nextToken()
+ end
+ if #result.returns == 0 then
+ return nil
+ end
+ result.finish = getFinish()
+ return result
+end
+
+local function parseField()
+ local result = {
+ type = 'doc.field',
+ }
+ try(function ()
+ local tp, value = nextToken()
+ if tp == 'name' then
+ if value == 'public'
+ or value == 'protected'
+ or value == 'private' then
+ result.visible = value
+ result.start = getStart()
+ return true
+ end
+ end
+ return false
+ end)
+ result.field = parseName('doc.field.name', result)
+ if not result.field then
+ pushError {
+ type = 'LUADOC_MISS_FIELD_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ if not result.start then
+ result.start = result.field.start
+ end
+ result.extends = parseType(result)
+ if not result.extends then
+ pushError {
+ type = 'LUADOC_MISS_FIELD_EXTENDS',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ result.finish = getFinish()
+ return result
+end
+
+local function parseGeneric()
+ local result = {
+ type = 'doc.generic',
+ generics = {},
+ }
+ while true do
+ local object = {
+ type = 'doc.generic.object',
+ parent = result,
+ }
+ object.generic = parseName('doc.generic.name', object)
+ if not object.generic then
+ pushError {
+ type = 'LUADOC_MISS_GENERIC_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ object.start = object.generic.start
+ if not result.start then
+ result.start = object.start
+ end
+ if checkToken('symbol', ':', 1) then
+ nextToken()
+ object.extends = parseName('doc.extends.name', object)
+ if not object.extends then
+ pushError {
+ type = 'LUADOC_MISS_EXTENDS_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ end
+ object.finish = getFinish()
+ result.generics[#result.generics+1] = object
+ if not checkToken('symbol', ',', 1) then
+ break
+ end
+ nextToken()
+ end
+ result.finish = getFinish()
+ return result
+end
+
+local function parseVararg()
+ local result = {
+ type = 'doc.vararg',
+ }
+ result.vararg = parseType(result)
+ if not result.vararg then
+ pushError {
+ type = 'LUADOC_MISS_VARARG_TYPE',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return
+ end
+ result.start = result.vararg.start
+ result.finish = result.vararg.finish
+ return result
+end
+
+local function parseOverload()
+ local tp, name = peekToken()
+ if tp ~= 'name' or name ~= 'fun' then
+ pushError {
+ type = 'LUADOC_MISS_FUN_AFTER_OVERLOAD',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ nextToken()
+ local result = {
+ type = 'doc.overload',
+ }
+ result.overload = parseTypeUnitFunction()
+ if not result.overload then
+ return nil
+ end
+ result.overload.parent = result
+ result.start = result.overload.start
+ result.finish = result.overload.finish
+ return result
+end
+
+local function convertTokens()
+ local tp, text = nextToken()
+ if not tp then
+ return
+ end
+ if tp ~= 'name' then
+ pushError {
+ type = 'LUADOC_MISS_CATE_NAME',
+ start = getStart(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ if text == 'class' then
+ return parseClass()
+ elseif text == 'type' then
+ return parseType()
+ elseif text == 'alias' then
+ return parseAlias()
+ elseif text == 'param' then
+ return parseParam()
+ elseif text == 'return' then
+ return parseReturn()
+ elseif text == 'field' then
+ return parseField()
+ elseif text == 'generic' then
+ return parseGeneric()
+ elseif text == 'vararg' then
+ return parseVararg()
+ elseif text == 'overload' then
+ return parseOverload()
+ end
+end
+
+local function buildLuaDoc(comment)
+ Offset = comment.start + 1
+ local text = comment.text
+ if text:sub(1, 2) ~= '-@' then
+ return
+ end
+ local finishPos = text:find('@', 3)
+ local doc, lastComment
+ if finishPos then
+ doc = text:sub(3, finishPos - 1)
+ lastComment = text:sub(finishPos)
+ else
+ doc = text:sub(3)
+ end
+ parseTokens(doc)
+ local result = convertTokens()
+ if result then
+ result.comment = lastComment
+ end
+ return result
+end
+
+return function (_, state)
+ local ast = state.ast
+ local comments = state.comms
+ table.sort(comments, function (a, b)
+ return a.start < b.start
+ end)
+ ast.docs = {}
+
+ pushError = state.pushError
+
+ for _, comment in ipairs(comments) do
+ local doc = buildLuaDoc(comment)
+ if doc then
+ ast.docs[#ast.docs+1] = doc
+ end
+ end
+end