From 1c60c97b6b05cbee7e106fae6d8582382cbe4b7c Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 23 Sep 2022 20:08:47 +0300 Subject: Add support for 'for' loop first form (for x:...) in script --- libbuild2/script/parser.cxx | 190 +++++++++++++++++++++++++++++++++++++++++--- libbuild2/script/parser.hxx | 17 ++-- libbuild2/script/script.cxx | 4 +- libbuild2/script/script.hxx | 1 + 4 files changed, 195 insertions(+), 17 deletions(-) (limited to 'libbuild2/script') 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 (), 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, + const function& exec_assign, const function& exec_cmd, const 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 ()) + { + 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&, + const function&, const function&, const 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 }; -- cgit v1.1