Skip to content

Commit 9d70ee9

Browse files
committed
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)
1 parent 33ed9aa commit 9d70ee9

File tree

6 files changed

+1109
-215
lines changed

6 files changed

+1109
-215
lines changed

src/language/parser.js

Lines changed: 18 additions & 215 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,
@@ -33,13 +32,9 @@ import type {
3332
ObjectField,
3433

3534
Directive,
36-
37-
Type,
38-
NamedType
3935
} from './ast';
4036

4137
import {
42-
NAME,
4338
VARIABLE,
4439

4540
DOCUMENT,
@@ -63,16 +58,28 @@ import {
6358
OBJECT_FIELD,
6459

6560
DIRECTIVE,
66-
67-
NAMED_TYPE,
68-
LIST_TYPE,
69-
NON_NULL_TYPE,
7061
} from './kinds';
7162

63+
import {
64+
makeParser,
65+
peek,
66+
skip,
67+
loc,
68+
any,
69+
many,
70+
parseName,
71+
expect,
72+
parseType,
73+
parseNamedType,
74+
unexpected,
75+
expectKeyword,
76+
advance,
77+
} from './parserCore';
78+
7279
/**
7380
* Configuration options to control parser behavior
7481
*/
75-
type ParseOptions = {
82+
export type ParseOptions = {
7683
/**
7784
* By default, the parser creates AST nodes that know the location
7885
* in the source that they correspond to. This configuration flag
@@ -101,171 +108,6 @@ export function parse(
101108
return parseDocument(parser);
102109
}
103110

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-
256-
/**
257-
* Converts a name lex token into a name parse node.
258-
*/
259-
function parseName(parser): Name {
260-
var token = expect(parser, TokenKind.NAME);
261-
return {
262-
kind: NAME,
263-
value: token.value,
264-
loc: loc(parser, token.start)
265-
};
266-
}
267-
268-
269111
// Implements the parsing rules in the Document section.
270112

271113
function parseDocument(parser): Document {
@@ -596,42 +438,3 @@ function parseDirective(parser): Directive {
596438
loc: loc(parser, start)
597439
};
598440
}
599-
600-
601-
// Implements the parsing rules in the Types section.
602-
603-
/**
604-
* Handles the Type: NamedType, ListType, and NonNullType parsing rules.
605-
*/
606-
function parseType(parser): Type {
607-
var start = parser.token.start;
608-
var type;
609-
if (skip(parser, TokenKind.BRACKET_L)) {
610-
type = parseType(parser);
611-
expect(parser, TokenKind.BRACKET_R);
612-
type = {
613-
kind: LIST_TYPE,
614-
type,
615-
loc: loc(parser, start)
616-
};
617-
} else {
618-
type = parseNamedType(parser);
619-
}
620-
if (skip(parser, TokenKind.BANG)) {
621-
return {
622-
kind: NON_NULL_TYPE,
623-
type,
624-
loc: loc(parser, start)
625-
};
626-
}
627-
return type;
628-
}
629-
630-
function parseNamedType(parser): NamedType {
631-
var start = parser.token.start;
632-
return {
633-
kind: NAMED_TYPE,
634-
name: parseName(parser),
635-
loc: loc(parser, start)
636-
};
637-
}

0 commit comments

Comments
 (0)