aboutsummaryrefslogtreecommitdiff
path: root/build/parser.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-03-06 09:15:40 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-03-06 09:15:40 +0200
commit897a0e4fdf9ca90ee8d236a38e138a8ae6bc3627 (patch)
treed4c00de32d028823906d342fcd984faee8d977ff /build/parser.cxx
parent9ef25ab2f9da89ab48ecce3fe1b8cbb0bc5f1e09 (diff)
Add support for lexing and parsing name pairs
We will need it for the buildspec and also if/when we support map variable types.
Diffstat (limited to 'build/parser.cxx')
-rw-r--r--build/parser.cxx134
1 files changed, 118 insertions, 16 deletions
diff --git a/build/parser.cxx b/build/parser.cxx
index 8f4f81d..56a61c5 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -191,6 +191,12 @@ namespace build
if (tt == type::colon)
{
+ // While '{}:' means empty name, '{$x}:' where x is empty list
+ // means empty list.
+ //
+ if (ns.empty ())
+ fail (t) << "target expected before :";
+
next (t, tt);
if (tt == type::newline)
@@ -625,8 +631,18 @@ namespace build
}
void parser::
- names (token& t, type& tt, names_type& ns, const path* dp, const string* tp)
+ names (token& t,
+ type& tt,
+ names_type& ns,
+ size_t pair,
+ const path* dp,
+ const string* tp)
{
+ // If pair is not 0, then it is an index + 1 of the first half of
+ // the pair for which we are parsing the second halves, e.g.,
+ // a={b c d{e f} {}}.
+ //
+
// Buffer that is used to collect the complete name in case of an
// unseparated variable expansion, e.g., 'foo$bar$(baz)fox'. The
// idea is to concatenate all the individual parts in this buffer
@@ -634,6 +650,12 @@ namespace build
//
string concat;
+ // Number of names in the last group. This is used to detect when
+ // we need to add an empty first pair element (e.g., {=y}) or when
+ // we have a for now unsupported multi-name LHS (e.g., {x y}=z).
+ //
+ size_t count (0);
+
for (bool first (true);; first = false)
{
// If the accumulating buffer is not empty, then we have two options:
@@ -714,7 +736,14 @@ namespace build
}
next (t, tt);
- names (t, tt, ns, dp1, tp1);
+ count = ns.size ();
+ names (t, tt,
+ ns,
+ (pair != 0
+ ? pair
+ : (ns.empty () || !ns.back ().pair ? 0 : ns.size ())),
+ dp1, tp1);
+ count = ns.size () - count;
if (tt != type::rcbrace)
fail (t) << "expected } instead of " << t;
@@ -723,6 +752,12 @@ namespace build
continue;
}
+ // If we are a second half of a pair, add another first half
+ // unless this is the first instance.
+ //
+ if (pair != 0 && pair != ns.size ())
+ ns.push_back (ns[pair - 1]);
+
// If it ends with a directory separator, then it is a directory.
// Note that at this stage we don't treat '.' and '..' as special
// (unless they are specified with a directory separator) because
@@ -753,20 +788,7 @@ namespace build
(dp != nullptr ? *dp : path ()),
move (name));
- continue;
- }
-
- // Untyped name group without a directory prefix, e.g., '{foo bar}'.
- //
- if (tt == type::lcbrace)
- {
- next (t, tt);
- names (t, tt, ns, dp, tp);
-
- if (tt != type::rcbrace)
- fail (t) << "expected } instead of " << t;
-
- tt = peek ();
+ count = 1;
continue;
}
@@ -887,20 +909,91 @@ namespace build
<< "expansion";
}
+ // If we are a second half of a pair.
+ //
+ if (pair != 0)
+ {
+ // Check that there are no nested pairs.
+ //
+ if (n.pair)
+ fail (t) << "nested pair in variable expansion";
+
+ // And add another first half unless this is the first instance.
+ //
+ if (pair != ns.size ())
+ ns.push_back (ns[pair - 1]);
+ }
+
ns.emplace_back ((tp1 != nullptr ? *tp1 : string ()),
(dp1 != nullptr ? *dp1 : path ()),
n.value);
}
+
+ count = lv.data.size ();
}
continue;
}
+ // Untyped name group without a directory prefix, e.g., '{foo bar}'.
+ //
+ if (tt == type::lcbrace)
+ {
+ next (t, tt);
+ count = ns.size ();
+ names (t, tt,
+ ns,
+ (pair != 0
+ ? pair
+ : (ns.empty () || !ns.back ().pair ? 0 : ns.size ())),
+ dp, tp);
+ count = ns.size () - count;
+
+ if (tt != type::rcbrace)
+ fail (t) << "expected } instead of " << t;
+
+ tt = peek ();
+ continue;
+ }
+
+ // A pair separator (only in the pair mode).
+ //
+ if (tt == type::equal && lexer_->mode () == lexer_mode::pairs)
+ {
+ if (pair != 0)
+ fail (t) << "nested pair on the right hand side of a pair";
+
+ if (count > 1)
+ fail (t) << "multiple names on the left hand side of a pair";
+
+ if (count == 0)
+ {
+ // Empty LHS, (e.g., {=y}), create an empty name.
+ //
+ ns.emplace_back ((tp != nullptr ? *tp : string ()),
+ (dp != nullptr ? *dp : path ()),
+ "");
+ count = 1;
+ }
+
+ ns.back ().pair = true;
+ tt = peek ();
+ continue;
+ }
+
if (!first)
break;
+ // Our caller expected this to be a name.
+ //
if (tt == type::rcbrace) // Empty name, e.g., dir{}.
{
+ // If we are a second half of a pair, add another first half
+ // unless this is the first instance.
+ //
+ if (pair != 0 && pair != ns.size ())
+ ns.push_back (ns[pair - 1]);
+
ns.emplace_back ((tp != nullptr ? *tp : string ()),
(dp != nullptr ? *dp : path ()),
"");
@@ -909,6 +1002,15 @@ namespace build
else
fail (t) << "expected name instead of " << t;
}
+
+ // Handle the empty RHS in a pair, (e.g., {y=}).
+ //
+ if (!ns.empty () && ns.back ().pair)
+ {
+ ns.emplace_back ((tp != nullptr ? *tp : string ()),
+ (dp != nullptr ? *dp : path ()),
+ "");
+ }
}
void parser::