summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvan Martin <martine@danga.com>2012-01-29 16:46:52 (GMT)
committerEvan Martin <martine@danga.com>2012-01-29 16:46:52 (GMT)
commit33206906ef36d7bebfefb9a8a798217ee6851df4 (patch)
tree970d5c7fb9207ec96beff9d9fdfcf60c60a06e0d
parentc8faeaa5a1c5e2f5e75d0f1be19c70479475c5ce (diff)
parent2e481086ad7337bb868327f12ffd317dc5143a3e (diff)
downloadNinja-33206906ef36d7bebfefb9a8a798217ee6851df4.zip
Ninja-33206906ef36d7bebfefb9a8a798217ee6851df4.tar.gz
Ninja-33206906ef36d7bebfefb9a8a798217ee6851df4.tar.bz2
Merge pull request #210 from syntheticpp/colon-escape
add colon escaping
-rw-r--r--src/lexer.cc72
-rw-r--r--src/lexer.h4
-rw-r--r--src/lexer.in.cc24
-rw-r--r--src/lexer_test.cc4
-rw-r--r--src/parsers.cc1
-rw-r--r--src/parsers_test.cc37
6 files changed, 115 insertions, 27 deletions
diff --git a/src/lexer.cc b/src/lexer.cc
index 5bc467b..9e4392c 100644
--- a/src/lexer.cc
+++ b/src/lexer.cc
@@ -90,6 +90,26 @@ const char* Lexer::TokenName(Token t) {
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 "";
+ }
+ return "";
+}
+
void Lexer::UnreadToken() {
ofs_ = last_token_;
}
@@ -606,7 +626,7 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) {
}
++p;
yych = *p;
- goto yy118;
+ goto yy120;
yy91:
{
eval->AddText(StringPiece(start, p - start));
@@ -629,7 +649,7 @@ yy94:
++p;
if ((yych = *p) <= '/') {
if (yych <= ' ') {
- if (yych == '\n') goto yy107;
+ if (yych == '\n') goto yy109;
if (yych <= 0x1F) goto yy98;
goto yy100;
} else {
@@ -643,17 +663,21 @@ yy94:
}
} else {
if (yych <= '^') {
- if (yych <= '9') goto yy104;
- if (yych <= '@') goto yy98;
- if (yych <= 'Z') goto yy104;
- goto yy98;
+ if (yych <= ':') {
+ if (yych <= '9') goto yy104;
+ goto yy106;
+ } else {
+ if (yych <= '@') goto yy98;
+ if (yych <= 'Z') goto yy104;
+ goto yy98;
+ }
} else {
if (yych <= '`') {
if (yych <= '_') goto yy104;
goto yy98;
} else {
if (yych <= 'z') goto yy104;
- if (yych <= '{') goto yy106;
+ if (yych <= '{') goto yy108;
goto yy98;
}
}
@@ -690,56 +714,62 @@ yy102:
yy104:
++p;
yych = *p;
- goto yy116;
+ goto yy118;
yy105:
{
eval->AddSpecial(StringPiece(start + 1, p - start - 1));
continue;
}
yy106:
+ ++p;
+ {
+ eval->AddText(StringPiece(":", 1));
+ continue;
+ }
+yy108:
yych = *(q = ++p);
if (yybm[0+yych] & 32) {
- goto yy110;
+ goto yy112;
}
goto yy99;
-yy107:
+yy109:
++p;
yych = *p;
if (yybm[0+yych] & 16) {
- goto yy107;
+ goto yy109;
}
{
continue;
}
-yy110:
+yy112:
++p;
yych = *p;
if (yybm[0+yych] & 32) {
- goto yy110;
+ goto yy112;
}
- if (yych == '}') goto yy113;
+ if (yych == '}') goto yy115;
p = q;
goto yy99;
-yy113:
+yy115:
++p;
{
eval->AddSpecial(StringPiece(start + 2, p - start - 3));
continue;
}
-yy115:
+yy117:
++p;
yych = *p;
-yy116:
+yy118:
if (yybm[0+yych] & 64) {
- goto yy115;
+ goto yy117;
}
goto yy105;
-yy117:
+yy119:
++p;
yych = *p;
-yy118:
+yy120:
if (yybm[0+yych] & 128) {
- goto yy117;
+ goto yy119;
}
goto yy91;
}
diff --git a/src/lexer.h b/src/lexer.h
index c872b98..75c1b2f 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -46,6 +46,10 @@ struct Lexer {
/// 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);
+
/// Start parsing some input.
void Start(StringPiece filename, StringPiece input);
diff --git a/src/lexer.in.cc b/src/lexer.in.cc
index 6c00dbd..28a5bdf 100644
--- a/src/lexer.in.cc
+++ b/src/lexer.in.cc
@@ -89,6 +89,26 @@ const char* Lexer::TokenName(Token t) {
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 "";
+ }
+ return "";
+}
+
void Lexer::UnreadToken() {
ofs_ = last_token_;
}
@@ -214,6 +234,10 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) {
eval->AddSpecial(StringPiece(start + 1, p - start - 1));
continue;
}
+ "$:" {
+ eval->AddText(StringPiece(":", 1));
+ continue;
+ }
"$". {
last_token_ = start;
return Error("bad $-escape (literal $ must be written as $$)", err);
diff --git a/src/lexer_test.cc b/src/lexer_test.cc
index 1c5894e..5795e5e 100644
--- a/src/lexer_test.cc
+++ b/src/lexer_test.cc
@@ -29,12 +29,12 @@ TEST(Lexer, ReadVarValue) {
}
TEST(Lexer, ReadEvalStringEscapes) {
- Lexer lexer("$ $$ab $\ncde\n");
+ Lexer lexer("$ $$ab c$: $\ncde\n");
EvalString eval;
string err;
EXPECT_TRUE(lexer.ReadVarValue(&eval, &err));
EXPECT_EQ("", err);
- EXPECT_EQ("[ $ab cde]",
+ EXPECT_EQ("[ $ab c: cde]",
eval.Serialize());
}
diff --git a/src/parsers.cc b/src/parsers.cc
index c1badd8..5d347b2 100644
--- a/src/parsers.cc
+++ b/src/parsers.cc
@@ -313,6 +313,7 @@ bool ManifestParser::ExpectToken(Lexer::Token expected, string* err) {
if (token != expected) {
string message = string("expected ") + Lexer::TokenName(expected);
message += string(", got ") + Lexer::TokenName(token);
+ message += Lexer::TokenErrorHint(expected);
return lexer_.Error(message, err);
}
return true;
diff --git a/src/parsers_test.cc b/src/parsers_test.cc
index f3ac517..2a30a83 100644
--- a/src/parsers_test.cc
+++ b/src/parsers_test.cc
@@ -377,14 +377,30 @@ TEST_F(ParserTest, Errors) {
ManifestParser parser(&state, NULL);
string err;
EXPECT_FALSE(parser.ParseTest("rule cat\n"
- " command = cat\nbuild $: cat foo\n",
+ " command = cat\n"
+ "build $.: cat foo\n",
&err));
EXPECT_EQ("input:3: bad $-escape (literal $ must be written as $$)\n"
- "build $: cat foo\n"
+ "build $.: cat foo\n"
" ^ near here"
, err);
}
+
+ {
+ State state;
+ ManifestParser parser(&state, NULL);
+ string err;
+ EXPECT_FALSE(parser.ParseTest("rule cat\n"
+ " command = cat\n"
+ "build $: cat foo\n",
+ &err));
+ EXPECT_EQ("input:3: expected ':', got newline ($ also escapes ':')\n"
+ "build $: cat foo\n"
+ " ^ near here"
+ , err);
+ }
+
{
State state;
ManifestParser parser(&state, NULL);
@@ -413,10 +429,10 @@ TEST_F(ParserTest, Errors) {
ManifestParser parser(&state, NULL);
string err;
EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n"
- "build $: cc bar.cc\n",
+ "build $.: cc bar.cc\n",
&err));
EXPECT_EQ("input:3: bad $-escape (literal $ must be written as $$)\n"
- "build $: cc bar.cc\n"
+ "build $.: cc bar.cc\n"
" ^ near here"
, err);
}
@@ -425,6 +441,19 @@ TEST_F(ParserTest, Errors) {
State state;
ManifestParser parser(&state, NULL);
string err;
+ EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n"
+ "build $: cc bar.cc\n",
+ &err));
+ EXPECT_EQ("input:3: expected ':', got newline ($ also escapes ':')\n"
+ "build $: cc bar.cc\n"
+ " ^ near here"
+ , err);
+ }
+
+ {
+ State state;
+ ManifestParser parser(&state, NULL);
+ string err;
EXPECT_FALSE(parser.ParseTest("default\n",
&err));
EXPECT_EQ("input:1: expected target name\n"