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/build/script/parser+for.test.testscript | 184 ++++++++++++++++++++++ libbuild2/build/script/parser.cxx | 119 ++++++++++---- libbuild2/build/script/parser.hxx | 2 +- 3 files changed, 271 insertions(+), 34 deletions(-) create mode 100644 libbuild2/build/script/parser+for.test.testscript (limited to 'libbuild2/build') diff --git a/libbuild2/build/script/parser+for.test.testscript b/libbuild2/build/script/parser+for.test.testscript new file mode 100644 index 0000000..f4ebf3c --- /dev/null +++ b/libbuild2/build/script/parser+for.test.testscript @@ -0,0 +1,184 @@ +# file : libbuild2/build/script/parser+for.test.testscript +# license : MIT; see accompanying LICENSE file + +: form-1 +: +: for x: ... +: +{ + : for + : + { + : no-var + : + $* <>EOE != 0 + for + cmd + end + EOI + buildfile:11:4: error: expected variable name instead of + EOE + + : untyped + : + $* <>EOO + for x: a b + cmd $x + end + EOI + cmd a + cmd b + EOO + + : null + : + $* <:'' + for x: [null] + cmd $x + end + EOI + + : empty + : + $* <:'' + for x: + cmd $x + end + EOI + + : expansion + : + $* <>EOO + vs = a b + for x: $vs + cmd $x + end + EOI + cmd a + cmd b + EOO + + : typed-value + : + $* <>~%EOO% + for x: [dir_paths] a b + cmd $x + end + EOI + %cmd (a/|'a\\')% + %cmd (b/|'b\\')% + EOO + + : typed-var + : + $* <>~%EOO% + for [dir_path] x: a b + cmd $x + end + EOI + %cmd (a/|'a\\')% + %cmd (b/|'b\\')% + EOO + + : type-mismatch + : + $* <>EOE != 0 + for [string] x: [dir_paths] a b + cmd $x + end + EOI + error: type mismatch in variable x + info: value type is dir_path + info: variable type is string + EOE + + : defined-var + : + $* <>EOO + x = x + + for x: a b + cmd $x + end + + cmd $x + EOI + cmd a + cmd b + cmd b + EOO + } + + : end + : + { + : without-end + : + $* <>EOE != 0 + for x: a b + cmd + EOI + buildfile:13:1: error: expected closing 'end' + EOE + } + + : elif + : + { + : without-if + : + $* <>EOE != 0 + for x: a b + elif true + cmd + end + end + EOI + buildfile:12:3: error: 'elif' without preceding 'if' + EOE + } + + : nested + : + { + $* -l -r <>EOO + for x: a b + cmd1 $x # 1 + if ($x == "a") # 2 + cmd2 # 3 + for y: x y + cmd3 # 4 + end + else + cmd4 # 5 + end + cmd5 # 6 + end + cmd6 # 7 + EOI + cmd1 a # 1 i1 + ? true # 2 i1 + cmd2 # 3 i1 + cmd3 # 4 i1 i1 + cmd3 # 4 i1 i2 + cmd5 # 6 i1 + cmd1 b # 1 i2 + ? false # 2 i2 + cmd4 # 5 i2 + cmd5 # 6 i2 + cmd6 # 7 + EOO + } + + : contained + : + { + : eos + : + $* <>EOE != 0 + for x: + EOI + buildfile:12:1: error: expected closing 'end' + EOE + } +} diff --git a/libbuild2/build/script/parser.cxx b/libbuild2/build/script/parser.cxx index 1a7f4e1..cb0dbbb 100644 --- a/libbuild2/build/script/parser.cxx +++ b/libbuild2/build/script/parser.cxx @@ -205,9 +205,10 @@ namespace build2 // enter: next token is peeked at (type in tt) // leave: newline - assert (!fct || - *fct == line_type::cmd_if || - *fct == line_type::cmd_while); + assert (!fct || + *fct == line_type::cmd_if || + *fct == line_type::cmd_while || + *fct == line_type::cmd_for); // Determine the line type/start token. // @@ -245,6 +246,52 @@ namespace build2 break; } + case line_type::cmd_for: + { + // First take care of the variable name. + // + mode (lexer_mode::normal); + + next_with_attributes (t, tt); + attributes_push (t, tt); + + if (tt != type::word || t.qtype != quote_type::unquoted) + fail (t) << "expected variable name instead of " << t; + + const string& n (t.value); + + if (special_variable (n)) + fail (t) << "attempt to set '" << n << "' variable directly"; + + // We don't pre-enter variables. + // + ln.var = nullptr; + + next (t, tt); + + if (tt != type::colon) + { + // @@ TMP We will need to fallback to parsing the 'for x <...' + // form instead. + // + fail (t) << "expected ':' instead of " << t + << " after variable name"; + } + + expire_mode (); // Expire the normal lexer mode. + + // Parse the value similar to the var line type (see above). + // + mode (lexer_mode::variable_line); + parse_variable_line (t, tt); + + if (tt != type::newline) + fail (t) << "expected newline instead of " << t << " after for"; + + ++level_; + + break; + } case line_type::cmd_elif: case line_type::cmd_elifn: case line_type::cmd_else: @@ -300,17 +347,25 @@ namespace build2 *save_line_ = move (ln); } - if (lt == line_type::cmd_if || lt == line_type::cmd_ifn) + switch (lt) { - tt = peek (lexer_mode::first_token); + case line_type::cmd_if: + case line_type::cmd_ifn: + { + tt = peek (lexer_mode::first_token); - pre_parse_if_else (t, tt); - } - else if (lt == line_type::cmd_while) - { - tt = peek (lexer_mode::first_token); + pre_parse_if_else (t, tt); + break; + } + case line_type::cmd_while: + case line_type::cmd_for: + { + tt = peek (lexer_mode::first_token); - pre_parse_while (t, tt); + pre_parse_loop (t, tt, lt); + break; + } + default: break; } } @@ -341,8 +396,9 @@ namespace build2 break; } case line_type::cmd_while: + case line_type::cmd_for: { - fct = line_type::cmd_while; + fct = bt; break; } default: assert(false); @@ -405,18 +461,20 @@ namespace build2 } void parser:: - pre_parse_while (token& t, type& tt) + pre_parse_loop (token& t, type& tt, line_type lt) { // enter: peeked first token of next line (type in tt) // leave: newline + assert (lt == line_type::cmd_while || lt == line_type::cmd_for); + // Parse lines until we see closing 'end'. // for (;; tt = peek (lexer_mode::first_token)) { size_t i (script_->body.size ()); - pre_parse_block_line (t, tt, line_type::cmd_while); + pre_parse_block_line (t, tt, lt); if (script_->body[i].type == line_type::cmd_end) break; @@ -1247,25 +1305,19 @@ namespace build2 // Note that we rely on "small function object" optimization for the // exec_*() lambdas. // - auto exec_set = [this] (const variable& var, - token& t, build2::script::token_type& tt, - const location&) + auto exec_assign = [this] (const variable& var, + value&& val, + type kind, + const location& l) { - next (t, tt); - type kind (tt); // Assignment kind. - - mode (lexer_mode::variable_line); - value rhs (parse_variable_line (t, tt)); - - assert (tt == type::newline); - - // Assign. - // value& lhs (kind == type::assign ? environment_->assign (var) : environment_->append (var)); - apply_value_attributes (&var, lhs, move (rhs), kind); + if (kind == type::assign) + lhs = move (val); + else + append_value (&var, lhs, move (val), l); }; auto exec_cond = [this] (token& t, build2::script::token_type& tt, @@ -1281,11 +1333,12 @@ namespace build2 return runner_->run_cond (*environment_, ce, ii, li, ll); }; - build2::script::parser::exec_lines (begin, end, - exec_set, exec_cmd, exec_cond, - nullptr /* iteration_index */, - environment_->exec_line, - &environment_->var_pool); + build2::script::parser::exec_lines ( + begin, end, + exec_assign, exec_cmd, exec_cond, + nullptr /* iteration_index */, + environment_->exec_line, + &environment_->var_pool); } names parser:: diff --git a/libbuild2/build/script/parser.hxx b/libbuild2/build/script/parser.hxx index 987ed1f..a81583c 100644 --- a/libbuild2/build/script/parser.hxx +++ b/libbuild2/build/script/parser.hxx @@ -75,7 +75,7 @@ namespace build2 pre_parse_if_else (token&, token_type&); void - pre_parse_while (token&, token_type&); + pre_parse_loop (token&, token_type&, line_type); command_expr parse_command_line (token&, token_type&); -- cgit v1.1