Skip to main content
Module

x/pbkit/core/parser/proto.ts

Protobuf toolkit for modern web development
Go to Latest
File
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153
import * as ast from "../ast/index.ts";import { createRecursiveDescentParser, Pattern, RecursiveDescentParser, SyntaxError, Token,} from "./recursive-descent-parser.ts";
export interface ParseResult<T = ast.Proto> { ast: T; parser: RecursiveDescentParser;}
export function parse(text: string): ParseResult { const parser = createRecursiveDescentParser(text); const statements = acceptStatements<ast.TopLevelStatement>(parser, [ acceptSyntax, acceptImport, acceptPackage, acceptOption, acceptMessage, acceptEnum, acceptExtend, acceptService, acceptEmpty, ]); const ast: ast.Proto = { statements }; return { ast, parser };}
export function parseConstant(text: string): ParseResult<ast.Constant> { const parser = createRecursiveDescentParser(text); const constant = expectConstant(parser); return { ast: constant, parser };}
interface AcceptFn<T> { (parser: RecursiveDescentParser): T | undefined;}
function acceptPatternAndThen<T>( pattern: Pattern, then: (token: Token) => T,): AcceptFn<T> { return function accept(parser) { const token = parser.accept(pattern); if (!token) return; return then(token); };}
function choice<T>(acceptFns: AcceptFn<T>[]): AcceptFn<T> { return function accept(parser) { for (const acceptFn of acceptFns) { const node = acceptFn(parser); if (node) return node; } };}
function many<T>(parser: RecursiveDescentParser, acceptFn: AcceptFn<T>): T[] { const nodes: T[] = []; let node: ReturnType<typeof acceptFn>; while (node = acceptFn(parser)) nodes.push(node); return nodes;}
interface AcceptStatementFn<T extends ast.StatementBase> { ( parser: RecursiveDescentParser, leadingComments: ast.Comment[], ): T | undefined;}function acceptStatements<T extends ast.StatementBase>( parser: RecursiveDescentParser, acceptStatementFns: AcceptStatementFn<T>[],) { const statements: T[] = []; statements: while (true) { const leadingComments = skipWsAndSweepComments(parser); for (const acceptStatementFn of acceptStatementFns) { const statement = acceptStatementFn(parser, leadingComments); if (statement) { statements.push(statement); continue statements; } } break; } return statements;}
const whitespacePattern = /^\s+/;const multilineCommentPattern = /^\/\*(?:.|\r?\n)*?\*\//;const singlelineCommentPattern = /^\/\/.*(?:\r?\n|$)/;const intLitPattern = /^0(?:[0-7]*|x[0-9a-f]+)|^[1-9]\d*/i;const floatLitPattern = /^\d+\.\d*(?:e[-+]?\d+)?|^\de[-+]?\d+|^\.\d+(?:e[-+]?\d+)?|^inf|^nan/i;const boolLitPattern = /^true|^false/;const strLitPattern = /^'(?:\\x[0-9a-f]{2}|\\[0-7]{3}|\\[abfnrtv\\'"]|[^'\0\n\\])*'|^"(?:\\x[0-9a-f]{2}|\\[0-7]{3}|\\[abfnrtv\\'"]|[^"\0\n\\])*"/i;const identPattern = /^[a-z_][a-z0-9_]*/i;
const acceptDot = acceptPatternAndThen<ast.Dot>( ".", (dot) => ({ type: "dot", ...dot }),);const acceptComma = acceptPatternAndThen<ast.Comma>( ",", (comma) => ({ type: "comma", ...comma }),);const acceptSemi = acceptPatternAndThen<ast.Semi>( ";", (semi) => ({ type: "semi", ...semi }),);function expectSemi(parser: RecursiveDescentParser): ast.Semi { const semi = acceptSemi(parser); if (semi) return semi; throw new SyntaxError(parser, [";"]);}const acceptIdent = acceptPatternAndThen<ast.Ident>( identPattern, (ident) => ({ type: "ident", ...ident }),);
function acceptSpecialToken<TType extends string>( parser: RecursiveDescentParser, type: TType, pattern: Pattern = identPattern,): (Token & { type: TType }) | undefined { const token = parser.accept(pattern); if (!token) return; return { type, ...token };}
function acceptKeyword( parser: RecursiveDescentParser, pattern: Pattern = identPattern,): ast.Keyword | undefined { return acceptSpecialToken(parser, "keyword", pattern);}
function skipWsAndSweepComments(parser: RecursiveDescentParser): ast.Comment[] { const result: ast.Comment[] = []; while (true) { const whitespace = parser.accept(whitespacePattern); if (whitespace) continue; const multilineComment = acceptSpecialToken( parser, "multiline-comment", multilineCommentPattern, ); if (multilineComment) { result.push(multilineComment); continue; } const singlelineComment = acceptSpecialToken( parser, "singleline-comment", singlelineCommentPattern, ); if (singlelineComment) { result.push(singlelineComment); continue; } break; } return result;}
function skipWsAndComments(parser: RecursiveDescentParser): undefined { skipWsAndSweepComments(parser); return;}
function acceptFullIdent( parser: RecursiveDescentParser,): ast.FullIdent | undefined { const identOrDots = many( parser, choice<ast.Dot | ast.Ident>([ acceptDot, acceptIdent, ]), ); if (identOrDots.length < 1) return; const first = identOrDots[0]; const last = identOrDots[identOrDots.length - 1]; return { start: first.start, end: last.end, type: "full-ident", identOrDots, };}
function expectFullIdent(parser: RecursiveDescentParser): ast.FullIdent { const fullIdent = acceptFullIdent(parser); if (fullIdent) return fullIdent; throw new SyntaxError(parser, [".", identPattern]);}
function acceptType( parser: RecursiveDescentParser,): ast.Type | undefined { const identOrDots = many( parser, choice<ast.Dot | ast.Ident>([ acceptDot, acceptIdent, ]), ); if (identOrDots.length < 1) return; const first = identOrDots[0]; const last = identOrDots[identOrDots.length - 1]; return { start: first.start, end: last.end, type: "type", identOrDots, };}
function expectType(parser: RecursiveDescentParser): ast.Type { const type = acceptType(parser); if (type) return type; throw new SyntaxError(parser, [".", identPattern]);}
function acceptIntLit(parser: RecursiveDescentParser): ast.IntLit | undefined { const intLit = parser.accept(intLitPattern); if (!intLit) return; return { type: "int-lit", ...intLit };}
function expectIntLit(parser: RecursiveDescentParser): ast.IntLit { const intLit = acceptIntLit(parser); if (intLit) return intLit; throw new SyntaxError(parser, [intLitPattern]);}
function acceptSignedIntLit( parser: RecursiveDescentParser,): ast.SignedIntLit | undefined { const loc = parser.loc; const sign = parser.accept("-") ?? parser.accept("+"); const intLit = acceptIntLit(parser); if (!intLit) { parser.loc = loc; return; } const start = sign?.start ?? intLit.start; const end = intLit.end; return { start, end, type: "signed-int-lit", sign, value: intLit };}
function expectSignedIntLit(parser: RecursiveDescentParser): ast.SignedIntLit { const signedIntLit = acceptSignedIntLit(parser); if (signedIntLit) return signedIntLit; throw new SyntaxError(parser, ["-", intLitPattern]);}
function acceptFloatLit( parser: RecursiveDescentParser,): ast.FloatLit | undefined { const floatLit = parser.accept(floatLitPattern); if (!floatLit) return; return { type: "float-lit", ...floatLit };}
function acceptSignedFloatLit( parser: RecursiveDescentParser,): ast.SignedFloatLit | undefined { const loc = parser.loc; const sign = parser.accept("-") ?? parser.accept("+"); const floatLit = acceptFloatLit(parser); if (!floatLit) { parser.loc = loc; return; } const start = sign?.start ?? floatLit.start; const end = floatLit.end; return { start, end, type: "signed-float-lit", sign, value: floatLit };}
function acceptBoolLit( parser: RecursiveDescentParser,): ast.BoolLit | undefined { const boolLit = parser.accept(boolLitPattern); if (!boolLit) return; return { type: "bool-lit", ...boolLit };}
function acceptStrLit(parser: RecursiveDescentParser): ast.StrLit | undefined { const strLit = parser.accept(strLitPattern); if (!strLit) return; return { type: "str-lit", ...strLit };}
function expectStrLit(parser: RecursiveDescentParser): ast.StrLit { const strLit = acceptStrLit(parser); if (strLit) return strLit; throw new SyntaxError(parser, [strLitPattern]);}
// https://github.com/protocolbuffers/protobuf/blob/c2148566c7/src/google/protobuf/compiler/parser.cc#L1429-L1452function acceptAggregate( parser: RecursiveDescentParser,): ast.Aggregate | undefined { const parenthesisOpen = parser.accept("{"); if (!parenthesisOpen) return; let character = parenthesisOpen; let depth = 1; while (character = parser.expect(/^./)) { switch (character.text) { case "{": ++depth; break; case "}": --depth; break; } if (depth === 0) { break; } } const start = parenthesisOpen.start; const end = character.end; return { type: "aggregate", start, end };}
function acceptConstant( parser: RecursiveDescentParser,): ast.Constant | undefined { return acceptSignedIntLit(parser) ?? acceptSignedFloatLit(parser) ?? acceptStrLit(parser) ?? acceptBoolLit(parser) ?? acceptFullIdent(parser) ?? acceptAggregate(parser);}
function expectConstant(parser: RecursiveDescentParser): ast.Constant { const constant = acceptConstant(parser); if (constant) return constant; throw new SyntaxError(parser, [ identPattern, "-", "+", intLitPattern, strLitPattern, boolLitPattern, ]);}
function acceptOptionNameSegment( parser: RecursiveDescentParser,): ast.OptionNameSegment | undefined { const bracketOpen = parser.accept("("); const name = acceptFullIdent(parser); if (!name) { if (bracketOpen) throw new SyntaxError(parser, [identPattern]); return; } const bracketClose = parser[bracketOpen ? "expect" : "accept"](")"); const start = bracketOpen?.start ?? name.start; const end = bracketClose?.end ?? name.end; return { start, end, type: "option-name-segment", bracketOpen, name, bracketClose, };}
function acceptOptionName( parser: RecursiveDescentParser,): ast.OptionName | undefined { const optionNameSegmentOrDots = many( parser, choice<ast.Dot | ast.OptionNameSegment>([ acceptDot, acceptOptionNameSegment, ]), ); if (optionNameSegmentOrDots.length < 1) return; const first = optionNameSegmentOrDots[0]; const last = optionNameSegmentOrDots[optionNameSegmentOrDots.length - 1]; return { start: first.start, end: last.end, type: "option-name", optionNameSegmentOrDots, };}
function expectOptionName(parser: RecursiveDescentParser): ast.OptionName { const optionName = acceptOptionName(parser); if (optionName) return optionName; throw new SyntaxError(parser, ["(", identPattern]);}
function acceptSyntax( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Syntax | undefined { const keyword = acceptKeyword(parser, "syntax"); if (!keyword) return; skipWsAndComments(parser); const eq = parser.expect("="); skipWsAndComments(parser); const quoteOpen = parser.expect(/^['"]/); const syntax = parser.expect(/^[^'"]+/); const quoteClose = parser.expect(/^['"]/); skipWsAndComments(parser); const semi = expectSemi(parser); return { start: keyword.start, end: semi.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "syntax", keyword, eq, quoteOpen, syntax, quoteClose, semi, };}
function acceptImport( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Import | undefined { const keyword = acceptKeyword(parser, "import"); if (!keyword) return; skipWsAndComments(parser); const weakOrPublic = parser.accept(/^weak|^public/); skipWsAndComments(parser); const strLit = expectStrLit(parser); skipWsAndComments(parser); const semi = expectSemi(parser); return { start: keyword.start, end: semi.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "import", keyword, weakOrPublic, strLit, semi, };}
function acceptPackage( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Package | undefined { const keyword = acceptKeyword(parser, "package"); if (!keyword) return; skipWsAndComments(parser); const fullIdent = expectFullIdent(parser); skipWsAndComments(parser); const semi = expectSemi(parser); return { start: keyword.start, end: semi.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "package", keyword, fullIdent, semi, };}
function acceptOption( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Option | undefined { const keyword = acceptKeyword(parser, /^option\b/); if (!keyword) return; skipWsAndComments(parser); const optionName = expectOptionName(parser); skipWsAndComments(parser); const eq = parser.expect("="); skipWsAndComments(parser); const constant = expectConstant(parser); skipWsAndComments(parser); const semi = expectSemi(parser); return { start: keyword.start, end: semi.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "option", keyword, optionName, eq, constant, semi, };}
function acceptEmpty( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Empty | undefined { const semi = acceptSemi(parser); if (!semi) return; return { start: semi.start, end: semi.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "empty", semi, };}
function acceptFieldOption( parser: RecursiveDescentParser,): ast.FieldOption | undefined { const optionName = acceptOptionName(parser); if (!optionName) return; skipWsAndComments(parser); const eq = parser.expect("="); skipWsAndComments(parser); const constant = expectConstant(parser); return { start: optionName.start, end: constant.end, type: "field-option", optionName, eq, constant, };}
function acceptFieldOptions( parser: RecursiveDescentParser,): ast.FieldOptions | undefined { const bracketOpen = parser.accept("["); if (!bracketOpen) return; const fieldOptionOrCommas = many( parser, choice<ast.Comma | ast.FieldOption>([ skipWsAndComments, acceptComma, acceptFieldOption, ]), ); const bracketClose = parser.expect("]"); return { start: bracketOpen.start, end: bracketClose.end, type: "field-options", bracketOpen, fieldOptionOrCommas, bracketClose, };}
function acceptEnumField( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.EnumField | undefined { const fieldName = parser.accept(identPattern); if (!fieldName) return; skipWsAndComments(parser); const eq = parser.expect("="); skipWsAndComments(parser); const fieldNumber = expectSignedIntLit(parser); skipWsAndComments(parser); const fieldOptions = acceptFieldOptions(parser); skipWsAndComments(parser); const semi = expectSemi(parser); return { start: fieldName.start, end: semi.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "enum-field", fieldName, eq, fieldNumber, fieldOptions, semi, };}
function expectEnumBody(parser: RecursiveDescentParser): ast.EnumBody { const bracketOpen = parser.expect("{"); const statements = acceptStatements<ast.EnumBodyStatement>(parser, [ acceptOption, acceptEnumField, acceptEmpty, ]); const bracketClose = parser.expect("}"); return { start: bracketOpen.start, end: bracketClose.end, type: "enum-body", bracketOpen, statements, bracketClose, };}
function acceptEnum( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Enum | undefined { const keyword = acceptKeyword(parser, "enum"); if (!keyword) return; skipWsAndComments(parser); const enumName = parser.expect(identPattern); skipWsAndComments(parser); const enumBody = expectEnumBody(parser); return { start: keyword.start, end: enumBody.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "enum", keyword, enumName, enumBody, };}
function acceptField( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Field | undefined { const loc = parser.loc; const fieldLabel = acceptKeyword(parser, /^required|^optional|^repeated/); skipWsAndComments(parser); const fieldType = acceptType(parser); if (!fieldType) { parser.loc = loc; return; } skipWsAndComments(parser); const fieldName = parser.expect(identPattern); skipWsAndComments(parser); const eq = parser.expect("="); skipWsAndComments(parser); const fieldNumber = expectIntLit(parser); skipWsAndComments(parser); const fieldOptions = acceptFieldOptions(parser); skipWsAndComments(parser); const semi = expectSemi(parser); return { start: (fieldLabel ?? fieldType).start, end: semi.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "field", fieldLabel, fieldType, fieldName, eq, fieldNumber, fieldOptions, semi, };}
function acceptOneofField( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.OneofField | undefined { const fieldType = acceptType(parser); if (!fieldType) return; skipWsAndComments(parser); const fieldName = parser.expect(identPattern); skipWsAndComments(parser); const eq = parser.expect("="); skipWsAndComments(parser); const fieldNumber = expectIntLit(parser); skipWsAndComments(parser); const fieldOptions = acceptFieldOptions(parser); skipWsAndComments(parser); const semi = expectSemi(parser); return { start: fieldType.start, end: semi.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "oneof-field", fieldType, fieldName, eq, fieldNumber, fieldOptions, semi, };}
function acceptMapField( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.MapField | undefined { const keyword = acceptKeyword(parser, "map"); if (!keyword) return; skipWsAndComments(parser); const typeBracketOpen = parser.expect("<"); skipWsAndComments(parser); const keyType = expectType(parser); skipWsAndComments(parser); const typeSep = parser.expect(","); skipWsAndComments(parser); const valueType = expectType(parser); skipWsAndComments(parser); const typeBracketClose = parser.expect(">"); skipWsAndComments(parser); const mapName = parser.expect(identPattern); skipWsAndComments(parser); const eq = parser.expect("="); skipWsAndComments(parser); const fieldNumber = expectIntLit(parser); skipWsAndComments(parser); const fieldOptions = acceptFieldOptions(parser); skipWsAndComments(parser); const semi = expectSemi(parser); return { start: keyword.start, end: semi.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "map-field", keyword, typeBracketOpen, keyType, typeSep, valueType, typeBracketClose, mapName, eq, fieldNumber, fieldOptions, semi, };}
function expectOneofBody(parser: RecursiveDescentParser): ast.OneofBody { const bracketOpen = parser.expect("{"); const statements = acceptStatements<ast.OneofBodyStatement>(parser, [ acceptOption, acceptOneofField, acceptEmpty, ]); const bracketClose = parser.expect("}"); return { start: bracketOpen.start, end: bracketClose.end, type: "oneof-body", bracketOpen, statements, bracketClose, };}
function acceptOneof( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Oneof | undefined { const keyword = acceptKeyword(parser, "oneof"); if (!keyword) return; skipWsAndComments(parser); const oneofName = parser.expect(identPattern); skipWsAndComments(parser); const oneofBody = expectOneofBody(parser); return { start: keyword.start, end: oneofBody.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "oneof", keyword, oneofName, oneofBody, };}
const acceptMax = acceptPatternAndThen<ast.Max>( "max", (max) => ({ type: "max", ...max }),);
function acceptRange(parser: RecursiveDescentParser): ast.Range | undefined { const rangeStart = acceptIntLit(parser); if (!rangeStart) return; skipWsAndComments(parser); const to = acceptKeyword(parser, "to"); if (!to) { return { start: rangeStart.start, end: rangeStart.end, type: "range", rangeStart, }; } skipWsAndComments(parser); const rangeEnd = acceptIntLit(parser) ?? acceptMax(parser); if (!rangeEnd) throw new SyntaxError(parser, [intLitPattern, "max"]); return { start: rangeStart.start, end: rangeEnd.end, type: "range", rangeStart, to, rangeEnd, };}
function expectRanges(parser: RecursiveDescentParser): ast.Ranges { const rangeOrCommas = many( parser, choice<ast.Range | ast.Comma>([ acceptComma, acceptRange, ]), ); const first = rangeOrCommas[0]; const last = rangeOrCommas[rangeOrCommas.length - 1]; return { start: first.start, end: last.end, type: "ranges", rangeOrCommas, };}
function acceptExtensions( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Extensions | undefined { const keyword = acceptKeyword(parser, "extensions"); if (!keyword) return; skipWsAndComments(parser); const ranges = expectRanges(parser); skipWsAndComments(parser); const semi = expectSemi(parser); return { start: keyword.start, end: semi.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "extensions", keyword, ranges, semi, };}
function expectFieldNames(parser: RecursiveDescentParser): ast.FieldNames { const strLitOrCommas = many( parser, choice<ast.StrLit | ast.Comma>([ acceptComma, acceptStrLit, ]), ); const first = strLitOrCommas[0]; const last = strLitOrCommas[strLitOrCommas.length - 1]; return { start: first.start, end: last.end, type: "field-names", strLitOrCommas, };}
function acceptReserved( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Reserved | undefined { const keyword = acceptKeyword(parser, "reserved"); if (!keyword) return; skipWsAndComments(parser); const reserved = parser.try(intLitPattern) ? expectRanges(parser) : expectFieldNames(parser); skipWsAndComments(parser); const semi = expectSemi(parser); return { start: keyword.start, end: semi.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "reserved", keyword, reserved, semi, };}
function expectExtendBody(parser: RecursiveDescentParser): ast.ExtendBody { const bracketOpen = parser.expect("{"); const statements = acceptStatements<ast.ExtendBodyStatement>(parser, [ acceptGroup, acceptField, acceptEmpty, ]); const bracketClose = parser.expect("}"); return { start: bracketOpen.start, end: bracketClose.end, type: "extend-body", bracketOpen, statements, bracketClose, };}
function acceptExtend( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Extend | undefined { const keyword = acceptKeyword(parser, "extend"); if (!keyword) return; skipWsAndComments(parser); const messageType = expectType(parser); skipWsAndComments(parser); const extendBody = expectExtendBody(parser); return { start: keyword.start, end: extendBody.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "extend", keyword, messageType, extendBody, };}
function acceptGroup( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Group | undefined { const loc = parser.loc; const groupLabel = acceptKeyword(parser, /^required|^optional|^repeated/); if (!groupLabel) { parser.loc = loc; return; } skipWsAndComments(parser); const keyword = acceptKeyword(parser, "group"); if (!keyword) { parser.loc = loc; return; } skipWsAndComments(parser); const groupName = parser.expect(identPattern); skipWsAndComments(parser); const eq = parser.expect("="); skipWsAndComments(parser); const fieldNumber = expectIntLit(parser); skipWsAndComments(parser); const messageBody = expectMessageBody(parser); return { start: groupLabel.start, end: messageBody.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "group", groupLabel, keyword, groupName, eq, fieldNumber, messageBody, };}
function expectMessageBody(parser: RecursiveDescentParser): ast.MessageBody { const bracketOpen = parser.expect("{"); const statements = acceptStatements<ast.MessageBodyStatement>(parser, [ acceptGroup, acceptEnum, acceptMessage, acceptExtend, acceptExtensions, acceptOption, acceptOneof, acceptMapField, acceptReserved, acceptField, acceptEmpty, ]); const bracketClose = parser.expect("}"); return { start: bracketOpen.start, end: bracketClose.end, type: "message-body", bracketOpen, statements, bracketClose, };}
function acceptMessage( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Message | undefined { const keyword = acceptKeyword(parser, "message"); if (!keyword) return; skipWsAndComments(parser); const messageName = parser.expect(identPattern); skipWsAndComments(parser); const messageBody = expectMessageBody(parser); return { start: keyword.start, end: messageBody.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "message", keyword, messageName, messageBody, };}
function expectRpcType(parser: RecursiveDescentParser): ast.RpcType { const bracketOpen = parser.expect("("); skipWsAndComments(parser); const stream = acceptKeyword(parser, "stream"); skipWsAndComments(parser); const messageType = expectType(parser); skipWsAndComments(parser); const bracketClose = parser.expect(")"); return { start: bracketOpen.start, end: bracketClose.end, bracketOpen, stream, messageType, bracketClose, };}
function acceptRpc( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Rpc | undefined { const keyword = acceptKeyword(parser, "rpc"); if (!keyword) return; skipWsAndComments(parser); const rpcName = parser.expect(identPattern); skipWsAndComments(parser); const reqType = expectRpcType(parser); skipWsAndComments(parser); const returns = parser.expect("returns"); skipWsAndComments(parser); const resType = expectRpcType(parser); skipWsAndComments(parser); const semiOrRpcBody = acceptSemi(parser) ?? expectRpcBody(parser); return { start: keyword.start, end: semiOrRpcBody.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "rpc", keyword, rpcName, reqType, returns, resType, semiOrRpcBody, };}
function expectRpcBody(parser: RecursiveDescentParser): ast.RpcBody { const bracketOpen = parser.expect("{"); const statements = acceptStatements<ast.RpcBodyStatement>(parser, [ acceptOption, acceptEmpty, ]); const bracketClose = parser.expect("}"); return { start: bracketOpen.start, end: bracketClose.end, type: "rpc-body", bracketOpen, statements, bracketClose, };}
function expectServiceBody(parser: RecursiveDescentParser): ast.ServiceBody { const bracketOpen = parser.expect("{"); const statements = acceptStatements<ast.ServiceBodyStatement>(parser, [ acceptOption, acceptRpc, acceptEmpty, ]); const bracketClose = parser.expect("}"); return { start: bracketOpen.start, end: bracketClose.end, type: "service-body", bracketOpen, statements, bracketClose, };}
function acceptService( parser: RecursiveDescentParser, leadingComments: ast.Comment[],): ast.Service | undefined { const keyword = acceptKeyword(parser, "service"); if (!keyword) return; skipWsAndComments(parser); const serviceName = parser.expect(identPattern); skipWsAndComments(parser); const serviceBody = expectServiceBody(parser); return { start: keyword.start, end: serviceBody.end, leadingComments, trailingComments: [], // TODO leadingDetachedComments: [], // TODO type: "service", keyword, serviceName, serviceBody, };}