blob: 3a58340e219fc0b7cc9fa31c7579e2ffdaed6af2 [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library parser;
class Token {
static final RegExp _alpha = RegExp(r'^[0-9a-zA-Z_\-@]+$');
final String text;
Token next;
Token(this.text);
bool get eof => text == null;
bool get isName {
if (text == null || text.isEmpty) return false;
return _alpha.hasMatch(text);
}
bool get isComment => text != null && text.startsWith('//');
String toString() => text == null ? 'EOF' : text;
}
class Tokenizer {
static final alphaNum =
'@abcdefghijklmnopqrstuvwxyz-_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
static final whitespace = ' \n\t\r';
String text;
Token _head;
Token _last;
Tokenizer(this.text);
Token tokenize() {
_emit(null);
for (int i = 0; i < text.length; i++) {
String c = text[i];
if (whitespace.contains(c)) {
// skip
} else if (c == '/' && _peek(i) == '/') {
int index = text.indexOf('\n', i);
if (index == -1) index = text.length;
_emit(text.substring(i, index));
i = index;
} else if (alphaNum.contains(c)) {
int start = i;
while (alphaNum.contains(_peek(i))) {
i++;
}
_emit(text.substring(start, i + 1));
} else {
_emit(c);
}
}
_emit(null);
_head = _head.next;
return _head;
}
void _emit(String value) {
Token token = Token(value);
if (_head == null) _head = token;
if (_last != null) _last.next = token;
_last = token;
}
String _peek(int i) {
i += 1;
return i < text.length ? text[i] : String.fromCharCodes([0]);
}
String toString() {
StringBuffer buf = StringBuffer();
Token t = _head;
buf.write('[${t}]\n');
while (!t.eof) {
t = t.next;
buf.write('[${t}]\n');
}
return buf.toString().trim();
}
}
abstract class Parser {
final Token startToken;
Token current;
Parser(this.startToken);
Token expect(String text) {
Token t = advance();
if (text != t.text) fail('expected ${text}, got ${t}');
return t;
}
bool consume(String text) {
if (peek().text == text) {
advance();
return true;
} else {
return false;
}
}
Token peek() =>
current == null ? startToken : current.eof ? current : current.next;
Token expectName() {
Token t = advance();
if (!t.isName) fail('expected name token, got ${t}');
return t;
}
Token advance() {
if (current == null) {
current = startToken;
} else if (!current.eof) {
current = current.next;
}
return current;
}
String collectComments() {
StringBuffer buf = StringBuffer();
while (peek().isComment) {
Token t = advance();
String str = t.text.substring(2);
if (str.startsWith(' ')) str = str.substring(1);
if (str.startsWith(' ')) {
buf.write('\n - ${str.substring(2)}');
} else if (str.isEmpty) {
buf.write('\n\n');
} else {
buf.write('${str} ');
}
}
if (buf.isEmpty) return null;
return buf
.toString()
.split('\n')
.map((line) => line.trimRight())
.join('\n')
.trim();
}
void validate(bool result, String message) {
if (!result) throw 'expected ${message}';
}
void fail(String message) => throw message;
}