@creditkarma/thrift-parser
Version:
A parser for Thrift written in TypeScript
853 lines • 35.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createParser = void 0;
const types_1 = require("./types");
const factory_1 = require("./factory");
const debugger_1 = require("./debugger");
function isStatementBeginning(token) {
switch (token.type) {
case types_1.SyntaxType.NamespaceKeyword:
case types_1.SyntaxType.IncludeKeyword:
case types_1.SyntaxType.ConstKeyword:
case types_1.SyntaxType.StructKeyword:
case types_1.SyntaxType.UnionKeyword:
case types_1.SyntaxType.ExceptionKeyword:
case types_1.SyntaxType.ServiceKeyword:
case types_1.SyntaxType.TypedefKeyword:
case types_1.SyntaxType.EnumKeyword:
return true;
default:
return false;
}
}
class ParseError extends Error {
constructor(msg, loc) {
super(msg);
this.message = msg;
this.loc = loc;
}
}
function createParser(tokens, report = debugger_1.noopReporter) {
let comments = [];
let currentIndex = 0;
// PUBLIC
function parse() {
const thrift = {
type: types_1.SyntaxType.ThriftDocument,
body: [],
};
while (!isAtEnd()) {
try {
const statement = parseStatement();
if (statement !== null) {
thrift.body.push(statement);
}
}
catch (e) {
report((0, factory_1.createParseError)(e.message, e.loc));
}
}
return thrift;
}
// Finds the beginning of the next statement so we can continue parse after error.
function synchronize() {
while (!isAtEnd() && !isStatementBeginning(currentToken())) {
advance();
}
}
function parseStatement() {
const next = currentToken();
// All Thrift statements must start with one of these types
switch (next.type) {
case types_1.SyntaxType.NamespaceKeyword:
return parseNamespace();
case types_1.SyntaxType.IncludeKeyword:
return parseInclude();
case types_1.SyntaxType.ConstKeyword:
return parseConst();
case types_1.SyntaxType.StructKeyword:
return parseStruct();
case types_1.SyntaxType.UnionKeyword:
return parseUnion();
case types_1.SyntaxType.ExceptionKeyword:
return parseException();
case types_1.SyntaxType.ServiceKeyword:
return parseService();
case types_1.SyntaxType.TypedefKeyword:
return parseTypedef();
case types_1.SyntaxType.EnumKeyword:
return parseEnum();
case types_1.SyntaxType.CommentBlock:
case types_1.SyntaxType.CommentLine:
consumeComments();
return null;
default:
throw reportError(`Invalid start to Thrift statement ${next.text}`);
}
}
// IncludeDefinition → 'include' StringLiteral
function parseInclude() {
const _keywordToken = consume(types_1.SyntaxType.IncludeKeyword);
const keywordToken = requireValue(_keywordToken, `'indcluded' keyword expected`);
const _pathToken = consume(types_1.SyntaxType.StringLiteral);
const pathToken = requireValue(_pathToken, `Include statement must include a path as string literal`);
return {
type: types_1.SyntaxType.IncludeDefinition,
path: (0, factory_1.createStringLiteral)(pathToken.text, pathToken.loc),
comments: getComments(),
loc: (0, factory_1.createTextLocation)(keywordToken.loc.start, pathToken.loc.end),
};
}
// ServiceDefinition → 'service' Identifier ( 'extends' Identifier )? '{' Function* '} Annotations?'
function parseService() {
const leadingComments = getComments();
const _keywordToken = consume(types_1.SyntaxType.ServiceKeyword);
const keywordToken = requireValue(_keywordToken, `Unable to find service keyword for service`);
const _nameToken = consume(types_1.SyntaxType.Identifier);
const nameToken = requireValue(_nameToken, `Unable to find identifier for service`);
const extendsId = parseExtends();
const _openBrace = consume(types_1.SyntaxType.LeftBraceToken);
const openBrace = requireValue(_openBrace, `Expected opening curly brace`);
const functions = parseFunctions();
const _closeBrace = consume(types_1.SyntaxType.RightBraceToken);
const closeBrace = requireValue(_closeBrace, `Expected closing curly brace`);
const annotations = parseAnnotations();
const location = (0, factory_1.createTextLocation)(keywordToken.loc.start, closeBrace.loc.end);
return {
type: types_1.SyntaxType.ServiceDefinition,
name: (0, factory_1.createIdentifier)(nameToken.text, nameToken.loc),
extends: extendsId,
functions,
annotations,
comments: leadingComments,
loc: location,
};
}
function parseExtends() {
if (checkText('extends')) {
const _keywordToken = consume(types_1.SyntaxType.ExtendsKeyword);
const keywordToken = requireValue(_keywordToken, `'extends keyword expected`);
const _nameToken = consume(types_1.SyntaxType.Identifier);
const nameToken = requireValue(_nameToken, `Identifier expected after 'extends' keyword`);
return (0, factory_1.createIdentifier)(nameToken.text, (0, factory_1.createTextLocation)(keywordToken.loc.start, nameToken.loc.end));
}
else {
return null;
}
}
function parseFunctions() {
const functions = [];
while (!check(types_1.SyntaxType.RightBraceToken)) {
if (check(types_1.SyntaxType.CommentBlock, types_1.SyntaxType.CommentLine)) {
advance();
}
else {
functions.push(parseFunction());
if (isStatementBeginning(currentToken())) {
throw reportError(`Closing curly brace expected, but new statement found`);
}
else if (check(types_1.SyntaxType.EOF)) {
throw reportError(`Closing curly brace expected but reached end of file`);
}
}
}
return functions;
}
// Function → 'oneway'? FunctionType Identifier '(' Field* ')' Throws? Annotations? ListSeparator?
function parseFunction() {
const leadingComments = getComments();
const onewayToken = consume(types_1.SyntaxType.OnewayKeyword);
const returnType = parseFunctionType();
const _nameToken = consume(types_1.SyntaxType.Identifier);
const nameToken = requireValue(_nameToken, `Unable to find function identifier`);
const params = parseParameterFields();
const throws = parseThrows();
const annotations = parseAnnotations();
const listSeparator = readListSeparator();
const endLoc = listSeparator !== null
? listSeparator.loc
: throws !== null
? throws.loc
: params.loc;
return {
type: types_1.SyntaxType.FunctionDefinition,
name: (0, factory_1.createIdentifier)(nameToken.text, nameToken.loc),
returnType,
fields: params.fields,
throws: throws !== null ? throws.fields : [],
annotations,
comments: leadingComments,
oneway: onewayToken !== null,
modifiers: onewayToken !== null ? [onewayToken] : [],
loc: {
start: returnType.loc.start,
end: endLoc.end,
},
};
}
function parseParameterFields() {
const fields = [];
const _openParen = consume(types_1.SyntaxType.LeftParenToken);
const openParen = requireValue(_openParen, `Opening paren expected to start list of fields`);
while (!check(types_1.SyntaxType.RightParenToken)) {
readListSeparator();
fields.push(parseField());
if (isStatementBeginning(currentToken())) {
throw reportError(`Closing paren ')' expected, but new statement found`);
}
else if (check(types_1.SyntaxType.EOF)) {
throw reportError(`Closing paren ')' expected but reached end of file`);
}
}
const _closeParen = consume(types_1.SyntaxType.RightParenToken);
const closeParen = requireValue(_closeParen, `Closing paren expected to end list of fields`);
return {
type: types_1.SyntaxType.ParametersDefinition,
fields,
loc: {
start: openParen.loc.start,
end: closeParen.loc.end,
},
};
}
// Throws → 'throws' '(' Field* ')'
function parseThrows() {
if (check(types_1.SyntaxType.ThrowsKeyword)) {
const _keywordToken = consume(types_1.SyntaxType.ThrowsKeyword);
const keywordToken = requireValue(_keywordToken, `'throws' keyword expected`);
const params = parseParameterFields();
return {
type: types_1.SyntaxType.ThrowsDefinition,
fields: params.fields,
loc: {
start: keywordToken.loc.start,
end: params.loc.end,
},
};
}
return null;
}
// Namespace → 'namespace' ( NamespaceScope Identifier )
function parseNamespace() {
const _keywordToken = consume(types_1.SyntaxType.NamespaceKeyword);
const keywordToken = requireValue(_keywordToken, `'namespace' keyword expected`);
const _scopeToken = consume(types_1.SyntaxType.Identifier);
const scopeToken = requireValue(_scopeToken, `Unable to find scope identifier for namespace`);
const _nameToken = consume(types_1.SyntaxType.Identifier);
const nameToken = requireValue(_nameToken, `Unable to find name identifier for namespace`);
return {
type: types_1.SyntaxType.NamespaceDefinition,
scope: (0, factory_1.createIdentifier)(scopeToken.text, scopeToken.loc),
name: (0, factory_1.createIdentifier)(nameToken.text, nameToken.loc),
comments: getComments(),
loc: (0, factory_1.createTextLocation)(keywordToken.loc.start, nameToken.loc.end),
};
}
// ConstDefinition → 'const' FieldType Identifier '=' ConstValue Annotations? ListSeparator?
function parseConst() {
const leadingComments = getComments();
const _keywordToken = consume(types_1.SyntaxType.ConstKeyword);
const keywordToken = requireValue(_keywordToken, `'const' keyword expected`);
const fieldType = parseFieldType();
const _nameToken = consume(types_1.SyntaxType.Identifier);
const nameToken = requireValue(_nameToken, `Const definition must have a name`);
const _initializer = parseValueAssignment();
const initializer = requireValue(_initializer, `Const must be initialized to a value`);
const annotations = parseAnnotations();
readListSeparator();
return {
type: types_1.SyntaxType.ConstDefinition,
name: (0, factory_1.createIdentifier)(nameToken.text, nameToken.loc),
fieldType,
initializer,
annotations,
comments: leadingComments,
loc: {
start: keywordToken.loc.start,
end: initializer.loc.end,
},
};
}
function parseValueAssignment() {
if (check(types_1.SyntaxType.EqualToken)) {
advance();
return parseValue();
}
return null;
}
// Annotations → '(' Annotation* ')'
function parseAnnotations() {
if (check(types_1.SyntaxType.LeftParenToken)) {
const annotations = [];
const startToken = advance();
while (!check(types_1.SyntaxType.RightParenToken)) {
annotations.push(parseAnnotation());
}
const endToken = advance();
return {
annotations,
type: types_1.SyntaxType.Annotations,
loc: (0, factory_1.createTextLocation)(startToken.loc.start, endToken.loc.end),
};
}
return undefined;
}
// Annotation → Identifier ('=' StringLiteral)? ListSeparator?
function parseAnnotation() {
const nameToken = requireValue(consume(types_1.SyntaxType.Identifier), `Annotation must have a name`);
let valueToken;
if (check(types_1.SyntaxType.EqualToken)) {
advance();
valueToken = requireValue(consume(types_1.SyntaxType.StringLiteral), `Annotation must have a value`);
}
readListSeparator();
return {
type: types_1.SyntaxType.Annotation,
name: (0, factory_1.createIdentifier)(nameToken.text, nameToken.loc),
value: valueToken
? (0, factory_1.createStringLiteral)(valueToken.text, valueToken.loc)
: undefined,
loc: (0, factory_1.createTextLocation)(nameToken.loc.start, (valueToken || nameToken).loc.end),
};
}
// TypedefDefinition → 'typedef' FieldType Identifier
function parseTypedef() {
const _keywordToken = consume(types_1.SyntaxType.TypedefKeyword);
const keywordToken = requireValue(_keywordToken, `'typedef' keyword expected`);
const type = parseFieldType();
const _nameToken = consume(types_1.SyntaxType.Identifier);
const nameToken = requireValue(_nameToken, `Typedef is expected to have name and none found`);
const leadingComments = getComments();
const annotations = parseAnnotations();
return {
type: types_1.SyntaxType.TypedefDefinition,
name: (0, factory_1.createIdentifier)(nameToken.text, nameToken.loc),
definitionType: type,
annotations,
comments: leadingComments,
loc: {
start: keywordToken.loc.start,
end: nameToken.loc.end,
},
};
}
// EnumDefinition → 'enum' Identifier '{' EnumMember* '} Annotations?'
function parseEnum() {
const leadingComments = getComments();
const _keywordToken = consume(types_1.SyntaxType.EnumKeyword);
const keywordToken = requireValue(_keywordToken, `'enum' keyword expected`);
const _nameToken = consume(types_1.SyntaxType.Identifier);
const nameToken = requireValue(_nameToken, `Expected identifier for enum definition`);
const openBrace = consume(types_1.SyntaxType.LeftBraceToken);
requireValue(openBrace, `Expected opening brace`);
const members = parseEnumMembers();
const _closeBrace = consume(types_1.SyntaxType.RightBraceToken);
const closeBrace = requireValue(_closeBrace, `Expected closing brace`);
const annotations = parseAnnotations();
const loc = {
start: keywordToken.loc.start,
end: closeBrace.loc.end,
};
return {
type: types_1.SyntaxType.EnumDefinition,
name: (0, factory_1.createIdentifier)(nameToken.text, nameToken.loc),
members,
annotations,
comments: leadingComments,
loc,
};
}
function parseEnumMembers() {
const members = [];
while (!check(types_1.SyntaxType.RightBraceToken)) {
if (check(types_1.SyntaxType.CommentBlock, types_1.SyntaxType.CommentLine)) {
advance();
}
else {
members.push(parseEnumMember());
// consume list separator if there is one
readListSeparator();
if (isStatementBeginning(currentToken())) {
throw reportError(`Closing curly brace expected, but new statement found`);
}
else if (check(types_1.SyntaxType.EOF)) {
throw reportError(`Closing curly brace expected but reached end of file`);
}
}
}
return members;
}
// EnumMember → (Identifier ('=' IntConstant)? Annotations? ListSeparator?)*
function parseEnumMember() {
const _nameToken = consume(types_1.SyntaxType.Identifier);
const nameToken = requireValue(_nameToken, `EnumMember must have identifier`);
let loc = null;
let initializer = null;
if (consume(types_1.SyntaxType.EqualToken) !== null) {
const _numToken = consume(types_1.SyntaxType.IntegerLiteral, types_1.SyntaxType.HexLiteral);
const numToken = requireValue(_numToken, `Equals token "=" must be followed by an Integer`);
initializer = parseIntValue(numToken);
loc = (0, factory_1.createTextLocation)(nameToken.loc.start, initializer.loc.end);
}
else {
loc = (0, factory_1.createTextLocation)(nameToken.loc.start, nameToken.loc.end);
}
const annotations = parseAnnotations();
return {
type: types_1.SyntaxType.EnumMember,
name: (0, factory_1.createIdentifier)(nameToken.text, nameToken.loc),
initializer,
annotations,
comments: getComments(),
loc,
};
}
// StructLike → ('struct' | 'union' | 'exception') Identifier 'xsd_all'? '{' Field* '} Annotations?'
function parseStructLikeInterface() {
const leadingComments = getComments();
const _keywordToken = consume(types_1.SyntaxType.StructKeyword, types_1.SyntaxType.UnionKeyword, types_1.SyntaxType.ExceptionKeyword);
const keywordToken = requireValue(_keywordToken, `'struct | union | exception' expected`);
const _nameToken = consume(types_1.SyntaxType.Identifier);
const nameToken = requireValue(_nameToken, `Struct-like must have an identifier`);
const openBrace = consume(types_1.SyntaxType.LeftBraceToken);
requireValue(openBrace, `Struct-like body must begin with opening curly brace '{'`);
const fields = parseFields();
const _closeBrace = consume(types_1.SyntaxType.RightBraceToken);
const closeBrace = requireValue(_closeBrace, `Struct-like body must end with a closing curly brace '}'`);
const annotations = parseAnnotations();
return {
name: (0, factory_1.createIdentifier)(nameToken.text, nameToken.loc),
fields,
annotations,
comments: leadingComments,
loc: {
start: keywordToken.loc.start,
end: closeBrace.loc.end,
},
};
}
// StructDefinition → 'struct' Identifier 'xsd_all'? '{' Field* '} Annotations?'
function parseStruct() {
const parsedData = parseStructLikeInterface();
return {
type: types_1.SyntaxType.StructDefinition,
name: parsedData.name,
fields: parsedData.fields,
annotations: parsedData.annotations,
comments: parsedData.comments,
loc: parsedData.loc,
};
}
// UnioinDefinition → 'union' Identifier 'xsd_all'? '{' Field* '} Annotations?'
function parseUnion() {
const parsedData = parseStructLikeInterface();
return {
type: types_1.SyntaxType.UnionDefinition,
name: parsedData.name,
fields: parsedData.fields.map((next) => {
// As per the Thrift spec, all union fields are optional
next.requiredness = 'optional';
return next;
}),
annotations: parsedData.annotations,
comments: parsedData.comments,
loc: parsedData.loc,
};
}
// ExceptionDefinition → 'exception' Identifier '{' Field* '} Annotations?'
function parseException() {
const parsedData = parseStructLikeInterface();
return {
type: types_1.SyntaxType.ExceptionDefinition,
name: parsedData.name,
fields: parsedData.fields,
annotations: parsedData.annotations,
comments: parsedData.comments,
loc: parsedData.loc,
};
}
function parseFields() {
const fields = [];
while (!check(types_1.SyntaxType.RightBraceToken)) {
if (check(types_1.SyntaxType.CommentBlock, types_1.SyntaxType.CommentLine)) {
advance();
}
else {
fields.push(parseField());
if (isStatementBeginning(currentToken())) {
throw reportError(`Closing curly brace expected, but new statement found`);
}
else if (check(types_1.SyntaxType.EOF)) {
throw reportError(`Closing curly brace expected but reached end of file`);
}
}
}
return fields;
}
// Field → FieldID? FieldReq? FieldType Identifier ('= ConstValue)? XsdFieldOptions Annotations? ListSeparator?
function parseField() {
const startLoc = currentToken().loc;
const fieldID = parseFieldId();
const fieldRequired = parserequireValuedness();
const fieldType = parseFieldType();
const _nameToken = consume(types_1.SyntaxType.Identifier);
const nameToken = requireValue(_nameToken, `Unable to find identifier for field`);
const defaultValue = parseValueAssignment();
const annotations = parseAnnotations();
const listSeparator = readListSeparator();
const endLoc = listSeparator !== null
? listSeparator.loc
: defaultValue !== null
? defaultValue.loc
: nameToken.loc;
const location = (0, factory_1.createTextLocation)(startLoc.start, endLoc.end);
return {
type: types_1.SyntaxType.FieldDefinition,
name: (0, factory_1.createIdentifier)(nameToken.text, nameToken.loc),
fieldID,
fieldType,
requiredness: fieldRequired,
defaultValue,
comments: getComments(),
annotations,
loc: location,
};
}
// ListSeparator → ',' | ';'
function readListSeparator() {
if (check(types_1.SyntaxType.CommaToken, types_1.SyntaxType.SemicolonToken)) {
return advance();
}
return null;
}
// FieldRequired → 'required' | 'optional'
function parserequireValuedness() {
const current = currentToken();
if (current.text === 'required' || current.text === 'optional') {
advance();
return current.text;
}
return null;
}
// FieldID → IntConstant ':'
function parseFieldId() {
if (currentToken().type === types_1.SyntaxType.IntegerLiteral &&
peek().type === types_1.SyntaxType.ColonToken) {
const fieldIDToken = consume(types_1.SyntaxType.IntegerLiteral);
const colonToken = consume(types_1.SyntaxType.ColonToken);
// return value of number token
return (0, factory_1.createFieldID)(parseInt(fieldIDToken.text, 10), (0, factory_1.createTextLocation)(fieldIDToken.loc.start, colonToken.loc.end));
}
else {
return null;
}
}
// ConstValue → Literal | ConstMap | ConstList
function parseValue() {
const next = advance();
switch (next.type) {
case types_1.SyntaxType.Identifier:
return (0, factory_1.createIdentifier)(next.text, next.loc);
case types_1.SyntaxType.StringLiteral:
return (0, factory_1.createStringLiteral)(next.text, next.loc);
case types_1.SyntaxType.IntegerLiteral:
case types_1.SyntaxType.HexLiteral:
return parseIntValue(next);
case types_1.SyntaxType.FloatLiteral:
case types_1.SyntaxType.ExponentialLiteral:
return parseDoubleValue(next);
case types_1.SyntaxType.TrueKeyword:
return (0, factory_1.createBooleanLiteral)(true, next.loc);
case types_1.SyntaxType.FalseKeyword:
return (0, factory_1.createBooleanLiteral)(false, next.loc);
case types_1.SyntaxType.LeftBraceToken:
return parseMapValue();
case types_1.SyntaxType.LeftBracketToken:
return parseListValue();
default:
return null;
}
}
function parseIntValue(token) {
switch (token.type) {
case types_1.SyntaxType.IntegerLiteral:
return (0, factory_1.createIntConstant)((0, factory_1.createIntegerLiteral)(token.text, token.loc), token.loc);
case types_1.SyntaxType.HexLiteral:
return (0, factory_1.createIntConstant)((0, factory_1.createHexLiteral)(token.text, token.loc), token.loc);
default:
throw reportError(`IntConstant expected but found: ${token.type}`);
}
}
function parseDoubleValue(token) {
switch (token.type) {
case types_1.SyntaxType.FloatLiteral:
return (0, factory_1.createDoubleConstant)((0, factory_1.createFloatLiteral)(token.text, token.loc), token.loc);
case types_1.SyntaxType.ExponentialLiteral:
return (0, factory_1.createDoubleConstant)((0, factory_1.createExponentialLiteral)(token.text, token.loc), token.loc);
default:
throw reportError(`DoubleConstant expected but found: ${token.type}`);
}
}
// ConstMap → '{' (ConstValue ':' ConstValue ListSeparator?)* '}'
function parseMapValue() {
// The parseValue method has already advanced the cursor
const startLoc = currentToken().loc;
const properties = check(types_1.SyntaxType.RightBraceToken)
? []
: readMapValues();
const _closeBrace = consume(types_1.SyntaxType.RightBraceToken);
const closeBrace = requireValue(_closeBrace, `Closing brace missing from map definition`);
const endLoc = closeBrace.loc;
const location = {
start: startLoc.start,
end: endLoc.end,
};
return (0, factory_1.createConstMap)(properties, location);
}
// ConstList → '[' (ConstValue ListSeparator?)* ']'
function parseListValue() {
// The parseValue method has already advanced the cursor
const startLoc = currentToken().loc;
const elements = check(types_1.SyntaxType.RightBracketToken)
? []
: readListValues();
const _closeBrace = consume(types_1.SyntaxType.RightBracketToken);
const closeBrace = requireValue(_closeBrace, `Closing square-bracket missing from list definition`);
const endLoc = closeBrace.loc;
return (0, factory_1.createConstList)(elements, {
start: startLoc.start,
end: endLoc.end,
});
}
function readMapValues() {
const properties = [];
while (true) {
const _key = parseValue();
const key = requireValue(_key, 'Key expected for map value');
const semicolon = consume(types_1.SyntaxType.ColonToken);
requireValue(semicolon, `Semicolon expected after key in map property assignment`);
const _value = parseValue();
const value = requireValue(_value, '');
properties.push((0, factory_1.creataePropertyAssignment)(key, value, {
start: key.loc.start,
end: value.loc.end,
}));
if (check(types_1.SyntaxType.CommaToken)) {
advance();
// trailing comma
if (check(types_1.SyntaxType.RightBraceToken)) {
break;
}
}
else {
break;
}
}
return properties;
}
function readListValues() {
const elements = [];
while (true) {
const value = parseValue();
if (value !== null) {
elements.push(value);
}
if (check(types_1.SyntaxType.CommaToken, types_1.SyntaxType.SemicolonToken)) {
advance();
// trailing comma
if (check(types_1.SyntaxType.RightBracketToken)) {
break;
}
}
else {
break;
}
}
return elements;
}
// FunctionType → FieldType | 'void'
function parseFunctionType() {
const typeToken = consume(types_1.SyntaxType.VoidKeyword);
if (typeToken !== null) {
return {
type: types_1.SyntaxType.VoidKeyword,
loc: typeToken.loc,
};
}
else {
return parseFieldType();
}
}
// FieldType → Identifier | BaseType | ContainerType
function parseFieldType() {
const typeToken = advance();
switch (typeToken.type) {
case types_1.SyntaxType.Identifier:
return (0, factory_1.createIdentifier)(typeToken.text, typeToken.loc, parseAnnotations());
case types_1.SyntaxType.MapKeyword:
return parseMapType();
case types_1.SyntaxType.ListKeyword:
return parseListType();
case types_1.SyntaxType.SetKeyword:
return parseSetType();
case types_1.SyntaxType.BinaryKeyword:
case types_1.SyntaxType.BoolKeyword:
case types_1.SyntaxType.ByteKeyword:
case types_1.SyntaxType.StringKeyword:
case types_1.SyntaxType.I8Keyword:
case types_1.SyntaxType.I16Keyword:
case types_1.SyntaxType.I32Keyword:
case types_1.SyntaxType.I64Keyword:
case types_1.SyntaxType.DoubleKeyword:
return (0, factory_1.createKeywordFieldType)(typeToken.type, typeToken.loc, parseAnnotations());
default:
throw reportError(`FieldType expected but found: ${typeToken.type}`);
}
}
// MapType → 'map' CppType? '<' FieldType ',' FieldType '>'
function parseMapType() {
const _openBracket = consume(types_1.SyntaxType.LessThanToken);
const openBracket = requireValue(_openBracket, `Map needs to defined contained types`);
const keyType = parseFieldType();
const _commaToken = consume(types_1.SyntaxType.CommaToken);
const commaToken = requireValue(_commaToken, `Comma expected to separate map types <key, value>`);
const valueType = parseFieldType();
const _closeBracket = consume(types_1.SyntaxType.GreaterThanToken);
const closeBracket = requireValue(_closeBracket, `Map needs to defined contained types`);
const location = {
start: openBracket.loc.start,
end: closeBracket.loc.end,
};
return (0, factory_1.createMapFieldType)(keyType, valueType, location, parseAnnotations());
}
// SetType → 'set' CppType? '<' FieldType '>'
function parseSetType() {
const _openBracket = consume(types_1.SyntaxType.LessThanToken);
const openBracket = requireValue(_openBracket, `Map needs to defined contained types`);
const valueType = parseFieldType();
const _closeBracket = consume(types_1.SyntaxType.GreaterThanToken);
const closeBracket = requireValue(_closeBracket, `Map needs to defined contained types`);
return {
type: types_1.SyntaxType.SetType,
valueType,
loc: {
start: openBracket.loc.start,
end: closeBracket.loc.end,
},
annotations: parseAnnotations(),
};
}
// ListType → 'list' '<' FieldType '>' CppType?
function parseListType() {
const _openBracket = consume(types_1.SyntaxType.LessThanToken);
const openBracket = requireValue(_openBracket, `Map needs to defined contained types`);
const valueType = parseFieldType();
const _closeBracket = consume(types_1.SyntaxType.GreaterThanToken);
const closeBracket = requireValue(_closeBracket, `Map needs to defined contained types`);
return {
type: types_1.SyntaxType.ListType,
valueType,
loc: {
start: openBracket.loc.start,
end: closeBracket.loc.end,
},
annotations: parseAnnotations(),
};
}
function consumeComments() {
while (true) {
const next = tokens[currentIndex];
switch (next.type) {
case types_1.SyntaxType.CommentBlock:
comments.push({
type: next.type,
value: next.text.split('\n'),
loc: next.loc,
});
currentIndex++;
break;
case types_1.SyntaxType.CommentLine:
comments.push({
type: next.type,
value: next.text,
loc: next.loc,
});
currentIndex++;
break;
default:
return;
}
}
}
function currentToken() {
consumeComments();
return tokens[currentIndex];
}
function previousToken() {
return tokens[currentIndex - 1];
}
function peek() {
return tokens[currentIndex + 1];
}
// Does the current token match the given type
function check(...types) {
for (const type of types) {
if (type === currentToken().type) {
return true;
}
}
return false;
}
// Does the current token match the given text
function checkText(...strs) {
for (const str of strs) {
if (str === currentToken().text) {
return true;
}
}
return false;
}
// requireToken the current token to match given type and advance, otherwise return null
function consume(...types) {
for (const type of types) {
if (check(type)) {
return advance();
}
}
return null;
}
// Move the cursor forward and return the previous token
function advance() {
if (!isAtEnd()) {
currentIndex += 1;
}
return previousToken();
}
function isAtEnd() {
return (currentIndex >= tokens.length ||
currentToken().type === types_1.SyntaxType.EOF);
}
function getComments() {
const current = comments;
comments = [];
return current;
}
function reportError(msg) {
return new ParseError(msg, previousToken().loc);
}
// Throw if the given value doesn't exist.
function requireValue(val, msg) {
if (val === null || val === undefined) {
throw reportError(msg);
}
else {
return val;
}
}
return {
parse,
synchronize,
};
}
exports.createParser = createParser;
//# sourceMappingURL=parser.js.map