Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(297)

Unified Diff: lib/src/scanner.dart

Issue 1712793002: Add the content of the package. (Closed) Base URL: git@github.com:dart-lang/boolean_selector@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/src/parser.dart ('k') | lib/src/token.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/scanner.dart
diff --git a/lib/src/scanner.dart b/lib/src/scanner.dart
new file mode 100644
index 0000000000000000000000000000000000000000..7a2ce798570c8a34fe129757ffa12e52c423f0ad
--- /dev/null
+++ b/lib/src/scanner.dart
@@ -0,0 +1,150 @@
+// Copyright (c) 2016, 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.
+
+import 'package:string_scanner/string_scanner.dart';
+
+import 'token.dart';
+
+/// A regular expression matching both whitespace and single-line comments.
+///
+/// This will only match if consumes at least one character.
+final _whitespaceAndSingleLineComments =
+ new RegExp(r"([ \t\n]+|//[^\n]*(\n|$))+");
+
+/// A regular expression matching the body of a multi-line comment, after `/*`
+/// but before `*/` or a nested `/*`.
+///
+/// This will only match if it consumes at least one character.
+final _multiLineCommentBody = new RegExp(r"([^/*]|/[^*]|\*[^/])+");
+
+/// A regular expression matching a hyphenated identifier.
+///
+/// This is like a standard Dart identifier, except that it can also contain
+/// hyphens.
+final _hyphenatedIdentifier = new RegExp(r"[a-zA-Z_-][a-zA-Z0-9_-]*");
+
+/// A scanner that converts a boolean selector string into a stream of tokens.
+class Scanner {
+ /// The underlying string scanner.
+ final SpanScanner _scanner;
+
+ /// The next token to emit.
+ Token _next;
+
+ /// Whether the scanner has emitted a [TokenType.endOfFile] token.
+ bool _endOfFileEmitted = false;
+
+ Scanner(String selector)
+ : _scanner = new SpanScanner(selector);
+
+ /// Returns the next token that will be returned by [next].
+ ///
+ /// Throws a [StateError] if a [TokenType.endOfFile] token has already been
+ /// consumed.
+ Token peek() {
+ if (_next == null) _next = _getNext();
+ return _next;
+ }
+
+ /// Consumes and returns the next token in the stream.
+ ///
+ /// Throws a [StateError] if a [TokenType.endOfFile] token has already been
+ /// consumed.
+ Token next() {
+ var token = _next == null ? _getNext() : _next;
+ _endOfFileEmitted = token.type == TokenType.endOfFile;
+ _next = null;
+ return token;
+ }
+
+ /// If the next token matches [type], consumes it and returns `true`;
+ /// otherwise, returns `false`.
+ ///
+ /// Throws a [StateError] if a [TokenType.endOfFile] token has already been
+ /// consumed.
+ bool scan(TokenType type) {
+ if (peek().type != type) return false;
+ next();
+ return true;
+ }
+
+ /// Scan and return the next token in the stream.
+ Token _getNext() {
+ if (_endOfFileEmitted) throw new StateError("No more tokens.");
+
+ _consumeWhitespace();
+ if (_scanner.isDone) {
+ return new Token(
+ TokenType.endOfFile, _scanner.spanFrom(_scanner.state));
+ }
+
+ switch (_scanner.peekChar()) {
+ case 0x28 /* ( */: return _scanOperator(TokenType.leftParen);
+ case 0x29 /* ) */: return _scanOperator(TokenType.rightParen);
+ case 0x3F /* ? */: return _scanOperator(TokenType.questionMark);
+ case 0x3A /* : */: return _scanOperator(TokenType.colon);
+ case 0x21 /* ! */: return _scanOperator(TokenType.not);
+ case 0x7C /* | */: return _scanOr();
+ case 0x26 /* & */: return _scanAnd();
+ default: return _scanIdentifier();
+ }
+ }
+
+ /// Scans a single-character operator and returns a token of type [type].
+ ///
+ /// This assumes that the caller has already verified that the next character
+ /// is correct for the given operator.
+ Token _scanOperator(TokenType type) {
+ var start = _scanner.state;
+ _scanner.readChar();
+ return new Token(type, _scanner.spanFrom(start));
+ }
+
+ /// Scans a `||` operator and returns the appropriate token.
+ ///
+ /// This validates that the next two characters are `||`.
+ Token _scanOr() {
+ var start = _scanner.state;
+ _scanner.expect("||");
+ return new Token(TokenType.or, _scanner.spanFrom(start));
+ }
+
+ /// Scans a `&&` operator and returns the appropriate token.
+ ///
+ /// This validates that the next two characters are `&&`.
+ Token _scanAnd() {
+ var start = _scanner.state;
+ _scanner.expect("&&");
+ return new Token(TokenType.and, _scanner.spanFrom(start));
+ }
+
+ /// Scans and returns an identifier token.
+ Token _scanIdentifier() {
+ _scanner.expect(_hyphenatedIdentifier, name: "expression");
+ return new IdentifierToken(_scanner.lastMatch[0], _scanner.lastSpan);
+ }
+
+ /// Consumes all whitespace and comments immediately following the cursor's
+ /// current position.
+ void _consumeWhitespace() {
+ while (_scanner.scan(_whitespaceAndSingleLineComments) ||
+ _multiLineComment()) {
+ // Do nothing.
+ }
+ }
+
+ /// Consumes a single multi-line comment.
+ ///
+ /// Returns whether or not a comment was consumed.
+ bool _multiLineComment() {
+ if (!_scanner.scan("/*")) return false;
+
+ while (_scanner.scan(_multiLineCommentBody) || _multiLineComment()) {
+ // Do nothing.
+ }
+ _scanner.expect("*/");
+
+ return true;
+ }
+}
« no previous file with comments | « lib/src/parser.dart ('k') | lib/src/token.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698