return NULL; // not reached
}
-const char* Lexer::TokenErrorHint(Token t) {
- switch (t) {
- case ERROR: return "";
- case BUILD: return "";
- case COLON: return " ($ also escapes ':')";
- case DEFAULT: return "";
- case EQUALS: return "";
- case IDENT: return "";
- case INCLUDE: return "";
- case INDENT: return "";
- case NEWLINE: return "";
- case PIPE2: return "";
- case PIPE: return "";
- case RULE: return "";
- case SUBNINJA: return "";
- case TEOF: return "";
+const char* Lexer::TokenErrorHint(Token expected) {
+ switch (expected) {
+ case COLON:
+ return " ($ also escapes ':')";
+ default:
+ return "";
+ }
+}
+
+string Lexer::DescribeLastError() {
+ if (last_token_) {
+ switch (last_token_[0]) {
+ case '\r':
+ return "carriage returns are not allowed, use newlines";
+ case '\t':
+ return "tabs are not allowed, use spaces";
+ }
}
- return "";
+ return "lexing error";
}
void Lexer::UnreadToken() {
yy95:
{
last_token_ = start;
- return Error("lexing error", err);
+ return Error(DescribeLastError(), err);
}
yy96:
++p;
/// Return a human-readable form of a token, used in error messages.
static const char* TokenName(Token t);
-
/// Return a human-readable token hint, used in error messages.
- static const char* TokenErrorHint(Token t);
+ static const char* TokenErrorHint(Token expected);
+
+ /// If the last token read was an ERROR token, provide more info
+ /// or the empty string.
+ string DescribeLastError();
/// Start parsing some input.
void Start(StringPiece filename, StringPiece input);
return NULL; // not reached
}
-const char* Lexer::TokenErrorHint(Token t) {
- switch (t) {
- case ERROR: return "";
- case BUILD: return "";
- case COLON: return " ($ also escapes ':')";
- case DEFAULT: return "";
- case EQUALS: return "";
- case IDENT: return "";
- case INCLUDE: return "";
- case INDENT: return "";
- case NEWLINE: return "";
- case PIPE2: return "";
- case PIPE: return "";
- case RULE: return "";
- case SUBNINJA: return "";
- case TEOF: return "";
+const char* Lexer::TokenErrorHint(Token expected) {
+ switch (expected) {
+ case COLON:
+ return " ($ also escapes ':')";
+ default:
+ return "";
+ }
+}
+
+string Lexer::DescribeLastError() {
+ if (last_token_) {
+ switch (last_token_[0]) {
+ case '\r':
+ return "carriage returns are not allowed, use newlines";
+ case '\t':
+ return "tabs are not allowed, use spaces";
+ }
}
- return "";
+ return "lexing error";
}
void Lexer::UnreadToken() {
}
[^] {
last_token_ = start;
- return Error("lexing error", err);
+ return Error(DescribeLastError(), err);
}
*/
}
Lexer::Token token = lexer.ReadToken();
EXPECT_EQ(Lexer::ERROR, token);
}
+
+TEST(Lexer, Tabs) {
+ // Verify we print a useful error on a disallowed character.
+ Lexer lexer(" \tfoobar");
+ Lexer::Token token = lexer.ReadToken();
+ EXPECT_EQ(Lexer::INDENT, token);
+ token = lexer.ReadToken();
+ EXPECT_EQ(Lexer::ERROR, token);
+ EXPECT_EQ("tabs are not allowed, use spaces", lexer.DescribeLastError());
+}
if (!ParseFileInclude(true, err))
return false;
break;
- case Lexer::ERROR:
- return lexer_.Error("lexing error", err);
+ case Lexer::ERROR: {
+ return lexer_.Error(lexer_.DescribeLastError(), err);
+ }
case Lexer::TEOF:
return true;
case Lexer::NEWLINE:
EXPECT_FALSE(parser.ParseTest("foo = foo\nbar = bar\r\n",
&err));
- EXPECT_EQ("input:2: lexing error\n"
+ EXPECT_EQ("input:2: carriage returns are not allowed, use newlines\n"
"bar = bar\r\n"
" ^ near here",
err);