From 17af650e17fafd4d3fa2bf0e06253e15f4e5b9c3 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 28 Jun 2022 09:50:05 +0200 Subject: Add support for querying out-qualified target-specific variables --- libbuild2/build/script/parser.cxx | 2 +- libbuild2/build/script/parser.hxx | 2 +- libbuild2/parser.cxx | 81 ++++++++++++++++++++----------- libbuild2/parser.hxx | 10 ++-- libbuild2/prerequisite.hxx | 4 +- libbuild2/test/script/parser.cxx | 2 +- libbuild2/test/script/parser.hxx | 2 +- tests/eval/qual.testscript | 5 +- tests/variable/target-specific/testscript | 14 +++--- 9 files changed, 79 insertions(+), 43 deletions(-) diff --git a/libbuild2/build/script/parser.cxx b/libbuild2/build/script/parser.cxx index 795dc72..a27ec41 100644 --- a/libbuild2/build/script/parser.cxx +++ b/libbuild2/build/script/parser.cxx @@ -2271,7 +2271,7 @@ namespace build2 } lookup parser:: - lookup_variable (name&& qual, string&& name, const location& loc) + lookup_variable (names&& qual, string&& name, const location& loc) { // In the pre-parse mode collect the referenced variable names for the // script semantics change tracking. diff --git a/libbuild2/build/script/parser.hxx b/libbuild2/build/script/parser.hxx index 362c834..1a6c39d 100644 --- a/libbuild2/build/script/parser.hxx +++ b/libbuild2/build/script/parser.hxx @@ -229,7 +229,7 @@ namespace build2 // protected: virtual lookup - lookup_variable (name&&, string&&, const location&) override; + lookup_variable (names&&, string&&, const location&) override; virtual void lookup_function (string&&, const location&) override; diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index 4c6cde0..8343112 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -5260,17 +5260,38 @@ namespace build2 if (pre_parse_) return v; // Empty. - if (v.type != nullptr || !v || v.as ().size () != 1) - fail (l) << "expected target before ':'"; - + // We used to return this as a : pair but that meant we + // could not handle an out-qualified target (which is represented as + // @ pair). As a somewhat of a hack, we deal with this by + // changing the order of the name and target to be : with + // the qualified case becoming a "tripple pair" :@. + // + // @@ This is actually not great since it's possible to observe such a + // tripple pair, for example with `print (file{x}@./:y)`. + // if (n.type != nullptr || !n || n.as ().size () != 1 || n.as ()[0].pattern) fail (nl) << "expected variable name after ':'"; - names& ns (v.as ()); + names& ns (n.as ()); ns.back ().pair = ':'; - ns.push_back (move (n.as ().back ())); - return v; + + if (v.type == nullptr && v) + { + names& ts (v.as ()); + + size_t s (ts.size ()); + if (s == 1 || (s == 2 && ts.front ().pair == '@')) + { + ns.push_back (move (ts.front ())); + if (s == 2) + ns.push_back (move (ts.back ())); + + return n; + } + } + + fail (l) << "expected target before ':'" << endf; } else { @@ -7004,7 +7025,7 @@ namespace build2 next (t, tt); loc = get_location (t); - name qual; + names qual; string name; if (t.separated) @@ -7036,8 +7057,6 @@ namespace build2 { using name_type = build2::name; - //@@ OUT will parse @-pair and do well? - // values vs (parse_eval (t, tt, pmode)); if (!pre_parse_) @@ -7059,17 +7078,26 @@ namespace build2 // we would be treating all paths as qualified variables. So // we have to do it here. // - if (n == 2 && ns[0].pair == ':') // $(foo: x) + if (n >= 2 && ns[0].pair == ':') // $(foo: x) { - qual = move (ns[0]); + // Note: name is first (see eval for details). + // + qual.push_back (move (ns[1])); - if (qual.empty ()) + if (qual.back ().empty ()) fail (loc) << "empty variable/function qualification"; + + if (n > 2) + qual.push_back (move (ns[2])); + + // Move name to the last position (see below). + // + swap (ns[0], ns[n - 1]); } else if (n == 2 && ns[0].directory ()) // $(foo/ x) { - qual = move (ns[0]); - qual.pair = '/'; + qual.push_back (move (ns[0])); + qual.back ().pair = '/'; } else if (n > 1) fail (loc) << "expected variable/function name instead of '" @@ -7093,8 +7121,8 @@ namespace build2 name = string (s, p + 1); s.resize (p + 1); - qual = name_type (dir_path (move (s))); - qual.pair = '/'; + qual.push_back (name_type (dir_path (move (s)))); + qual.back ().pair = '/'; } else name = move (ns[n - 1].value); @@ -7903,7 +7931,7 @@ namespace build2 } lookup parser:: - lookup_variable (name&& qual, string&& name, const location& loc) + lookup_variable (names&& qual, string&& name, const location& loc) { if (pre_parse_) return lookup (); @@ -7927,27 +7955,26 @@ namespace build2 } else { - switch (qual.pair) + switch (qual.front ().pair) { case '/': { - assert (qual.directory ()); - sg = enter_scope (*this, move (qual.dir)); + assert (qual.front ().directory ()); + sg = enter_scope (*this, move (qual.front ().dir)); s = scope_; break; } - case ':': + default: { - qual.pair = '\0'; + build2::name n (move (qual.front ())), o; - // @@ OUT TODO - // - tg = enter_target ( - *this, move (qual), build2::name (), true, loc, trace); + if (n.pair) + o = move (qual.back ()); + + tg = enter_target (*this, move (n), move (o), true, loc, trace); t = target_; break; } - default: assert (false); } } diff --git a/libbuild2/parser.hxx b/libbuild2/parser.hxx index 9e9f926..f806568 100644 --- a/libbuild2/parser.hxx +++ b/libbuild2/parser.hxx @@ -560,8 +560,12 @@ namespace build2 // Customization hooks. // protected: - // If qual is not empty, then its pair member should indicate the kind - // of qualification: ':' -- target, '/' -- scope. + // If qual is not empty, then firt element's pair member indicates the + // kind of qualification: + // + // '\0' -- target + // '@' -- out-qualified target + // '/' -- scope // // Note that this function is called even during pre-parse with the result // unused. In this case a valid name will only be provided for variables @@ -574,7 +578,7 @@ namespace build2 // if/when extending this and audit all the existing use-cases. // virtual lookup - lookup_variable (name&& qual, string&& name, const location&); + lookup_variable (names&& qual, string&& name, const location&); // This function is only called during pre-parse and is the continuation // of the similar logic in lookup_variable() above (including the fact diff --git a/libbuild2/prerequisite.hxx b/libbuild2/prerequisite.hxx index 476ed9d..3b64eae 100644 --- a/libbuild2/prerequisite.hxx +++ b/libbuild2/prerequisite.hxx @@ -29,7 +29,9 @@ namespace build2 using target_type_type = build2::target_type; // Note that unlike targets, for prerequisites an empty out directory - // means undetermined rather than being definitely in the out tree. + // means undetermined rather than being definitely in the out tree (but + // maybe we should make this explicit via optional<>; see the from-target + // constructor). // // It might seem natural to keep the reference to the owner target instead // of to the scope. But that's not the semantics that we have, consider: diff --git a/libbuild2/test/script/parser.cxx b/libbuild2/test/script/parser.cxx index 2cc1b65..eb7b140 100644 --- a/libbuild2/test/script/parser.cxx +++ b/libbuild2/test/script/parser.cxx @@ -1661,7 +1661,7 @@ namespace build2 } lookup parser:: - lookup_variable (name&& qual, string&& name, const location& loc) + lookup_variable (names&& qual, string&& name, const location& loc) { if (pre_parse_) return lookup (); diff --git a/libbuild2/test/script/parser.hxx b/libbuild2/test/script/parser.hxx index c63bce6..66160d9 100644 --- a/libbuild2/test/script/parser.hxx +++ b/libbuild2/test/script/parser.hxx @@ -117,7 +117,7 @@ namespace build2 // protected: virtual lookup - lookup_variable (name&&, string&&, const location&) override; + lookup_variable (names&&, string&&, const location&) override; // Insert id into the id map checking for duplicates. // diff --git a/tests/eval/qual.testscript b/tests/eval/qual.testscript index 29f6340..88339fd 100644 --- a/tests/eval/qual.testscript +++ b/tests/eval/qual.testscript @@ -5,8 +5,9 @@ .include ../common.testscript -$* <'print (file{foo}: bar)' >'file{foo}:bar' : target -$* <'print (foo/dir{}: bar)' >'dir{foo/}:bar' : scope +$* <'print (file{foo}: bar)' >'bar:file{foo}' : target +$* <'print (file{foo}@./: bar)' >'bar:file{foo}@./' : target-out +$* <'print (foo/dir{}: bar)' >'bar:dir{foo/}' : target-dir : attribute : diff --git a/tests/variable/target-specific/testscript b/tests/variable/target-specific/testscript index 627d8ab..c52712b 100644 --- a/tests/variable/target-specific/testscript +++ b/tests/variable/target-specific/testscript @@ -65,13 +65,15 @@ print (foo: bar) print (foo : bar) print (foo/: bar) print (foo/file{fox}: bar) +print (file{fox}@./: bar) EOI -foo:bar -foo:bar -foo:bar -foo:bar -foo/:bar -foo/file{fox}:bar +bar:foo +bar:foo +bar:foo +bar:foo +bar:foo/ +bar:foo/file{fox} +bar:file{fox}@./ EOO : eval-qual-name-expected -- cgit v1.1