OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011, 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 dart2js.scanner.array_based; |
| 6 |
| 7 import '../io/source_file.dart' show SourceFile; |
| 8 import '../tokens/keyword.dart' show Keyword; |
| 9 import '../tokens/precedence.dart' show PrecedenceInfo; |
| 10 import '../tokens/precedence_constants.dart' as Precedence |
| 11 show COMMENT_INFO, EOF_INFO; |
| 12 import '../tokens/token.dart' |
| 13 show BeginGroupToken, ErrorToken, KeywordToken, SymbolToken, Token; |
| 14 import '../tokens/token_constants.dart' as Tokens |
| 15 show LT_TOKEN, OPEN_CURLY_BRACKET_TOKEN, STRING_INTERPOLATION_TOKEN; |
| 16 import '../util/characters.dart' show $LF, $STX; |
| 17 import '../util/util.dart' show Link; |
| 18 import 'scanner.dart' show AbstractScanner; |
| 19 |
| 20 abstract class ArrayBasedScanner extends AbstractScanner { |
| 21 ArrayBasedScanner(SourceFile file, bool includeComments) |
| 22 : super(file, includeComments); |
| 23 |
| 24 /** |
| 25 * The stack of open groups, e.g [: { ... ( .. :] |
| 26 * Each BeginGroupToken has a pointer to the token where the group |
| 27 * ends. This field is set when scanning the end group token. |
| 28 */ |
| 29 Link<BeginGroupToken> groupingStack = const Link<BeginGroupToken>(); |
| 30 |
| 31 /** |
| 32 * Appends a fixed token whose kind and content is determined by [info]. |
| 33 * Appends an *operator* token from [info]. |
| 34 * |
| 35 * An operator token represent operators like ':', '.', ';', '&&', '==', '--', |
| 36 * '=>', etc. |
| 37 */ |
| 38 void appendPrecedenceToken(PrecedenceInfo info) { |
| 39 tail.next = new SymbolToken(info, tokenStart); |
| 40 tail = tail.next; |
| 41 } |
| 42 |
| 43 /** |
| 44 * Appends a fixed token based on whether the current char is [choice] or not. |
| 45 * If the current char is [choice] a fixed token whose kind and content |
| 46 * is determined by [yes] is appended, otherwise a fixed token whose kind |
| 47 * and content is determined by [no] is appended. |
| 48 */ |
| 49 int select(int choice, PrecedenceInfo yes, PrecedenceInfo no) { |
| 50 int next = advance(); |
| 51 if (identical(next, choice)) { |
| 52 appendPrecedenceToken(yes); |
| 53 return advance(); |
| 54 } else { |
| 55 appendPrecedenceToken(no); |
| 56 return next; |
| 57 } |
| 58 } |
| 59 |
| 60 /** |
| 61 * Appends a keyword token whose kind is determined by [keyword]. |
| 62 */ |
| 63 void appendKeywordToken(Keyword keyword) { |
| 64 String syntax = keyword.syntax; |
| 65 // Type parameters and arguments cannot contain 'this'. |
| 66 if (identical(syntax, 'this')) { |
| 67 discardOpenLt(); |
| 68 } |
| 69 tail.next = new KeywordToken(keyword, tokenStart); |
| 70 tail = tail.next; |
| 71 } |
| 72 |
| 73 void appendEofToken() { |
| 74 beginToken(); |
| 75 discardOpenLt(); |
| 76 while (!groupingStack.isEmpty) { |
| 77 unmatchedBeginGroup(groupingStack.head); |
| 78 groupingStack = groupingStack.tail; |
| 79 } |
| 80 tail.next = new SymbolToken(Precedence.EOF_INFO, tokenStart); |
| 81 tail = tail.next; |
| 82 // EOF points to itself so there's always infinite look-ahead. |
| 83 tail.next = tail; |
| 84 } |
| 85 |
| 86 /** |
| 87 * Notifies scanning a whitespace character. Note that [appendWhiteSpace] is |
| 88 * not always invoked for [$SPACE] characters. |
| 89 * |
| 90 * This method is used by the scanners to track line breaks and create the |
| 91 * [lineStarts] map. |
| 92 */ |
| 93 void appendWhiteSpace(int next) { |
| 94 if (next == $LF && file != null) { |
| 95 lineStarts.add(stringOffset + 1); // +1, the line starts after the $LF. |
| 96 } |
| 97 } |
| 98 |
| 99 /** |
| 100 * Notifies on [$LF] characters in multi-line comments or strings. |
| 101 * |
| 102 * This method is used by the scanners to track line breaks and create the |
| 103 * [lineStarts] map. |
| 104 */ |
| 105 void lineFeedInMultiline() { |
| 106 if (file != null) { |
| 107 lineStarts.add(stringOffset + 1); |
| 108 } |
| 109 } |
| 110 |
| 111 /** |
| 112 * Appends a token that begins a new group, represented by [value]. |
| 113 * Group begin tokens are '{', '(', '[' and '${'. |
| 114 */ |
| 115 void appendBeginGroup(PrecedenceInfo info) { |
| 116 Token token = new BeginGroupToken(info, tokenStart); |
| 117 tail.next = token; |
| 118 tail = tail.next; |
| 119 |
| 120 // { ( [ ${ cannot appear inside a type parameters / arguments. |
| 121 if (!identical(info.kind, Tokens.LT_TOKEN)) discardOpenLt(); |
| 122 groupingStack = groupingStack.prepend(token); |
| 123 } |
| 124 |
| 125 /** |
| 126 * Appends a token that begins an end group, represented by [value]. |
| 127 * It handles the group end tokens '}', ')' and ']'. The tokens '>' and |
| 128 * '>>' are handled separately bo [appendGt] and [appendGtGt]. |
| 129 */ |
| 130 int appendEndGroup(PrecedenceInfo info, int openKind) { |
| 131 assert(!identical(openKind, Tokens.LT_TOKEN)); // openKind is < for > and >> |
| 132 discardBeginGroupUntil(openKind); |
| 133 appendPrecedenceToken(info); |
| 134 Token close = tail; |
| 135 if (groupingStack.isEmpty) { |
| 136 return advance(); |
| 137 } |
| 138 BeginGroupToken begin = groupingStack.head; |
| 139 if (!identical(begin.kind, openKind)) { |
| 140 assert(begin.kind == Tokens.STRING_INTERPOLATION_TOKEN && |
| 141 openKind == Tokens.OPEN_CURLY_BRACKET_TOKEN); |
| 142 // We're ending an interpolated expression. |
| 143 begin.endGroup = close; |
| 144 groupingStack = groupingStack.tail; |
| 145 // Using "start-of-text" to signal that we're back in string |
| 146 // scanning mode. |
| 147 return $STX; |
| 148 } |
| 149 begin.endGroup = close; |
| 150 groupingStack = groupingStack.tail; |
| 151 return advance(); |
| 152 } |
| 153 |
| 154 /** |
| 155 * Discards begin group tokens until a match with [openKind] is found. |
| 156 * This recovers nicely from from a situation like "{[}". |
| 157 */ |
| 158 void discardBeginGroupUntil(int openKind) { |
| 159 while (!groupingStack.isEmpty) { |
| 160 // Don't report unmatched errors for <; it is also the less-than operator. |
| 161 discardOpenLt(); |
| 162 if (groupingStack.isEmpty) return; |
| 163 BeginGroupToken begin = groupingStack.head; |
| 164 if (openKind == begin.kind) return; |
| 165 if (openKind == Tokens.OPEN_CURLY_BRACKET_TOKEN && |
| 166 begin.kind == Tokens.STRING_INTERPOLATION_TOKEN) return; |
| 167 unmatchedBeginGroup(begin); |
| 168 groupingStack = groupingStack.tail; |
| 169 } |
| 170 } |
| 171 |
| 172 /** |
| 173 * Appends a token for '>'. |
| 174 * This method does not issue unmatched errors, because > is also the |
| 175 * greater-than operator. It does not necessarily have to close a group. |
| 176 */ |
| 177 void appendGt(PrecedenceInfo info) { |
| 178 appendPrecedenceToken(info); |
| 179 if (groupingStack.isEmpty) return; |
| 180 if (identical(groupingStack.head.kind, Tokens.LT_TOKEN)) { |
| 181 groupingStack.head.endGroup = tail; |
| 182 groupingStack = groupingStack.tail; |
| 183 } |
| 184 } |
| 185 |
| 186 /** |
| 187 * Appends a token for '>>'. |
| 188 * This method does not issue unmatched errors, because >> is also the |
| 189 * shift operator. It does not necessarily have to close a group. |
| 190 */ |
| 191 void appendGtGt(PrecedenceInfo info) { |
| 192 appendPrecedenceToken(info); |
| 193 if (groupingStack.isEmpty) return; |
| 194 if (identical(groupingStack.head.kind, Tokens.LT_TOKEN)) { |
| 195 // Don't assign endGroup: in "T<U<V>>", the '>>' token closes the outer |
| 196 // '<', the inner '<' is left without endGroup. |
| 197 groupingStack = groupingStack.tail; |
| 198 } |
| 199 if (groupingStack.isEmpty) return; |
| 200 if (identical(groupingStack.head.kind, Tokens.LT_TOKEN)) { |
| 201 groupingStack.head.endGroup = tail; |
| 202 groupingStack = groupingStack.tail; |
| 203 } |
| 204 } |
| 205 |
| 206 void appendComment(start, bool asciiOnly) { |
| 207 if (!includeComments) return; |
| 208 appendSubstringToken(Precedence.COMMENT_INFO, start, asciiOnly); |
| 209 } |
| 210 |
| 211 void appendErrorToken(ErrorToken token) { |
| 212 tail.next = token; |
| 213 tail = token; |
| 214 } |
| 215 |
| 216 /** |
| 217 * This method is called to discard '<' from the "grouping" stack. |
| 218 * |
| 219 * [PartialParser.skipExpression] relies on the fact that we do not |
| 220 * create groups for stuff like: |
| 221 * [:a = b < c, d = e > f:]. |
| 222 * |
| 223 * In other words, this method is called when the scanner recognizes |
| 224 * something which cannot possibly be part of a type parameter/argument |
| 225 * list, like the '=' in the above example. |
| 226 */ |
| 227 void discardOpenLt() { |
| 228 while (!groupingStack.isEmpty && |
| 229 identical(groupingStack.head.kind, Tokens.LT_TOKEN)) { |
| 230 groupingStack = groupingStack.tail; |
| 231 } |
| 232 } |
| 233 } |
OLD | NEW |