blob: 35b46528b732bb5f4936e2f82fb08100a022368a [file] [log] [blame]
// Copyright (c) 2017, 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.
library dart_scanner.error_token;
import '../messages/codes.dart'
show
Code,
Message,
messageEncoding,
templateAsciiControlCharacter,
templateNonAsciiIdentifier,
templateNonAsciiWhitespace,
templateUnmatchedToken,
templateUnsupportedOperator,
templateUnterminatedString;
import 'recover.dart' show closeBraceFor, closeQuoteFor;
import 'scanner.dart' show Token, unicodeReplacementCharacter;
import 'token.dart' show BeginToken, SimpleToken, TokenType;
ErrorToken buildUnexpectedCharacterToken(int character, int charOffset) {
if (character < 0x1f) {
return new AsciiControlCharacterToken(character, charOffset);
}
switch (character) {
case unicodeReplacementCharacter:
return new EncodingErrorToken(charOffset);
/// See [General Punctuation]
/// (http://www.unicode.org/charts/PDF/U2000.pdf).
case 0x00A0: // No-break space.
case 0x1680: // Ogham space mark.
case 0x180E: // Mongolian vowel separator.
case 0x2000: // En quad.
case 0x2001: // Em quad.
case 0x2002: // En space.
case 0x2003: // Em space.
case 0x2004: // Three-per-em space.
case 0x2005: // Four-per-em space.
case 0x2006: // Six-per-em space.
case 0x2007: // Figure space.
case 0x2008: // Punctuation space.
case 0x2009: // Thin space.
case 0x200A: // Hair space.
case 0x200B: // Zero width space.
case 0x2028: // Line separator.
case 0x2029: // Paragraph separator.
case 0x202F: // Narrow no-break space.
case 0x205F: // Medium mathematical space.
case 0x3000: // Ideographic space.
case 0xFEFF: // Zero width no-break space.
return new NonAsciiWhitespaceToken(character, charOffset);
default:
return new NonAsciiIdentifierToken(character, charOffset);
}
}
/// Common superclass for all error tokens.
///
/// It's considered an implementation error to access [lexeme] of an
/// [ErrorToken].
abstract class ErrorToken extends SimpleToken {
ErrorToken(int offset) : super(TokenType.BAD_INPUT, offset, null);
/// This is a token that wraps around an error message. Return 1
/// instead of the size of the length of the error message.
@override
int get length => 1;
String get lexeme {
String errorMsg = assertionMessage.problemMessage;
// Attempt to include the location which is calling the parser
// in an effort to debug https://github.com/dart-lang/sdk/issues/37528
RegExp pattern = new RegExp('^#[0-9]* *Parser');
List<String> traceLines = StackTrace.current.toString().split('\n');
for (int index = traceLines.length - 2; index >= 0; --index) {
String line = traceLines[index];
if (line.startsWith(pattern)) {
errorMsg = '$errorMsg - ${traceLines[index + 1]}';
break;
}
}
throw errorMsg;
}
Message get assertionMessage;
Code<dynamic> get errorCode => assertionMessage.code;
int? get character => null;
String? get start => null;
int? get endOffset => null;
BeginToken? get begin => null;
@override
Token copy() {
throw 'unsupported operation';
}
}
/// Represents an encoding error.
class EncodingErrorToken extends ErrorToken {
EncodingErrorToken(int charOffset) : super(charOffset);
String toString() => "EncodingErrorToken()";
Message get assertionMessage => messageEncoding;
}
/// Represents a non-ASCII character outside a string or comment.
class NonAsciiIdentifierToken extends ErrorToken {
final int character;
NonAsciiIdentifierToken(this.character, int charOffset) : super(charOffset);
String toString() => "NonAsciiIdentifierToken($character)";
Message get assertionMessage => templateNonAsciiIdentifier.withArguments(
new String.fromCharCodes([character]), character);
}
/// Represents a non-ASCII whitespace outside a string or comment.
class NonAsciiWhitespaceToken extends ErrorToken {
final int character;
NonAsciiWhitespaceToken(this.character, int charOffset) : super(charOffset);
String toString() => "NonAsciiWhitespaceToken($character)";
Message get assertionMessage =>
templateNonAsciiWhitespace.withArguments(character);
}
/// Represents an ASCII control character outside a string or comment.
class AsciiControlCharacterToken extends ErrorToken {
final int character;
AsciiControlCharacterToken(this.character, int charOffset)
: super(charOffset);
String toString() => "AsciiControlCharacterToken($character)";
Message get assertionMessage =>
templateAsciiControlCharacter.withArguments(character);
}
/// Denotes an operator that is not supported in the Dart language.
class UnsupportedOperator extends ErrorToken {
Token token;
UnsupportedOperator(this.token, int charOffset) : super(charOffset);
@override
Message get assertionMessage =>
templateUnsupportedOperator.withArguments(token);
@override
String toString() => "UnsupportedOperator(${token.lexeme})";
}
/// Represents an unterminated string.
class UnterminatedString extends ErrorToken {
final String start;
final int endOffset;
UnterminatedString(this.start, int charOffset, this.endOffset)
: super(charOffset);
String toString() => "UnterminatedString($start)";
int get charCount => endOffset - charOffset;
int get length => charCount;
Message get assertionMessage =>
templateUnterminatedString.withArguments(start, closeQuoteFor(start));
}
/// Represents an unterminated token.
class UnterminatedToken extends ErrorToken {
final Message assertionMessage;
final int endOffset;
UnterminatedToken(this.assertionMessage, int charOffset, this.endOffset)
: super(charOffset);
String toString() => "UnterminatedToken(${assertionMessage.code.name})";
int get charCount => endOffset - charOffset;
}
/// Represents an open brace without a matching close brace.
///
/// In this case, brace means any of `(`, `{`, `[`, and `<`, parenthesis, curly
/// brace, square brace, and angle brace, respectively.
class UnmatchedToken extends ErrorToken {
final BeginToken begin;
UnmatchedToken(BeginToken begin)
: this.begin = begin,
super(begin.charOffset);
String toString() => "UnmatchedToken(${begin.lexeme})";
Message get assertionMessage =>
templateUnmatchedToken.withArguments(closeBraceFor(begin.lexeme), begin);
}