aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-01-21 15:03:26 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-01-21 15:03:26 +0200
commit67382c15f7e9176dea44c3da7f7013759c88ce33 (patch)
tree5ff4fe1436792a01d4a8576f1e0efa3ce2ff9c20
parent41f091f46b47c20dc1571db5d56a1eaade25cb1e (diff)
Add support for ==, != in eval context
-rw-r--r--build2/lexer.cxx54
-rw-r--r--build2/parser9
-rw-r--r--build2/parser.cxx45
-rw-r--r--build2/token8
-rw-r--r--build2/token.cxx2
-rw-r--r--tests/eval/buildfile30
-rw-r--r--tests/eval/test.out16
-rw-r--r--tests/if-else/buildfile17
-rw-r--r--tests/if-else/test.out3
9 files changed, 167 insertions, 17 deletions
diff --git a/build2/lexer.cxx b/build2/lexer.cxx
index 65bb5e9..da3d64e 100644
--- a/build2/lexer.cxx
+++ b/build2/lexer.cxx
@@ -126,8 +126,8 @@ namespace build2
uint64_t ln (c.line), cn (c.column);
- // This mode is quite a bit like the value mode when it comes
- // to special characters.
+ // This mode is quite a bit like the value mode when it comes to special
+ // characters, except that we have some of our own.
//
switch (c)
{
@@ -143,6 +143,15 @@ namespace build2
mode_.pop (); // Expire eval mode.
return token (type::rparen, sep, ln, cn);
}
+ case '=':
+ case '!':
+ {
+ if (peek () == '=')
+ {
+ get ();
+ return token (c == '=' ? type::equal : type::not_equal, sep, ln, cn);
+ }
+ }
}
// Otherwise it is a name.
@@ -194,31 +203,34 @@ namespace build2
if (m == lexer_mode::pairs && c == pair_separator_)
break;
- // The following characters are not treated as special in the
- // value/pairs, eval, and quoted modes.
+ // The following characters are only special in the normal and
+ // variable name modes.
//
- if (m != lexer_mode::value &&
- m != lexer_mode::pairs &&
- m != lexer_mode::eval &&
- m != lexer_mode::quoted)
+ if (m == lexer_mode::normal || m == lexer_mode::variable)
{
switch (c)
{
case ':':
- case '+':
case '=':
{
done = true;
break;
}
+ case '+':
+ {
+ get ();
+ done = (peek () == '=');
+ unget (c);
+ break;
+ }
}
if (done)
break;
}
- // While these extra characters are treated as the name end in
- // the variable mode.
+ // These extra characters are treated as the name end in the variable
+ // mode.
//
if (m == lexer_mode::variable)
{
@@ -236,6 +248,26 @@ namespace build2
break;
}
+ // These extra characters are treated as the name end in the eval mode.
+ //
+ if (m == lexer_mode::eval)
+ {
+ switch (c)
+ {
+ case '=':
+ case '!':
+ {
+ get ();
+ done = (peek () == '=');
+ unget (c);
+ break;
+ }
+ }
+
+ if (done)
+ break;
+ }
+
// If we are quoted, these are ordinary characters.
//
if (m != lexer_mode::quoted)
diff --git a/build2/parser b/build2/parser
index 2d523c3..a379bd7 100644
--- a/build2/parser
+++ b/build2/parser
@@ -93,6 +93,9 @@ namespace build2
names_type
eval (token&, token_type&);
+ void
+ eval_trailer (token&, token_type&, names_type&);
+
// If chunk is true, then parse the smallest but complete, name-wise,
// chunk of input. Note that in this case you may still end up with
// multiple names, for example, {foo bar}.
@@ -106,6 +109,12 @@ namespace build2
}
void
+ names (token& t, token_type& tt, names_type& ns, bool chunk = false)
+ {
+ names (t, tt, ns, chunk, 0, nullptr, nullptr, nullptr);
+ }
+
+ void
names (token&, token_type&,
names_type&,
bool chunk,
diff --git a/build2/parser.cxx b/build2/parser.cxx
index b1a4414..187c47e 100644
--- a/build2/parser.cxx
+++ b/build2/parser.cxx
@@ -1134,12 +1134,51 @@ namespace build2
mode (lexer_mode::eval);
next (t, tt);
- names_type ns (tt != type::rparen ? names (t, tt) : names_type ());
+ names_type ns;
+ eval_trailer (t, tt, ns);
+ return ns;
+ }
+ void parser::
+ eval_trailer (token& t, type& tt, names_type& ns)
+ {
+ // Note that names() will handle the ( == foo) case since if it gets
+ // called, it expects to see a name.
+ //
if (tt != type::rparen)
- fail (t) << "expected ')' instead of " << t;
+ names (t, tt, ns);
- return ns;
+ switch (tt)
+ {
+ case type::equal:
+ case type::not_equal:
+ {
+ type op (tt);
+
+ // ==, != are left-associative, so get the rhs name and evaluate.
+ //
+ next (t, tt);
+ names_type rhs (names (t, tt));
+
+ bool r;
+ switch (op)
+ {
+ case type::equal: r = ns == rhs; break;
+ case type::not_equal: r = ns != rhs; break;
+ default: r = false; assert (false);
+ }
+
+ ns.resize (1);
+ ns[0] = name (r ? "true" : "false");
+
+ eval_trailer (t, tt, ns);
+ break;
+ }
+ case type::rparen:
+ break;
+ default:
+ fail (t) << "expected ')' instead of " << t;
+ }
}
// Parse names inside {} and handle the following "crosses" (i.e.,
diff --git a/build2/token b/build2/token
index f2e9978..5b75e1c 100644
--- a/build2/token
+++ b/build2/token
@@ -22,9 +22,11 @@ namespace build2
colon,
lcbrace,
rcbrace,
- assign, // =
- prepend, // =+
- append, // +=
+ assign, // =
+ prepend, // =+
+ append, // +=
+ equal, // ==
+ not_equal, // !=
dollar,
lparen,
rparen
diff --git a/build2/token.cxx b/build2/token.cxx
index ed47740..a80ff44 100644
--- a/build2/token.cxx
+++ b/build2/token.cxx
@@ -24,6 +24,8 @@ namespace build2
case token_type::assign: os << "="; break;
case token_type::prepend: os << "=+"; break;
case token_type::append: os << "+="; break;
+ case token_type::equal: os << "=="; break;
+ case token_type::not_equal: os << "!="; break;
case token_type::dollar: os << "$"; break;
case token_type::lparen: os << "("; break;
case token_type::rparen: os << ")"; break;
diff --git a/tests/eval/buildfile b/tests/eval/buildfile
index cf315b6..06fb5fb 100644
--- a/tests/eval/buildfile
+++ b/tests/eval/buildfile
@@ -12,3 +12,33 @@ print ((foo) (bar))
print (foo\
bar)
+
+# !=, == vs !, = recognition
+#
+print (=)
+print (!)
+print (= foo)
+print (foo!)
+
+# !=, == evaluation
+#
+
+# print ( == bar)
+# print (foo == )
+
+print (foo == bar)
+print (foo == foo)
+print (foo != bar)
+print (foo != foo)
+
+print (foo == (foo))
+print ((foo bar) == foo bar)
+print (foo != foo bar)
+print ("" == '')
+
+print ((foo != bar) baz)
+print "foo equals bar is (foo == bar)"
+
+foo = foo
+print ($foo == foo)
+print (bar != $foo)
diff --git a/tests/eval/test.out b/tests/eval/test.out
index 5885c7d..599f928 100644
--- a/tests/eval/test.out
+++ b/tests/eval/test.out
@@ -2,3 +2,19 @@
foobar
foo bar
foobar
+=
+!
+= foo
+foo!
+false
+true
+true
+false
+true
+true
+true
+true
+true baz
+foo equals bar is false
+true
+true
diff --git a/tests/if-else/buildfile b/tests/if-else/buildfile
index 30da40f..2b87b8c 100644
--- a/tests/if-else/buildfile
+++ b/tests/if-else/buildfile
@@ -106,6 +106,23 @@ if! $f
./:
+# With eval context.
+#
+if (foo == foo)
+{
+ print 1
+}
+
+if(foo != bar)
+{
+ print 1
+}
+
+if!(foo == bar)
+{
+ print 1
+}
+
# EOF test.
#
if true
diff --git a/tests/if-else/test.out b/tests/if-else/test.out
index bb08505..4dff9ef 100644
--- a/tests/if-else/test.out
+++ b/tests/if-else/test.out
@@ -7,3 +7,6 @@
1
1
1
+1
+1
+1