2017-02-05 23:08:03 +00:00
|
|
|
library graphql_parser.language.parser;
|
|
|
|
|
2017-01-22 23:15:53 +00:00
|
|
|
import 'ast/ast.dart';
|
2017-01-25 04:28:09 +00:00
|
|
|
import 'syntax_error.dart';
|
2017-01-22 23:15:53 +00:00
|
|
|
import 'token.dart';
|
2017-01-25 04:28:09 +00:00
|
|
|
import 'token_type.dart';
|
2017-01-22 23:15:53 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
class Parser {
|
|
|
|
Token _current;
|
|
|
|
int _index = -1;
|
2017-01-22 23:15:53 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
final List<Token> tokens;
|
2017-01-25 04:28:09 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
Parser(this.tokens);
|
2017-01-25 04:28:09 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
Token get current => _current;
|
2017-01-25 04:28:09 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
bool next(TokenType type) {
|
|
|
|
if (peek()?.type == type) {
|
|
|
|
_current = tokens[++_index];
|
|
|
|
return true;
|
|
|
|
}
|
2017-01-25 04:28:09 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
return false;
|
2017-01-22 23:15:53 +00:00
|
|
|
}
|
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
Token peek() {
|
|
|
|
if (_index < tokens.length - 1) {
|
|
|
|
return tokens[_index + 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
2017-01-25 04:28:09 +00:00
|
|
|
}
|
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
DocumentContext parseDocument() {}
|
2017-01-25 04:28:09 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
FragmentDefinitionContext parseFragmentDefinition() {}
|
2017-01-25 04:28:09 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
FragmentSpreadContext parseFragmentSpread() {}
|
2017-01-25 04:28:09 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
InlineFragmentContext parseInlineFragment() {}
|
2017-01-25 04:28:09 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
SelectionSetContext parseSelectionSet() {}
|
2017-01-25 04:28:09 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
SelectionContext parseSelection() {}
|
2017-01-25 04:28:09 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
FieldContext parseField() {}
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
FieldNameContext parseFieldName() {}
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
AliasContext parseAlias() {}
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
VariableDefinitionsContext parseVariableDefinitions() {}
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
VariableDefinitionContext parseVariableDefinition() {}
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
List<DirectiveContext> parseDirectives() {}
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
DirectiveContext parseDirective() {
|
|
|
|
if (next(TokenType.ARROBA)) {
|
|
|
|
var ARROBA = current;
|
|
|
|
if (next(TokenType.NAME)) {
|
|
|
|
var NAME = current;
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
if (next(TokenType.COLON)) {
|
|
|
|
var COLON = current;
|
|
|
|
var val = parseValueOrVariable();
|
|
|
|
if (val != null)
|
|
|
|
return new DirectiveContext(
|
|
|
|
ARROBA, NAME, COLON, null, null, null, val);
|
|
|
|
else
|
|
|
|
throw new SyntaxError.fromSourceLocation(
|
|
|
|
'Expected value or variable in directive after colon.',
|
|
|
|
COLON.span.end);
|
|
|
|
} else if (next(TokenType.LPAREN)) {
|
|
|
|
var LPAREN = current;
|
|
|
|
var arg = parseArgument();
|
|
|
|
if (arg != null) {
|
|
|
|
if (next(TokenType.RPAREN)) {
|
|
|
|
return new DirectiveContext(
|
|
|
|
ARROBA, NAME, null, LPAREN, current, arg, null);
|
|
|
|
} else
|
|
|
|
throw new SyntaxError.fromSourceLocation(
|
2017-07-03 23:03:36 +00:00
|
|
|
'Expected \')\'', arg.valueOrVariable.span.end);
|
2017-07-03 15:37:35 +00:00
|
|
|
} else
|
|
|
|
throw new SyntaxError.fromSourceLocation(
|
|
|
|
'Expected argument in directive.', LPAREN.span.end);
|
|
|
|
} else
|
|
|
|
return new DirectiveContext(
|
|
|
|
ARROBA, NAME, null, null, null, null, null);
|
|
|
|
} else
|
|
|
|
throw new SyntaxError.fromSourceLocation(
|
|
|
|
'Expected name for directive.', ARROBA.span.end);
|
|
|
|
} else
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
ArgumentContext parseArgument() {
|
|
|
|
if (next(TokenType.NAME)) {
|
|
|
|
var NAME = current;
|
|
|
|
if (next(TokenType.COLON)) {
|
|
|
|
var COLON = current;
|
|
|
|
var val = parseValueOrVariable();
|
|
|
|
if (val != null)
|
|
|
|
return new ArgumentContext(NAME, COLON, val);
|
|
|
|
else
|
|
|
|
throw new SyntaxError.fromSourceLocation(
|
|
|
|
'Expected value or variable in argument.', COLON.span.end);
|
|
|
|
} else
|
|
|
|
throw new SyntaxError.fromSourceLocation(
|
|
|
|
'Expected colon after name in argument.', NAME.span.end);
|
|
|
|
} else
|
|
|
|
return null;
|
2017-01-25 04:28:09 +00:00
|
|
|
}
|
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
ValueOrVariableContext parseValueOrVariable() {
|
|
|
|
var value = parseValue();
|
|
|
|
if (value != null)
|
|
|
|
return new ValueOrVariableContext(value, null);
|
|
|
|
else {
|
|
|
|
var variable = parseVariable();
|
|
|
|
if (variable != null)
|
|
|
|
return new ValueOrVariableContext(null, variable);
|
|
|
|
else
|
|
|
|
return null;
|
2017-02-05 23:08:03 +00:00
|
|
|
}
|
2017-07-03 15:37:35 +00:00
|
|
|
}
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
VariableContext parseVariable() {
|
|
|
|
if (next(TokenType.DOLLAR)) {
|
|
|
|
var DOLLAR = current;
|
|
|
|
if (next(TokenType.NAME))
|
|
|
|
return new VariableContext(DOLLAR, current);
|
|
|
|
else
|
|
|
|
throw new SyntaxError.fromSourceLocation(
|
|
|
|
'Expected name for variable; found a lone "\$" instead.',
|
|
|
|
DOLLAR.span.end);
|
|
|
|
} else
|
|
|
|
return null;
|
2017-02-05 23:08:03 +00:00
|
|
|
}
|
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
DefaultValueContext parseDefaultValue() {}
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
TypeConditionContext parseTypeCondition() {}
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
ValueContext parseValue() {
|
|
|
|
return parseStringValue() ??
|
|
|
|
parseNumberValue() ??
|
|
|
|
parseBooleanValue() ??
|
|
|
|
parseArrayValue();
|
|
|
|
}
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
StringValueContext parseStringValue() =>
|
|
|
|
next(TokenType.STRING) ? new StringValueContext(current) : null;
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
NumberValueContext parseNumberValue() =>
|
|
|
|
next(TokenType.NUMBER) ? new NumberValueContext(current) : null;
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
BooleanValueContext parseBooleanValue() =>
|
|
|
|
next(TokenType.BOOLEAN) ? new BooleanValueContext(current) : null;
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
ArrayValueContext parseArrayValue() {
|
|
|
|
if (next(TokenType.LBRACKET)) {
|
|
|
|
var LBRACKET = current;
|
|
|
|
List<ValueContext> values = [];
|
|
|
|
ValueContext value = parseValue();
|
2017-02-05 23:08:03 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
while (value != null) {
|
|
|
|
values.add(value);
|
|
|
|
if (next(TokenType.COMMA)) {
|
|
|
|
value = parseValue();
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
2017-01-22 23:15:53 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
if (next(TokenType.RBRACKET)) {
|
|
|
|
return new ArrayValueContext(LBRACKET, current)..values.addAll(values);
|
|
|
|
} else
|
|
|
|
throw new SyntaxError.fromSourceLocation(
|
|
|
|
'Unterminated array literal.', LBRACKET.span.end);
|
|
|
|
} else
|
|
|
|
return null;
|
2017-01-22 23:15:53 +00:00
|
|
|
}
|
|
|
|
}
|