aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/build/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/build/script
parent744e8215261fbf81b9348d115d4916a9c88b52cc (diff)
Add support for 'for' loop first form (for x:...) in script
Diffstat (limited to 'libbuild2/build/script')
-rw-r--r--libbuild2/build/script/parser+for.test.testscript184
-rw-r--r--libbuild2/build/script/parser.cxx119
-rw-r--r--libbuild2/build/script/parser.hxx2
3 files changed, 271 insertions, 34 deletions
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
+ :
+ $* <<EOI 2>>EOE != 0
+ for
+ cmd
+ end
+ EOI
+ buildfile:11:4: error: expected variable name instead of <newline>
+ EOE
+
+ : untyped
+ :
+ $* <<EOI >>EOO
+ for x: a b
+ cmd $x
+ end
+ EOI
+ cmd a
+ cmd b
+ EOO
+
+ : null
+ :
+ $* <<EOI >:''
+ for x: [null]
+ cmd $x
+ end
+ EOI
+
+ : empty
+ :
+ $* <<EOI >:''
+ for x:
+ cmd $x
+ end
+ EOI
+
+ : expansion
+ :
+ $* <<EOI >>EOO
+ vs = a b
+ for x: $vs
+ cmd $x
+ end
+ EOI
+ cmd a
+ cmd b
+ EOO
+
+ : typed-value
+ :
+ $* <<EOI >>~%EOO%
+ for x: [dir_paths] a b
+ cmd $x
+ end
+ EOI
+ %cmd (a/|'a\\')%
+ %cmd (b/|'b\\')%
+ EOO
+
+ : typed-var
+ :
+ $* <<EOI >>~%EOO%
+ for [dir_path] x: a b
+ cmd $x
+ end
+ EOI
+ %cmd (a/|'a\\')%
+ %cmd (b/|'b\\')%
+ EOO
+
+ : type-mismatch
+ :
+ $* <<EOI 2>>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
+ :
+ $* <<EOI >>EOO
+ x = x
+
+ for x: a b
+ cmd $x
+ end
+
+ cmd $x
+ EOI
+ cmd a
+ cmd b
+ cmd b
+ EOO
+ }
+
+ : end
+ :
+ {
+ : without-end
+ :
+ $* <<EOI 2>>EOE != 0
+ for x: a b
+ cmd
+ EOI
+ buildfile:13:1: error: expected closing 'end'
+ EOE
+ }
+
+ : elif
+ :
+ {
+ : without-if
+ :
+ $* <<EOI 2>>EOE != 0
+ for x: a b
+ elif true
+ cmd
+ end
+ end
+ EOI
+ buildfile:12:3: error: 'elif' without preceding 'if'
+ EOE
+ }
+
+ : nested
+ :
+ {
+ $* -l -r <<EOI >>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
+ :
+ $* <<EOI 2>>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&);