aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/script
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2022-09-23 20:08:47 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2022-09-28 19:07:50 +0300
commit1c60c97b6b05cbee7e106fae6d8582382cbe4b7c (patch)
treeee389ea12fbe0dcecd39c32fd39fcb6c754af45a /libbuild2/script
parent744e8215261fbf81b9348d115d4916a9c88b52cc (diff)
Add support for 'for' loop first form (for x:...) in script
Diffstat (limited to 'libbuild2/script')
-rw-r--r--libbuild2/script/parser.cxx190
-rw-r--r--libbuild2/script/parser.hxx17
-rw-r--r--libbuild2/script/script.cxx4
-rw-r--r--libbuild2/script/script.hxx1
4 files changed, 195 insertions, 17 deletions
diff --git a/libbuild2/script/parser.cxx b/libbuild2/script/parser.cxx
index c371e5b..aa186ff 100644
--- a/libbuild2/script/parser.cxx
+++ b/libbuild2/script/parser.cxx
@@ -2035,6 +2035,37 @@ namespace build2
build2::parser::apply_value_attributes (var, lhs, move (rhs), kind);
}
+ void parser::
+ append_value (const variable* var,
+ value& v,
+ value&& rhs,
+ const location& l)
+ {
+ if (rhs) // Don't append/prepent NULL.
+ {
+ // Perform the type conversion (see
+ // build2::parser::apply_value_attributes() for the approach
+ // reasoning).
+ //
+ if (rhs.type != nullptr)
+ {
+ if (!v)
+ v.type = rhs.type;
+ else if (v.type == nullptr)
+ typify (v, *rhs.type, var);
+ else if (v.type != rhs.type)
+ fail (l) << "conflicting original value type " << v.type->name
+ << " and append value type " << rhs.type->name;
+
+ // Reduce this to the untyped value case.
+ //
+ untypify (rhs);
+ }
+
+ v.append (move (rhs).as<names> (), var);
+ }
+ }
+
line_type parser::
pre_parse_line_start (token& t, token_type& tt, lexer_mode stm)
{
@@ -2062,6 +2093,7 @@ namespace build2
else if (n == "elif!") r = line_type::cmd_elifn;
else if (n == "else") r = line_type::cmd_else;
else if (n == "while") r = line_type::cmd_while;
+ else if (n == "for") r = line_type::cmd_for;
else if (n == "end") r = line_type::cmd_end;
else
{
@@ -2090,7 +2122,7 @@ namespace build2
bool parser::
exec_lines (lines::const_iterator i, lines::const_iterator e,
- const function<exec_set_function>& exec_set,
+ const function<exec_assign_function>& exec_assign,
const function<exec_cmd_function>& exec_cmd,
const function<exec_cond_function>& exec_cond,
const iteration_index* ii, size_t& li,
@@ -2134,9 +2166,10 @@ namespace build2
{
line_type lt (j->type);
- if (lt == line_type::cmd_if ||
- lt == line_type::cmd_ifn ||
- lt == line_type::cmd_while)
+ if (lt == line_type::cmd_if ||
+ lt == line_type::cmd_ifn ||
+ lt == line_type::cmd_while ||
+ lt == line_type::cmd_for)
++n;
// If we are nested then we just wait until we get back
@@ -2163,6 +2196,9 @@ namespace build2
{
// Note that we don't count else and end as commands.
//
+ // @@ Note that for the for-loop's second and third forms
+ // will probably need to increment li.
+ //
switch (lt)
{
case line_type::cmd:
@@ -2197,7 +2233,25 @@ namespace build2
var = &var_pool->insert (t.value);
}
- exec_set (*var, t, tt, ll);
+ next (t, tt);
+ type kind (tt); // Assignment kind.
+
+ assert (kind == type::assign || kind == type::append);
+
+ // Parse the value with the potential attributes.
+ //
+ // Note that we don't really need to change the mode since we
+ // are replaying the tokens.
+ //
+ value val;
+ apply_value_attributes (var,
+ val,
+ parse_variable_line (t, tt),
+ kind);
+
+ assert (tt == type::newline);
+
+ exec_assign (*var, move (val), kind, ll);
replay_stop ();
break;
@@ -2258,7 +2312,7 @@ namespace build2
lines::const_iterator j (fcend (i, false, false));
if (!exec_lines (i + 1, j,
- exec_set, exec_cmd, exec_cond,
+ exec_assign, exec_cmd, exec_cond,
ii, li,
var_pool))
return false;
@@ -2281,6 +2335,10 @@ namespace build2
}
case line_type::cmd_while:
{
+ // The while-loop construct end. Set on the first iteration.
+ //
+ lines::const_iterator we (e);
+
size_t wli (li);
for (iteration_index wi {1, ii};; wi.index++)
@@ -2300,12 +2358,13 @@ namespace build2
//
if (exec)
{
- // Find construct end.
+ // Find the construct end, if it is not found yet.
//
- lines::const_iterator j (fcend (i, true, false));
+ if (we == e)
+ we = fcend (i, true, false);
- if (!exec_lines (i + 1, j,
- exec_set, exec_cmd, exec_cond,
+ if (!exec_lines (i + 1, we,
+ exec_assign, exec_cmd, exec_cond,
&wi, li,
var_pool))
return false;
@@ -2318,7 +2377,8 @@ namespace build2
}
else
{
- // Find construct end.
+ // Position to the construct end, always incrementing the
+ // line index (skip is true).
//
i = fcend (i, true, true);
break; // Bail out from the while-loop.
@@ -2327,9 +2387,117 @@ namespace build2
break;
}
+ case line_type::cmd_for:
+ {
+ // Parse the variable name with the potential attributes.
+ //
+ next_with_attributes (t, tt);
+ attributes_push (t, tt);
+
+ assert (tt == type::word && t.qtype == quote_type::unquoted);
+
+ string vn (move (t.value));
+
+ // Enter the variable into the pool if this is not done during
+ // the script parsing (see the var line type handling for
+ // details).
+ //
+ const variable* var (ln.var);
+
+ if (var == nullptr)
+ {
+ assert (var_pool != nullptr);
+
+ var = &var_pool->insert (move (vn));
+ }
+
+ apply_variable_attributes (*var);
+
+ next (t, tt); // Skip the colon.
+ assert (tt == type::colon);
+
+ // Parse the value with the potential attributes.
+ //
+ // Note that we don't really need to change the mode since we
+ // are replaying the tokens.
+ //
+ value val;
+ apply_value_attributes (nullptr /* variable */,
+ val,
+ parse_variable_line (t, tt),
+ type::assign);
+
+ replay_stop ();
+
+ // If the value is not NULL then iterate over its elements,
+ // assigning them to the for-loop variable, and parsing all the
+ // construct lines afterwards. Then position to the end line of
+ // the construct and continue parsing.
+
+ // The for-loop construct end. Set on the first iteration.
+ //
+ lines::const_iterator fe (e);
+
+ if (val)
+ {
+ // If this value is a vector, then save its element type so
+ // that we can typify each element below.
+ //
+ const value_type* etype (nullptr);
+
+ if (val.type != nullptr)
+ {
+ etype = val.type->element_type;
+ untypify (val);
+ }
+
+ size_t fli (li);
+ iteration_index fi {1, ii};
+
+ // @@ Handle pairs.
+ //
+ // Do we need to always lex the variable values (for-loop
+ // and var lines) pair-character aware?
+ //
+ // Can there be any harm if a value with pairs is
+ // substituted into the command line?
+ //
+ for (name& n: val.as<names> ())
+ {
+ li = fli;
+
+ value v (names {move (n)}); // Untyped.
+
+ if (etype != nullptr)
+ typify (v, *etype, var);
+
+ exec_assign (*var, move (v), type::assign, ll);
+
+ // Find the construct end, if it is not found yet.
+ //
+ if (fe == e)
+ fe = fcend (i, true, false);
+
+ if (!exec_lines (i + 1, fe,
+ exec_assign, exec_cmd, exec_cond,
+ &fi, li,
+ var_pool))
+ return false;
+
+ fi.index++;
+ }
+ }
+
+ // Position to construct end.
+ //
+ i = (fe != e ? fe : fcend (i, true, true));
+
+ break;
+ }
case line_type::cmd_end:
{
assert (false);
+ break;
}
}
}
diff --git a/libbuild2/script/parser.hxx b/libbuild2/script/parser.hxx
index 9edb6ca..0f276e1 100644
--- a/libbuild2/script/parser.hxx
+++ b/libbuild2/script/parser.hxx
@@ -42,6 +42,11 @@ namespace build2
using build2::parser::apply_value_attributes;
+ // The variable is optional and is only used for diagnostics.
+ //
+ void
+ append_value (const variable*, value& lhs, value&& rhs, const location&);
+
// Return true if a command line element needs to be re-lexed.
//
// Specifically, it needs to be re-lexed if it contains any of the
@@ -159,11 +164,13 @@ namespace build2
protected:
// Return false if the execution of the script should be terminated with
// the success status (e.g., as a result of encountering the exit
- // builtin). For unsuccessful termination the failed exception is thrown.
+ // builtin). For unsuccessful termination the failed exception is
+ // thrown.
//
- using exec_set_function = void (const variable&,
- token&, token_type&,
- const location&);
+ using exec_assign_function = void (const variable&,
+ value&&,
+ token_type kind,
+ const location&);
using exec_cmd_function = void (token&, token_type&,
const iteration_index*, size_t li,
@@ -181,7 +188,7 @@ namespace build2
//
bool
exec_lines (lines::const_iterator b, lines::const_iterator e,
- const function<exec_set_function>&,
+ const function<exec_assign_function>&,
const function<exec_cmd_function>&,
const function<exec_cond_function>&,
const iteration_index*, size_t& li,
diff --git a/libbuild2/script/script.cxx b/libbuild2/script/script.cxx
index d4096cf..33c4c30 100644
--- a/libbuild2/script/script.cxx
+++ b/libbuild2/script/script.cxx
@@ -28,6 +28,7 @@ namespace build2
case line_type::cmd_elifn: s = "'elif!'"; break;
case line_type::cmd_else: s = "'else'"; break;
case line_type::cmd_while: s = "'while'"; break;
+ case line_type::cmd_for: s = "'for'"; break;
case line_type::cmd_end: s = "'end'"; break;
}
@@ -225,7 +226,8 @@ namespace build2
case line_type::cmd_elif:
case line_type::cmd_elifn:
case line_type::cmd_else:
- case line_type::cmd_while: fc_ind += " "; break;
+ case line_type::cmd_while:
+ case line_type::cmd_for: fc_ind += " "; break;
default: break;
}
diff --git a/libbuild2/script/script.hxx b/libbuild2/script/script.hxx
index d6018f0..5eb4ee9 100644
--- a/libbuild2/script/script.hxx
+++ b/libbuild2/script/script.hxx
@@ -28,6 +28,7 @@ namespace build2
cmd_elifn,
cmd_else,
cmd_while,
+ cmd_for,
cmd_end
};