Skip to content

[WIP][0.13] Separate multiple inherited interfaces with & #237

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,14 @@ Parses string containing GraphQL query or [type definition](type-system/type-lan
* (By default, the parser creates AST nodes that know the location
* in the source that they correspond to. This configuration flag
* disables that behavior for performance or testing.)
*
* allowLegacySDLImplementsInterfaces: boolean,
* (If enabled, the parser will parse implemented interfaces with no `&`
* character between each interface. Otherwise, the parser will follow the
* current specification.
*
* This option is provided to ease adoption of the final SDL specification
* and will be removed in a future major release.)
*
* @api
* @param Source|string $source
Expand Down
2 changes: 2 additions & 0 deletions src/Language/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ private function readToken(Token $prev)
return $this->readComment($line, $col, $prev);
case 36: // $
return new Token(Token::DOLLAR, $position, $position + 1, $line, $col, $prev);
case 38: // &
return new Token(Token::AMP, $position, $position + 1, $line, $col, $prev);
case 40: // (
return new Token(Token::PAREN_L, $position, $position + 1, $line, $col, $prev);
case 41: // )
Expand Down
20 changes: 19 additions & 1 deletion src/Language/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ class Parser
* (By default, the parser creates AST nodes that know the location
* in the source that they correspond to. This configuration flag
* disables that behavior for performance or testing.)
*
* allowLegacySDLImplementsInterfaces: boolean,
* (If enabled, the parser will parse implemented interfaces with no `&`
* character between each interface. Otherwise, the parser will follow the
* current specification.
*
* This option is provided to ease adoption of the final SDL specification
* and will be removed in a future major release.)
*
* @api
* @param Source|string $source
Expand Down Expand Up @@ -966,9 +974,19 @@ function parseImplementsInterfaces()
$types = [];
if ($this->lexer->token->value === 'implements') {
$this->lexer->advance();
// Optional leading ampersand
$this->skip(Token::AMP);
do {
$types[] = $this->parseNamedType();
} while ($this->peek(Token::NAME));
} while (
$this->skip(Token::AMP) ||
// Legacy support for the SDL?
(
isset($this->lexer->options['allowLegacySDLImplementsInterfaces']) &&
$this->peek(Token::NAME)
)

);
}
return $types;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Language/Printer.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ public function printAST($ast)
return $this->join([
'type',
$def->name,
$this->wrap('implements ', $this->join($def->interfaces, ', ')),
$this->wrap('implements ', $this->join($def->interfaces, ' & ')),
$this->join($def->directives, ' '),
$this->block($def->fields)
], ' ');
Expand Down
2 changes: 2 additions & 0 deletions src/Language/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class Token
const EOF = '<EOF>';
const BANG = '!';
const DOLLAR = '$';
const AMP = '&';
const PAREN_L = '(';
const PAREN_R = ')';
const SPREAD = '...';
Expand Down Expand Up @@ -42,6 +43,7 @@ public static function getKindDescription($kind)
$description[self::EOF] = '<EOF>';
$description[self::BANG] = '!';
$description[self::DOLLAR] = '$';
$description[self::AMP] = '&';
$description[self::PAREN_L] = '(';
$description[self::PAREN_R] = ')';
$description[self::SPREAD] = '...';
Expand Down
78 changes: 73 additions & 5 deletions tests/Language/SchemaParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public function testSimpleTypeInheritingInterface()
*/
public function testSimpleTypeInheritingMultipleInterfaces()
{
$body = 'type Hello implements Wo, rld { }';
$body = 'type Hello implements Wo & rld { field: String }';
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
$doc = Parser::parse($body);

Expand All @@ -177,15 +177,58 @@ public function testSimpleTypeInheritingMultipleInterfaces()
'name' => $this->nameNode('Hello', $loc(5, 10)),
'interfaces' => [
$this->typeNode('Wo', $loc(22,24)),
$this->typeNode('rld', $loc(26,29))
$this->typeNode('rld', $loc(27,30)),
],
'directives' => [],
'fields' => [],
'loc' => $loc(0, 33),
'fields' => [
$this->fieldNode(
$this->nameNode('field', $loc(33,38)),
$this->typeNode('String', $loc(40,46)),
$loc(33,46)
),
],
'loc' => $loc(0, 48),
'description' => null
]
],
'loc' => $loc(0, 33)
'loc' => $loc(0, 48)
];

$this->assertEquals($expected, TestUtils::nodeToArray($doc));
}

/**
* @it Simple type inheriting multiple interfaces with leading ampersand
*/
public function testSimpleTypeInheritingMultipleInterfacesWithLeadingAmpersand()
{
$body = 'type Hello implements & Wo & rld { field: String }';
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
$doc = Parser::parse($body);

$expected = [
'kind' => NodeKind::DOCUMENT,
'definitions' => [
[
'kind' => NodeKind::OBJECT_TYPE_DEFINITION,
'name' => $this->nameNode('Hello', $loc(5, 10)),
'interfaces' => [
$this->typeNode('Wo', $loc(24,26)),
$this->typeNode('rld', $loc(29,32)),
],
'directives' => [],
'fields' => [
$this->fieldNode(
$this->nameNode('field', $loc(35,40)),
$this->typeNode('String', $loc(42,48)),
$loc(35,48)
),
],
'loc' => $loc(0, 50),
'description' => null
]
],
'loc' => $loc(0, 50)
];

$this->assertEquals($expected, TestUtils::nodeToArray($doc));
Expand Down Expand Up @@ -705,6 +748,31 @@ public function testSimpleTypeDescriptionInComments()
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
}

/**
* @it Option: allowLegacySDLImplementsInterfaces
*/
public function testOptionAllowLegacySDLImplementationsInterfaces()
{
$body = 'type Hello implements Wo rld { field: String }';
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
$thrown = false;
try {
Parser::parse($body);
} catch (SyntaxError $e) {
$thrown = true;
}

$this->assertTrue($thrown, 'Parsing old SDL should throw without legacy option');

$doc = Parser::parse($body, ['allowLegacySDLImplementsInterfaces' => true]);
$expected = [
$this->typeNode('Wo', $loc(22,24)),
$this->typeNode('rld', $loc(25,28)),
];

$this->assertEquals($expected, TestUtils::nodeToArray($doc)['definitions'][0]['interfaces']);
}

private function typeNode($name, $loc)
{
return [
Expand Down
2 changes: 1 addition & 1 deletion tests/Language/SchemaPrinterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function testPrintsKitchenSink()
mutation: MutationType
}

type Foo implements Bar {
type Foo implements Bar & Baz {
one: Type
two(argument: InputType!): Type
three(argument: InputType, other: String): Int
Expand Down
2 changes: 1 addition & 1 deletion tests/Language/schema-kitchen-sink.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ schema {
mutation: MutationType
}

type Foo implements Bar {
type Foo implements Bar & Baz {
one: Type
two(argument: InputType!): Type
three(argument: InputType, other: String): Int
Expand Down