From 67382c15f7e9176dea44c3da7f7013759c88ce33 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 21 Jan 2016 15:03:26 +0200 Subject: Add support for ==, != in eval context --- build2/lexer.cxx | 54 +++++++++++++++++++++++++++++++++++++++---------- build2/parser | 9 +++++++++ build2/parser.cxx | 45 ++++++++++++++++++++++++++++++++++++++--- build2/token | 8 +++++--- build2/token.cxx | 2 ++ tests/eval/buildfile | 30 +++++++++++++++++++++++++++ tests/eval/test.out | 16 +++++++++++++++ tests/if-else/buildfile | 17 ++++++++++++++++ tests/if-else/test.out | 3 +++ 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 -- cgit v1.1