diff options
author | 最萌小汐 <sumneko@hotmail.com> | 2020-10-19 20:50:18 +0800 |
---|---|---|
committer | 最萌小汐 <sumneko@hotmail.com> | 2020-10-19 20:50:18 +0800 |
commit | bb319039fdf41326f23ef0c7288123889434b0a8 (patch) | |
tree | 3fe4677dad3fd571fa216279a5992e4bf09b4bff | |
parent | f90a6366f4a4212e23612f17bffa506ffe25e030 (diff) | |
download | lua-language-server-bb319039fdf41326f23ef0c7288123889434b0a8.zip |
更新parser
-rw-r--r-- | script-beta/parser/emmy.lua | 321 | ||||
-rw-r--r-- | script-beta/parser/init.lua | 15 | ||||
-rw-r--r-- | script-beta/parser/luadoc.lua | 684 |
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 |