From 12268f7741ba73c75a73fafb6063f1393e485aae Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 27 Sep 2019 13:55:07 +0200 Subject: Add support for custom match/extract functions in switch expression --- libbuild2/parser.cxx | 128 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 82 insertions(+), 46 deletions(-) (limited to 'libbuild2/parser.cxx') diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index 33b6d11..d457c68 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -879,10 +879,7 @@ namespace build2 attributes_push (t, tt); location nloc (get_location (t)); - names ns (parse_names (t, tt, - pattern_mode::ignore, - false /* chunk */, - "variable name")); + names ns (parse_names (t, tt, pattern_mode::ignore, "variable name")); if (tt != type::assign && tt != type::prepend && @@ -1367,11 +1364,7 @@ namespace build2 next (t, tt); const location l (get_location (t)); names ns (tt != type::newline && tt != type::eos - ? parse_names (t, tt, - pattern_mode::expand, - false, - "path", - nullptr) + ? parse_names (t, tt, pattern_mode::expand, "path", nullptr) : names ()); for (name& n: ns) @@ -1425,11 +1418,7 @@ namespace build2 next (t, tt); const location l (get_location (t)); names ns (tt != type::newline && tt != type::eos - ? parse_names (t, tt, - pattern_mode::expand, - false, - "path", - nullptr) + ? parse_names (t, tt, pattern_mode::expand, "path", nullptr) : names ()); for (name& n: ns) @@ -1556,13 +1545,10 @@ namespace build2 strings args; try { - args = convert (tt != type::newline && tt != type::eos - ? parse_names (t, tt, - pattern_mode::ignore, - false, - "argument", - nullptr) - : names ()); + args = convert ( + tt != type::newline && tt != type::eos + ? parse_names (t, tt, pattern_mode::ignore, "argument", nullptr) + : names ()); } catch (const invalid_argument& e) { @@ -1852,11 +1838,7 @@ namespace build2 next (t, tt); const location l (get_location (t)); names ns (tt != type::newline && tt != type::eos - ? parse_names (t, tt, - pattern_mode::ignore, - false, - "module", - nullptr) + ? parse_names (t, tt, pattern_mode::ignore, "module", nullptr) : names ()); for (auto i (ns.begin ()); i != ns.end (); ++i) @@ -2077,22 +2059,22 @@ namespace build2 void parser:: parse_switch (token& t, type& tt) { - // switch [, ....] + // switch [: []] [, ...] // { - // case [, ...] + // case [, ...] // // - // case [, ...] + // case [, ...] // { // // } // - // case [, ...] + // case [, ...] // ... - // case [, ...] + // case [, ...] // ... // - // case [|] + // case [| ... ] // // default // ... @@ -2103,23 +2085,49 @@ namespace build2 // Parse and evaluate the values we are matching. Similar to if-else, we // expand patterns. // - values vs; + struct expr + { + build2::value value; + optional func; + names arg; + }; + small_vector exprs; + + mode (lexer_mode::switch_expressions); // Recognize `:` and `,`. + + do { - mode (lexer_mode::values); // Recognize `,`. + next (t, tt); + if (tt == type::newline || tt == type::eos) + fail (t) << "expected switch expression instead of " << t; + + expr e; + + e.value = + parse_value (t, tt, pattern_mode::expand, "expression", nullptr); - do + if (tt == type::colon) { next (t, tt); - if (tt == type::newline || tt == type::eos) - fail (t) << "expected switch expression instead of " << t; + const location l (get_location (t)); + names ns (parse_names (t, tt, pattern_mode::ignore, "function name")); + + if (ns.empty () || ns[0].empty ()) + fail (l) << "function name expected after ':'"; + + if (!ns[0].simple ()) + fail (l) << "function name expected instead of " << ns[0]; - vs.push_back ( - parse_value (t, tt, pattern_mode::expand, "expression", nullptr)); + e.func = move (ns[0].value); + ns.erase (ns.begin ()); + e.arg = move (ns); } - while (tt == type::comma); - next_after_newline (t, tt, "switch expression"); + exprs.push_back (move (e)); } + while (tt == type::comma); + + next_after_newline (t, tt, "switch expression"); // Next we should always have a block. // @@ -2204,7 +2212,7 @@ namespace build2 if (tt == type::newline || tt == type::eos) fail (t) << "expected case pattern instead of " << t; - if (i == vs.size ()) + if (i == exprs.size ()) fail (t) << "more patterns than switch expressions"; // Handle pattern alternatives (|). @@ -2213,7 +2221,37 @@ namespace build2 { const location l (get_location (t)); value p (parse_pattern (t, tt)); - take = compare_values (type::equal, vs[i], p, l); + expr& e (exprs[i]); // Note: value might be modified (typified). + + if (e.func) + { + // Call (, [, ]). + // + small_vector args {value (e.value), move (p)}; + + if (!e.arg.empty ()) + args.push_back (value (e.arg)); + + value r (ctx.functions.call (scope_, *e.func, args, l)); + + // We support two types of functions: matchers and extractors: + // a matcher returns a statically-typed bool value while an + // extractor returns NULL if there is no match and the + // extracted value otherwise. + // + if (r.type == &value_traits::value_type) + { + if (r.null) + fail (l) << "match function " << *e.func << " returned " + << "null"; + + take = r.as (); + } + else + take = !r.null; + } + else + take = compare_values (type::equal, e.value, p, l); if (tt != type::bit_or) break; @@ -2515,7 +2553,6 @@ namespace build2 names ns (tt != type::newline && tt != type::eos ? parse_names (t, tt, pattern_mode::ignore, - false, "description", nullptr) : names ()); @@ -3391,8 +3428,7 @@ namespace build2 if (has) { names ns ( - parse_names ( - t, tt, pattern_mode::ignore, false, "attribute", nullptr)); + parse_names (t, tt, pattern_mode::ignore, "attribute", nullptr)); if (!pre_parse_) { -- cgit v1.1