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

Side by Side 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 unified diff | Download patch
« no previous file with comments | « lib/src/parser.dart ('k') | lib/src/token.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 import 'package:string_scanner/string_scanner.dart';
6
7 import 'token.dart';
8
9 /// A regular expression matching both whitespace and single-line comments.
10 ///
11 /// This will only match if consumes at least one character.
12 final _whitespaceAndSingleLineComments =
13 new RegExp(r"([ \t\n]+|//[^\n]*(\n|$))+");
14
15 /// A regular expression matching the body of a multi-line comment, after `/*`
16 /// but before `*/` or a nested `/*`.
17 ///
18 /// This will only match if it consumes at least one character.
19 final _multiLineCommentBody = new RegExp(r"([^/*]|/[^*]|\*[^/])+");
20
21 /// A regular expression matching a hyphenated identifier.
22 ///
23 /// This is like a standard Dart identifier, except that it can also contain
24 /// hyphens.
25 final _hyphenatedIdentifier = new RegExp(r"[a-zA-Z_-][a-zA-Z0-9_-]*");
26
27 /// A scanner that converts a boolean selector string into a stream of tokens.
28 class Scanner {
29 /// The underlying string scanner.
30 final SpanScanner _scanner;
31
32 /// The next token to emit.
33 Token _next;
34
35 /// Whether the scanner has emitted a [TokenType.endOfFile] token.
36 bool _endOfFileEmitted = false;
37
38 Scanner(String selector)
39 : _scanner = new SpanScanner(selector);
40
41 /// Returns the next token that will be returned by [next].
42 ///
43 /// Throws a [StateError] if a [TokenType.endOfFile] token has already been
44 /// consumed.
45 Token peek() {
46 if (_next == null) _next = _getNext();
47 return _next;
48 }
49
50 /// Consumes and returns the next token in the stream.
51 ///
52 /// Throws a [StateError] if a [TokenType.endOfFile] token has already been
53 /// consumed.
54 Token next() {
55 var token = _next == null ? _getNext() : _next;
56 _endOfFileEmitted = token.type == TokenType.endOfFile;
57 _next = null;
58 return token;
59 }
60
61 /// If the next token matches [type], consumes it and returns `true`;
62 /// otherwise, returns `false`.
63 ///
64 /// Throws a [StateError] if a [TokenType.endOfFile] token has already been
65 /// consumed.
66 bool scan(TokenType type) {
67 if (peek().type != type) return false;
68 next();
69 return true;
70 }
71
72 /// Scan and return the next token in the stream.
73 Token _getNext() {
74 if (_endOfFileEmitted) throw new StateError("No more tokens.");
75
76 _consumeWhitespace();
77 if (_scanner.isDone) {
78 return new Token(
79 TokenType.endOfFile, _scanner.spanFrom(_scanner.state));
80 }
81
82 switch (_scanner.peekChar()) {
83 case 0x28 /* ( */: return _scanOperator(TokenType.leftParen);
84 case 0x29 /* ) */: return _scanOperator(TokenType.rightParen);
85 case 0x3F /* ? */: return _scanOperator(TokenType.questionMark);
86 case 0x3A /* : */: return _scanOperator(TokenType.colon);
87 case 0x21 /* ! */: return _scanOperator(TokenType.not);
88 case 0x7C /* | */: return _scanOr();
89 case 0x26 /* & */: return _scanAnd();
90 default: return _scanIdentifier();
91 }
92 }
93
94 /// Scans a single-character operator and returns a token of type [type].
95 ///
96 /// This assumes that the caller has already verified that the next character
97 /// is correct for the given operator.
98 Token _scanOperator(TokenType type) {
99 var start = _scanner.state;
100 _scanner.readChar();
101 return new Token(type, _scanner.spanFrom(start));
102 }
103
104 /// Scans a `||` operator and returns the appropriate token.
105 ///
106 /// This validates that the next two characters are `||`.
107 Token _scanOr() {
108 var start = _scanner.state;
109 _scanner.expect("||");
110 return new Token(TokenType.or, _scanner.spanFrom(start));
111 }
112
113 /// Scans a `&&` operator and returns the appropriate token.
114 ///
115 /// This validates that the next two characters are `&&`.
116 Token _scanAnd() {
117 var start = _scanner.state;
118 _scanner.expect("&&");
119 return new Token(TokenType.and, _scanner.spanFrom(start));
120 }
121
122 /// Scans and returns an identifier token.
123 Token _scanIdentifier() {
124 _scanner.expect(_hyphenatedIdentifier, name: "expression");
125 return new IdentifierToken(_scanner.lastMatch[0], _scanner.lastSpan);
126 }
127
128 /// Consumes all whitespace and comments immediately following the cursor's
129 /// current position.
130 void _consumeWhitespace() {
131 while (_scanner.scan(_whitespaceAndSingleLineComments) ||
132 _multiLineComment()) {
133 // Do nothing.
134 }
135 }
136
137 /// Consumes a single multi-line comment.
138 ///
139 /// Returns whether or not a comment was consumed.
140 bool _multiLineComment() {
141 if (!_scanner.scan("/*")) return false;
142
143 while (_scanner.scan(_multiLineCommentBody) || _multiLineComment()) {
144 // Do nothing.
145 }
146 _scanner.expect("*/");
147
148 return true;
149 }
150 }
OLDNEW
« 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