platform/lib/src/language/lexer.dart

78 lines
2.3 KiB
Dart
Raw Normal View History

2017-01-22 23:15:53 +00:00
import 'dart:async';
import 'package:string_scanner/string_scanner.dart';
import 'syntax_error.dart';
import 'token.dart';
import 'token_type.dart';
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])))|([^"\\]))*"');
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,
'on': TokenType.ON,
'query': TokenType.QUERY,
_boolean: TokenType.BOOLEAN,
_number: TokenType.NUMBER,
_string: TokenType.STRING,
_name: TokenType.NAME
};
class Lexer implements StreamTransformer<String, Token> {
@override
Stream<Token> bind(Stream<String> stream) {
var ctrl = new StreamController<Token>();
stream.listen((str) {
2017-02-05 23:08:03 +00:00
var scanner = new SpanScanner(str);
2017-01-22 23:15:53 +00:00
while (!scanner.isDone) {
List<Token> potential = [];
2017-02-05 23:08:03 +00:00
if (scanner.scan(_whitespace)) continue;
2017-01-22 23:15:53 +00:00
for (var pattern in _patterns.keys) {
if (scanner.matches(pattern)) {
potential.add(new Token(_patterns[pattern], scanner.lastMatch[0]));
}
}
if (potential.isEmpty) {
var ch = new String.fromCharCode(scanner.readChar());
2017-02-05 23:08:03 +00:00
ctrl.addError(new SyntaxError("Unexpected token '$ch'.",
scanner.state.line, scanner.state.column));
2017-01-22 23:15:53 +00:00
} else {
// Choose longest token
2017-02-05 23:08:03 +00:00
potential.sort((a, b) => b.text.length.compareTo(a.text.length));
2017-01-22 23:15:53 +00:00
var chosen = potential.first;
2017-02-05 23:08:03 +00:00
var start = scanner.state;
2017-01-22 23:15:53 +00:00
ctrl.add(chosen);
2017-02-05 23:08:03 +00:00
scanner.scan(chosen.text);
chosen.span = scanner.spanFrom(start);
2017-01-22 23:15:53 +00:00
}
}
})
..onDone(ctrl.close)
..onError(ctrl.addError);
return ctrl.stream;
}
}