Skip to content

Commit 30e1025

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

2 files changed

Lines changed: 42 additions & 43 deletions

File tree

src/language/lexer.ts

Lines changed: 17 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,22 @@ 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+
export class SchemaCoordinateLexer extends Lexer {
93+
override get [Symbol.toStringTag]() {
94+
return 'SchemaCoordinateLexer';
95+
}
96+
97+
override validateIgnoredToken(position: number): void {
98+
throw syntaxError(
99+
this.source,
100+
position,
101+
`Invalid character: ${printCodePointAt(this, position)}.`,
102+
);
110103
}
111104
}
112105

src/language/parser.ts

Lines changed: 25 additions & 19 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,21 @@ 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+
export interface ParseSchemaCoordinateOptions {
130+
/**
131+
* By default, the parser creates AST nodes that know the location
132+
* in the source that they correspond to. This configuration flag
133+
* disables that behavior for performance or testing.
134+
*/
135+
noLocation?: boolean | undefined;
126136
}
127137

128138
/**
@@ -208,12 +218,11 @@ export function parseType(
208218
*/
209219
export function parseSchemaCoordinate(
210220
source: string | Source,
211-
options: ParseOptions & { noIgnoredTokens?: true } = {},
221+
options: ParseSchemaCoordinateOptions,
212222
): SchemaCoordinateNode {
213223
// Ignored tokens are excluded syntax for the schema coordinates.
214-
const _options = { ...options, noIgnoredTokens: true };
215-
216-
const parser = new Parser(source, _options);
224+
const parserOptions = { ...options, Lexer: SchemaCoordinateLexer };
225+
const parser = new Parser(source, parserOptions);
217226
parser.expectToken(TokenKind.SOF);
218227
const coordinate = parser.parseSchemaCoordinate();
219228
parser.expectToken(TokenKind.EOF);
@@ -239,11 +248,8 @@ export class Parser {
239248
constructor(source: string | Source, options: ParseOptions = {}) {
240249
const sourceObj = isSource(source) ? source : new Source(source);
241250

242-
const lexerOptions: LexerOptions = {
243-
noIgnoredTokens: options.noIgnoredTokens ?? false,
244-
};
245-
246-
this._lexer = new Lexer(sourceObj, lexerOptions);
251+
const LexerClass = options.Lexer ?? Lexer;
252+
this._lexer = new LexerClass(sourceObj);
247253
this._options = options;
248254
this._tokenCounter = 0;
249255
}

0 commit comments

Comments
 (0)