From 1ea33ab70f88fcfebf388a9a438e3c1e56fbdf0f Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 10 Jun 2020 10:01:45 +0200 Subject: Handle special variable names when spelled as $() rather than $ --- libbuild2/lexer.cxx | 3 +++ libbuild2/lexer.hxx | 3 +++ libbuild2/parser.cxx | 43 +++++++++++++++++++++++++++++++++++++++++-- libbuild2/parser.hxx | 25 +++++++++++++++++++++---- libbuild2/token.hxx | 1 + 5 files changed, 69 insertions(+), 6 deletions(-) diff --git a/libbuild2/lexer.cxx b/libbuild2/lexer.cxx index 2f2ace4..4256422 100644 --- a/libbuild2/lexer.cxx +++ b/libbuild2/lexer.cxx @@ -111,6 +111,9 @@ namespace build2 } case lexer_mode::eval: { + // NOTE: remember to update special() lambda in parse_names() if + // adding any new single-character tokens to the eval mode. + // s1 = ":<>=!&|?, $(){}#\t\n"; s2 = " = &| "; break; diff --git a/libbuild2/lexer.hxx b/libbuild2/lexer.hxx index f577828..6c2b90b 100644 --- a/libbuild2/lexer.hxx +++ b/libbuild2/lexer.hxx @@ -161,6 +161,9 @@ namespace build2 lexer_mode mode () const {return state_.top ().mode;} + uintptr_t + mode_data () const {return state_.top ().data;} + char pair_separator () const {return state_.top ().sep_pair;} diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index db00633..36e75a3 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -5815,7 +5815,44 @@ namespace build2 // token is a paren or a word, we turn it on and switch to the eval // mode if what we get next is a paren. // + // Also sniff out the special variables string from mode data for + // the ad hoc $() handling below. + // mode (lexer_mode::variable); + + auto special = [s = reinterpret_cast (mode_data ())] + (const token& t) -> char + { + char r ('\0'); + + if (s != nullptr) + { + switch (t.type) + { + case type::less: r = '<'; break; + case type::greater: r = '>'; break; + case type::colon: r = ':'; break; + case type::dollar: r = '$'; break; + case type::question: r = '?'; break; + case type::comma: r = ','; break; + case type::log_not: r = '!'; break; + case type::lparen: r = '('; break; + case type::rparen: r = ')'; break; + case type::lcbrace: r = '{'; break; + case type::rcbrace: r = '}'; break; + case type::lsbrace: r = '['; break; + case type::rsbrace: r = ']'; break; + case type::pair_separator: r = t.value[0]; break; + default: break; + } + + if (r != '\0' && strchr (s, r) == nullptr) + r = '\0'; + } + + return r; + }; + next (t, tt); loc = get_location (t); @@ -5838,9 +5875,11 @@ namespace build2 // the variable name even during pre-parse. It should also be // faster. // - if (tt == type::word && peek () == type::rparen) + char c; + if ((tt == type::word || (c = special (t))) && + peek () == type::rparen) { - name = move (t.value); + name = (tt == type::word ? move (t.value) : string (1, c)); next (t, tt); // Get `)`. } else diff --git a/libbuild2/parser.hxx b/libbuild2/parser.hxx index 78c7d17..2db7ade 100644 --- a/libbuild2/parser.hxx +++ b/libbuild2/parser.hxx @@ -611,8 +611,9 @@ namespace build2 lexer_->mode (m, ps, nullopt, d); else // As a sanity check, make sure the mode matches the next token. Note - // that we don't check the attributes flags or the pair separator - // since they can be overridden by the lexer's mode() implementation. + // that we don't check the attributes flags, the pair separator, or + // the mode data since they can be overridden by the lexer's mode() + // implementation. // assert (replay_i_ != replay_data_.size () && replay_data_[replay_i_].mode == m); @@ -630,6 +631,18 @@ namespace build2 } } + uintptr_t + mode_data () const + { + if (replay_ != replay::play) + return lexer_->mode_data (); + else + { + assert (replay_i_ != replay_data_.size ()); + return replay_data_[replay_i_].mode_data; + } + } + void enable_attributes () { @@ -753,8 +766,12 @@ namespace build2 replay_token lexer_next () { - lexer_mode m (lexer_->mode ()); // Get it first since it may expire. - return replay_token {lexer_->next (), path_, m}; + // Get these first since the mode may expire. + // + lexer_mode m (lexer_->mode ()); + uintptr_t d (lexer_->mode_data ()); + + return replay_token {lexer_->next (), path_, m, d}; } const replay_token& diff --git a/libbuild2/token.hxx b/libbuild2/token.hxx index 156e428..7344ecd 100644 --- a/libbuild2/token.hxx +++ b/libbuild2/token.hxx @@ -206,6 +206,7 @@ namespace build2 build2::token token; const path_name* file; lexer_mode_base mode; + uintptr_t mode_data; using location_type = build2::location; -- cgit v1.1