Chromium Code Reviews

Side by Side Diff: lib/src/backend/platform_selector/scanner.dart

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

Powered by Google App Engine