Skip to content

Commit 8727a08

Browse files
committed
Merge pull request #74 from graphql/schema-dsl
Schema Definition Language Parser This is a parser for the ad-hoc type definition language included in the spec. This should also serve as a de-facto spec for the language. Clearly once this is stable we should include it in the actual spec as well. This should end up being useful in a few contexts, including client-side types and also a DSL for producing the JS type definitions which can be tedious to write, and a way to specify acceptance tests. (This would be a clear next step for this project)
2 parents 1af7d4e + 4a7a7a6 commit 8727a08

File tree

6 files changed

+1088
-177
lines changed

6 files changed

+1088
-177
lines changed

src/language/parser.js

Lines changed: 18 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99

1010
import { Source } from './source';
1111
import { syntaxError } from '../error';
12-
import { lex, TokenKind, getTokenKindDesc, getTokenDesc } from './lexer';
13-
import type { Token } from './lexer';
12+
import { TokenKind } from './lexer';
1413
import type {
1514
Name,
1615
Variable,
@@ -35,7 +34,7 @@ import type {
3534
Directive,
3635

3736
Type,
38-
NamedType
37+
NamedType,
3938
} from './ast';
4039

4140
import {
@@ -69,24 +68,19 @@ import {
6968
NON_NULL_TYPE,
7069
} from './kinds';
7170

72-
/**
73-
* Configuration options to control parser behavior
74-
*/
75-
type ParseOptions = {
76-
/**
77-
* By default, the parser creates AST nodes that know the location
78-
* in the source that they correspond to. This configuration flag
79-
* disables that behavior for performance or testing.
80-
*/
81-
noLocation?: boolean,
82-
83-
/**
84-
* By default, the parser creates AST nodes that contain a reference
85-
* to the source that they were created from. This configuration flag
86-
* disables that behavior for performance or testing.
87-
*/
88-
noSource?: boolean,
89-
}
71+
import {
72+
makeParser,
73+
peek,
74+
skip,
75+
loc,
76+
any,
77+
many,
78+
expect,
79+
unexpected,
80+
expectKeyword,
81+
advance,
82+
ParseOptions,
83+
} from './parserCore';
9084

9185
/**
9286
* Given a GraphQL source, parses it into a Document.
@@ -101,162 +95,10 @@ export function parse(
10195
return parseDocument(parser);
10296
}
10397

104-
/**
105-
* Returns the parser object that is used to store state throughout the
106-
* process of parsing.
107-
*/
108-
function makeParser(source: Source, options: ParseOptions) {
109-
var _lexToken = lex(source);
110-
return {
111-
_lexToken,
112-
source,
113-
options,
114-
prevEnd: 0,
115-
token: _lexToken(),
116-
};
117-
}
118-
119-
/**
120-
* Returns a location object, used to identify the place in
121-
* the source that created a given parsed object.
122-
*/
123-
function loc(parser, start: number) {
124-
if (parser.options.noLocation) {
125-
return null;
126-
}
127-
if (parser.options.noSource) {
128-
return {
129-
start: start,
130-
end: parser.prevEnd
131-
};
132-
}
133-
return {
134-
start: start,
135-
end: parser.prevEnd,
136-
source: parser.source
137-
};
138-
}
139-
140-
/**
141-
* Moves the internal parser object to the next lexed token.
142-
*/
143-
function advance(parser): void {
144-
var prevEnd = parser.token.end;
145-
parser.prevEnd = prevEnd;
146-
parser.token = parser._lexToken(prevEnd);
147-
}
148-
149-
/**
150-
* Determines if the next token is of a given kind
151-
*/
152-
function peek(parser, kind: string): boolean {
153-
return parser.token.kind === kind;
154-
}
155-
156-
/**
157-
* If the next token is of the given kind, return true after advancing
158-
* the parser. Otherwise, do not change the parser state and return false.
159-
*/
160-
function skip(parser, kind: string): boolean {
161-
var match = parser.token.kind === kind;
162-
if (match) {
163-
advance(parser);
164-
}
165-
return match;
166-
}
167-
168-
/**
169-
* If the next token is of the given kind, return that token after advancing
170-
* the parser. Otherwise, do not change the parser state and return false.
171-
*/
172-
function expect(parser, kind: string): Token {
173-
var token = parser.token;
174-
if (token.kind === kind) {
175-
advance(parser);
176-
return token;
177-
}
178-
throw syntaxError(
179-
parser.source,
180-
token.start,
181-
`Expected ${getTokenKindDesc(kind)}, found ${getTokenDesc(token)}`
182-
);
183-
}
184-
185-
/**
186-
* If the next token is a keyword with the given value, return that token after
187-
* advancing the parser. Otherwise, do not change the parser state and return
188-
* false.
189-
*/
190-
function expectKeyword(parser, value: string): Token {
191-
var token = parser.token;
192-
if (token.kind === TokenKind.NAME && token.value === value) {
193-
advance(parser);
194-
return token;
195-
}
196-
throw syntaxError(
197-
parser.source,
198-
token.start,
199-
`Expected "${value}", found ${getTokenDesc(token)}`
200-
);
201-
}
202-
203-
/**
204-
* Helper function for creating an error when an unexpected lexed token
205-
* is encountered.
206-
*/
207-
function unexpected(parser, atToken?: ?Token): Error {
208-
var token = atToken || parser.token;
209-
return syntaxError(
210-
parser.source,
211-
token.start,
212-
`Unexpected ${getTokenDesc(token)}`
213-
);
214-
}
215-
216-
/**
217-
* Returns a possibly empty list of parse nodes, determined by
218-
* the parseFn. This list begins with a lex token of openKind
219-
* and ends with a lex token of closeKind. Advances the parser
220-
* to the next lex token after the closing token.
221-
*/
222-
function any<T>(
223-
parser,
224-
openKind: number,
225-
parseFn: (parser: any) => T,
226-
closeKind: number
227-
): Array<T> {
228-
expect(parser, openKind);
229-
var nodes = [];
230-
while (!skip(parser, closeKind)) {
231-
nodes.push(parseFn(parser));
232-
}
233-
return nodes;
234-
}
235-
236-
/**
237-
* Returns a non-empty list of parse nodes, determined by
238-
* the parseFn. This list begins with a lex token of openKind
239-
* and ends with a lex token of closeKind. Advances the parser
240-
* to the next lex token after the closing token.
241-
*/
242-
function many<T>(
243-
parser,
244-
openKind: number,
245-
parseFn: (parser: any) => T,
246-
closeKind: number
247-
): Array<T> {
248-
expect(parser, openKind);
249-
var nodes = [parseFn(parser)];
250-
while (!skip(parser, closeKind)) {
251-
nodes.push(parseFn(parser));
252-
}
253-
return nodes;
254-
}
255-
25698
/**
25799
* Converts a name lex token into a name parse node.
258100
*/
259-
function parseName(parser): Name {
101+
export function parseName(parser): Name {
260102
var token = expect(parser, TokenKind.NAME);
261103
return {
262104
kind: NAME,
@@ -265,7 +107,6 @@ function parseName(parser): Name {
265107
};
266108
}
267109

268-
269110
// Implements the parsing rules in the Document section.
270111

271112
function parseDocument(parser): Document {
@@ -603,7 +444,7 @@ function parseDirective(parser): Directive {
603444
/**
604445
* Handles the Type: NamedType, ListType, and NonNullType parsing rules.
605446
*/
606-
function parseType(parser): Type {
447+
export function parseType(parser): Type {
607448
var start = parser.token.start;
608449
var type;
609450
if (skip(parser, TokenKind.BRACKET_L)) {
@@ -627,7 +468,7 @@ function parseType(parser): Type {
627468
return type;
628469
}
629470

630-
function parseNamedType(parser): NamedType {
471+
export function parseNamedType(parser): NamedType {
631472
var start = parser.token.start;
632473
return {
633474
kind: NAMED_TYPE,

0 commit comments

Comments
 (0)