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