2017-01-22 23:15:53 +00:00
|
|
|
import 'dart:async';
|
|
|
|
import 'ast/ast.dart';
|
|
|
|
import 'stream_reader.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
|
|
|
|
|
|
|
class Parser implements StreamConsumer<Token> {
|
|
|
|
bool _closed = false;
|
2017-01-25 04:28:09 +00:00
|
|
|
final Completer _closer = new Completer();
|
|
|
|
final List<SyntaxError> _errors = [];
|
2017-01-22 23:15:53 +00:00
|
|
|
final StreamReader<Token> _reader = new StreamReader();
|
|
|
|
|
2017-01-25 04:28:09 +00:00
|
|
|
final StreamController<BooleanValueContext> _onBooleanValue =
|
|
|
|
new StreamController<BooleanValueContext>();
|
|
|
|
final StreamController<DocumentContext> _onDocument =
|
|
|
|
new StreamController<DocumentContext>();
|
2017-01-22 23:15:53 +00:00
|
|
|
final StreamController<Node> _onNode = new StreamController<Node>();
|
|
|
|
|
2017-01-25 04:28:09 +00:00
|
|
|
List<SyntaxError> get errors => new List<SyntaxError>.unmodifiable(_errors);
|
|
|
|
|
|
|
|
Stream<Node> get onBooleanValue => _onBooleanValue.stream;
|
|
|
|
Stream<Node> get onDocument => _onDocument.stream;
|
2017-01-22 23:15:53 +00:00
|
|
|
Stream<Node> get onNode => _onNode.stream;
|
|
|
|
|
2017-01-25 04:28:09 +00:00
|
|
|
Future _waterfall(List<Function> futures) async {
|
|
|
|
for (var f in futures) {
|
|
|
|
var r = await f();
|
|
|
|
if (r != null) return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-22 23:15:53 +00:00
|
|
|
@override
|
2017-01-25 04:28:09 +00:00
|
|
|
Future addStream(Stream<Token> stream) {
|
2017-01-22 23:15:53 +00:00
|
|
|
if (_closed) throw new StateError('Parser is already closed.');
|
2017-01-25 04:28:09 +00:00
|
|
|
|
|
|
|
_closed = true;
|
|
|
|
|
|
|
|
_reader.onData.listen((data) => _waterfall([document, booleanValue]))
|
|
|
|
..onDone(() => Future.wait([
|
|
|
|
_onBooleanValue.close(),
|
|
|
|
_onDocument.close(),
|
|
|
|
_onNode.close(),
|
|
|
|
]))
|
|
|
|
..onError(_closer.completeError);
|
|
|
|
|
|
|
|
return stream.pipe(_reader);
|
2017-01-22 23:15:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2017-01-25 04:28:09 +00:00
|
|
|
Future close() {
|
|
|
|
return _closer.future;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> expect(TokenType type) async {
|
|
|
|
var peek = await _reader.peek();
|
|
|
|
|
|
|
|
if (peek?.type != type) {
|
|
|
|
_errors.add(new SyntaxError.fromSourceLocation(
|
|
|
|
"Expected $type, found '${peek?.text ?? 'empty text'}' instead.",
|
|
|
|
peek?.span?.start));
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
await _reader.consume();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> maybe(TokenType type) async {
|
|
|
|
var peek = await _reader.peek();
|
|
|
|
|
|
|
|
if (peek?.type == type) {
|
|
|
|
await _reader.consume();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> nextIs(TokenType type) =>
|
|
|
|
_reader.peek().then((t) => t?.type == type);
|
|
|
|
|
|
|
|
Future<DocumentContext> document() async {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<BooleanValueContext> booleanValue() async {
|
|
|
|
if (await nextIs(TokenType.BOOLEAN)) {
|
|
|
|
var result = new BooleanValueContext(await _reader.consume());
|
|
|
|
_onBooleanValue.add(result);
|
|
|
|
return result;
|
|
|
|
}
|
2017-01-22 23:15:53 +00:00
|
|
|
|
2017-01-25 04:28:09 +00:00
|
|
|
return null;
|
2017-01-22 23:15:53 +00:00
|
|
|
}
|
|
|
|
}
|