2017-01-22 23:15:53 +00:00
|
|
|
import 'package:string_scanner/string_scanner.dart';
|
2018-08-04 19:41:40 +00:00
|
|
|
|
2017-01-22 23:15:53 +00:00
|
|
|
import 'syntax_error.dart';
|
|
|
|
import 'token.dart';
|
|
|
|
import 'token_type.dart';
|
|
|
|
|
2018-08-02 13:25:41 +00:00
|
|
|
final RegExp _comment = new RegExp(r'#[^\n]*');
|
2017-01-22 23:15:53 +00:00
|
|
|
final RegExp _whitespace = new RegExp('[ \t\n\r]+');
|
|
|
|
final RegExp _boolean = new RegExp(r'true|false');
|
|
|
|
final RegExp _number = new RegExp(r'-?[0-9]+(\.[0-9]+)?(E|e(\+|-)?[0-9]+)?');
|
|
|
|
final RegExp _string = new RegExp(
|
|
|
|
r'"((\\(["\\/bfnrt]|(u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])))|([^"\\]))*"');
|
2018-08-04 19:18:53 +00:00
|
|
|
final RegExp _blockString = new RegExp(r'"""(([^"])|(\\"""))*"""');
|
2017-01-22 23:15:53 +00:00
|
|
|
final RegExp _name = new RegExp(r'[_A-Za-z][_0-9A-Za-z]*');
|
|
|
|
|
|
|
|
final Map<Pattern, TokenType> _patterns = {
|
|
|
|
'@': TokenType.ARROBA,
|
|
|
|
':': TokenType.COLON,
|
|
|
|
',': TokenType.COMMA,
|
|
|
|
r'$': TokenType.DOLLAR,
|
|
|
|
'...': TokenType.ELLIPSIS,
|
|
|
|
'=': TokenType.EQUALS,
|
|
|
|
'!': TokenType.EXCLAMATION,
|
|
|
|
'{': TokenType.LBRACE,
|
|
|
|
'}': TokenType.RBRACE,
|
|
|
|
'[': TokenType.LBRACKET,
|
|
|
|
']': TokenType.RBRACKET,
|
|
|
|
'(': TokenType.LPAREN,
|
|
|
|
')': TokenType.RPAREN,
|
|
|
|
'fragment': TokenType.FRAGMENT,
|
|
|
|
'mutation': TokenType.MUTATION,
|
2019-03-29 18:32:31 +00:00
|
|
|
'subscription': TokenType.SUBSCRIPTION,
|
2017-01-22 23:15:53 +00:00
|
|
|
'on': TokenType.ON,
|
|
|
|
'query': TokenType.QUERY,
|
2018-08-04 19:18:53 +00:00
|
|
|
'null': TokenType.NULL,
|
2017-01-22 23:15:53 +00:00
|
|
|
_boolean: TokenType.BOOLEAN,
|
|
|
|
_number: TokenType.NUMBER,
|
|
|
|
_string: TokenType.STRING,
|
2018-08-04 19:18:53 +00:00
|
|
|
_blockString: TokenType.BLOCK_STRING,
|
2017-01-22 23:15:53 +00:00
|
|
|
_name: TokenType.NAME
|
|
|
|
};
|
|
|
|
|
2018-08-02 15:17:14 +00:00
|
|
|
List<Token> scan(String text, {sourceUrl}) {
|
2017-07-03 15:37:35 +00:00
|
|
|
List<Token> out = [];
|
2018-08-02 15:17:14 +00:00
|
|
|
var scanner = new SpanScanner(text, sourceUrl: sourceUrl);
|
2017-01-22 23:15:53 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
while (!scanner.isDone) {
|
|
|
|
List<Token> potential = [];
|
2017-01-22 23:15:53 +00:00
|
|
|
|
2018-08-04 19:41:40 +00:00
|
|
|
if (scanner.scan(_comment) ||
|
|
|
|
scanner.scan(_whitespace) ||
|
|
|
|
scanner.scan(',') ||
|
|
|
|
scanner.scan('\ufeff')) continue;
|
2017-01-22 23:15:53 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
for (var pattern in _patterns.keys) {
|
|
|
|
if (scanner.matches(pattern)) {
|
2017-07-03 15:53:19 +00:00
|
|
|
potential.add(new Token(
|
|
|
|
_patterns[pattern], scanner.lastMatch[0], scanner.lastSpan));
|
2017-01-22 23:15:53 +00:00
|
|
|
}
|
2017-07-03 15:37:35 +00:00
|
|
|
}
|
2017-01-22 23:15:53 +00:00
|
|
|
|
2017-07-03 15:37:35 +00:00
|
|
|
if (potential.isEmpty) {
|
|
|
|
var ch = new String.fromCharCode(scanner.readChar());
|
2018-08-04 19:18:53 +00:00
|
|
|
throw new SyntaxError("Unexpected token '$ch'.", scanner.emptySpan);
|
2017-07-03 15:37:35 +00:00
|
|
|
} else {
|
|
|
|
// Choose longest token
|
|
|
|
potential.sort((a, b) => b.text.length.compareTo(a.text.length));
|
|
|
|
var chosen = potential.first;
|
|
|
|
out.add(chosen);
|
|
|
|
scanner.scan(chosen.text);
|
|
|
|
}
|
2017-01-22 23:15:53 +00:00
|
|
|
}
|
2017-07-03 15:37:35 +00:00
|
|
|
|
|
|
|
return out;
|
2017-01-22 23:15:53 +00:00
|
|
|
}
|