More
This commit is contained in:
parent
4478f72015
commit
96aba898f3
7 changed files with 145 additions and 60 deletions
|
@ -12,10 +12,11 @@ Stream<String> input() async* {
|
|||
.trim();
|
||||
}
|
||||
|
||||
main() async {
|
||||
main() {
|
||||
var lexer = new Lexer(), parser = new Parser();
|
||||
var stream = input().transform(lexer).asBroadcastStream();
|
||||
await stream.forEach(print);
|
||||
stream.pipe(parser);
|
||||
await parser.onNode.forEach(print);
|
||||
stream
|
||||
..forEach(print)
|
||||
..pipe(parser);
|
||||
parser.onNode.forEach(print);
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import 'dart:async';
|
||||
import 'package:graphql_parser/src/language/language.dart';
|
||||
|
||||
Stream<String> input() async* {
|
||||
yield 'true';
|
||||
}
|
||||
|
||||
main() async {
|
||||
var lexer = new Lexer(), parser = new Parser();
|
||||
input().transform(lexer).pipe(parser);
|
||||
await parser.onBooleanValue.forEach(print);
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
library graphql_parser.language.ast;
|
||||
|
||||
export 'alias.dart';
|
||||
export 'array_value.dart';
|
||||
export 'argument.dart';
|
||||
export 'boolean_value.dart';
|
||||
export 'default_value.dart';
|
||||
|
|
|
@ -10,6 +10,8 @@ class StringValueContext extends ValueContext {
|
|||
@override
|
||||
SourceSpan get span => STRING.span;
|
||||
|
||||
String get stringValue => STRING.text.substring(0, STRING.text.length - 1);
|
||||
|
||||
@override
|
||||
String toSource() => STRING.text;
|
||||
}
|
||||
|
|
25
lib/src/language/base_parser.dart
Normal file
25
lib/src/language/base_parser.dart
Normal file
|
@ -0,0 +1,25 @@
|
|||
part of graphql_parser.language.parser;
|
||||
|
||||
abstract class BaseParser implements StreamConsumer<Token> {
|
||||
final StreamController<ArrayValueContext> _onArrayValue =
|
||||
new StreamController<ArrayValueContext>();
|
||||
final StreamController<BooleanValueContext> _onBooleanValue =
|
||||
new StreamController<BooleanValueContext>();
|
||||
final StreamController<DocumentContext> _onDocument =
|
||||
new StreamController<DocumentContext>();
|
||||
final StreamController<Node> _onNode = new StreamController<Node>();
|
||||
final StreamController<NumberValueContext> _onNumberValue =
|
||||
new StreamController<NumberValueContext>();
|
||||
final StreamController<StringValueContext> _onStringValue =
|
||||
new StreamController<StringValueContext>();
|
||||
final StreamController<ValueContext> _onValue =
|
||||
new StreamController<ValueContext>();
|
||||
|
||||
Stream<Node> get onArrayValue => _onArrayValue.stream;
|
||||
Stream<Node> get onBooleanValue => _onBooleanValue.stream;
|
||||
Stream<Node> get onDocument => _onDocument.stream;
|
||||
Stream<Node> get onNode => _onNode.stream;
|
||||
Stream<Node> get onNumberValue => _onNumberValue.stream;
|
||||
Stream<Node> get onStringValue => _onStringValue.stream;
|
||||
Stream<Node> get onValue => _onValue.stream;
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:string_scanner/string_scanner.dart';
|
||||
import 'package:source_span/source_span.dart';
|
||||
import 'syntax_error.dart';
|
||||
import 'token.dart';
|
||||
import 'token_type.dart';
|
||||
|
@ -42,24 +41,12 @@ class Lexer implements StreamTransformer<String, Token> {
|
|||
var ctrl = new StreamController<Token>();
|
||||
|
||||
stream.listen((str) {
|
||||
var scanner = new StringScanner(str);
|
||||
int line = 1, column = 1;
|
||||
var scanner = new SpanScanner(str);
|
||||
|
||||
while (!scanner.isDone) {
|
||||
List<Token> potential = [];
|
||||
|
||||
if (scanner.scan(_whitespace)) {
|
||||
var text = scanner.lastMatch[0];
|
||||
line += '\n'.allMatches(text).length;
|
||||
var lastNewLine = text.lastIndexOf('\n');
|
||||
|
||||
if (lastNewLine != -1) {
|
||||
int len = text.substring(lastNewLine + 1).length;
|
||||
column = 1 + len;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
if (scanner.scan(_whitespace)) continue;
|
||||
|
||||
for (var pattern in _patterns.keys) {
|
||||
if (scanner.matches(pattern)) {
|
||||
|
@ -69,19 +56,16 @@ class Lexer implements StreamTransformer<String, Token> {
|
|||
|
||||
if (potential.isEmpty) {
|
||||
var ch = new String.fromCharCode(scanner.readChar());
|
||||
ctrl.addError(new SyntaxError("Unexpected token '$ch'.", line, column));
|
||||
ctrl.addError(new SyntaxError("Unexpected token '$ch'.",
|
||||
scanner.state.line, scanner.state.column));
|
||||
} else {
|
||||
// Choose longest token
|
||||
potential.sort((a, b) => a.text.length.compareTo(b.text.length));
|
||||
potential.sort((a, b) => b.text.length.compareTo(a.text.length));
|
||||
var chosen = potential.first;
|
||||
var start =
|
||||
new SourceLocation(scanner.position, line: line, column: column);
|
||||
var start = scanner.state;
|
||||
ctrl.add(chosen);
|
||||
scanner.position += chosen.text.length;
|
||||
column += chosen.text.length;
|
||||
var end =
|
||||
new SourceLocation(scanner.position, line: line, column: column);
|
||||
chosen.span = new SourceSpan(start, end, chosen.text);
|
||||
scanner.scan(chosen.text);
|
||||
chosen.span = scanner.spanFrom(start);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,28 +1,21 @@
|
|||
library graphql_parser.language.parser;
|
||||
|
||||
import 'dart:async';
|
||||
import 'ast/ast.dart';
|
||||
import 'stream_reader.dart';
|
||||
import 'syntax_error.dart';
|
||||
import 'token.dart';
|
||||
import 'token_type.dart';
|
||||
part 'base_parser.dart';
|
||||
|
||||
class Parser implements StreamConsumer<Token> {
|
||||
class Parser extends BaseParser {
|
||||
bool _closed = false;
|
||||
final Completer _closer = new Completer();
|
||||
final List<SyntaxError> _errors = [];
|
||||
final StreamReader<Token> _reader = new StreamReader();
|
||||
|
||||
final StreamController<BooleanValueContext> _onBooleanValue =
|
||||
new StreamController<BooleanValueContext>();
|
||||
final StreamController<DocumentContext> _onDocument =
|
||||
new StreamController<DocumentContext>();
|
||||
final StreamController<Node> _onNode = new StreamController<Node>();
|
||||
|
||||
List<SyntaxError> get errors => new List<SyntaxError>.unmodifiable(_errors);
|
||||
|
||||
Stream<Node> get onBooleanValue => _onBooleanValue.stream;
|
||||
Stream<Node> get onDocument => _onDocument.stream;
|
||||
Stream<Node> get onNode => _onNode.stream;
|
||||
|
||||
Future _waterfall(List<Function> futures) async {
|
||||
for (var f in futures) {
|
||||
var r = await f();
|
||||
|
@ -36,13 +29,14 @@ class Parser implements StreamConsumer<Token> {
|
|||
|
||||
_closed = true;
|
||||
|
||||
_reader.onData.listen((data) => _waterfall([document, booleanValue]))
|
||||
..onDone(() => Future.wait([
|
||||
_onBooleanValue.close(),
|
||||
_onDocument.close(),
|
||||
_onNode.close(),
|
||||
]))
|
||||
..onError(_closer.completeError);
|
||||
_reader.onData
|
||||
.listen((data) => _waterfall([parseDocument, parseBooleanValue]))
|
||||
..onDone(() => Future.wait([
|
||||
_onBooleanValue.close(),
|
||||
_onDocument.close(),
|
||||
_onNode.close(),
|
||||
]))
|
||||
..onError(_closer.completeError);
|
||||
|
||||
return stream.pipe(_reader);
|
||||
}
|
||||
|
@ -80,11 +74,101 @@ class Parser implements StreamConsumer<Token> {
|
|||
Future<bool> nextIs(TokenType type) =>
|
||||
_reader.peek().then((t) => t?.type == type);
|
||||
|
||||
Future<DocumentContext> document() async {
|
||||
Future<DocumentContext> parseDocument() async {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<BooleanValueContext> booleanValue() async {
|
||||
Future<ValueContext> parseValue() async {
|
||||
ValueContext value;
|
||||
|
||||
var string = await parseStringValue();
|
||||
if (string != null)
|
||||
value = string;
|
||||
else {
|
||||
var number = await parseNumberValue();
|
||||
if (number != null)
|
||||
value = number;
|
||||
else {
|
||||
var boolean = await parseBooleanValue();
|
||||
if (boolean != null)
|
||||
value = boolean;
|
||||
else {
|
||||
var array = await parseArrayValue();
|
||||
if (array != null) value = array;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value != null) _onValue.add(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
Future<StringValueContext> parseStringValue() async {
|
||||
if (await nextIs(TokenType.STRING)) {
|
||||
var result = new StringValueContext(await _reader.consume());
|
||||
_onStringValue.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<NumberValueContext> parseNumberValue() async {
|
||||
if (await nextIs(TokenType.NUMBER)) {
|
||||
var result = new NumberValueContext(await _reader.consume());
|
||||
_onNumberValue.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<BooleanValueContext> parseBooleanValue() async {
|
||||
if (await nextIs(TokenType.BOOLEAN)) {
|
||||
var result = new BooleanValueContext(await _reader.consume());
|
||||
_onBooleanValue.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<ArrayValueContext> parseArrayValue() async {
|
||||
if (await nextIs(TokenType.LBRACKET)) {
|
||||
ArrayValueContext result;
|
||||
var LBRACKET = await _reader.consume();
|
||||
List<ValueContext> values = [];
|
||||
|
||||
if (await nextIs(TokenType.RBRACKET)) {
|
||||
result = new ArrayValueContext(LBRACKET, await _reader.consume());
|
||||
_onArrayValue.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
while (!_reader.isDone) {
|
||||
ValueContext value = await parseValue();
|
||||
if (value == null) break;
|
||||
|
||||
values.add(value);
|
||||
|
||||
if (await nextIs(TokenType.COMMA)) {
|
||||
await _reader.consume();
|
||||
continue;
|
||||
} else if (await nextIs(TokenType.RBRACKET)) {
|
||||
result = new ArrayValueContext(LBRACKET, await _reader.consume());
|
||||
_onArrayValue.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
throw new SyntaxError.fromSourceLocation(
|
||||
'Expected comma or right bracket in array',
|
||||
(await _reader.current())?.span?.start);
|
||||
}
|
||||
|
||||
throw new SyntaxError.fromSourceLocation(
|
||||
'Unterminated array literal.', LBRACKET.span?.start);
|
||||
}
|
||||
|
||||
if (await nextIs(TokenType.BOOLEAN)) {
|
||||
var result = new BooleanValueContext(await _reader.consume());
|
||||
_onBooleanValue.add(result);
|
||||
|
|
Loading…
Reference in a new issue