1500 lines
35 KiB
JavaScript
1500 lines
35 KiB
JavaScript
import inspect from '../jsutils/inspect';
|
|
import defineToJSON from '../jsutils/defineToJSON';
|
|
import { Source } from './source';
|
|
import { syntaxError } from '../error/syntaxError';
|
|
import { TokenKind } from './tokenKind';
|
|
import { getTokenDesc, createLexer } from './lexer';
|
|
import { Kind } from './kinds';
|
|
import { DirectiveLocation } from './directiveLocation';
|
|
/**
|
|
* Configuration options to control parser behavior
|
|
*/
|
|
|
|
/**
|
|
* Given a GraphQL source, parses it into a Document.
|
|
* Throws GraphQLError if a syntax error is encountered.
|
|
*/
|
|
export function parse(source, options) {
|
|
var sourceObj = typeof source === 'string' ? new Source(source) : source;
|
|
|
|
if (!(sourceObj instanceof Source)) {
|
|
throw new TypeError("Must provide Source. Received: ".concat(inspect(sourceObj)));
|
|
}
|
|
|
|
var lexer = createLexer(sourceObj, options || {});
|
|
return parseDocument(lexer);
|
|
}
|
|
/**
|
|
* Given a string containing a GraphQL value (ex. `[42]`), parse the AST for
|
|
* that value.
|
|
* Throws GraphQLError if a syntax error is encountered.
|
|
*
|
|
* This is useful within tools that operate upon GraphQL Values directly and
|
|
* in isolation of complete GraphQL documents.
|
|
*
|
|
* Consider providing the results to the utility function: valueFromAST().
|
|
*/
|
|
|
|
export function parseValue(source, options) {
|
|
var sourceObj = typeof source === 'string' ? new Source(source) : source;
|
|
var lexer = createLexer(sourceObj, options || {});
|
|
expectToken(lexer, TokenKind.SOF);
|
|
var value = parseValueLiteral(lexer, false);
|
|
expectToken(lexer, TokenKind.EOF);
|
|
return value;
|
|
}
|
|
/**
|
|
* Given a string containing a GraphQL Type (ex. `[Int!]`), parse the AST for
|
|
* that type.
|
|
* Throws GraphQLError if a syntax error is encountered.
|
|
*
|
|
* This is useful within tools that operate upon GraphQL Types directly and
|
|
* in isolation of complete GraphQL documents.
|
|
*
|
|
* Consider providing the results to the utility function: typeFromAST().
|
|
*/
|
|
|
|
export function parseType(source, options) {
|
|
var sourceObj = typeof source === 'string' ? new Source(source) : source;
|
|
var lexer = createLexer(sourceObj, options || {});
|
|
expectToken(lexer, TokenKind.SOF);
|
|
var type = parseTypeReference(lexer);
|
|
expectToken(lexer, TokenKind.EOF);
|
|
return type;
|
|
}
|
|
/**
|
|
* Converts a name lex token into a name parse node.
|
|
*/
|
|
|
|
function parseName(lexer) {
|
|
var token = expectToken(lexer, TokenKind.NAME);
|
|
return {
|
|
kind: Kind.NAME,
|
|
value: token.value,
|
|
loc: loc(lexer, token)
|
|
};
|
|
} // Implements the parsing rules in the Document section.
|
|
|
|
/**
|
|
* Document : Definition+
|
|
*/
|
|
|
|
|
|
function parseDocument(lexer) {
|
|
var start = lexer.token;
|
|
return {
|
|
kind: Kind.DOCUMENT,
|
|
definitions: many(lexer, TokenKind.SOF, parseDefinition, TokenKind.EOF),
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* Definition :
|
|
* - ExecutableDefinition
|
|
* - TypeSystemDefinition
|
|
* - TypeSystemExtension
|
|
*/
|
|
|
|
|
|
function parseDefinition(lexer) {
|
|
if (peek(lexer, TokenKind.NAME)) {
|
|
switch (lexer.token.value) {
|
|
case 'query':
|
|
case 'mutation':
|
|
case 'subscription':
|
|
case 'fragment':
|
|
return parseExecutableDefinition(lexer);
|
|
|
|
case 'schema':
|
|
case 'scalar':
|
|
case 'type':
|
|
case 'interface':
|
|
case 'union':
|
|
case 'enum':
|
|
case 'input':
|
|
case 'directive':
|
|
return parseTypeSystemDefinition(lexer);
|
|
|
|
case 'extend':
|
|
return parseTypeSystemExtension(lexer);
|
|
}
|
|
} else if (peek(lexer, TokenKind.BRACE_L)) {
|
|
return parseExecutableDefinition(lexer);
|
|
} else if (peekDescription(lexer)) {
|
|
return parseTypeSystemDefinition(lexer);
|
|
}
|
|
|
|
throw unexpected(lexer);
|
|
}
|
|
/**
|
|
* ExecutableDefinition :
|
|
* - OperationDefinition
|
|
* - FragmentDefinition
|
|
*/
|
|
|
|
|
|
function parseExecutableDefinition(lexer) {
|
|
if (peek(lexer, TokenKind.NAME)) {
|
|
switch (lexer.token.value) {
|
|
case 'query':
|
|
case 'mutation':
|
|
case 'subscription':
|
|
return parseOperationDefinition(lexer);
|
|
|
|
case 'fragment':
|
|
return parseFragmentDefinition(lexer);
|
|
}
|
|
} else if (peek(lexer, TokenKind.BRACE_L)) {
|
|
return parseOperationDefinition(lexer);
|
|
}
|
|
|
|
throw unexpected(lexer);
|
|
} // Implements the parsing rules in the Operations section.
|
|
|
|
/**
|
|
* OperationDefinition :
|
|
* - SelectionSet
|
|
* - OperationType Name? VariableDefinitions? Directives? SelectionSet
|
|
*/
|
|
|
|
|
|
function parseOperationDefinition(lexer) {
|
|
var start = lexer.token;
|
|
|
|
if (peek(lexer, TokenKind.BRACE_L)) {
|
|
return {
|
|
kind: Kind.OPERATION_DEFINITION,
|
|
operation: 'query',
|
|
name: undefined,
|
|
variableDefinitions: [],
|
|
directives: [],
|
|
selectionSet: parseSelectionSet(lexer),
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
|
|
var operation = parseOperationType(lexer);
|
|
var name;
|
|
|
|
if (peek(lexer, TokenKind.NAME)) {
|
|
name = parseName(lexer);
|
|
}
|
|
|
|
return {
|
|
kind: Kind.OPERATION_DEFINITION,
|
|
operation: operation,
|
|
name: name,
|
|
variableDefinitions: parseVariableDefinitions(lexer),
|
|
directives: parseDirectives(lexer, false),
|
|
selectionSet: parseSelectionSet(lexer),
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* OperationType : one of query mutation subscription
|
|
*/
|
|
|
|
|
|
function parseOperationType(lexer) {
|
|
var operationToken = expectToken(lexer, TokenKind.NAME);
|
|
|
|
switch (operationToken.value) {
|
|
case 'query':
|
|
return 'query';
|
|
|
|
case 'mutation':
|
|
return 'mutation';
|
|
|
|
case 'subscription':
|
|
return 'subscription';
|
|
}
|
|
|
|
throw unexpected(lexer, operationToken);
|
|
}
|
|
/**
|
|
* VariableDefinitions : ( VariableDefinition+ )
|
|
*/
|
|
|
|
|
|
function parseVariableDefinitions(lexer) {
|
|
return peek(lexer, TokenKind.PAREN_L) ? many(lexer, TokenKind.PAREN_L, parseVariableDefinition, TokenKind.PAREN_R) : [];
|
|
}
|
|
/**
|
|
* VariableDefinition : Variable : Type DefaultValue? Directives[Const]?
|
|
*/
|
|
|
|
|
|
function parseVariableDefinition(lexer) {
|
|
var start = lexer.token;
|
|
return {
|
|
kind: Kind.VARIABLE_DEFINITION,
|
|
variable: parseVariable(lexer),
|
|
type: (expectToken(lexer, TokenKind.COLON), parseTypeReference(lexer)),
|
|
defaultValue: expectOptionalToken(lexer, TokenKind.EQUALS) ? parseValueLiteral(lexer, true) : undefined,
|
|
directives: parseDirectives(lexer, true),
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* Variable : $ Name
|
|
*/
|
|
|
|
|
|
function parseVariable(lexer) {
|
|
var start = lexer.token;
|
|
expectToken(lexer, TokenKind.DOLLAR);
|
|
return {
|
|
kind: Kind.VARIABLE,
|
|
name: parseName(lexer),
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* SelectionSet : { Selection+ }
|
|
*/
|
|
|
|
|
|
function parseSelectionSet(lexer) {
|
|
var start = lexer.token;
|
|
return {
|
|
kind: Kind.SELECTION_SET,
|
|
selections: many(lexer, TokenKind.BRACE_L, parseSelection, TokenKind.BRACE_R),
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* Selection :
|
|
* - Field
|
|
* - FragmentSpread
|
|
* - InlineFragment
|
|
*/
|
|
|
|
|
|
function parseSelection(lexer) {
|
|
return peek(lexer, TokenKind.SPREAD) ? parseFragment(lexer) : parseField(lexer);
|
|
}
|
|
/**
|
|
* Field : Alias? Name Arguments? Directives? SelectionSet?
|
|
*
|
|
* Alias : Name :
|
|
*/
|
|
|
|
|
|
function parseField(lexer) {
|
|
var start = lexer.token;
|
|
var nameOrAlias = parseName(lexer);
|
|
var alias;
|
|
var name;
|
|
|
|
if (expectOptionalToken(lexer, TokenKind.COLON)) {
|
|
alias = nameOrAlias;
|
|
name = parseName(lexer);
|
|
} else {
|
|
name = nameOrAlias;
|
|
}
|
|
|
|
return {
|
|
kind: Kind.FIELD,
|
|
alias: alias,
|
|
name: name,
|
|
arguments: parseArguments(lexer, false),
|
|
directives: parseDirectives(lexer, false),
|
|
selectionSet: peek(lexer, TokenKind.BRACE_L) ? parseSelectionSet(lexer) : undefined,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* Arguments[Const] : ( Argument[?Const]+ )
|
|
*/
|
|
|
|
|
|
function parseArguments(lexer, isConst) {
|
|
var item = isConst ? parseConstArgument : parseArgument;
|
|
return peek(lexer, TokenKind.PAREN_L) ? many(lexer, TokenKind.PAREN_L, item, TokenKind.PAREN_R) : [];
|
|
}
|
|
/**
|
|
* Argument[Const] : Name : Value[?Const]
|
|
*/
|
|
|
|
|
|
function parseArgument(lexer) {
|
|
var start = lexer.token;
|
|
var name = parseName(lexer);
|
|
expectToken(lexer, TokenKind.COLON);
|
|
return {
|
|
kind: Kind.ARGUMENT,
|
|
name: name,
|
|
value: parseValueLiteral(lexer, false),
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
|
|
function parseConstArgument(lexer) {
|
|
var start = lexer.token;
|
|
return {
|
|
kind: Kind.ARGUMENT,
|
|
name: parseName(lexer),
|
|
value: (expectToken(lexer, TokenKind.COLON), parseConstValue(lexer)),
|
|
loc: loc(lexer, start)
|
|
};
|
|
} // Implements the parsing rules in the Fragments section.
|
|
|
|
/**
|
|
* Corresponds to both FragmentSpread and InlineFragment in the spec.
|
|
*
|
|
* FragmentSpread : ... FragmentName Directives?
|
|
*
|
|
* InlineFragment : ... TypeCondition? Directives? SelectionSet
|
|
*/
|
|
|
|
|
|
function parseFragment(lexer) {
|
|
var start = lexer.token;
|
|
expectToken(lexer, TokenKind.SPREAD);
|
|
var hasTypeCondition = expectOptionalKeyword(lexer, 'on');
|
|
|
|
if (!hasTypeCondition && peek(lexer, TokenKind.NAME)) {
|
|
return {
|
|
kind: Kind.FRAGMENT_SPREAD,
|
|
name: parseFragmentName(lexer),
|
|
directives: parseDirectives(lexer, false),
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
|
|
return {
|
|
kind: Kind.INLINE_FRAGMENT,
|
|
typeCondition: hasTypeCondition ? parseNamedType(lexer) : undefined,
|
|
directives: parseDirectives(lexer, false),
|
|
selectionSet: parseSelectionSet(lexer),
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* FragmentDefinition :
|
|
* - fragment FragmentName on TypeCondition Directives? SelectionSet
|
|
*
|
|
* TypeCondition : NamedType
|
|
*/
|
|
|
|
|
|
function parseFragmentDefinition(lexer) {
|
|
var start = lexer.token;
|
|
expectKeyword(lexer, 'fragment'); // Experimental support for defining variables within fragments changes
|
|
// the grammar of FragmentDefinition:
|
|
// - fragment FragmentName VariableDefinitions? on TypeCondition Directives? SelectionSet
|
|
|
|
if (lexer.options.experimentalFragmentVariables) {
|
|
return {
|
|
kind: Kind.FRAGMENT_DEFINITION,
|
|
name: parseFragmentName(lexer),
|
|
variableDefinitions: parseVariableDefinitions(lexer),
|
|
typeCondition: (expectKeyword(lexer, 'on'), parseNamedType(lexer)),
|
|
directives: parseDirectives(lexer, false),
|
|
selectionSet: parseSelectionSet(lexer),
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
|
|
return {
|
|
kind: Kind.FRAGMENT_DEFINITION,
|
|
name: parseFragmentName(lexer),
|
|
typeCondition: (expectKeyword(lexer, 'on'), parseNamedType(lexer)),
|
|
directives: parseDirectives(lexer, false),
|
|
selectionSet: parseSelectionSet(lexer),
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* FragmentName : Name but not `on`
|
|
*/
|
|
|
|
|
|
function parseFragmentName(lexer) {
|
|
if (lexer.token.value === 'on') {
|
|
throw unexpected(lexer);
|
|
}
|
|
|
|
return parseName(lexer);
|
|
} // Implements the parsing rules in the Values section.
|
|
|
|
/**
|
|
* Value[Const] :
|
|
* - [~Const] Variable
|
|
* - IntValue
|
|
* - FloatValue
|
|
* - StringValue
|
|
* - BooleanValue
|
|
* - NullValue
|
|
* - EnumValue
|
|
* - ListValue[?Const]
|
|
* - ObjectValue[?Const]
|
|
*
|
|
* BooleanValue : one of `true` `false`
|
|
*
|
|
* NullValue : `null`
|
|
*
|
|
* EnumValue : Name but not `true`, `false` or `null`
|
|
*/
|
|
|
|
|
|
function parseValueLiteral(lexer, isConst) {
|
|
var token = lexer.token;
|
|
|
|
switch (token.kind) {
|
|
case TokenKind.BRACKET_L:
|
|
return parseList(lexer, isConst);
|
|
|
|
case TokenKind.BRACE_L:
|
|
return parseObject(lexer, isConst);
|
|
|
|
case TokenKind.INT:
|
|
lexer.advance();
|
|
return {
|
|
kind: Kind.INT,
|
|
value: token.value,
|
|
loc: loc(lexer, token)
|
|
};
|
|
|
|
case TokenKind.FLOAT:
|
|
lexer.advance();
|
|
return {
|
|
kind: Kind.FLOAT,
|
|
value: token.value,
|
|
loc: loc(lexer, token)
|
|
};
|
|
|
|
case TokenKind.STRING:
|
|
case TokenKind.BLOCK_STRING:
|
|
return parseStringLiteral(lexer);
|
|
|
|
case TokenKind.NAME:
|
|
if (token.value === 'true' || token.value === 'false') {
|
|
lexer.advance();
|
|
return {
|
|
kind: Kind.BOOLEAN,
|
|
value: token.value === 'true',
|
|
loc: loc(lexer, token)
|
|
};
|
|
} else if (token.value === 'null') {
|
|
lexer.advance();
|
|
return {
|
|
kind: Kind.NULL,
|
|
loc: loc(lexer, token)
|
|
};
|
|
}
|
|
|
|
lexer.advance();
|
|
return {
|
|
kind: Kind.ENUM,
|
|
value: token.value,
|
|
loc: loc(lexer, token)
|
|
};
|
|
|
|
case TokenKind.DOLLAR:
|
|
if (!isConst) {
|
|
return parseVariable(lexer);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
throw unexpected(lexer);
|
|
}
|
|
|
|
function parseStringLiteral(lexer) {
|
|
var token = lexer.token;
|
|
lexer.advance();
|
|
return {
|
|
kind: Kind.STRING,
|
|
value: token.value,
|
|
block: token.kind === TokenKind.BLOCK_STRING,
|
|
loc: loc(lexer, token)
|
|
};
|
|
}
|
|
|
|
export function parseConstValue(lexer) {
|
|
return parseValueLiteral(lexer, true);
|
|
}
|
|
|
|
function parseValueValue(lexer) {
|
|
return parseValueLiteral(lexer, false);
|
|
}
|
|
/**
|
|
* ListValue[Const] :
|
|
* - [ ]
|
|
* - [ Value[?Const]+ ]
|
|
*/
|
|
|
|
|
|
function parseList(lexer, isConst) {
|
|
var start = lexer.token;
|
|
var item = isConst ? parseConstValue : parseValueValue;
|
|
return {
|
|
kind: Kind.LIST,
|
|
values: any(lexer, TokenKind.BRACKET_L, item, TokenKind.BRACKET_R),
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* ObjectValue[Const] :
|
|
* - { }
|
|
* - { ObjectField[?Const]+ }
|
|
*/
|
|
|
|
|
|
function parseObject(lexer, isConst) {
|
|
var start = lexer.token;
|
|
|
|
var item = function item() {
|
|
return parseObjectField(lexer, isConst);
|
|
};
|
|
|
|
return {
|
|
kind: Kind.OBJECT,
|
|
fields: any(lexer, TokenKind.BRACE_L, item, TokenKind.BRACE_R),
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* ObjectField[Const] : Name : Value[?Const]
|
|
*/
|
|
|
|
|
|
function parseObjectField(lexer, isConst) {
|
|
var start = lexer.token;
|
|
var name = parseName(lexer);
|
|
expectToken(lexer, TokenKind.COLON);
|
|
return {
|
|
kind: Kind.OBJECT_FIELD,
|
|
name: name,
|
|
value: parseValueLiteral(lexer, isConst),
|
|
loc: loc(lexer, start)
|
|
};
|
|
} // Implements the parsing rules in the Directives section.
|
|
|
|
/**
|
|
* Directives[Const] : Directive[?Const]+
|
|
*/
|
|
|
|
|
|
function parseDirectives(lexer, isConst) {
|
|
var directives = [];
|
|
|
|
while (peek(lexer, TokenKind.AT)) {
|
|
directives.push(parseDirective(lexer, isConst));
|
|
}
|
|
|
|
return directives;
|
|
}
|
|
/**
|
|
* Directive[Const] : @ Name Arguments[?Const]?
|
|
*/
|
|
|
|
|
|
function parseDirective(lexer, isConst) {
|
|
var start = lexer.token;
|
|
expectToken(lexer, TokenKind.AT);
|
|
return {
|
|
kind: Kind.DIRECTIVE,
|
|
name: parseName(lexer),
|
|
arguments: parseArguments(lexer, isConst),
|
|
loc: loc(lexer, start)
|
|
};
|
|
} // Implements the parsing rules in the Types section.
|
|
|
|
/**
|
|
* Type :
|
|
* - NamedType
|
|
* - ListType
|
|
* - NonNullType
|
|
*/
|
|
|
|
|
|
export function parseTypeReference(lexer) {
|
|
var start = lexer.token;
|
|
var type;
|
|
|
|
if (expectOptionalToken(lexer, TokenKind.BRACKET_L)) {
|
|
type = parseTypeReference(lexer);
|
|
expectToken(lexer, TokenKind.BRACKET_R);
|
|
type = {
|
|
kind: Kind.LIST_TYPE,
|
|
type: type,
|
|
loc: loc(lexer, start)
|
|
};
|
|
} else {
|
|
type = parseNamedType(lexer);
|
|
}
|
|
|
|
if (expectOptionalToken(lexer, TokenKind.BANG)) {
|
|
return {
|
|
kind: Kind.NON_NULL_TYPE,
|
|
type: type,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
|
|
return type;
|
|
}
|
|
/**
|
|
* NamedType : Name
|
|
*/
|
|
|
|
export function parseNamedType(lexer) {
|
|
var start = lexer.token;
|
|
return {
|
|
kind: Kind.NAMED_TYPE,
|
|
name: parseName(lexer),
|
|
loc: loc(lexer, start)
|
|
};
|
|
} // Implements the parsing rules in the Type Definition section.
|
|
|
|
/**
|
|
* TypeSystemDefinition :
|
|
* - SchemaDefinition
|
|
* - TypeDefinition
|
|
* - DirectiveDefinition
|
|
*
|
|
* TypeDefinition :
|
|
* - ScalarTypeDefinition
|
|
* - ObjectTypeDefinition
|
|
* - InterfaceTypeDefinition
|
|
* - UnionTypeDefinition
|
|
* - EnumTypeDefinition
|
|
* - InputObjectTypeDefinition
|
|
*/
|
|
|
|
function parseTypeSystemDefinition(lexer) {
|
|
// Many definitions begin with a description and require a lookahead.
|
|
var keywordToken = peekDescription(lexer) ? lexer.lookahead() : lexer.token;
|
|
|
|
if (keywordToken.kind === TokenKind.NAME) {
|
|
switch (keywordToken.value) {
|
|
case 'schema':
|
|
return parseSchemaDefinition(lexer);
|
|
|
|
case 'scalar':
|
|
return parseScalarTypeDefinition(lexer);
|
|
|
|
case 'type':
|
|
return parseObjectTypeDefinition(lexer);
|
|
|
|
case 'interface':
|
|
return parseInterfaceTypeDefinition(lexer);
|
|
|
|
case 'union':
|
|
return parseUnionTypeDefinition(lexer);
|
|
|
|
case 'enum':
|
|
return parseEnumTypeDefinition(lexer);
|
|
|
|
case 'input':
|
|
return parseInputObjectTypeDefinition(lexer);
|
|
|
|
case 'directive':
|
|
return parseDirectiveDefinition(lexer);
|
|
}
|
|
}
|
|
|
|
throw unexpected(lexer, keywordToken);
|
|
}
|
|
|
|
function peekDescription(lexer) {
|
|
return peek(lexer, TokenKind.STRING) || peek(lexer, TokenKind.BLOCK_STRING);
|
|
}
|
|
/**
|
|
* Description : StringValue
|
|
*/
|
|
|
|
|
|
function parseDescription(lexer) {
|
|
if (peekDescription(lexer)) {
|
|
return parseStringLiteral(lexer);
|
|
}
|
|
}
|
|
/**
|
|
* SchemaDefinition : schema Directives[Const]? { OperationTypeDefinition+ }
|
|
*/
|
|
|
|
|
|
function parseSchemaDefinition(lexer) {
|
|
var start = lexer.token;
|
|
expectKeyword(lexer, 'schema');
|
|
var directives = parseDirectives(lexer, true);
|
|
var operationTypes = many(lexer, TokenKind.BRACE_L, parseOperationTypeDefinition, TokenKind.BRACE_R);
|
|
return {
|
|
kind: Kind.SCHEMA_DEFINITION,
|
|
directives: directives,
|
|
operationTypes: operationTypes,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* OperationTypeDefinition : OperationType : NamedType
|
|
*/
|
|
|
|
|
|
function parseOperationTypeDefinition(lexer) {
|
|
var start = lexer.token;
|
|
var operation = parseOperationType(lexer);
|
|
expectToken(lexer, TokenKind.COLON);
|
|
var type = parseNamedType(lexer);
|
|
return {
|
|
kind: Kind.OPERATION_TYPE_DEFINITION,
|
|
operation: operation,
|
|
type: type,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* ScalarTypeDefinition : Description? scalar Name Directives[Const]?
|
|
*/
|
|
|
|
|
|
function parseScalarTypeDefinition(lexer) {
|
|
var start = lexer.token;
|
|
var description = parseDescription(lexer);
|
|
expectKeyword(lexer, 'scalar');
|
|
var name = parseName(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
return {
|
|
kind: Kind.SCALAR_TYPE_DEFINITION,
|
|
description: description,
|
|
name: name,
|
|
directives: directives,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* ObjectTypeDefinition :
|
|
* Description?
|
|
* type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition?
|
|
*/
|
|
|
|
|
|
function parseObjectTypeDefinition(lexer) {
|
|
var start = lexer.token;
|
|
var description = parseDescription(lexer);
|
|
expectKeyword(lexer, 'type');
|
|
var name = parseName(lexer);
|
|
var interfaces = parseImplementsInterfaces(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
var fields = parseFieldsDefinition(lexer);
|
|
return {
|
|
kind: Kind.OBJECT_TYPE_DEFINITION,
|
|
description: description,
|
|
name: name,
|
|
interfaces: interfaces,
|
|
directives: directives,
|
|
fields: fields,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* ImplementsInterfaces :
|
|
* - implements `&`? NamedType
|
|
* - ImplementsInterfaces & NamedType
|
|
*/
|
|
|
|
|
|
function parseImplementsInterfaces(lexer) {
|
|
var types = [];
|
|
|
|
if (expectOptionalKeyword(lexer, 'implements')) {
|
|
// Optional leading ampersand
|
|
expectOptionalToken(lexer, TokenKind.AMP);
|
|
|
|
do {
|
|
types.push(parseNamedType(lexer));
|
|
} while (expectOptionalToken(lexer, TokenKind.AMP) || // Legacy support for the SDL?
|
|
lexer.options.allowLegacySDLImplementsInterfaces && peek(lexer, TokenKind.NAME));
|
|
}
|
|
|
|
return types;
|
|
}
|
|
/**
|
|
* FieldsDefinition : { FieldDefinition+ }
|
|
*/
|
|
|
|
|
|
function parseFieldsDefinition(lexer) {
|
|
// Legacy support for the SDL?
|
|
if (lexer.options.allowLegacySDLEmptyFields && peek(lexer, TokenKind.BRACE_L) && lexer.lookahead().kind === TokenKind.BRACE_R) {
|
|
lexer.advance();
|
|
lexer.advance();
|
|
return [];
|
|
}
|
|
|
|
return peek(lexer, TokenKind.BRACE_L) ? many(lexer, TokenKind.BRACE_L, parseFieldDefinition, TokenKind.BRACE_R) : [];
|
|
}
|
|
/**
|
|
* FieldDefinition :
|
|
* - Description? Name ArgumentsDefinition? : Type Directives[Const]?
|
|
*/
|
|
|
|
|
|
function parseFieldDefinition(lexer) {
|
|
var start = lexer.token;
|
|
var description = parseDescription(lexer);
|
|
var name = parseName(lexer);
|
|
var args = parseArgumentDefs(lexer);
|
|
expectToken(lexer, TokenKind.COLON);
|
|
var type = parseTypeReference(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
return {
|
|
kind: Kind.FIELD_DEFINITION,
|
|
description: description,
|
|
name: name,
|
|
arguments: args,
|
|
type: type,
|
|
directives: directives,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* ArgumentsDefinition : ( InputValueDefinition+ )
|
|
*/
|
|
|
|
|
|
function parseArgumentDefs(lexer) {
|
|
if (!peek(lexer, TokenKind.PAREN_L)) {
|
|
return [];
|
|
}
|
|
|
|
return many(lexer, TokenKind.PAREN_L, parseInputValueDef, TokenKind.PAREN_R);
|
|
}
|
|
/**
|
|
* InputValueDefinition :
|
|
* - Description? Name : Type DefaultValue? Directives[Const]?
|
|
*/
|
|
|
|
|
|
function parseInputValueDef(lexer) {
|
|
var start = lexer.token;
|
|
var description = parseDescription(lexer);
|
|
var name = parseName(lexer);
|
|
expectToken(lexer, TokenKind.COLON);
|
|
var type = parseTypeReference(lexer);
|
|
var defaultValue;
|
|
|
|
if (expectOptionalToken(lexer, TokenKind.EQUALS)) {
|
|
defaultValue = parseConstValue(lexer);
|
|
}
|
|
|
|
var directives = parseDirectives(lexer, true);
|
|
return {
|
|
kind: Kind.INPUT_VALUE_DEFINITION,
|
|
description: description,
|
|
name: name,
|
|
type: type,
|
|
defaultValue: defaultValue,
|
|
directives: directives,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* InterfaceTypeDefinition :
|
|
* - Description? interface Name Directives[Const]? FieldsDefinition?
|
|
*/
|
|
|
|
|
|
function parseInterfaceTypeDefinition(lexer) {
|
|
var start = lexer.token;
|
|
var description = parseDescription(lexer);
|
|
expectKeyword(lexer, 'interface');
|
|
var name = parseName(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
var fields = parseFieldsDefinition(lexer);
|
|
return {
|
|
kind: Kind.INTERFACE_TYPE_DEFINITION,
|
|
description: description,
|
|
name: name,
|
|
directives: directives,
|
|
fields: fields,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* UnionTypeDefinition :
|
|
* - Description? union Name Directives[Const]? UnionMemberTypes?
|
|
*/
|
|
|
|
|
|
function parseUnionTypeDefinition(lexer) {
|
|
var start = lexer.token;
|
|
var description = parseDescription(lexer);
|
|
expectKeyword(lexer, 'union');
|
|
var name = parseName(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
var types = parseUnionMemberTypes(lexer);
|
|
return {
|
|
kind: Kind.UNION_TYPE_DEFINITION,
|
|
description: description,
|
|
name: name,
|
|
directives: directives,
|
|
types: types,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* UnionMemberTypes :
|
|
* - = `|`? NamedType
|
|
* - UnionMemberTypes | NamedType
|
|
*/
|
|
|
|
|
|
function parseUnionMemberTypes(lexer) {
|
|
var types = [];
|
|
|
|
if (expectOptionalToken(lexer, TokenKind.EQUALS)) {
|
|
// Optional leading pipe
|
|
expectOptionalToken(lexer, TokenKind.PIPE);
|
|
|
|
do {
|
|
types.push(parseNamedType(lexer));
|
|
} while (expectOptionalToken(lexer, TokenKind.PIPE));
|
|
}
|
|
|
|
return types;
|
|
}
|
|
/**
|
|
* EnumTypeDefinition :
|
|
* - Description? enum Name Directives[Const]? EnumValuesDefinition?
|
|
*/
|
|
|
|
|
|
function parseEnumTypeDefinition(lexer) {
|
|
var start = lexer.token;
|
|
var description = parseDescription(lexer);
|
|
expectKeyword(lexer, 'enum');
|
|
var name = parseName(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
var values = parseEnumValuesDefinition(lexer);
|
|
return {
|
|
kind: Kind.ENUM_TYPE_DEFINITION,
|
|
description: description,
|
|
name: name,
|
|
directives: directives,
|
|
values: values,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* EnumValuesDefinition : { EnumValueDefinition+ }
|
|
*/
|
|
|
|
|
|
function parseEnumValuesDefinition(lexer) {
|
|
return peek(lexer, TokenKind.BRACE_L) ? many(lexer, TokenKind.BRACE_L, parseEnumValueDefinition, TokenKind.BRACE_R) : [];
|
|
}
|
|
/**
|
|
* EnumValueDefinition : Description? EnumValue Directives[Const]?
|
|
*
|
|
* EnumValue : Name
|
|
*/
|
|
|
|
|
|
function parseEnumValueDefinition(lexer) {
|
|
var start = lexer.token;
|
|
var description = parseDescription(lexer);
|
|
var name = parseName(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
return {
|
|
kind: Kind.ENUM_VALUE_DEFINITION,
|
|
description: description,
|
|
name: name,
|
|
directives: directives,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* InputObjectTypeDefinition :
|
|
* - Description? input Name Directives[Const]? InputFieldsDefinition?
|
|
*/
|
|
|
|
|
|
function parseInputObjectTypeDefinition(lexer) {
|
|
var start = lexer.token;
|
|
var description = parseDescription(lexer);
|
|
expectKeyword(lexer, 'input');
|
|
var name = parseName(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
var fields = parseInputFieldsDefinition(lexer);
|
|
return {
|
|
kind: Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
|
description: description,
|
|
name: name,
|
|
directives: directives,
|
|
fields: fields,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* InputFieldsDefinition : { InputValueDefinition+ }
|
|
*/
|
|
|
|
|
|
function parseInputFieldsDefinition(lexer) {
|
|
return peek(lexer, TokenKind.BRACE_L) ? many(lexer, TokenKind.BRACE_L, parseInputValueDef, TokenKind.BRACE_R) : [];
|
|
}
|
|
/**
|
|
* TypeSystemExtension :
|
|
* - SchemaExtension
|
|
* - TypeExtension
|
|
*
|
|
* TypeExtension :
|
|
* - ScalarTypeExtension
|
|
* - ObjectTypeExtension
|
|
* - InterfaceTypeExtension
|
|
* - UnionTypeExtension
|
|
* - EnumTypeExtension
|
|
* - InputObjectTypeDefinition
|
|
*/
|
|
|
|
|
|
function parseTypeSystemExtension(lexer) {
|
|
var keywordToken = lexer.lookahead();
|
|
|
|
if (keywordToken.kind === TokenKind.NAME) {
|
|
switch (keywordToken.value) {
|
|
case 'schema':
|
|
return parseSchemaExtension(lexer);
|
|
|
|
case 'scalar':
|
|
return parseScalarTypeExtension(lexer);
|
|
|
|
case 'type':
|
|
return parseObjectTypeExtension(lexer);
|
|
|
|
case 'interface':
|
|
return parseInterfaceTypeExtension(lexer);
|
|
|
|
case 'union':
|
|
return parseUnionTypeExtension(lexer);
|
|
|
|
case 'enum':
|
|
return parseEnumTypeExtension(lexer);
|
|
|
|
case 'input':
|
|
return parseInputObjectTypeExtension(lexer);
|
|
}
|
|
}
|
|
|
|
throw unexpected(lexer, keywordToken);
|
|
}
|
|
/**
|
|
* SchemaExtension :
|
|
* - extend schema Directives[Const]? { OperationTypeDefinition+ }
|
|
* - extend schema Directives[Const]
|
|
*/
|
|
|
|
|
|
function parseSchemaExtension(lexer) {
|
|
var start = lexer.token;
|
|
expectKeyword(lexer, 'extend');
|
|
expectKeyword(lexer, 'schema');
|
|
var directives = parseDirectives(lexer, true);
|
|
var operationTypes = peek(lexer, TokenKind.BRACE_L) ? many(lexer, TokenKind.BRACE_L, parseOperationTypeDefinition, TokenKind.BRACE_R) : [];
|
|
|
|
if (directives.length === 0 && operationTypes.length === 0) {
|
|
throw unexpected(lexer);
|
|
}
|
|
|
|
return {
|
|
kind: Kind.SCHEMA_EXTENSION,
|
|
directives: directives,
|
|
operationTypes: operationTypes,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* ScalarTypeExtension :
|
|
* - extend scalar Name Directives[Const]
|
|
*/
|
|
|
|
|
|
function parseScalarTypeExtension(lexer) {
|
|
var start = lexer.token;
|
|
expectKeyword(lexer, 'extend');
|
|
expectKeyword(lexer, 'scalar');
|
|
var name = parseName(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
|
|
if (directives.length === 0) {
|
|
throw unexpected(lexer);
|
|
}
|
|
|
|
return {
|
|
kind: Kind.SCALAR_TYPE_EXTENSION,
|
|
name: name,
|
|
directives: directives,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* ObjectTypeExtension :
|
|
* - extend type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition
|
|
* - extend type Name ImplementsInterfaces? Directives[Const]
|
|
* - extend type Name ImplementsInterfaces
|
|
*/
|
|
|
|
|
|
function parseObjectTypeExtension(lexer) {
|
|
var start = lexer.token;
|
|
expectKeyword(lexer, 'extend');
|
|
expectKeyword(lexer, 'type');
|
|
var name = parseName(lexer);
|
|
var interfaces = parseImplementsInterfaces(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
var fields = parseFieldsDefinition(lexer);
|
|
|
|
if (interfaces.length === 0 && directives.length === 0 && fields.length === 0) {
|
|
throw unexpected(lexer);
|
|
}
|
|
|
|
return {
|
|
kind: Kind.OBJECT_TYPE_EXTENSION,
|
|
name: name,
|
|
interfaces: interfaces,
|
|
directives: directives,
|
|
fields: fields,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* InterfaceTypeExtension :
|
|
* - extend interface Name Directives[Const]? FieldsDefinition
|
|
* - extend interface Name Directives[Const]
|
|
*/
|
|
|
|
|
|
function parseInterfaceTypeExtension(lexer) {
|
|
var start = lexer.token;
|
|
expectKeyword(lexer, 'extend');
|
|
expectKeyword(lexer, 'interface');
|
|
var name = parseName(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
var fields = parseFieldsDefinition(lexer);
|
|
|
|
if (directives.length === 0 && fields.length === 0) {
|
|
throw unexpected(lexer);
|
|
}
|
|
|
|
return {
|
|
kind: Kind.INTERFACE_TYPE_EXTENSION,
|
|
name: name,
|
|
directives: directives,
|
|
fields: fields,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* UnionTypeExtension :
|
|
* - extend union Name Directives[Const]? UnionMemberTypes
|
|
* - extend union Name Directives[Const]
|
|
*/
|
|
|
|
|
|
function parseUnionTypeExtension(lexer) {
|
|
var start = lexer.token;
|
|
expectKeyword(lexer, 'extend');
|
|
expectKeyword(lexer, 'union');
|
|
var name = parseName(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
var types = parseUnionMemberTypes(lexer);
|
|
|
|
if (directives.length === 0 && types.length === 0) {
|
|
throw unexpected(lexer);
|
|
}
|
|
|
|
return {
|
|
kind: Kind.UNION_TYPE_EXTENSION,
|
|
name: name,
|
|
directives: directives,
|
|
types: types,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* EnumTypeExtension :
|
|
* - extend enum Name Directives[Const]? EnumValuesDefinition
|
|
* - extend enum Name Directives[Const]
|
|
*/
|
|
|
|
|
|
function parseEnumTypeExtension(lexer) {
|
|
var start = lexer.token;
|
|
expectKeyword(lexer, 'extend');
|
|
expectKeyword(lexer, 'enum');
|
|
var name = parseName(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
var values = parseEnumValuesDefinition(lexer);
|
|
|
|
if (directives.length === 0 && values.length === 0) {
|
|
throw unexpected(lexer);
|
|
}
|
|
|
|
return {
|
|
kind: Kind.ENUM_TYPE_EXTENSION,
|
|
name: name,
|
|
directives: directives,
|
|
values: values,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* InputObjectTypeExtension :
|
|
* - extend input Name Directives[Const]? InputFieldsDefinition
|
|
* - extend input Name Directives[Const]
|
|
*/
|
|
|
|
|
|
function parseInputObjectTypeExtension(lexer) {
|
|
var start = lexer.token;
|
|
expectKeyword(lexer, 'extend');
|
|
expectKeyword(lexer, 'input');
|
|
var name = parseName(lexer);
|
|
var directives = parseDirectives(lexer, true);
|
|
var fields = parseInputFieldsDefinition(lexer);
|
|
|
|
if (directives.length === 0 && fields.length === 0) {
|
|
throw unexpected(lexer);
|
|
}
|
|
|
|
return {
|
|
kind: Kind.INPUT_OBJECT_TYPE_EXTENSION,
|
|
name: name,
|
|
directives: directives,
|
|
fields: fields,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* DirectiveDefinition :
|
|
* - Description? directive @ Name ArgumentsDefinition? `repeatable`? on DirectiveLocations
|
|
*/
|
|
|
|
|
|
function parseDirectiveDefinition(lexer) {
|
|
var start = lexer.token;
|
|
var description = parseDescription(lexer);
|
|
expectKeyword(lexer, 'directive');
|
|
expectToken(lexer, TokenKind.AT);
|
|
var name = parseName(lexer);
|
|
var args = parseArgumentDefs(lexer);
|
|
var repeatable = expectOptionalKeyword(lexer, 'repeatable');
|
|
expectKeyword(lexer, 'on');
|
|
var locations = parseDirectiveLocations(lexer);
|
|
return {
|
|
kind: Kind.DIRECTIVE_DEFINITION,
|
|
description: description,
|
|
name: name,
|
|
arguments: args,
|
|
repeatable: repeatable,
|
|
locations: locations,
|
|
loc: loc(lexer, start)
|
|
};
|
|
}
|
|
/**
|
|
* DirectiveLocations :
|
|
* - `|`? DirectiveLocation
|
|
* - DirectiveLocations | DirectiveLocation
|
|
*/
|
|
|
|
|
|
function parseDirectiveLocations(lexer) {
|
|
// Optional leading pipe
|
|
expectOptionalToken(lexer, TokenKind.PIPE);
|
|
var locations = [];
|
|
|
|
do {
|
|
locations.push(parseDirectiveLocation(lexer));
|
|
} while (expectOptionalToken(lexer, TokenKind.PIPE));
|
|
|
|
return locations;
|
|
}
|
|
/*
|
|
* DirectiveLocation :
|
|
* - ExecutableDirectiveLocation
|
|
* - TypeSystemDirectiveLocation
|
|
*
|
|
* ExecutableDirectiveLocation : one of
|
|
* `QUERY`
|
|
* `MUTATION`
|
|
* `SUBSCRIPTION`
|
|
* `FIELD`
|
|
* `FRAGMENT_DEFINITION`
|
|
* `FRAGMENT_SPREAD`
|
|
* `INLINE_FRAGMENT`
|
|
*
|
|
* TypeSystemDirectiveLocation : one of
|
|
* `SCHEMA`
|
|
* `SCALAR`
|
|
* `OBJECT`
|
|
* `FIELD_DEFINITION`
|
|
* `ARGUMENT_DEFINITION`
|
|
* `INTERFACE`
|
|
* `UNION`
|
|
* `ENUM`
|
|
* `ENUM_VALUE`
|
|
* `INPUT_OBJECT`
|
|
* `INPUT_FIELD_DEFINITION`
|
|
*/
|
|
|
|
|
|
function parseDirectiveLocation(lexer) {
|
|
var start = lexer.token;
|
|
var name = parseName(lexer);
|
|
|
|
if (DirectiveLocation[name.value] !== undefined) {
|
|
return name;
|
|
}
|
|
|
|
throw unexpected(lexer, start);
|
|
} // Core parsing utility functions
|
|
|
|
/**
|
|
* Returns a location object, used to identify the place in
|
|
* the source that created a given parsed object.
|
|
*/
|
|
|
|
|
|
function loc(lexer, startToken) {
|
|
if (!lexer.options.noLocation) {
|
|
return new Loc(startToken, lexer.lastToken, lexer.source);
|
|
}
|
|
}
|
|
|
|
function Loc(startToken, endToken, source) {
|
|
this.start = startToken.start;
|
|
this.end = endToken.end;
|
|
this.startToken = startToken;
|
|
this.endToken = endToken;
|
|
this.source = source;
|
|
} // Print a simplified form when appearing in JSON/util.inspect.
|
|
|
|
|
|
defineToJSON(Loc, function () {
|
|
return {
|
|
start: this.start,
|
|
end: this.end
|
|
};
|
|
});
|
|
/**
|
|
* Determines if the next token is of a given kind
|
|
*/
|
|
|
|
function peek(lexer, kind) {
|
|
return lexer.token.kind === kind;
|
|
}
|
|
/**
|
|
* If the next token is of the given kind, return that token after advancing
|
|
* the lexer. Otherwise, do not change the parser state and throw an error.
|
|
*/
|
|
|
|
|
|
function expectToken(lexer, kind) {
|
|
var token = lexer.token;
|
|
|
|
if (token.kind === kind) {
|
|
lexer.advance();
|
|
return token;
|
|
}
|
|
|
|
throw syntaxError(lexer.source, token.start, "Expected ".concat(kind, ", found ").concat(getTokenDesc(token)));
|
|
}
|
|
/**
|
|
* If the next token is of the given kind, return that token after advancing
|
|
* the lexer. Otherwise, do not change the parser state and return undefined.
|
|
*/
|
|
|
|
|
|
function expectOptionalToken(lexer, kind) {
|
|
var token = lexer.token;
|
|
|
|
if (token.kind === kind) {
|
|
lexer.advance();
|
|
return token;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
/**
|
|
* If the next token is a given keyword, advance the lexer.
|
|
* Otherwise, do not change the parser state and throw an error.
|
|
*/
|
|
|
|
|
|
function expectKeyword(lexer, value) {
|
|
var token = lexer.token;
|
|
|
|
if (token.kind === TokenKind.NAME && token.value === value) {
|
|
lexer.advance();
|
|
} else {
|
|
throw syntaxError(lexer.source, token.start, "Expected \"".concat(value, "\", found ").concat(getTokenDesc(token)));
|
|
}
|
|
}
|
|
/**
|
|
* If the next token is a given keyword, return "true" after advancing
|
|
* the lexer. Otherwise, do not change the parser state and return "false".
|
|
*/
|
|
|
|
|
|
function expectOptionalKeyword(lexer, value) {
|
|
var token = lexer.token;
|
|
|
|
if (token.kind === TokenKind.NAME && token.value === value) {
|
|
lexer.advance();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
/**
|
|
* Helper function for creating an error when an unexpected lexed token
|
|
* is encountered.
|
|
*/
|
|
|
|
|
|
function unexpected(lexer, atToken) {
|
|
var token = atToken || lexer.token;
|
|
return syntaxError(lexer.source, token.start, "Unexpected ".concat(getTokenDesc(token)));
|
|
}
|
|
/**
|
|
* Returns a possibly empty list of parse nodes, determined by
|
|
* the parseFn. This list begins with a lex token of openKind
|
|
* and ends with a lex token of closeKind. Advances the parser
|
|
* to the next lex token after the closing token.
|
|
*/
|
|
|
|
|
|
function any(lexer, openKind, parseFn, closeKind) {
|
|
expectToken(lexer, openKind);
|
|
var nodes = [];
|
|
|
|
while (!expectOptionalToken(lexer, closeKind)) {
|
|
nodes.push(parseFn(lexer));
|
|
}
|
|
|
|
return nodes;
|
|
}
|
|
/**
|
|
* Returns a non-empty list of parse nodes, determined by
|
|
* the parseFn. This list begins with a lex token of openKind
|
|
* and ends with a lex token of closeKind. Advances the parser
|
|
* to the next lex token after the closing token.
|
|
*/
|
|
|
|
|
|
function many(lexer, openKind, parseFn, closeKind) {
|
|
expectToken(lexer, openKind);
|
|
var nodes = [parseFn(lexer)];
|
|
|
|
while (!expectOptionalToken(lexer, closeKind)) {
|
|
nodes.push(parseFn(lexer));
|
|
}
|
|
|
|
return nodes;
|
|
}
|