Skip to content

Commit 586b717

Browse files
committed
wip hacky thing
1 parent 7d0c7a8 commit 586b717

File tree

4 files changed

+240
-140
lines changed

4 files changed

+240
-140
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { expect } from 'chai';
2+
import { it } from 'mocha';
3+
import { parseSchemaCoordinate } from '../schemaCoordinate.js'
4+
import {
5+
expectJSON,
6+
} from '../../__testUtils__/expectJSON.js';
7+
import { Kind } from '../kinds.js';
8+
import { Source } from '../source.js';
9+
10+
it('parses Name', () => {
11+
const result = parseSchemaCoordinate('@@MyType');
12+
expectJSON(result).toDeepEqual({
13+
kind: Kind.TYPE_COORDINATE,
14+
name: {
15+
kind: Kind.NAME,
16+
value: 'MyType',
17+
},
18+
});
19+
});
20+
21+
it('parses Name . Name', () => {
22+
const result = parseSchemaCoordinate('MyType.field');
23+
expectJSON(result).toDeepEqual({
24+
kind: Kind.MEMBER_COORDINATE,
25+
name: {
26+
kind: Kind.NAME,
27+
value: 'MyType',
28+
},
29+
memberName: {
30+
kind: Kind.NAME,
31+
value: 'field',
32+
},
33+
});
34+
});
35+
36+
it('rejects Name . Name . Name', () => {
37+
expect(() => parseSchemaCoordinate('MyType.field.deep'))
38+
.to.throw()
39+
.to.deep.include({
40+
message: 'Syntax Error: Expected <EOF>, found ..',
41+
});
42+
});
43+
44+
it('parses Name . Name ( Name : )', () => {
45+
const result = parseSchemaCoordinate('MyType.field(arg:)');
46+
expectJSON(result).toDeepEqual({
47+
kind: Kind.ARGUMENT_COORDINATE,
48+
name: {
49+
kind: Kind.NAME,
50+
value: 'MyType',
51+
},
52+
fieldName: {
53+
kind: Kind.NAME,
54+
value: 'field',
55+
},
56+
argumentName: {
57+
kind: Kind.NAME,
58+
value: 'arg',
59+
},
60+
});
61+
});
62+
63+
it('rejects Name . Name ( Name : Name )', () => {
64+
expect(() => parseSchemaCoordinate('MyType.field(arg: value)'))
65+
.to.throw()
66+
.to.deep.include({
67+
message: 'Syntax Error: Invalid character: " ".',
68+
});
69+
});
70+
71+
it('parses @ Name', () => {
72+
const result = parseSchemaCoordinate('@myDirective');
73+
expectJSON(result).toDeepEqual({
74+
kind: Kind.DIRECTIVE_COORDINATE,
75+
name: {
76+
kind: Kind.NAME,
77+
value: 'myDirective',
78+
},
79+
});
80+
});
81+
82+
it('parses @ Name ( Name : )', () => {
83+
const result = parseSchemaCoordinate('@myDirective(arg:)');
84+
expectJSON(result).toDeepEqual({
85+
kind: Kind.DIRECTIVE_ARGUMENT_COORDINATE,
86+
name: {
87+
kind: Kind.NAME,
88+
value: 'myDirective',
89+
},
90+
argumentName: {
91+
kind: Kind.NAME,
92+
value: 'arg',
93+
},
94+
});
95+
});
96+
97+
it('rejects @ Name . Name', () => {
98+
expect(() => parseSchemaCoordinate('@myDirective.field'))
99+
.to.throw()
100+
.to.deep.include({
101+
message: 'Syntax Error: Expected <EOF>, found ..',
102+
});
103+
});
104+
105+
it('accepts a Source object', () => {
106+
expect(parseSchemaCoordinate('MyType')).to.deep.equal(
107+
parseSchemaCoordinate(new Source('MyType')),
108+
);
109+
});

src/language/lexer.ts

