UNPKG

@dillonkearns/elm-graphql

Version:

<img src="https://cdn.jsdelivr.net/gh/martimatix/logo-graphqelm/logo.svg" alt="dillonearns/elm-graphql logo" width="40%" align="right">

1,026 lines (944 loc) 25.9 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.parse = parse; exports.parseValue = parseValue; exports.parseType = parseType; exports.parseConstValue = parseConstValue; exports.parseTypeReference = parseTypeReference; exports.parseNamedType = parseNamedType; var _source = require('./source'); var _error = require('../error'); var _lexer = require('./lexer'); var _kinds = require('./kinds'); /** * Given a GraphQL source, parses it into a Document. * Throws GraphQLError if a syntax error is encountered. */ /** * Configuration options to control parser behavior */ /** * Copyright (c) 2015-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * */ function parse(source, options) { var sourceObj = typeof source === 'string' ? new _source.Source(source) : source; if (!(sourceObj instanceof _source.Source)) { throw new TypeError('Must provide Source. Received: ' + String(sourceObj)); } var lexer = (0, _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(). */ function parseValue(source, options) { var sourceObj = typeof source === 'string' ? new _source.Source(source) : source; var lexer = (0, _lexer.createLexer)(sourceObj, options || {}); expect(lexer, _lexer.TokenKind.SOF); var value = parseValueLiteral(lexer, false); expect(lexer, _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(). */ function parseType(source, options) { var sourceObj = typeof source === 'string' ? new _source.Source(source) : source; var lexer = (0, _lexer.createLexer)(sourceObj, options || {}); expect(lexer, _lexer.TokenKind.SOF); var type = parseTypeReference(lexer); expect(lexer, _lexer.TokenKind.EOF); return type; } /** * Converts a name lex token into a name parse node. */ function parseName(lexer) { var token = expect(lexer, _lexer.TokenKind.NAME); return { kind: _kinds.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; expect(lexer, _lexer.TokenKind.SOF); var definitions = []; do { definitions.push(parseDefinition(lexer)); } while (!skip(lexer, _lexer.TokenKind.EOF)); return { kind: _kinds.DOCUMENT, definitions: definitions, loc: loc(lexer, start) }; } /** * Definition : * - OperationDefinition * - FragmentDefinition * - TypeSystemDefinition */ function parseDefinition(lexer) { if (peek(lexer, _lexer.TokenKind.BRACE_L)) { return parseOperationDefinition(lexer); } if (peek(lexer, _lexer.TokenKind.NAME)) { switch (lexer.token.value) { // Note: subscription is an experimental non-spec addition. case 'query': case 'mutation': case 'subscription': return parseOperationDefinition(lexer); case 'fragment': return parseFragmentDefinition(lexer); // Note: the Type System IDL is an experimental non-spec addition. case 'schema': case 'scalar': case 'type': case 'interface': case 'union': case 'enum': case 'input': case 'extend': case 'directive': return parseTypeSystemDefinition(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, _lexer.TokenKind.BRACE_L)) { return { kind: _kinds.OPERATION_DEFINITION, operation: 'query', name: null, variableDefinitions: null, directives: [], selectionSet: parseSelectionSet(lexer), loc: loc(lexer, start) }; } var operation = parseOperationType(lexer); var name = void 0; if (peek(lexer, _lexer.TokenKind.NAME)) { name = parseName(lexer); } return { kind: _kinds.OPERATION_DEFINITION, operation: operation, name: name, variableDefinitions: parseVariableDefinitions(lexer), directives: parseDirectives(lexer), selectionSet: parseSelectionSet(lexer), loc: loc(lexer, start) }; } /** * OperationType : one of query mutation subscription */ function parseOperationType(lexer) { var operationToken = expect(lexer, _lexer.TokenKind.NAME); switch (operationToken.value) { case 'query': return 'query'; case 'mutation': return 'mutation'; // Note: subscription is an experimental non-spec addition. case 'subscription': return 'subscription'; } throw unexpected(lexer, operationToken); } /** * VariableDefinitions : ( VariableDefinition+ ) */ function parseVariableDefinitions(lexer) { return peek(lexer, _lexer.TokenKind.PAREN_L) ? many(lexer, _lexer.TokenKind.PAREN_L, parseVariableDefinition, _lexer.TokenKind.PAREN_R) : []; } /** * VariableDefinition : Variable : Type DefaultValue? */ function parseVariableDefinition(lexer) { var start = lexer.token; return { kind: _kinds.VARIABLE_DEFINITION, variable: parseVariable(lexer), type: (expect(lexer, _lexer.TokenKind.COLON), parseTypeReference(lexer)), defaultValue: skip(lexer, _lexer.TokenKind.EQUALS) ? parseValueLiteral(lexer, true) : null, loc: loc(lexer, start) }; } /** * Variable : $ Name */ function parseVariable(lexer) { var start = lexer.token; expect(lexer, _lexer.TokenKind.DOLLAR); return { kind: _kinds.VARIABLE, name: parseName(lexer), loc: loc(lexer, start) }; } /** * SelectionSet : { Selection+ } */ function parseSelectionSet(lexer) { var start = lexer.token; return { kind: _kinds.SELECTION_SET, selections: many(lexer, _lexer.TokenKind.BRACE_L, parseSelection, _lexer.TokenKind.BRACE_R), loc: loc(lexer, start) }; } /** * Selection : * - Field * - FragmentSpread * - InlineFragment */ function parseSelection(lexer) { return peek(lexer, _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 = void 0; var name = void 0; if (skip(lexer, _lexer.TokenKind.COLON)) { alias = nameOrAlias; name = parseName(lexer); } else { alias = null; name = nameOrAlias; } return { kind: _kinds.FIELD, alias: alias, name: name, arguments: parseArguments(lexer), directives: parseDirectives(lexer), selectionSet: peek(lexer, _lexer.TokenKind.BRACE_L) ? parseSelectionSet(lexer) : null, loc: loc(lexer, start) }; } /** * Arguments : ( Argument+ ) */ function parseArguments(lexer) { return peek(lexer, _lexer.TokenKind.PAREN_L) ? many(lexer, _lexer.TokenKind.PAREN_L, parseArgument, _lexer.TokenKind.PAREN_R) : []; } /** * Argument : Name : Value */ function parseArgument(lexer) { var start = lexer.token; return { kind: _kinds.ARGUMENT, name: parseName(lexer), value: (expect(lexer, _lexer.TokenKind.COLON), parseValueLiteral(lexer, false)), 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; expect(lexer, _lexer.TokenKind.SPREAD); if (peek(lexer, _lexer.TokenKind.NAME) && lexer.token.value !== 'on') { return { kind: _kinds.FRAGMENT_SPREAD, name: parseFragmentName(lexer), directives: parseDirectives(lexer), loc: loc(lexer, start) }; } var typeCondition = null; if (lexer.token.value === 'on') { lexer.advance(); typeCondition = parseNamedType(lexer); } return { kind: _kinds.INLINE_FRAGMENT, typeCondition: typeCondition, directives: parseDirectives(lexer), 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'); return { kind: _kinds.FRAGMENT_DEFINITION, name: parseFragmentName(lexer), typeCondition: (expectKeyword(lexer, 'on'), parseNamedType(lexer)), directives: parseDirectives(lexer), 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 _lexer.TokenKind.BRACKET_L: return parseList(lexer, isConst); case _lexer.TokenKind.BRACE_L: return parseObject(lexer, isConst); case _lexer.TokenKind.INT: lexer.advance(); return { kind: _kinds.INT, value: token.value, loc: loc(lexer, token) }; case _lexer.TokenKind.FLOAT: lexer.advance(); return { kind: _kinds.FLOAT, value: token.value, loc: loc(lexer, token) }; case _lexer.TokenKind.STRING: lexer.advance(); return { kind: _kinds.STRING, value: token.value, loc: loc(lexer, token) }; case _lexer.TokenKind.NAME: if (token.value === 'true' || token.value === 'false') { lexer.advance(); return { kind: _kinds.BOOLEAN, value: token.value === 'true', loc: loc(lexer, token) }; } else if (token.value === 'null') { lexer.advance(); return { kind: _kinds.NULL, loc: loc(lexer, token) }; } lexer.advance(); return { kind: _kinds.ENUM, value: token.value, loc: loc(lexer, token) }; case _lexer.TokenKind.DOLLAR: if (!isConst) { return parseVariable(lexer); } break; } throw unexpected(lexer); } 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: _kinds.LIST, values: any(lexer, _lexer.TokenKind.BRACKET_L, item, _lexer.TokenKind.BRACKET_R), loc: loc(lexer, start) }; } /** * ObjectValue[Const] : * - { } * - { ObjectField[?Const]+ } */ function parseObject(lexer, isConst) { var start = lexer.token; expect(lexer, _lexer.TokenKind.BRACE_L); var fields = []; while (!skip(lexer, _lexer.TokenKind.BRACE_R)) { fields.push(parseObjectField(lexer, isConst)); } return { kind: _kinds.OBJECT, fields: fields, loc: loc(lexer, start) }; } /** * ObjectField[Const] : Name : Value[?Const] */ function parseObjectField(lexer, isConst) { var start = lexer.token; return { kind: _kinds.OBJECT_FIELD, name: parseName(lexer), value: (expect(lexer, _lexer.TokenKind.COLON), parseValueLiteral(lexer, isConst)), loc: loc(lexer, start) }; } // Implements the parsing rules in the Directives section. /** * Directives : Directive+ */ function parseDirectives(lexer) { var directives = []; while (peek(lexer, _lexer.TokenKind.AT)) { directives.push(parseDirective(lexer)); } return directives; } /** * Directive : @ Name Arguments? */ function parseDirective(lexer) { var start = lexer.token; expect(lexer, _lexer.TokenKind.AT); return { kind: _kinds.DIRECTIVE, name: parseName(lexer), arguments: parseArguments(lexer), loc: loc(lexer, start) }; } // Implements the parsing rules in the Types section. /** * Type : * - NamedType * - ListType * - NonNullType */ function parseTypeReference(lexer) { var start = lexer.token; var type = void 0; if (skip(lexer, _lexer.TokenKind.BRACKET_L)) { type = parseTypeReference(lexer); expect(lexer, _lexer.TokenKind.BRACKET_R); type = { kind: _kinds.LIST_TYPE, type: type, loc: loc(lexer, start) }; } else { type = parseNamedType(lexer); } if (skip(lexer, _lexer.TokenKind.BANG)) { return { kind: _kinds.NON_NULL_TYPE, type: type, loc: loc(lexer, start) }; } return type; } /** * NamedType : Name */ function parseNamedType(lexer) { var start = lexer.token; return { kind: _kinds.NAMED_TYPE, name: parseName(lexer), loc: loc(lexer, start) }; } // Implements the parsing rules in the Type Definition section. /** * TypeSystemDefinition : * - SchemaDefinition * - TypeDefinition * - TypeExtensionDefinition * - DirectiveDefinition * * TypeDefinition : * - ScalarTypeDefinition * - ObjectTypeDefinition * - InterfaceTypeDefinition * - UnionTypeDefinition * - EnumTypeDefinition * - InputObjectTypeDefinition */ function parseTypeSystemDefinition(lexer) { if (peek(lexer, _lexer.TokenKind.NAME)) { switch (lexer.token.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 'extend': return parseTypeExtensionDefinition(lexer); case 'directive': return parseDirectiveDefinition(lexer); } } throw unexpected(lexer); } /** * SchemaDefinition : schema Directives? { OperationTypeDefinition+ } * * OperationTypeDefinition : OperationType : NamedType */ function parseSchemaDefinition(lexer) { var start = lexer.token; expectKeyword(lexer, 'schema'); var directives = parseDirectives(lexer); var operationTypes = many(lexer, _lexer.TokenKind.BRACE_L, parseOperationTypeDefinition, _lexer.TokenKind.BRACE_R); return { kind: _kinds.SCHEMA_DEFINITION, directives: directives, operationTypes: operationTypes, loc: loc(lexer, start) }; } function parseOperationTypeDefinition(lexer) { var start = lexer.token; var operation = parseOperationType(lexer); expect(lexer, _lexer.TokenKind.COLON); var type = parseNamedType(lexer); return { kind: _kinds.OPERATION_TYPE_DEFINITION, operation: operation, type: type, loc: loc(lexer, start) }; } /** * ScalarTypeDefinition : scalar Name Directives? */ function parseScalarTypeDefinition(lexer) { var start = lexer.token; expectKeyword(lexer, 'scalar'); var name = parseName(lexer); var directives = parseDirectives(lexer); return { kind: _kinds.SCALAR_TYPE_DEFINITION, name: name, directives: directives, loc: loc(lexer, start) }; } /** * ObjectTypeDefinition : * - type Name ImplementsInterfaces? Directives? { FieldDefinition+ } */ function parseObjectTypeDefinition(lexer) { var start = lexer.token; expectKeyword(lexer, 'type'); var name = parseName(lexer); var interfaces = parseImplementsInterfaces(lexer); var directives = parseDirectives(lexer); var fields = any(lexer, _lexer.TokenKind.BRACE_L, parseFieldDefinition, _lexer.TokenKind.BRACE_R); return { kind: _kinds.OBJECT_TYPE_DEFINITION, name: name, interfaces: interfaces, directives: directives, fields: fields, loc: loc(lexer, start) }; } /** * ImplementsInterfaces : implements NamedType+ */ function parseImplementsInterfaces(lexer) { var types = []; if (lexer.token.value === 'implements') { lexer.advance(); do { types.push(parseNamedType(lexer)); } while (peek(lexer, _lexer.TokenKind.NAME)); } return types; } /** * FieldDefinition : Name ArgumentsDefinition? : Type Directives? */ function parseFieldDefinition(lexer) { var start = lexer.token; var name = parseName(lexer); var args = parseArgumentDefs(lexer); expect(lexer, _lexer.TokenKind.COLON); var type = parseTypeReference(lexer); var directives = parseDirectives(lexer); return { kind: _kinds.FIELD_DEFINITION, name: name, arguments: args, type: type, directives: directives, loc: loc(lexer, start) }; } /** * ArgumentsDefinition : ( InputValueDefinition+ ) */ function parseArgumentDefs(lexer) { if (!peek(lexer, _lexer.TokenKind.PAREN_L)) { return []; } return many(lexer, _lexer.TokenKind.PAREN_L, parseInputValueDef, _lexer.TokenKind.PAREN_R); } /** * InputValueDefinition : Name : Type DefaultValue? Directives? */ function parseInputValueDef(lexer) { var start = lexer.token; var name = parseName(lexer); expect(lexer, _lexer.TokenKind.COLON); var type = parseTypeReference(lexer); var defaultValue = null; if (skip(lexer, _lexer.TokenKind.EQUALS)) { defaultValue = parseConstValue(lexer); } var directives = parseDirectives(lexer); return { kind: _kinds.INPUT_VALUE_DEFINITION, name: name, type: type, defaultValue: defaultValue, directives: directives, loc: loc(lexer, start) }; } /** * InterfaceTypeDefinition : interface Name Directives? { FieldDefinition+ } */ function parseInterfaceTypeDefinition(lexer) { var start = lexer.token; expectKeyword(lexer, 'interface'); var name = parseName(lexer); var directives = parseDirectives(lexer); var fields = any(lexer, _lexer.TokenKind.BRACE_L, parseFieldDefinition, _lexer.TokenKind.BRACE_R); return { kind: _kinds.INTERFACE_TYPE_DEFINITION, name: name, directives: directives, fields: fields, loc: loc(lexer, start) }; } /** * UnionTypeDefinition : union Name Directives? = UnionMembers */ function parseUnionTypeDefinition(lexer) { var start = lexer.token; expectKeyword(lexer, 'union'); var name = parseName(lexer); var directives = parseDirectives(lexer); expect(lexer, _lexer.TokenKind.EQUALS); var types = parseUnionMembers(lexer); return { kind: _kinds.UNION_TYPE_DEFINITION, name: name, directives: directives, types: types, loc: loc(lexer, start) }; } /** * UnionMembers : * - `|`? NamedType * - UnionMembers | NamedType */ function parseUnionMembers(lexer) { // Optional leading pipe skip(lexer, _lexer.TokenKind.PIPE); var members = []; do { members.push(parseNamedType(lexer)); } while (skip(lexer, _lexer.TokenKind.PIPE)); return members; } /** * EnumTypeDefinition : enum Name Directives? { EnumValueDefinition+ } */ function parseEnumTypeDefinition(lexer) { var start = lexer.token; expectKeyword(lexer, 'enum'); var name = parseName(lexer); var directives = parseDirectives(lexer); var values = many(lexer, _lexer.TokenKind.BRACE_L, parseEnumValueDefinition, _lexer.TokenKind.BRACE_R); return { kind: _kinds.ENUM_TYPE_DEFINITION, name: name, directives: directives, values: values, loc: loc(lexer, start) }; } /** * EnumValueDefinition : EnumValue Directives? * * EnumValue : Name */ function parseEnumValueDefinition(lexer) { var start = lexer.token; var name = parseName(lexer); var directives = parseDirectives(lexer); return { kind: _kinds.ENUM_VALUE_DEFINITION, name: name, directives: directives, loc: loc(lexer, start) }; } /** * InputObjectTypeDefinition : input Name Directives? { InputValueDefinition+ } */ function parseInputObjectTypeDefinition(lexer) { var start = lexer.token; expectKeyword(lexer, 'input'); var name = parseName(lexer); var directives = parseDirectives(lexer); var fields = any(lexer, _lexer.TokenKind.BRACE_L, parseInputValueDef, _lexer.TokenKind.BRACE_R); return { kind: _kinds.INPUT_OBJECT_TYPE_DEFINITION, name: name, directives: directives, fields: fields, loc: loc(lexer, start) }; } /** * TypeExtensionDefinition : extend ObjectTypeDefinition */ function parseTypeExtensionDefinition(lexer) { var start = lexer.token; expectKeyword(lexer, 'extend'); var definition = parseObjectTypeDefinition(lexer); return { kind: _kinds.TYPE_EXTENSION_DEFINITION, definition: definition, loc: loc(lexer, start) }; } /** * DirectiveDefinition : * - directive @ Name ArgumentsDefinition? on DirectiveLocations */ function parseDirectiveDefinition(lexer) { var start = lexer.token; expectKeyword(lexer, 'directive'); expect(lexer, _lexer.TokenKind.AT); var name = parseName(lexer); var args = parseArgumentDefs(lexer); expectKeyword(lexer, 'on'); var locations = parseDirectiveLocations(lexer); return { kind: _kinds.DIRECTIVE_DEFINITION, name: name, arguments: args, locations: locations, loc: loc(lexer, start) }; } /** * DirectiveLocations : * - `|`? Name * - DirectiveLocations | Name */ function parseDirectiveLocations(lexer) { // Optional leading pipe skip(lexer, _lexer.TokenKind.PIPE); var locations = []; do { locations.push(parseName(lexer)); } while (skip(lexer, _lexer.TokenKind.PIPE)); return locations; } // 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. Loc.prototype.toJSON = Loc.prototype.inspect = function toJSON() { 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 true after advancing * the lexer. Otherwise, do not change the parser state and return false. */ function skip(lexer, kind) { var match = lexer.token.kind === kind; if (match) { lexer.advance(); } return match; } /** * 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 expect(lexer, kind) { var token = lexer.token; if (token.kind === kind) { lexer.advance(); return token; } throw (0, _error.syntaxError)(lexer.source, token.start, 'Expected ' + kind + ', found ' + (0, _lexer.getTokenDesc)(token)); } /** * If the next token is a keyword with the given value, return that token after * advancing the lexer. Otherwise, do not change the parser state and return * false. */ function expectKeyword(lexer, value) { var token = lexer.token; if (token.kind === _lexer.TokenKind.NAME && token.value === value) { lexer.advance(); return token; } throw (0, _error.syntaxError)(lexer.source, token.start, 'Expected "' + value + '", found ' + (0, _lexer.getTokenDesc)(token)); } /** * Helper function for creating an error when an unexpected lexed token * is encountered. */ function unexpected(lexer, atToken) { var token = atToken || lexer.token; return (0, _error.syntaxError)(lexer.source, token.start, 'Unexpected ' + (0, _lexer.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) { expect(lexer, openKind); var nodes = []; while (!skip(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) { expect(lexer, openKind); var nodes = [parseFn(lexer)]; while (!skip(lexer, closeKind)) { nodes.push(parseFn(lexer)); } return nodes; }