Skip to content

Commit dac9234

Browse files
committed
Subclass Lexer for schema coordinates
1 parent 2b29ea3 commit dac9234

4 files changed

Lines changed: 51 additions & 44 deletions

File tree

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ export {
230230
printSourceLocation,
231231
// Lex
232232
Lexer,
233+
SchemaCoordinateLexer,
233234
TokenKind,
234235
// Parse
235236
parse,
@@ -261,6 +262,7 @@ export {
261262

262263
export type {
263264
ParseOptions,
265+
ParseSchemaCoordinateOptions,
264266
SourceLocation,
265267
// Visitor utilities
266268
ASTVisitor,

src/language/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export { Kind } from './kinds.js';
1111

1212
export { TokenKind } from './tokenKind.js';
1313

14-
export { Lexer } from './lexer.js';
14+
export { Lexer, SchemaCoordinateLexer } from './lexer.js';
1515

1616
export {
1717
parse,
@@ -20,7 +20,7 @@ export {
2020
parseType,
2121
parseSchemaCoordinate,
2222
} from './parser.js';
23-
export type { ParseOptions } from './parser.js';
23+
export type { ParseOptions, ParseSchemaCoordinateOptions } from './parser.js';
2424

2525
export { print } from './printer.js';
2626

src/language/lexer.ts

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,6 @@ import { isDigit, isNameContinue, isNameStart } from './characterClasses.js';
66
import type { Source } from './source.js';
77
import { TokenKind } from './tokenKind.js';
88

9-
/**
10-
* Configuration options to control lexer behavior
11-
*/
12-
export interface LexerOptions {
13-
/**
14-
* By default, ignored tokens are valid syntax and ignored when lexing.
15-
* This may be disabled for certain grammars that specifically disallow
16-
* ignored tokens (e.g. schema coordinates).
17-
*/
18-
noIgnoredTokens?: boolean | undefined;
19-
}
20-
219
/**
2210
* Given a Source object, creates a Lexer for that source.
2311
* A Lexer is a stateful stream generator in that every time
@@ -49,17 +37,14 @@ export class Lexer {
4937
*/
5038
lineStart: number;
5139

52-
protected _options: LexerOptions;
53-
54-
constructor(source: Source, options: LexerOptions = {}) {
40+
constructor(source: Source) {
5541
const startOfFileToken = new Token(TokenKind.SOF, 0, 0, 0, 0);
5642

5743
this.source = source;
5844
this.lastToken = startOfFileToken;
5945
this.token = startOfFileToken;
6046
this.line = 1;
6147
this.lineStart = 0;
62-
this._options = options;
6348
}
6449

6550
get [Symbol.toStringTag]() {
@@ -99,14 +84,25 @@ export class Lexer {
9984
return token;
10085
}
10186

102-
validateIgnoredToken(position: number): void {
103-
if (this._options.noIgnoredTokens === true) {
104-
throw syntaxError(
105-
this.source,
106-
position,
107-
`Invalid character: ${printCodePointAt(this, position)}.`,
108-
);
109-
}
87+
validateIgnoredToken(_position: number): void {
88+
/* noop - ignored tokens are ignored */
89+
}
90+
}
91+
92+
/**
93+
* As `Lexer`, but forbids ignored tokens as required of schema coordinates.
94+
*/
95+
export class SchemaCoordinateLexer extends Lexer {
96+
override get [Symbol.toStringTag]() {
97+
return 'SchemaCoordinateLexer';
98+
}
99+
100+
override validateIgnoredToken(position: number): void {
101+
throw syntaxError(
102+
this.source,
103+
position,
104+
`Invalid character: ${printCodePointAt(this, position)}.`,
105+
);
110106
}
111107
}
112108

src/language/parser.ts

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,23 +70,18 @@ import type {
7070
import { Location, OperationTypeNode } from './ast.js';
7171
import { DirectiveLocation } from './directiveLocation.js';
7272
import { Kind } from './kinds.js';
73-
import type { LexerOptions } from './lexer.js';
74-
import { isPunctuatorTokenKind, Lexer } from './lexer.js';
73+
import {
74+
isPunctuatorTokenKind,
75+
Lexer,
76+
SchemaCoordinateLexer,
77+
} from './lexer.js';
7578
import { isSource, Source } from './source.js';
7679
import { TokenKind } from './tokenKind.js';
7780

7881
/**
7982
* Configuration options to control parser behavior
8083
*/
8184
export interface ParseOptions {
82-
/**
83-
* By default, ignored tokens are valid syntax and ignored when lexing.
84-
* This may be disabled for certain grammars that specifically disallow
85-
* ignored tokens (e.g. schema coordinates).
86-
* This is passed down to the lexer to enforce.
87-
*/
88-
noIgnoredTokens?: boolean | undefined;
89-
9085
/**
9186
* By default, the parser creates AST nodes that know the location
9287
* in the source that they correspond to. This configuration flag
@@ -123,6 +118,24 @@ export interface ParseOptions {
123118
* ```
124119
*/
125120
experimentalFragmentArguments?: boolean | undefined;
121+
122+
/**
123+
* You may override the Lexer class used to lex the source; this is used by
124+
* schema coordinates to introduce a lexer that forbids ignored tokens.
125+
*/
126+
Lexer?: typeof Lexer | undefined;
127+
}
128+
129+
/**
130+
* Configuration options to control schema coordinate parser behavior
131+
*/
132+
export interface ParseSchemaCoordinateOptions {
133+
/**
134+
* By default, the parser creates AST nodes that know the location
135+
* in the source that they correspond to. This configuration flag
136+
* disables that behavior for performance or testing.
137+
*/
138+
noLocation?: boolean | undefined;
126139
}
127140

128141
/**
@@ -208,11 +221,10 @@ export function parseType(
208221
*/
209222
export function parseSchemaCoordinate(
210223
source: string | Source,
211-
options: ParseOptions & { noIgnoredTokens?: true } = {},
224+
options?: ParseSchemaCoordinateOptions,
212225
): SchemaCoordinateNode {
213226
// Ignored tokens are excluded syntax for the schema coordinates.
214-
const _options = { ...options, noIgnoredTokens: true };
215-
227+
const _options = { ...options, Lexer: SchemaCoordinateLexer };
216228
const parser = new Parser(source, _options);
217229
parser.expectToken(TokenKind.SOF);
218230
const coordinate = parser.parseSchemaCoordinate();
@@ -239,11 +251,8 @@ export class Parser {
239251
constructor(source: string | Source, options: ParseOptions = {}) {
240252
const sourceObj = isSource(source) ? source : new Source(source);
241253

242-
const lexerOptions: LexerOptions = {
243-
noIgnoredTokens: options.noIgnoredTokens ?? false,
244-
};
245-
246-
this._lexer = new Lexer(sourceObj, lexerOptions);
254+
const LexerClass = options.Lexer ?? Lexer;
255+
this._lexer = new LexerClass(sourceObj);
247256
this._options = options;
248257
this._tokenCounter = 0;
249258
}

0 commit comments

Comments
 (0)