Lines changed: 20 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export class Lexer {
6262

6363
/**
6464
* Looks ahead and returns the next non-ignored token, but does not change
65-
* the current Lexer token.
65+
* the state of Lexer.
6666
*/
6767
lookahead(): Token {
6868
let token = this.token;
@@ -95,7 +95,6 @@ export function isPunctuatorTokenKind(kind: TokenKind): boolean {
9595
kind === TokenKind.AMP ||
9696
kind === TokenKind.PAREN_L ||
9797
kind === TokenKind.PAREN_R ||
98-
kind === TokenKind.DOT ||
9998
kind === TokenKind.SPREAD ||
10099
kind === TokenKind.COLON ||
101100
kind === TokenKind.EQUALS ||
@@ -151,8 +150,10 @@ function isTrailingSurrogate(code: number): boolean {
151150
*
152151
* Printable ASCII is printed quoted, while other points are printed in Unicode
153152
* code point form (ie. U+1234).
153+
*
154+
* @internal
154155
*/
155-
function printCodePointAt(lexer: Lexer, location: number): string {
156+
export function printCodePointAt(lexer: Lexer, location: number): string {
156157
const code = lexer.source.body.codePointAt(location);
157158

158159
if (code === undefined) {
@@ -247,11 +248,7 @@ function readNextToken(lexer: Lexer, start: number): Token {
247248
// - FloatValue
248249
// - StringValue
249250
//
250-
// Punctuator ::
251-
// - DotPunctuator
252-
// - OtherPunctuator
253-
//
254-
// OtherPunctuator :: one of ! $ & ( ) ... : = @ [ ] { | }
251+
// Punctuator :: one of ! $ & ( ) ... : = @ [ ] { | }
255252
case 0x0021: // !
256253
return createToken(lexer, TokenKind.BANG, position, position + 1);
257254
case 0x0024: // $
@@ -262,14 +259,14 @@ function readNextToken(lexer: Lexer, start: number): Token {
262259
return createToken(lexer, TokenKind.PAREN_L, position, position + 1);
263260
case 0x0029: // )
264261
return createToken(lexer, TokenKind.PAREN_R, position, position + 1);
265-
case 0x002e: {
266-
// .
267-
const nextCode = body.charCodeAt(position + 1);
268-
if (nextCode === 0x002e && body.charCodeAt(position + 2) === 0x002e) {
262+
case 0x002e: // .
263+
if (
264+
body.charCodeAt(position + 1) === 0x002e &&
265+
body.charCodeAt(position + 2) === 0x002e
266+
) {
269267
return createToken(lexer, TokenKind.SPREAD, position, position + 3);
270268
}
271-
return readDot(lexer, position);
272-
}
269+
break;
273270
case 0x003a: // :
274271
return createToken(lexer, TokenKind.COLON, position, position + 1);
275272
case 0x003d: // =
@@ -313,43 +310,14 @@ function readNextToken(lexer: Lexer, start: number): Token {
313310
code === 0x0027
314311
? 'Unexpected single quote character (\'), did you mean to use a double quote (")?'
315312
: isUnicodeScalarValue(code) || isSupplementaryCodePoint(body, position)
316-
? `Unexpected character: ${printCodePointAt(lexer, position)}.`
317-
: `Invalid character: ${printCodePointAt(lexer, position)}.`,
313+
? `Unexpected character: ${printCodePointAt(lexer, position)}.`
314+
: `Invalid character: ${printCodePointAt(lexer, position)}.`,
318315
);
319316
}
320317

321318
return createToken(lexer, TokenKind.EOF, bodyLength, bodyLength);
322319
}
323320

324-
/**
325-
* Reads a dot token with helpful messages for negative lookahead.
326-
*
327-
* DotPunctuator :: `.` [lookahead != {`.`, Digit}]
328-
*/
329-
function readDot(lexer: Lexer, start: number): Token {
330-
const nextCode = lexer.source.body.charCodeAt(start + 1);
331-
// Full Stop (.)
332-
if (nextCode === 0x002e) {
333-
throw syntaxError(
334-
lexer.source,
335-
start,
336-
'Unexpected "..", did you mean "..."?',
337-
);
338-
}
339-
if (isDigit(nextCode)) {
340-
const digits = lexer.source.body.slice(
341-
start + 1,
342-
readDigits(lexer, start + 1, nextCode),
343-
);
344-
throw syntaxError(
345-
lexer.source,
346-
start,
347-
`Invalid number, expected digit before ".", did you mean "0.${digits}"?`,
348-
);
349-
}
350-
return createToken(lexer, TokenKind.DOT, start, start + 1);
351-
}
352-
353321
/**
354322
* Reads a comment token from the source file.
355323
*
@@ -709,10 +677,10 @@ function readHexDigit(code: number): number {
709677
return code >= 0x0030 && code <= 0x0039 // 0-9
710678
? code - 0x0030
711679
: code >= 0x0041 && code <= 0x0046 // A-F
712-
? code - 0x0037
713-
: code >= 0x0061 && code <= 0x0066 // a-f
714-
? code - 0x0057
715-
: -1;
680+
? code - 0x0037
681+
: code >= 0x0061 && code <= 0x0066 // a-f
682+
? code - 0x0057
683+
: -1;
716684
}
717685

718686
/**
@@ -863,8 +831,10 @@ function readBlockString(lexer: Lexer, start: number): Token {
863831
* Name ::
864832
* - NameStart NameContinue* [lookahead != NameContinue]
865833
* ```
834+
*
835+
* @internal
866836
*/
867-
function readName(lexer: Lexer, start: number): Token {
837+
export function readName(lexer: Lexer, start: number): Token {
868838
const body = lexer.source.body;
869839
const bodyLength = body.length;
870840
let position = start + 1;

src/language/parser.ts

Lines changed: 1 addition & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import type { GraphQLError } from '../error/GraphQLError.js';
44
import { syntaxError } from '../error/syntaxError.js';
55

66
import type {
7-
ArgumentCoordinateNode,
87
ArgumentNode,
98
BooleanValueNode,
109
ConstArgumentNode,
@@ -13,10 +12,7 @@ import type {
1312
ConstObjectFieldNode,
1413
ConstObjectValueNode,
1514
ConstValueNode,
16-
DefinitionNode,
17-
DirectiveArgumentCoordinateNode,
18-
DirectiveCoordinateNode,
19-
DirectiveDefinitionNode,
15+
DefinitionNode
2016
DirectiveNode,
2117
DocumentNode,
2218
EnumTypeDefinitionNode,
@@ -38,7 +34,6 @@ import type {
3834
IntValueNode,
3935
ListTypeNode,
4036
ListValueNode,
41-
MemberCoordinateNode,
4237
NamedTypeNode,
4338
NameNode,
4439
NonNullTypeNode,
@@ -51,14 +46,12 @@ import type {
5146
OperationTypeDefinitionNode,
5247
ScalarTypeDefinitionNode,
5348
ScalarTypeExtensionNode,
54-
SchemaCoordinateNode,
5549
SchemaDefinitionNode,
5650
SchemaExtensionNode,
5751
SelectionNode,
5852
SelectionSetNode,
5953
StringValueNode,
6054
Token,
61-
TypeCoordinateNode,
6255
TypeNode,
6356
TypeSystemExtensionNode,
6457
UnionTypeDefinitionNode,
@@ -188,26 +181,6 @@ export function parseType(
188181
return type;
189182
}
190183

191-
/**
192-
* Given a string containing a GraphQL Schema Coordinate (ex. `Type.field`),
193-
* parse the AST for that schema coordinate.
194-
* Throws GraphQLError if a syntax error is encountered.
195-
*
196-
* Consider providing the results to the utility function:
197-
* resolveASTSchemaCoordinate(). Or calling resolveSchemaCoordinate() directly
198-
* with an unparsed source.
199-
*/
200-
export function parseSchemaCoordinate(
201-
source: string | Source,
202-
options?: ParseOptions,
203-
): SchemaCoordinateNode {
204-
const parser = new Parser(source, options);
205-
parser.expectToken(TokenKind.SOF);
206-
const coordinate = parser.parseSchemaCoordinate();
207-
parser.expectToken(TokenKind.EOF);
208-
return coordinate;
209-
}
210-
211184
/**
212185
* This class is exported only to assist people in implementing their own parsers
213186
* without duplicating too much code and should be used only as last resort for cases
@@ -1459,68 +1432,6 @@ export class Parser {
14591432
throw this.unexpected(start);
14601433
}
14611434

1462-
// Schema Coordinates
1463-
1464-
/**
1465-
* SchemaCoordinate :
1466-
* - Name
1467-
* - Name . Name
1468-
* - Name . Name ( Name : )
1469-
* - @ Name
1470-
* - @ Name ( Name : )
1471-
*/
1472-
parseSchemaCoordinate(): SchemaCoordinateNode {
1473-
const start = this._lexer.token;
1474-
const ofDirective = this.expectOptionalToken(TokenKind.AT);
1475-
const name = this.parseName();
1476-
let memberName: NameNode | undefined;
1477-
if (!ofDirective && this.expectOptionalToken(TokenKind.DOT)) {
1478-
memberName = this.parseName();
1479-
}
1480-
let argumentName: NameNode | undefined;
1481-
if (
1482-
(ofDirective || memberName) &&
1483-
this.expectOptionalToken(TokenKind.PAREN_L)
1484-
) {
1485-
argumentName = this.parseName();
1486-
this.expectToken(TokenKind.COLON);
1487-
this.expectToken(TokenKind.PAREN_R);
1488-
}
1489-
1490-
if (ofDirective) {
1491-
if (argumentName) {
1492-
return this.node<DirectiveArgumentCoordinateNode>(start, {
1493-
kind: Kind.DIRECTIVE_ARGUMENT_COORDINATE,
1494-
name,
1495-
argumentName,
1496-
});
1497-
}
1498-
return this.node<DirectiveCoordinateNode>(start, {
1499-
kind: Kind.DIRECTIVE_COORDINATE,
1500-
name,
1501-
});
1502-
} else if (memberName) {
1503-
if (argumentName) {
1504-
return this.node<ArgumentCoordinateNode>(start, {
1505-
kind: Kind.ARGUMENT_COORDINATE,
1506-
name,
1507-
fieldName: memberName,
1508-
argumentName,
1509-
});
1510-
}
1511-
return this.node<MemberCoordinateNode>(start, {
1512-
kind: Kind.MEMBER_COORDINATE,
1513-
name,
1514-
memberName,
1515-
});
1516-
}
1517-
1518-
return this.node<TypeCoordinateNode>(start, {
1519-
kind: Kind.TYPE_COORDINATE,
1520-
name,
1521-
});
1522-
}
1523-
15241435
// Core parsing utility functions
15251436

15261437
/**

0 commit comments

Comments
 (0)