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

Unified Diff: dart/compiler/java/com/google/dart/compiler/parser/DartScanner.java

Issue 20722006: Removed compiler/ directory from repository (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 7 years, 5 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
Index: dart/compiler/java/com/google/dart/compiler/parser/DartScanner.java
diff --git a/dart/compiler/java/com/google/dart/compiler/parser/DartScanner.java b/dart/compiler/java/com/google/dart/compiler/parser/DartScanner.java
deleted file mode 100644
index a019d93eb19113686d42f2d0ba7f7e5c16b6e83e..0000000000000000000000000000000000000000
--- a/dart/compiler/java/com/google/dart/compiler/parser/DartScanner.java
+++ /dev/null
@@ -1,1361 +0,0 @@
-// Copyright (c) 2012, 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.
-
-package com.google.dart.compiler.parser;
-
-import com.google.dart.compiler.DartCompilationError;
-import com.google.dart.compiler.DartCompilerListener;
-import com.google.dart.compiler.Source;
-import com.google.dart.compiler.common.SourceInfo;
-import com.google.dart.compiler.metrics.DartEventType;
-import com.google.dart.compiler.metrics.Tracer;
-import com.google.dart.compiler.metrics.Tracer.TraceEvent;
-import com.google.dart.compiler.parser.DartScanner.InternalState.Mode;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Stack;
-
-/**
- * The Dart scanner. Should normally be used only by {@link DartParser}.
- */
-public class DartScanner {
-
- /**
- * Represents a span of characters in a source file.
- */
- public static class Location {
- public static final Location NONE = null;
- private int begin;
- private int end;
-
- public Location(int begin, int end) {
- this.begin = begin;
- this.end = end;
- }
-
- public Location(int begin) {
- this.begin = this.end = begin;
- }
-
- public int getBegin() {
- return begin;
- }
-
- public int getEnd() {
- return end;
- }
-
- @Override
- public String toString() {
- return begin + "::" + end;
- }
- }
-
- public static class State {
- State(int baseOffset) {
- this.baseOffset = baseOffset;
- }
-
- static class RollbackToken {
- public final int absoluteOffset;
- final Token replacedToken;
-
- public RollbackToken(int tokenOffset, Token token) {
- absoluteOffset = tokenOffset;
- replacedToken = token;
- }
- }
-
- /* Stack of tokens present before setPeek() */
- Stack<RollbackToken> rollbackTokens = null;
- final int baseOffset;
-
- @Override
- public String toString() {
- return "ofs=" + baseOffset;
- }
- }
-
- /**
- * Stores the entire state for the scanner.
- */
- protected static class InternalState {
- enum Mode {
- DEFAULT,
-
- IN_STRING,
-
- /**
- * Inside a string, scanning a string-interpolation expression.
- * Ex: "${foo}".
- */
- IN_STRING_EMBEDDED_EXPRESSION,
-
- /**
- * Inside a string, scanning a string-interpolation identifier.
- * <pre>
- * Ex: "$foo bc".
- * ^
- * </pre>
- */
- IN_STRING_EMBEDDED_EXPRESSION_IDENTIFIER,
-
- /**
- * Inside a string, just after having scanned a string-interpolation identifier.
- * <pre>
- * Ex: "$foo bc".
- * ^
- * </pre>
- */
- IN_STRING_EMBEDDED_EXPRESSION_END
- }
-
- /**
- * Maintains the state of scanning strings, including interpolated
- * expressions/identifiers, nested braces for terminating an interpolated
- * expression, the quote character used to start/end the string, and whether
- * it is a multiline string.
- */
- public static class StringState {
- private int bracesCount;
- private Mode mode;
- private final boolean multiLine;
- private final int quote;
-
- /**
- * Push a new mode on state stack. If the new mode is
- * {@link Mode#IN_STRING_EMBEDDED_EXPRESSION}, mark that we have seen an
- * opening brace.
- *
- * @param mode
- * @param quote
- * @param multiLine
- */
- public StringState(Mode mode, int quote, boolean multiLine) {
- this.bracesCount = mode == Mode.IN_STRING_EMBEDDED_EXPRESSION ? 1 : 0;
- this.mode = mode;
- this.quote = quote;
- this.multiLine = multiLine;
- }
-
- /**
- * Mark that we have seen an opening brace.
- */
- public void openBrace() {
- if (mode == Mode.IN_STRING_EMBEDDED_EXPRESSION) {
- bracesCount++;
- }
- }
-
- /**
- * Mark that we have seen a closing brace.
- *
- * @return true if the current mode is now complete and should be popped
- * off the stack
- */
- public boolean closeBrace() {
- if (mode == Mode.IN_STRING_EMBEDDED_EXPRESSION) {
- return --bracesCount == 0;
- }
- return false;
- }
-
- /**
- * @return the string scanning mode.
- */
- public Mode getMode() {
- return mode;
- }
-
- /**
- * @return the codepoint of the quote character used to bound the current
- * string.
- */
- public int getQuote() {
- return quote;
- }
-
- /**
- * @return true if the current string is a multi-line string.
- */
- public boolean isMultiLine() {
- return multiLine;
- }
-
- /**
- * @param mode the string scanning mode.
- */
- public void setMode(Mode mode) {
- this.mode = mode;
- }
-
- @Override
- public String toString() {
- StringBuilder buf = new StringBuilder();
- buf.append(mode).append("/quote=").appendCodePoint(quote);
- if (multiLine) {
- buf.append("/multiline");
- }
- return buf.toString();
- }
- }
-
- private int lookahead[] = new int[NUM_LOOKAHEAD];
- private int lookaheadPos[] = new int[NUM_LOOKAHEAD];
- private int nextLookaheadPos;
- private ArrayList<TokenData> tokens;
- private TokenData lastToken;
-
- // Current offset in the token list
- int currentOffset;
-
- // The following fields store data used for parsing string interpolation.
- // The scanner splits the interpolated string in segments, alternating
- // strings and expressions so that the parser can construct the embedded
- // expressions as it goes. The following information is used to ensure that
- // the string is closed with matching quotes, and to deal with parsing
- // ambiguity of "}" (which closes both embedded expressions and braces
- // within embedded expressions).
-
- /** The string scanning state stack. */
- private List<StringState> stringStateStack = new ArrayList<StringState>();
-
- public InternalState() {
- currentOffset = 0;
- }
-
- @Override
- public String toString() {
- StringBuilder ret = new StringBuilder();
-
- ret.append("currentOffset(");
- ret.append(currentOffset);
- ret.append(")");
- if ( currentOffset > -1 ) {
- TokenData tok = tokens.get(currentOffset);
- ret.append(" = [");
- ret.append(tok.token);
- if (tok.value != null) {
- ret.append(" (" + tok.value + ")");
- }
- ret.append("], ");
- }
-
- ret.append("[");
- for (int i = 0; i < tokens.size(); i++) {
- TokenData tok = tokens.get(i);
- ret.append(tok.token);
- if (tok.value != null) {
- ret.append(" (" + tok.value + ")");
- }
- if (i < tokens.size() - 1) {
- ret.append(", ");
- }
- }
- ret.append("]");
- if (getMode() != InternalState.Mode.DEFAULT) {
- ret.append("(within string starting with ");
- ret.appendCodePoint(getQuote());
- if (isMultiLine()) {
- ret.appendCodePoint(getQuote());
- ret.appendCodePoint(getQuote());
- }
- ret.append(')');
- }
- return ret.toString();
- }
-
- /**
- * @return the current scanning mode
- */
- protected Mode getMode() {
- return stringStateStack.isEmpty() ? Mode.DEFAULT : getCurrentState().getMode();
- }
-
- /**
- * Mark that we have seen an open brace.
- */
- protected void openBrace() {
- if (!stringStateStack.isEmpty()) {
- getCurrentState().openBrace();
- }
- }
-
- /**
- * Mark that we have seen a close brace.
- *
- * @return true if the current mode is now complete and should be popped
- */
- protected boolean closeBrace() {
- if (!stringStateStack.isEmpty()) {
- return getCurrentState().closeBrace();
- }
- return false;
- }
-
- /**
- * Pop the current mode.
- */
- protected void popMode() {
- if (!stringStateStack.isEmpty()) {
- stringStateStack.remove(stringStateStack.size() - 1);
- }
- }
-
- /**
- * @param mode the mode to push
- */
- protected void pushMode(Mode mode, int quote, boolean multiLine) {
- stringStateStack.add(new StringState(mode, quote, multiLine));
- }
-
- /**
- * @param mode the mode to push
- */
- protected void replaceMode(Mode mode) {
- getCurrentState().setMode(mode);
- }
-
- /**
- * Remove all modes, returning to the default state.
- */
- public void resetModes() {
- stringStateStack.clear();
- }
-
- /**
- * @return the quote
- */
- private int getQuote() {
- return getCurrentState().getQuote();
- }
-
- /**
- * @return the current string scanning state
- */
- private StringState getCurrentState() {
- assert !stringStateStack.isEmpty() : "called with empty state stack";
- return stringStateStack.get(stringStateStack.size() - 1);
- }
-
- /**
- * @return the multiLine
- */
- private boolean isMultiLine() {
- return getCurrentState().isMultiLine();
- }
- }
-
- private static class TokenData {
- Token token;
- Location location;
- String value;
-
- @Override
- public String toString() {
- String str = token.toString();
- return (value != null) ? str + "(" + value + ")" : str;
- }
- }
-
- private static final int NUM_LOOKAHEAD = 2;
-
- private static boolean isDecimalDigit(int c) {
- return c >= '0' && c <= '9';
- }
-
- private static boolean isHexDigit(int c) {
- return isDecimalDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
- }
-
- private static boolean isIdentifierPart(int c) {
- return isIdentifierStart(c) || isDecimalDigit(c);
- }
-
- private static boolean isIdentifierStart(int c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_') || (c == '$');
- }
-
- private static boolean isLineTerminator(int c) {
- return c == '\r' || c == '\n';
- }
-
- private static boolean isWhiteSpace(int c) {
- return c == ' ' || c == '\t';
- }
-
- private int commentCharCount;
- private int lastCommentStart;
- private int lastCommentStop;
- private String source;
- private InternalState internalState;
- private Source sourceReference;
- private DartCompilerListener listener;
-
- public DartScanner(String source) {
- this(source, 0, null, null);
- }
-
- public DartScanner(String source, int start) {
- this(source, 0, null, null);
- }
-
- public DartScanner(String source, int start, Source sourceReference, DartCompilerListener listener) {
- final TraceEvent logEvent = Tracer.canTrace() ? Tracer.start(DartEventType.SCANNER) : null;
- try {
- this.source = source;
- this.sourceReference = sourceReference;
- this.listener = listener;
- internalState = new InternalState();
- internalState.tokens = new ArrayList<TokenData>(source.length()/2);
-
- // Initialize lookahead positions.
- // TODO Determine if line & column should be relative to 0 or 'start'
- internalState.nextLookaheadPos = start;
- for (int i = 0; i < internalState.lookaheadPos.length; ++i) {
- internalState.lookaheadPos[i] = start;
- }
-
- // Fill all the characters in the look-ahead and all the peek
- // elements in the tokens buffer.
- for (int i = 0; i < NUM_LOOKAHEAD; i++) {
- advance();
- }
-
- // Scan all the tokens up front
- scanFile();
- } finally {
- Tracer.end(logEvent);
- }
- }
-
- /**
- * Returns the number of characters of source code that were scanned.
- */
- public int getCharCount() {
- return internalState.nextLookaheadPos;
- }
-
- /**
- * Returns the number of characters of source code that were scanned excluding the number of
- * characters consumed by comments.
- */
- public int getNonCommentCharCount() {
- return getCharCount() - commentCharCount;
- }
-
- /**
- * Get the token value for one of the look-ahead tokens.
- */
- public String getPeekTokenValue(int n) {
- assert (0 <= n && (internalState.currentOffset + n + 1) < internalState.tokens.size());
- return internalState.tokens.get(internalState.currentOffset + n + 1).value;
- }
-
- /**
- * Gets a copy of the current scanner state. This state can be passed to {@link
- * #restoreState(State)}.
- */
- public State getState() {
-// System.out.println("get state: " + internalState.currentOffset + " hash: 0x" + Integer.toHexString(this.hashCode()));
- return new State(internalState.currentOffset);
- }
-
- /**
- * Gets the current offset of the scanner.
- */
- public int getOffset() {
- return internalState.currentOffset;
- }
-
- /**
- * Gets the current token.
- */
- public Token getToken() {
- return internalState.tokens.get(internalState.currentOffset).token;
- }
-
- /**
- * Gets the location of the current token.
- */
- public Location getTokenLocation() {
- return internalState.tokens.get(internalState.currentOffset).location;
- }
-
- public Location peekTokenLocation(int n) {
- if ((internalState.currentOffset + n + 1) < internalState.tokens.size()) {
- return internalState.tokens.get(internalState.currentOffset + n + 1).location;
- } else {
- // It is not valid to read beyond the end of the token stream, so we
- // return the Location of the EOS token.
- return internalState.tokens.get(internalState.tokens.size() - 1).location;
- }
-
- }
-
- /**
- * Get the token value or location for the current token previously returned
- * by a call to next().
- */
- public String getTokenValue() {
- return internalState.tokens.get(internalState.currentOffset).value;
- }
-
- public String peekTokenValue(int n) {
- if ((internalState.currentOffset + n + 1) < internalState.tokens.size()) {
- return internalState.tokens.get(internalState.currentOffset + n + 1).value;
- } else {
- // It is not valid to read beyond the end of the token stream, so we
- // return the null, the default value of an EOS token.
- return null;
- }
- }
-
- /**
- * Returns the next token.
- */
- public Token next() {
- // Do not advance the current offset beyond the end of the stoken stream
- if (internalState.currentOffset + 1 < internalState.tokens.size()) {
- internalState.currentOffset++;
- }
- return getToken();
- }
-
- /**
- * Token look-ahead - past the token returned by next().
- */
- public Token peek(int n) {
- if ((internalState.currentOffset + n + 1) < internalState.tokens.size()) {
- return internalState.tokens.get(internalState.currentOffset + n + 1).token;
- } else {
- // It is not valid to read beyond the end of the token stream, so we
- // return the EOS token
- return Token.EOS;
- }
- }
-
- /**
- * Sets the scanner's state, using a state object returned from {@link #getState()}.
- */
- public void restoreState(State oldState) {
-// System.out.println("restore state " + oldState.baseOffset + " hash: 0x" + Integer.toHexString(this.hashCode()));
- // reset offset
- internalState.currentOffset = oldState.baseOffset;
- }
-
- /**
- * Sets the token at the specified slot in the lookahead buffer.
- */
- public void setPeek(int n, Token token) {
- assert (0 <= n && (internalState.currentOffset + n + 1) < internalState.tokens.size());
- internalState.tokens.get(internalState.currentOffset + n + 1).token = token;
- }
-
- /**
- * Sets the token at the specified slot in the lookahead buffer.
- */
- public void setAbsolutePeek(int n, Token token) {
- assert (0 <= n && n < internalState.tokens.size());
- internalState.tokens.get(n).token = token;
- }
-
- @Override
- public String toString() {
- if (internalState == null) {
- return super.toString();
- }
- return internalState.toString();
- }
-
- /**
- * A hook into low-level scanning machinery. Use with care and only as directed.<p>
- * Record the location of a comment. Given a source string <code>source,</code>
- * the actual comment string is <code>source.substring(start - 1, stop)</code>
- * because the comment cannot be recognized until its second character is
- * scanned.<p>
- * Note: A single comment may be scanned multiple times. If the scanner has
- * to backtrack it will re-scan comments until it no longer has to backtrack.
- * Clients are responsible for filtering duplicate comment locations.<p>
- * Warning: This method may be called during initialization of the scanner in
- * the <code>DartScanner</code> constructor. Fields defined in the subclass
- * that implements this method may not have been initialized before the first
- * invocation.
- * @param start the character position of the second character in the comment
- * @param stop the character position of the final character in the comment
- */
- protected void recordCommentLocation(int start, int stop) {
- }
-
- private void advance() {
- for (int i = 0; i < NUM_LOOKAHEAD - 1; ++i) {
- internalState.lookahead[i] = internalState.lookahead[i + 1];
- internalState.lookaheadPos[i] = internalState.lookaheadPos[i + 1];
- }
- if (internalState.nextLookaheadPos < source.length()) {
- int ch = source.codePointAt(internalState.nextLookaheadPos);
- internalState.lookahead[NUM_LOOKAHEAD - 1] = ch;
- internalState.lookaheadPos[NUM_LOOKAHEAD - 1] = internalState.nextLookaheadPos;
- internalState.nextLookaheadPos = source.offsetByCodePoints(internalState.nextLookaheadPos, 1);
- } else {
- // Let the last look-ahead position be past the source. This makes
- // the position information for the last token correct.
- internalState.lookahead[NUM_LOOKAHEAD - 1] = -1;
- internalState.lookaheadPos[NUM_LOOKAHEAD - 1] = source.length();
-
- // Leave the nextLookahead position pointing to the line after the last line
- internalState.nextLookaheadPos = source.length();
- }
- }
-
- /**
- * Called when comments are identified to aggregate the total number of comment lines and comment
- * characters then delegate to {@link #recordCommentLocation(int, int)}. This provides
- * a light weight way to track how much of the code is made up of comments without having to keep
- * all comments.
- *
- * @param start the character position of the second character in the comment
- * @param stop the character position of the final character in the comment
- */
- private void commentLocation(int start, int stop) {
- if (start <= lastCommentStart && stop <= lastCommentStop) {
- return;
- }
-
- lastCommentStart = start;
- lastCommentStop = stop;
- commentCharCount += stop - start + 1;
-
- recordCommentLocation(start, stop);
- }
-
- private boolean is(int c) {
- return internalState.lookahead[0] == c;
- }
-
- private boolean isEos() {
- return internalState.lookahead[0] < 0;
- }
-
- private int lookahead(int n) {
- assert (0 <= n && n < NUM_LOOKAHEAD);
- return internalState.lookahead[n];
- }
-
- // Get the current source code position.
- private int position() {
- return internalState.lookaheadPos[0];
- }
-
- private void scanFile() {
- // First node inserted as a dummy.
- internalState.lastToken = new TokenData();
- internalState.tokens.add(internalState.lastToken);
-
- while (true) {
- internalState.lastToken = new TokenData();
- Token token;
- int begin, end;
- do {
- skipWhiteSpace();
- begin = position();
- token = scanToken();
- } while (token == Token.COMMENT);
- end = position();
-
- internalState.lastToken.token = token;
- internalState.lastToken.location = new Location(begin, end);
- internalState.tokens.add(internalState.lastToken);
- if (token == Token.EOS) {
-// System.out.print("tokens: ");
-// for(TokenData t : internalState.tokens) {
-// if (t != null) {
-// if (t.token != null) {
-// System.out.print(t + ", ");
-// } else {
-// System.out.print("Null, ");
-// }
-// }
-// }
-// System.out.println();
- return;
- }
- }
- }
-
- private Token scanIdentifier(boolean allowDollars) {
- assert (isIdentifierStart(lookahead(0)));
- int begin = position();
- while (true) {
- int nextChar = lookahead(0);
- if (!isIdentifierPart(nextChar) || (!allowDollars && nextChar == '$')) {
- break;
- }
- advance();
- }
- int size = position() - begin;
-
- // Use a substring of the source string instead of copying all the
- // characters to the token value buffer.
- String result = source.substring(begin, begin + size);
- internalState.lastToken.value = result;
- return Token.lookup(result);
- }
-
- private Token scanNumber() {
- boolean isDouble = false;
- assert (isDecimalDigit(lookahead(0)) || is('.'));
- int begin = position();
- while (isDecimalDigit(lookahead(0)))
- advance();
- if (is('.') && isDecimalDigit(lookahead(1))) {
- isDouble = true;
- advance(); // Consume .
- while (isDecimalDigit(lookahead(0)))
- advance();
- }
- if (isE()) {
- isDouble = true;
- advance();
- if (is('+') || is('-')) {
- advance();
- }
- if (!isDecimalDigit(lookahead(0))) {
- return Token.ILLEGAL;
- }
- while (isDecimalDigit(lookahead(0)))
- advance();
- } else if (isIdentifierStart(lookahead(0))) {
- // Number literals must not be followed directly by an identifier.
- return Token.ILLEGAL;
- }
- int size = position() - begin;
- internalState.lastToken.value = source.substring(begin, begin + size);
- return isDouble ? Token.DOUBLE_LITERAL : Token.INTEGER_LITERAL;
- }
-
- private boolean isE() {
- return is('e') || is('E');
- }
-
- private Token scanHexNumber() {
- assert (isDecimalDigit(lookahead(0)) && (lookahead(1) == 'x' || lookahead(1) == 'X'));
- // Skip 0x/0X.
- advance();
- advance();
-
- int begin = position();
- if (!isHexDigit(lookahead(0))) {
- return Token.ILLEGAL;
- }
- advance();
- while (isHexDigit(lookahead(0))) {
- advance();
- }
- if (isIdentifierStart(lookahead(0))) {
- return Token.ILLEGAL;
- }
- internalState.lastToken.value = source.substring(begin, position());
- return Token.HEX_LITERAL;
- }
-
- private Token scanString(boolean isRaw) {
- int quote = lookahead(0);
- assert (is('\'') || is('"'));
- boolean multiLine = false;
- advance();
-
- // detect whether this is a multi-line string:
- if (lookahead(0) == quote && lookahead(1) == quote) {
- multiLine = true;
- advance();
- advance();
- // according to the dart guide, when multi-line strings start immediately
- // with a \n, the \n is not part of the string:
- if (is('\n')) {
- advance();
- }
- }
- internalState.pushMode(InternalState.Mode.IN_STRING, quote, multiLine);
- if (isRaw) {
- return scanRawString();
- } else {
- return scanWithinString(true);
- }
- }
-
- private Token scanRawString() {
- assert (internalState.getMode() == InternalState.Mode.IN_STRING);
- int quote = internalState.getQuote();
- boolean multiLine = internalState.isMultiLine();
- // TODO(floitsch): Do we really need a StringBuffer to accumulate the characters?
- StringBuilder tokenValueBuffer = new StringBuilder();
- while (true) {
- if (isEos()) {
- // Unterminated string (either multi-line or not).
- internalState.popMode();
- return Token.ILLEGAL;
- }
- int c = lookahead(0);
- advance();
- if (c == quote) {
- if (!multiLine) {
- // Done parsing the string literal.
- break;
- } else if (lookahead(0) == quote && lookahead(1) == quote) {
- // Done parsing the multi-line string literal.
- advance();
- advance();
- break;
- }
- } else if (c == '\n' && !multiLine) {
- advance();
- internalState.popMode();
- // unterminated (non multi-line) string
- return Token.ILLEGAL;
- }
- tokenValueBuffer.appendCodePoint(c);
- }
- internalState.lastToken.value = tokenValueBuffer.toString();
- internalState.popMode();
- return Token.STRING;
- }
-
- /**
- * Scan within a string watching for embedded expressions (string
- * interpolation). This function returns 4 kinds of tokens:
- * <ul>
- * <li> {@link Token#STRING} when {@code start} is true and no embedded
- * expressions are found (default to string literals when no interpolation
- * was used).
- * <li> {@link Token#STRING_SEGMENT} when the string is interrupted with an
- * embedded expression.
- * <li> {@link Token#STRING_EMBED_EXP_START} when an embedded expression is
- * found right away (the lookahead is "${").
- * <li> {@link Token#STRING_LAST_SEGMENT} when {@code start} is false and no
- * more embedded expressions are found.
- * </ul>
- */
- private Token scanWithinString(boolean start) {
- assert (internalState.getMode() == InternalState.Mode.IN_STRING);
- int quote = internalState.getQuote();
- boolean multiLine = internalState.isMultiLine();
- StringBuffer tokenValueBuffer = new StringBuffer();
- while (true) {
- if (isEos()) {
- // Unterminated string (either multi-line or not).
- internalState.resetModes();
- return Token.EOS;
- }
- int c = lookahead(0);
- if (c == quote) {
- advance();
- if (!multiLine) {
- // Done parsing string constant.
- break;
- } else if (lookahead(0) == quote && lookahead(1) == quote) {
- // Done parsing multi-line string constant.
- advance();
- advance();
- break;
- }
- } else if (c == '\n' && !multiLine) {
- advance();
- internalState.popMode();
- // unterminated (non multi-line) string
- return Token.ILLEGAL;
- } else if (c == '\\') {
- advance();
- if (isEos()) {
- // Unterminated string (either multi-line or not).
- internalState.resetModes();
- return Token.EOS;
- }
- c = lookahead(0);
- advance();
- switch (c) {
- case '\n':
- reportError(position() - 1, ParserErrorCode.ESCAPED_NEWLINE);
- c = '\n';
- break;
- case 'b':
- c = 0x08;
- break;
- case 'f':
- c = 0x0C;
- break;
- case 'n':
- c = '\n';
- break;
- case 'r':
- c = '\r';
- break;
- case 't':
- c = '\t';
- break;
- case 'v':
- c = 0x0B;
- break;
- case 'x':
- case 'u':
- // Parse Unicode escape sequences, which are of the form (backslash) xXX, (backslash)
- // uXXXX or (backslash) u{X*} where X is a hexadecimal digit - the delimited form must
- // be between 1 and 6 digits.
- int len = (c == 'u') ? 4 : 2;
- if (isEos()) {
- // Unterminated string (either multi-line or not).
- internalState.resetModes();
- return Token.EOS;
- }
- c = lookahead(0);
- int unicodeCodePoint = 0;
- // count of characters remaining or negative if delimited
- if (c == '{') {
- len = -1;
- advance();
- if (isEos()) {
- // Unterminated string (either multi-line or not).
- internalState.resetModes();
- return Token.EOS;
- }
- c = lookahead(0);
- }
- while (len != 0) {
- advance();
- int digit = Character.getNumericValue(c);
- if (digit < 0 || digit > 15) {
- // TODO(jat): how to handle an error? We would prefer to give a better error
- // message about an invalid Unicode escape sequence
- return Token.ILLEGAL;
- }
- unicodeCodePoint = unicodeCodePoint * 16 + digit;
- c = lookahead(0);
- if (len-- < 0 && c == '}') {
- advance();
- break;
- }
- if (isEos()) {
- // Unterminated string (either multi-line or not).
- internalState.resetModes();
- return Token.EOS;
- }
- if (len < -6) {
- // TODO(jat): better way to indicate error
- // too many characters for a delimited character
- return Token.ILLEGAL;
- }
- }
- c = unicodeCodePoint;
- // Unicode escapes must specify a valid Unicode scalar value, and may not specify
- // UTF16 surrogates.
- if (!Character.isValidCodePoint(c) || (c < 0x10000
- && (Character.isHighSurrogate((char) c) || Character.isLowSurrogate((char) c)))) {
- // TODO(jat): better way to indicate error
- return Token.ILLEGAL;
- }
- // TODO(jat): any other checks? We could use Character.isDefined, but then we risk
- // version skew with the JRE's Unicode data. For now, assume anything in the Unicode
- // range besides surrogates are fine.
- break;
-
- default:
- // any other character following a backslash is just itself
- // see Dart guide 3.3
- break;
- }
- } else if (c == '$') {
- // TODO(sigmund): add support for named embedded expressions and
- // function embedded expressions for string templates.
- if (tokenValueBuffer.length() == 0) {
- advance();
- int nextChar = lookahead(0);
- if (nextChar == '{') {
- advance();
- internalState.pushMode(InternalState.Mode.IN_STRING_EMBEDDED_EXPRESSION, quote,
- multiLine);
- } else {
- internalState.pushMode(InternalState.Mode.IN_STRING_EMBEDDED_EXPRESSION_IDENTIFIER,
- quote, multiLine);
- }
- return Token.STRING_EMBED_EXP_START;
- } else {
- // Encountered the beginning of an embedded expression (string
- // interpolation), return the current segment, and keep the "$" for
- // the next token.
- internalState.lastToken.value = tokenValueBuffer.toString();
- return Token.STRING_SEGMENT;
- }
- } else {
- advance();
- }
- tokenValueBuffer.appendCodePoint(c);
- }
-
- internalState.lastToken.value = tokenValueBuffer.toString();
- internalState.popMode();
- if (start) {
- return Token.STRING;
- } else {
- return Token.STRING_LAST_SEGMENT;
- }
- }
-
- private Token scanToken() {
- switch (internalState.getMode()) {
- case IN_STRING:
- return scanWithinString(false);
- case IN_STRING_EMBEDDED_EXPRESSION_IDENTIFIER:
- // We are inside a string looking for an identifier. Ex: "$foo".
- internalState.replaceMode(InternalState.Mode.IN_STRING_EMBEDDED_EXPRESSION_END);
- int c = lookahead(0);
- if (isIdentifierStart(c) && c != '$') {
- boolean allowDollars = false;
- return scanIdentifier(allowDollars);
- } else {
- internalState.popMode();
- if (!isEos()) {
- internalState.lastToken.value = String.valueOf(c);
- }
- return Token.ILLEGAL;
- }
- case IN_STRING_EMBEDDED_EXPRESSION_END:
- // We scanned the identifier of a string-interpolation. New we return the
- // end-of-embedded-expression token.
- internalState.popMode();
- return Token.STRING_EMBED_EXP_END;
- default:
- // fall through
- }
-
- switch (lookahead(0)) {
- case '"':
- case '\'': {
- boolean isRaw = false;
- return scanString(isRaw);
- }
-
- case '<':
- // < <= << <<=
- advance();
- if (is('='))
- return select(Token.LTE);
- if (is('<'))
- return select('=', Token.ASSIGN_SHL, Token.SHL);
- return Token.LT;
-
- case '>':
- // > >= >> >>=
- advance();
- if (is('='))
- return select(Token.GTE);
- if (is('>')) {
- // >> >>=
- advance();
- if (is('='))
- return select(Token.ASSIGN_SAR);
- return Token.SAR;
- }
- return Token.GT;
-
- case '=':
- // = == === =>
- advance();
- if (is('>')) {
- return select(Token.ARROW);
- }
- if (is('='))
- return select('=', Token.EQ_STRICT, Token.EQ);
- return Token.ASSIGN;
-
- case '!':
- // ! != !==
- advance();
- if (is('='))
- return select('=', Token.NE_STRICT, Token.NE);
- return Token.NOT;
-
- case '+':
- // + ++ +=
- advance();
- if (is('+'))
- return select(Token.INC);
- if (is('='))
- return select(Token.ASSIGN_ADD);
- return Token.ADD;
-
- case '-':
- // - -- -=
- advance();
- if (is('-'))
- return select(Token.DEC);
- if (is('='))
- return select(Token.ASSIGN_SUB);
- return Token.SUB;
-
- case '*':
- // * *=
- return select('=', Token.ASSIGN_MUL, Token.MUL);
-
- case '%':
- // % %=
- return select('=', Token.ASSIGN_MOD, Token.MOD);
-
- case '/':
- // / // /* /=
- advance();
- if (is('/'))
- return skipSingleLineComment();
- if (is('*'))
- return skipMultiLineComment();
- if (is('='))
- return select(Token.ASSIGN_DIV);
- return Token.DIV;
-
- case '&':
- // & && &=
- advance();
- if (is('&'))
- return select(Token.AND);
- if (is('='))
- return select(Token.ASSIGN_BIT_AND);
- return Token.BIT_AND;
-
- case '|':
- // | || |=
- advance();
- if (is('|'))
- return select(Token.OR);
- if (is('='))
- return select(Token.ASSIGN_BIT_OR);
- return Token.BIT_OR;
-
- case '^':
- // ^ ^=
- return select('=', Token.ASSIGN_BIT_XOR, Token.BIT_XOR);
-
- case '.':
- // . <number>
- if (isDecimalDigit(lookahead(1))) {
- return scanNumber();
- } else {
- advance();
- if (lookahead(0) == '.') {
- if (lookahead(1) == '.') {
- advance();
- advance();
- return Token.ELLIPSIS;
- }
- advance();
- return Token.CASCADE;
- }
- return Token.PERIOD;
- }
-
- case ':':
- return select(Token.COLON);
-
- case ';':
- return select(Token.SEMICOLON);
-
- case ',':
- return select(Token.COMMA);
-
- case '(':
- return select(Token.LPAREN);
-
- case ')':
- return select(Token.RPAREN);
-
- case '[':
- advance();
- if (is(']')) {
- return select('=', Token.ASSIGN_INDEX, Token.INDEX);
- }
- return Token.LBRACK;
-
- case ']':
- return select(Token.RBRACK);
-
- case '{':
- internalState.openBrace();
- return select(Token.LBRACE);
-
- case '}':
- if (internalState.closeBrace()) {
- internalState.popMode();
- return select(Token.STRING_EMBED_EXP_END);
- }
- return select(Token.RBRACE);
-
- case '?':
- return select(Token.CONDITIONAL);
-
- case '~':
- // ~ ~/ ~/=
- advance();
- if (is('/')) {
- if (lookahead(1) == '=') {
- advance();
- return select(Token.ASSIGN_TRUNC);
- } else {
- return select(Token.TRUNC);
- }
- } else {
- return Token.BIT_NOT;
- }
-
- case '@':
- // Raw strings.
- advance();
- if (is('\'') || is('"')) {
- reportError(position() - 1, ParserErrorCode.DEPRECATED_RAW_STRING);
- Token token = scanString(true);
- return token;
- } else {
- return Token.AT;
- }
-
- case '#':
- return scanDirective();
-
- case 'r':
- if (lookahead(1) == '\'' || lookahead(1) == '"') {
- advance();
- return scanString(true);
- }
- return scanIdentifier(true);
-
- default:
- if (isIdentifierStart(lookahead(0))) {
- boolean allowDollars = true;
- return scanIdentifier(allowDollars);
- }
- if (isDecimalDigit(lookahead(0))) {
- if (lookahead(0) == '0' && (lookahead(1) == 'x' || lookahead(1) == 'X')) {
- return scanHexNumber();
- } else {
- return scanNumber();
- }
- }
- if (isEos())
- return Token.EOS;
- return select(Token.ILLEGAL);
- }
- }
-
- private void reportError(int offset, ParserErrorCode errorCode) {
- if (listener != null) {
- listener.onError(new DartCompilationError(
- new SourceInfo(sourceReference, offset, position() - offset),
- errorCode));
- }
- }
-
- /**
- * Scan for #library, #import, #source, and #resource directives
- */
- private Token scanDirective() {
- assert (is('#'));
- int currPos = position();
- int start = currPos;
-
- // Skip over the #! if it exists and consider it a comment
- if (start == 0) {
- if (lookahead(1) == '!') {
- while (!isEos() && !isLineTerminator(lookahead(0)))
- advance();
- int stop = internalState.lookaheadPos[0];
- commentLocation(start, stop);
- return Token.COMMENT;
- }
- }
-
- // Directives must start at the beginning of a line
- if (start > 0 && !isLineTerminator(source.codePointBefore(start)))
- return select(Token.ILLEGAL);
-
- // Determine which directive is being specified
- advance();
- while (true) {
- int ch = lookahead(0);
- if (ch < 'a' || ch > 'z') {
- break;
- }
- advance();
- }
- String syntax = source.substring(start, position());
- Token token = Token.lookup(syntax);
- return token == Token.IDENTIFIER ? Token.ILLEGAL : token;
- }
-
- private Token select(int next, Token yes, Token no) {
- advance();
- if (lookahead(0) != next)
- return no;
- advance();
- return yes;
- }
-
- private Token select(Token token) {
- advance();
- return token;
- }
-
- private Token skipMultiLineComment() {
- assert (is('*'));
- int currPos = internalState.lookaheadPos[0];
- int start = currPos - 1;
- int commentDepth = 1;
- advance();
- while (!isEos()) {
- int first = lookahead(0);
- advance();
- if (first == '*' && is('/')) {
- if(--commentDepth == 0) {
- Token result = select(Token.COMMENT);
- int stop = internalState.lookaheadPos[0];
- commentLocation(start, stop);
- return result;
- }
- advance();
- } else if (first == '/' && is('*')) {
- commentDepth++;
- advance();
- }
- }
- int stop = internalState.lookaheadPos[0];
- commentLocation(start, stop);
- // Unterminated multi-line comment.
- return Token.ILLEGAL;
- }
-
- private Token skipSingleLineComment() {
- assert (is('/'));
- int currPos = internalState.lookaheadPos[0];
- int start = currPos - 1;
- advance();
- while (!isEos() && !isLineTerminator(lookahead(0)))
- advance();
- int stop = internalState.lookaheadPos[0];
- commentLocation(start, stop);
- return Token.COMMENT;
- }
-
- private void skipWhiteSpace() {
- Mode mode = internalState.getMode();
- if ((mode != InternalState.Mode.DEFAULT)
- && (mode != InternalState.Mode.IN_STRING_EMBEDDED_EXPRESSION)) {
- return;
- }
- while (true) {
- int c = lookahead(0);
- if (isLineTerminator(c)) {
- } else if (!isWhiteSpace(c)) {
- break;
- }
- advance();
- }
- }
-}

Powered by Google App Engine
This is Rietveld 408576698