This commit is contained in:
thosakwe 2017-02-05 18:08:03 -05:00
parent 4478f72015
commit 96aba898f3
7 changed files with 145 additions and 60 deletions

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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';

View file

@ -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;
}

View 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;
}

View file

@ -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);
}
}
})

View file

@ -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);