| // Copyright (c) 2018, 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 kernel.text_reader; | |
| // S-expressions | |
| // | |
| // An S-expression is an atom or an S-list, an atom is a string that does not | |
| // contain the delimiters '(', ')', or ' ', and an S-list is a space delimited | |
| // sequence of S-expressions enclosed in parentheses: | |
| // | |
| // <S-expression> ::= <Atom> | |
| // | <S-list> | |
| // <S-list> ::= '(' ')' | |
| // | '(' <S-expression> {' ' <S-expression>}* ')' | |
| // | |
| // We use an iterator to read S-expressions. The iterator produces a stream | |
| // of atoms (strings) and nested iterators (S-lists). | |
| class TextIterator implements Iterator<Object? /* String? | TextIterator? */> { | |
| static final int space = ' '.codeUnitAt(0); | |
| static final int lparen = '('.codeUnitAt(0); | |
| static final int rparen = ')'.codeUnitAt(0); | |
| static final int dquote = '"'.codeUnitAt(0); | |
| static final int bslash = '\\'.codeUnitAt(0); | |
| final String input; | |
| int index; | |
| new(this.input, this.index); | |
| // Consume spaces. | |
| void skipWhitespace() { | |
| while (index < input.length && input.codeUnitAt(index) == space) { | |
| ++index; | |
| } | |
| } | |
| // Consume the rest of a nested S-expression and the closing delimiter. | |
| void skipToEndOfNested() { | |
| if (current is TextIterator) { | |
| TextIterator it = current as TextIterator; | |
| while (it.moveNext()) {} | |
| index = it.index + 1; | |
| } | |
| } | |
| void skipToEndOfAtom() { | |
| bool isQuoted = false; | |
| if (index < input.length && input.codeUnitAt(index) == dquote) { | |
| ++index; | |
| isQuoted = true; | |
| } | |
| do { | |
| if (index >= input.length) return; | |
| int codeUnit = input.codeUnitAt(index); | |
| if (isQuoted) { | |
| // Terminator. | |
| if (codeUnit == dquote) { | |
| ++index; | |
| return; | |
| } | |
| // Escaped double quote. | |
| if (codeUnit == bslash && | |
| index < input.length + 1 && | |
| input.codeUnitAt(index + 1) == dquote) { | |
| index += 2; | |
| } else { | |
| ++index; | |
| } | |
| } else { | |
| // Terminator. | |
| if (codeUnit == space || codeUnit == rparen) return; | |
| ++index; | |
| } | |
| } while (true); | |
| } | |
| @override | |
| Object? current = null; | |
| @override | |
| bool moveNext() { | |
| skipToEndOfNested(); | |
| skipWhitespace(); | |
| if (index >= input.length || input.codeUnitAt(index) == rparen) { | |
| current = null; | |
| return false; | |
| } | |
| if (input.codeUnitAt(index) == lparen) { | |
| current = new TextIterator(input, index + 1); | |
| return true; | |
| } | |
| int start = index; | |
| skipToEndOfAtom(); | |
| current = input.substring(start, index); | |
| return true; | |
| } | |
| } |