Skip to content

Commit 3a900a9

Browse files
committed
parser tests passing
1 parent eb9b6c8 commit 3a900a9

File tree

4 files changed

+99
-34
lines changed

4 files changed

+99
-34
lines changed

src/language/__tests__/parser-test.ts

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Kind } from '../kinds';
1111
import { parse, parseConstValue, parseType, parseValue } from '../parser';
1212
import { Source } from '../source';
1313
import { TokenKind } from '../tokenKind';
14+
import { Console } from 'console';
1415

1516
function expectSyntaxError(text: string) {
1617
return expectToThrowJSON(() => parse(text));
@@ -631,11 +632,46 @@ describe('Parser', () => {
631632
});
632633
});
633634

635+
it('parses nested types', () => {
636+
const result = parseType('[MyType!]');
637+
expectJSON(result).toDeepEqual({
638+
kind: Kind.LIST_TYPE,
639+
loc: { start: 0, end: 9 },
640+
type: {
641+
kind: Kind.NON_NULL_TYPE,
642+
loc: { start: 1, end: 8 },
643+
type: {
644+
kind: Kind.NAMED_TYPE,
645+
loc: { start: 1, end: 7 },
646+
name: {
647+
kind: Kind.NAME,
648+
loc: { start: 1, end: 7 },
649+
value: 'MyType',
650+
},
651+
},
652+
},
653+
});
654+
});
655+
});
656+
657+
describe('parseDocumentDirective', () => {
658+
it('doesnt throw on document-level directive', () => {
659+
parse(dedent`
660+
@SemanticNullability
661+
662+
type Query {
663+
hello: String
664+
world: String?
665+
foo: String!
666+
}
667+
`);
668+
});
669+
634670
it('parses semantic-non-null types', () => {
635-
const result = parseType('MyType*');
671+
const result = parseType('MyType', { useSemanticNullability: true });
636672
expectJSON(result).toDeepEqual({
637673
kind: Kind.SEMANTIC_NON_NULL_TYPE,
638-
loc: { start: 0, end: 7 },
674+
loc: { start: 0, end: 6 },
639675
type: {
640676
kind: Kind.NAMED_TYPE,
641677
loc: { start: 0, end: 6 },
@@ -648,22 +684,31 @@ describe('Parser', () => {
648684
});
649685
});
650686

651-
it('parses nested types', () => {
652-
const result = parseType('[MyType!]');
687+
it('parses nullable types', () => {
688+
const result = parseType('MyType?', { useSemanticNullability: true });
653689
expectJSON(result).toDeepEqual({
654-
kind: Kind.LIST_TYPE,
655-
loc: { start: 0, end: 9 },
690+
kind: Kind.NAMED_TYPE,
691+
loc: { start: 0, end: 6 },
692+
name: {
693+
kind: Kind.NAME,
694+
loc: { start: 0, end: 6 },
695+
value: 'MyType',
696+
},
697+
});
698+
});
699+
700+
it('parses non-nullable types', () => {
701+
const result = parseType('MyType!', { useSemanticNullability: true });
702+
expectJSON(result).toDeepEqual({
703+
kind: Kind.NON_NULL_TYPE,
704+
loc: { start: 0, end: 7 },
656705
type: {
657-
kind: Kind.NON_NULL_TYPE,
658-
loc: { start: 1, end: 8 },
659-
type: {
660-
kind: Kind.NAMED_TYPE,
661-
loc: { start: 1, end: 7 },
662-
name: {
663-
kind: Kind.NAME,
664-
loc: { start: 1, end: 7 },
665-
value: 'MyType',
666-
},
706+
kind: Kind.NAMED_TYPE,
707+
loc: { start: 0, end: 6 },
708+
name: {
709+
kind: Kind.NAME,
710+
loc: { start: 0, end: 6 },
711+
value: 'MyType',
667712
},
668713
},
669714
});

src/language/lexer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export class Lexer {
9191
export function isPunctuatorTokenKind(kind: TokenKind): boolean {
9292
return (
9393
kind === TokenKind.BANG ||
94-
kind === TokenKind.ASTERISK ||
94+
kind === TokenKind.QUESTION_MARK ||
9595
kind === TokenKind.DOLLAR ||
9696
kind === TokenKind.AMP ||
9797
kind === TokenKind.PAREN_L ||
@@ -247,7 +247,7 @@ function readNextToken(lexer: Lexer, start: number): Token {
247247
// - FloatValue
248248
// - StringValue
249249
//
250-
// Punctuator :: one of ! $ & ( ) * ... : = @ [ ] { | }
250+
// Punctuator :: one of ! $ & ( ) ? ... : = @ [ ] { | }
251251
case 0x0021: // !
252252
return createToken(lexer, TokenKind.BANG, position, position + 1);
253253
case 0x0024: // $
@@ -258,8 +258,8 @@ function readNextToken(lexer: Lexer, start: number): Token {
258258
return createToken(lexer, TokenKind.PAREN_L, position, position + 1);
259259
case 0x0029: // )
260260
return createToken(lexer, TokenKind.PAREN_R, position, position + 1);
261-
case 0x002a: // *
262-
return createToken(lexer, TokenKind.ASTERISK, position, position + 1);
261+
case 0x003f: // ?
262+
return createToken(lexer, TokenKind.QUESTION_MARK, position, position + 1);
263263
case 0x002e: // .
264264
if (
265265
body.charCodeAt(position + 1) === 0x002e &&

src/language/parser.ts

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ export interface ParseOptions {
104104
* ```
105105
*/
106106
allowLegacyFragmentVariables?: boolean;
107+
108+
useSemanticNullability?: boolean;
107109
}
108110

109111
/**
@@ -250,6 +252,14 @@ export class Parser {
250252
* - InputObjectTypeDefinition
251253
*/
252254
parseDefinition(): DefinitionNode {
255+
// TODO: I don't know what isConst represents. Every other callsite has it false
256+
let directives = this.parseDirectives(false);
257+
for (let directive of directives) {
258+
if (directive.name.value == "SemanticNullability") {
259+
this._options.useSemanticNullability = true;
260+
}
261+
}
262+
253263
if (this.peek(TokenKind.BRACE_L)) {
254264
return this.parseOperationDefinition();
255265
}
@@ -757,20 +767,30 @@ export class Parser {
757767
type = this.parseNamedType();
758768
}
759769

760-
if (this.expectOptionalToken(TokenKind.BANG)) {
761-
return this.node<NonNullTypeNode>(start, {
762-
kind: Kind.NON_NULL_TYPE,
763-
type,
764-
});
765-
}
766-
if (this.expectOptionalToken(TokenKind.ASTERISK)) {
767-
return this.node<SemanticNonNullTypeNode>(start, {
768-
kind: Kind.SEMANTIC_NON_NULL_TYPE,
769-
type,
770-
});
771-
}
770+
if (this._options.useSemanticNullability) {
771+
if (this.expectOptionalToken(TokenKind.BANG)) {
772+
return this.node<NonNullTypeNode>(start, {
773+
kind: Kind.NON_NULL_TYPE,
774+
type,
775+
});
776+
} else if (this.expectOptionalToken(TokenKind.QUESTION_MARK)) {
777+
return type;
778+
} else {
779+
return this.node<SemanticNonNullTypeNode>(start, {
780+
kind: Kind.SEMANTIC_NON_NULL_TYPE,
781+
type,
782+
});
783+
}
784+
} else {
785+
if (this.expectOptionalToken(TokenKind.BANG)) {
786+
return this.node<NonNullTypeNode>(start, {
787+
kind: Kind.NON_NULL_TYPE,
788+
type,
789+
});
790+
}
772791

773-
return type;
792+
return type;
793+
}
774794
}
775795

776796
/**

src/language/tokenKind.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ enum TokenKind {
66
SOF = '<SOF>',
77
EOF = '<EOF>',
88
BANG = '!',
9-
ASTERISK = '*',
9+
QUESTION_MARK = '?',
1010
DOLLAR = '$',
1111
AMP = '&',
1212
PAREN_L = '(',

0 commit comments

Comments
 (0)