aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2019-09-26 13:36:44 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2019-09-30 15:30:35 +0200
commite59c2bc979293d8cdea3f9733ecd59c080fce63c (patch)
tree337fefcc20ee6b6de53514caceefc9d45e5bd7d8
parent333f468d264f0fa36a772b10b885fff6160ae4c7 (diff)
Add support for `case` pattern alternatives
case <pattern>[ | <pattern>...]
-rw-r--r--libbuild2/lexer.cxx27
-rw-r--r--libbuild2/lexer.hxx32
-rw-r--r--libbuild2/parser.cxx46
-rw-r--r--libbuild2/token.hxx2
-rw-r--r--tests/switch/testscript23
5 files changed, 97 insertions, 33 deletions
diff --git a/libbuild2/lexer.cxx b/libbuild2/lexer.cxx
index 6da8a80..ac62996 100644
--- a/libbuild2/lexer.cxx
+++ b/libbuild2/lexer.cxx
@@ -55,6 +55,12 @@ namespace build2
s2 = " ";
break;
}
+ case lexer_mode::case_patterns:
+ {
+ s1 = " $(){}[],|#\t\n";
+ s2 = " ";
+ break;
+ }
case lexer_mode::attribute:
{
s1 = " $(]#\t\n";
@@ -113,6 +119,7 @@ namespace build2
case lexer_mode::normal:
case lexer_mode::value:
case lexer_mode::values:
+ case lexer_mode::case_patterns:
case lexer_mode::attribute:
case lexer_mode::variable:
case lexer_mode::buildspec: break;
@@ -150,7 +157,9 @@ namespace build2
{
// Expire value/values modes at the end of the line.
//
- if (m == lexer_mode::value || m == lexer_mode::values)
+ if (m == lexer_mode::value ||
+ m == lexer_mode::values ||
+ m == lexer_mode::case_patterns)
state_.pop ();
sep = true; // Treat newline as always separated.
@@ -227,7 +236,9 @@ namespace build2
// The following characters are special in the values and buildspec mode.
//
- if (m == lexer_mode::values || m == lexer_mode::buildspec)
+ if (m == lexer_mode::buildspec ||
+ m == lexer_mode::values ||
+ m == lexer_mode::case_patterns)
{
// NOTE: remember to update mode() if adding new special characters.
//
@@ -237,6 +248,18 @@ namespace build2
}
}
+ // The following characters are special in the case_patterns mode.
+ //
+ if (m == lexer_mode::case_patterns)
+ {
+ // NOTE: remember to update mode() if adding new special characters.
+ //
+ switch (c)
+ {
+ case '|': return make_token (type::bit_or);
+ }
+ }
+
// Otherwise it is a word.
//
unget (c);
diff --git a/libbuild2/lexer.hxx b/libbuild2/lexer.hxx
index e92980b..a629ba7 100644
--- a/libbuild2/lexer.hxx
+++ b/libbuild2/lexer.hxx
@@ -19,22 +19,25 @@
namespace build2
{
- // Context-dependent lexing mode. In the value mode we don't treat certain
- // characters (e.g., '+', '=') as special so that we can use them in the
- // variable values, e.g., 'foo = g++'. In contrast, in the variable mode, we
- // restrict certain character (e.g., '/') from appearing in the name. The
- // values mode is like value but recogizes ',' as special (used in contexts
- // where we need to list multiple values). The attribute mode is also like
- // value except it doesn't treat '{' and '}' as special (so we cannot have
- // name groups in attributes). The eval mode is used in the evaluation
- // context. Quoted modes are internal and should not be set explicitly.
+ // Context-dependent lexing mode. Quoted modes are internal and should not
+ // be set explicitly. In the value mode we don't treat certain characters
+ // (e.g., '+', '=') as special so that we can use them in the variable
+ // values, e.g., 'foo = g++'. In contrast, in the variable mode, we restrict
+ // certain character (e.g., '/') from appearing in the name. The values mode
+ // is like value but recogizes ',' as special (used in contexts where we
+ // need to list multiple values). The attribute mode is also like value
+ // except it doesn't treat '{' and '}' as special (so we cannot have name
+ // groups in attributes). The eval mode is used in the evaluation context.
//
- // Note that the normal, value/values, and eval modes split words separated
- // by the pair character (to disable pairs one can pass '\0' as a pair
- // character).
+ // A number of modes are "derived" from the value/values mode by recognizing
+ // a few extra characters: case_patterns (values plus '|').
//
- // The alternative modes must be set manually. The value/values mode
- // automatically expires after the end of the line. The attribute mode
+ // Note that the normal, value/values and derived, as well as eval modes
+ // split words separated by the pair character (to disable pairs one can
+ // pass '\0' as a pair character).
+ //
+ // The alternative modes must be set manually. The value/values and derived
+ // modes automatically expires after the end of the line. The attribute mode
// expires after the closing ']'. The variable mode expires after the word
// token. And the eval mode expires after the closing ')'.
//
@@ -57,6 +60,7 @@ namespace build2
variable,
value,
values,
+ case_patterns,
attribute,
eval,
single_quoted,
diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx
index 2a5c31d..33b6d11 100644
--- a/libbuild2/parser.cxx
+++ b/libbuild2/parser.cxx
@@ -2092,10 +2092,14 @@ namespace build2
// case <pattern>[, <pattern>...]
// ...
//
+ // case <pattern>[|<pattern>]
+ //
// default
// ...
// }
+ assert (!pre_parse_); // Used to skip pattern alternatives.
+
// Parse and evaluate the values we are matching. Similar to if-else, we
// expand patterns.
//
@@ -2186,7 +2190,13 @@ namespace build2
// Parse the patterns and match them against the values. Note that
// here we don't expand patterns in names.
//
- mode (lexer_mode::values); // Recognize `,`.
+ mode (lexer_mode::case_patterns); // Recognize `|` and `,`.
+
+ auto parse_pattern = [this] (token& t, type& tt)
+ {
+ return parse_value (
+ t, tt, pattern_mode::ignore, "pattern", nullptr);
+ };
for (size_t i (0);; ++i)
{
@@ -2194,16 +2204,36 @@ namespace build2
if (tt == type::newline || tt == type::eos)
fail (t) << "expected case pattern instead of " << t;
- const location l (get_location (t));
+ if (i == vs.size ())
+ fail (t) << "more patterns than switch expressions";
- value p (
- parse_value (
- t, tt, pattern_mode::ignore, "pattern", nullptr));
+ // Handle pattern alternatives (<pattern>|<pattern>).
+ //
+ for (;; next (t, tt))
+ {
+ const location l (get_location (t));
+ value p (parse_pattern (t, tt));
+ take = compare_values (type::equal, vs[i], p, l);
- if (i == vs.size ())
- fail (l) << "more patterns than switch expressions";
+ if (tt != type::bit_or)
+ break;
+
+ if (take)
+ {
+ // Use the pre-parse mechanism to skip remaining alternatives.
+ //
+ pre_parse_ = true;
+ do
+ {
+ next (t, tt); // Skip `|`.
+ parse_pattern (t, tt);
+ }
+ while (tt == type::bit_or);
+ pre_parse_ = false;
- take = compare_values (type::equal, vs[i], p, l);
+ break;
+ }
+ }
if (!take)
{
diff --git a/libbuild2/token.hxx b/libbuild2/token.hxx
index a9b9a11..0ee5248 100644
--- a/libbuild2/token.hxx
+++ b/libbuild2/token.hxx
@@ -59,6 +59,8 @@ namespace build2
less_equal, // <=
greater_equal, // >=
+ bit_or, // |
+
log_or, // ||
log_and, // &&
log_not, // !
diff --git a/tests/switch/testscript b/tests/switch/testscript
index 877f640..1399df0 100644
--- a/tests/switch/testscript
+++ b/tests/switch/testscript
@@ -9,7 +9,7 @@
: basics
:
$* <<EOI >>EOO
-for i: 1 2 3 4
+for i: 1 2 3 4 5
{
switch $i
{
@@ -19,9 +19,11 @@ for i: 1 2 3 4
{
print 2
}
- case 5
+ case 0
case 3
- print 3,5
+ print 0,3
+ case 0|4
+ print 0,4
default
print d
}
@@ -29,14 +31,15 @@ for i: 1 2 3 4
EOI
1
2
-3,5
+0,3
+0,4
d
EOO
: basics-multiple
:
$* <<EOI >>EOO
-for i: 1 2 3 4
+for i: 1 2 3 4 5
{
switch $i, $i
{
@@ -49,8 +52,10 @@ for i: 1 2 3 4
print 2
}
case 3, 3
- case 5, 5
- print 3,5
+ case 0, 0
+ print 3,0
+ case 4|0, 0|4|0
+ print 4,0
default
print d
}
@@ -58,11 +63,11 @@ for i: 1 2 3 4
EOI
1
2
-3,5
+3,0
+4,0
d
EOO
-
: empty
:
$* <<EOI