Commit e1013777 authored by Samuel Newbold's avatar Samuel Newbold

break&continue with outer variants, sigint throws

The weight of a commit is not measured by the number of lines changed.
parent d18ef87f
......@@ -61,6 +61,12 @@ call stack .source .try_catch_recursive(body)
.function_all_flags .binary_not_found name path stack ... {
echo $name : binary not found in $path
echo call stack: $stack$}
.function_all_flags .break stack ... {
echo .break thrown outside of control flow body
echo call stack: $stack$}
.function_all_flags .continue stack ... {
echo .continue thrown outside of control flow body
echo call stack: $stack$}
.function_all_flags .dash_dash_argument args stack ... {
echo optional -- parameter cannot take arguments \( $args \) with call stack: $stack$}
.function_all_flags .dash_star_argument args stack ... {
......@@ -166,6 +172,9 @@ call stack .source .try_catch_recursive(body)
.function_all_flags .not_a_number -- nan stack ... {
echo $nan is not a number
echo call stack: $stack$}
.function_all_flags .not_catching_exception stack ... {
echo .replace_exception called outside of an exception handler
echo call stack: $stack$}
.function_all_flags .not_executable file stack ... {
echo $file is not executable
echo call stack: $stack$}
......@@ -190,30 +199,30 @@ call stack .source .try_catch_recursive(body)
.echo $nl
if_only .var_exists stack {echo call stack: $stack$}
.return -1}
.function_all_flags .sigcont [args ...] {
echo received SIGCONT: $args$; .return -1}
.function_all_flags .sigchld [args ...] {
echo received SIGCHLD: $args$; .return -1}
.function_all_flags .sighup [args ...] {
echo received SIGHUP: $args$; .return -1}
.function_all_flags .sigint [args ...] {
echo received SIGINT: $args$; .return -1}
.function_all_flags .sigquit [args ...] {
echo received SIGQUIT: $args$; .return -1}
.function_all_flags .sigpipe [args ...] {
echo received SIGPIPE: $args$; .return -1}
.function_all_flags .sigterm [args ...] {
echo received SIGTERM: $args$; .return -1}
.function_all_flags .sigtstp [args ...] {
echo received SIGTSTP: $args$; .return -1}
.function_all_flags .siginfo [args ...] {
echo received SIGINFO: $args$; .return -1}
.function_all_flags .sigusr1 [args ...] {
echo received SIGUSR1: $args$; .return -1}
.function_all_flags .sigusr2 [args ...] {
echo received SIGUSR2: $args$; .return -1}
.function_all_flags .sigunknown [args ...] {
echo received unknown unix signal: $args$; .return -1}
.function_all_flags .sigcont {
echo received SIGCONT; .return -1}
.function_all_flags .sigchld {
echo received SIGCHLD; .return -1}
.function_all_flags .sighup {
echo received SIGHUP; .return -1}
.function_all_flags .sigint {
.throw echo $nl received SIGINT}
.function_all_flags .sigquit {
echo received SIGQUIT; .return -1}
.function_all_flags .sigpipe {
echo received SIGPIPE; .return -1}
.function_all_flags .sigterm {
.throw echo $nl received SIGTERM}
.function_all_flags .sigtstp {
echo received SIGTSTP; .return -1}
.function_all_flags .siginfo {
echo received SIGINFO; .return -1}
.function_all_flags .sigusr1 {
echo received SIGUSR1; .return -1}
.function_all_flags .sigusr2 {
echo received SIGUSR2; .return -1}
.function_all_flags .sigunknown {
echo received unknown unix signal; .return -1}
.function_all_flags .shutdown -- args ... {
.nop $args
.combine $nl (now terminating normally) $nl}
......@@ -254,6 +263,11 @@ call stack .source .try_catch_recursive(body)
.function_all_flags af -- [argv ...] {.argfunction}
.function_all_flags c -- text ... {.combine $text$}
.function_all_flags e -- text ... {.echo $text}
.function_all_flags do_while argv ... {
.try_catch_recursive .break {
.nop $argv
.try_catch_recursive .continue {.argfunction}
.while $argv$ {.argfunction}}}
.function_all_flags echo -- text ... {.echo $text; .combine $nl}
.function_all_flags echo-comments cmd ... {
.stepwise $cmd$ {if_only .test_in $1 .nop # #! ## {echo &&&*}}}
......@@ -274,6 +288,20 @@ fn ntimes n {
.while test_var_greater n 0 {
.mapped_argfunction {.argfunction}
.var_subtract n 1}}
.function_all_flags outer_break stack ... {
.replace_exception .break $stack$}
.function_all_flags outer_continue stack ... {
.replace_exception .continue $stack$}
.function_all_flags scope_for var vals ... {
.for $vals$ {
.scope $1 $var {.argfunction}}}
.function_all_flags outer_for var vals ... {
.for $vals$ {
.scope $1 $var {
.try_catch_recursive outer_break outer_continue {.argfunction}}}}
.function_all_flags outer_while argv ... {
.while $argv$ {
.try_catch_recursive outer_break outer_continue {.argfunction}}}
.function_all_flags setf var value {
.if .var_exists $var {.set $var $value}
.else {.global $var $value}}
......@@ -289,7 +317,13 @@ fn whence command {
.try_catch_recursive .function_not_found {
.combine ${.whence_function $command {.argfunction}} $nl}}
fn whichp binary {.which_path $binary $PATH}
.function_all_flags while_and_one_more argv ... {
.try_catch_recursive .break {
.while $argv$ {.argfunction}
.try_catch_recursive .continue {.argfunction}}}
.function_all_flags var_exists -- var {.var_exists $var}
.function_all_flags var_in var set ... {.test_in $$var $set$}
.function_all_flags var_less var N {.test_less $$var $N}
.function_all_flags var_val -- [var ...] {
.if .var_exists var {.for $var$ {.combine $1 \( $$1 \) \ }}
.else {.nop}}
......
......@@ -234,6 +234,10 @@ void Error_list::add_error(const Argm& error){
push_back(error);
Base_executable::add_error();}
void Error_list::replace_error(const Argm& error){
push_back(error);
Base_executable::replace_error();}
Old_argv::Old_argv(const Argm::Argv& src) : argc_v(src.size()) {
focus = new char*[src.size()+1];
copy_to_cstr(src.begin(), src.end(), focus);}
......
......@@ -37,6 +37,8 @@ class Argm {
Bad_if_nest,
Binary_does_not_exist,
Binary_not_found,
Break,
Continue,
Dash_dash_argument,
Dash_star_argument,
Directory_not_found,
......@@ -74,6 +76,7 @@ class Argm {
Not_a_directory,
Not_a_function,
Not_a_number,
Not_catching_exception,
Not_executable,
Not_soon_enough,
Raw_command,
......@@ -158,6 +161,7 @@ class Argm {
struct Error_list : public std::list<Argm> {
void add_error(const Argm& error);
void replace_error(const Argm& error);
void reset(void); };
struct Exception : public Argm {
......
......@@ -215,7 +215,9 @@ int b_for(const Argm& argm, Error_list& exceptions) {
if (argm.argfunction()) {
body[1] = i;
ret = (*argm.argfunction())(body, exceptions);
if (Named_executable::unwind_stack()) return -1;}
(void) Base_executable::remove_exceptions(".continue", exceptions);
if (Base_executable::remove_exceptions(".break", exceptions)) return 0;
else if (Named_executable::unwind_stack()) return -1;}
else ret = 0;}
return ret;}
......@@ -234,7 +236,10 @@ int b_for_each_line(const Argm& argm, Error_list& exceptions) {
if (argm.input.fail() && !line.size()) break;
tokenize(line, std::back_inserter(body),
std::bind2nd(std::equal_to<char>(), ' '));
ret = (*argm.argfunction())(body, exceptions);}
ret = (*argm.argfunction())(body, exceptions);
(void) Base_executable::remove_exceptions(".continue", exceptions);
if (Base_executable::remove_exceptions(".break", exceptions)) return 0;
else if (Named_executable::unwind_stack()) return -1;}
return ret;}
int b_fork(const Argm& argm, Error_list& exceptions) {
......@@ -559,6 +564,18 @@ int b_getppid(const Argm& argm, Error_list& exceptions) {
argm.output <<(unsigned) getppid();
return 0;}
// add the given exception as a replacement for the current one (if nothing
// afterwards fails)
int b_replace_exception(const Argm& argm, Error_list& exceptions) {
if (argm.argc() < 2) throw Exception(Argm::Bad_argc, argm.argc()-1, 1, 0);
if (!Base_executable::in_exception_handler())
throw Exception(Argm::Not_catching_exception);
Argm new_exception(argm.subrange(1), argm.argfunction(),
Variable_map::global_map,
argm.input, argm.output.child_stream(), argm.error);
exceptions.replace_error(new_exception);
return 0;}
// return the value given by the argument
int b_return(const Argm& argm, Error_list& exceptions) {
if (argm.argc() != 2) throw Exception(Argm::Bad_argc, argm.argc()-1, 1, 0);
......@@ -715,9 +732,9 @@ int b_stepwise(const Argm& argm, Error_list& exceptions) {
Argm body(".mapped_argfunction", body_i.argv(), nullptr,
body_i.parent_map(), body_i.input, body_i.output, body_i.error);
ret = (*argm.argfunction())(body, exceptions);
if (Named_executable::unwind_stack()) {
ret = -1;
break;}}
(void) Base_executable::remove_exceptions(".continue", exceptions);
if (Base_executable::remove_exceptions(".break", exceptions)) break;
else if (Named_executable::unwind_stack()) break;}
f->unused_var_check(&locals, exceptions);
return ret;} // last return value from argfunction
......@@ -1124,5 +1141,7 @@ int b_while(const Argm& argm, Error_list& exceptions) {
argm.error);
mapped_argm.push_back(".mapped_argfunction");
ret = (*argm.argfunction())(mapped_argm, exceptions);
if (Named_executable::unwind_stack()) return -1;}
(void) Base_executable::remove_exceptions(".continue", exceptions);
if (Base_executable::remove_exceptions(".break", exceptions)) return 0;
else if (Named_executable::unwind_stack()) return -1;}
return ret;}
......@@ -43,6 +43,7 @@ int b_local(const Argm& argm, Error_list& exceptions);
int b_ls(const Argm& argm, Error_list& exceptions);
int b_nop(const Argm& argm, Error_list& exceptions);
int b_return(const Argm& argm, Error_list& exceptions);
int b_replace_exception(const Argm& argm, Error_list& exceptions);
int b_rm_executable(const Argm& argm, Error_list& exceptions);
int b_scope(const Argm& argm, Error_list& exceptions);
int b_selection_set(const Argm& argm, Error_list& exceptions);
......
......@@ -81,6 +81,8 @@ void Base_executable::add_error(void) {
unwind_stack_v = true;
++current_exception_count;}
void Base_executable::replace_error(void) {++current_exception_count;}
void Base_executable::reset(void) {
unwind_stack_v = false;
current_exception_count = 0;}
......@@ -97,14 +99,15 @@ void Base_executable::unix_signal_handler(int sig) {
main does not need to do this handling, because anything that it calls
directly will have an initial nesting of 0.*/
void Base_executable::exception_handler(Error_list& exceptions) {
in_exception_handler = true;
in_exception_handler_v = true;
unwind_stack_v = false;
std::set<std::string> failed_handlers;
for(;exceptions.size(); exceptions.pop_front(), --current_exception_count){
Argm& focus(exceptions.front());
if (failed_handlers.find(focus[0]) == failed_handlers.end()) {
unsigned previous_count = current_exception_count;
executable_map.run(focus, exceptions);
if (unwind_stack()) {
if (unwind_stack() || current_exception_count > previous_count) {
failed_handlers.insert(focus[0]);
exceptions.insert(++exceptions.begin(), exceptions.back());
exceptions.pop_back();
......@@ -115,12 +118,12 @@ void Base_executable::exception_handler(Error_list& exceptions) {
focus.input, focus.output, focus.error),
exceptions);}
Variable_map::dollar_question = -1;
dropped_catches = 0;
collect_excess_thrown = execution_handler_excess_thrown = false;
in_exception_handler = false;}
in_exception_handler_v = false;}
// code to call exception handlers when requested within a function
void Base_executable::catch_blocks(const Argm& argm, Error_list& exceptions) {
unsigned dropped_catches = 0;
for (auto focus = exceptions.begin(); focus != exceptions.end();)
if (find(argm.begin() + 1, argm.end(), (*focus)[0]) != argm.end()) {
if (dropped_catches >= max_extra) {
......@@ -130,10 +133,10 @@ void Base_executable::catch_blocks(const Argm& argm, Error_list& exceptions) {
execution_handler_excess_thrown = true;
return;}
unsigned previous_count = current_exception_count;
in_exception_handler = true;
in_exception_handler_v = true;
unwind_stack_v = false;
executable_map.run(*focus, exceptions);
in_exception_handler = false;
in_exception_handler_v = false;
dropped_catches += current_exception_count - previous_count;
if (!unwind_stack()) {
focus = exceptions.erase(focus);
......@@ -143,7 +146,7 @@ void Base_executable::catch_blocks(const Argm& argm, Error_list& exceptions) {
if (current_exception_count) unwind_stack_v = true;
else unwind_stack_v = false;
Variable_map::dollar_question = -1;
in_exception_handler = false;}
in_exception_handler_v = false;}
bool Base_executable::remove_exceptions(const std::string &name,
Error_list& exceptions) {
......@@ -160,10 +163,10 @@ bool Base_executable::remove_exceptions(const std::string &name,
// run one exception handler restoring unwind_stack afterwards
void Base_executable::catch_one(Argm& argm, Error_list& exceptions) {
in_exception_handler = true;
in_exception_handler_v = true;
unwind_stack_v = false;
executable_map.run(argm, exceptions);
in_exception_handler = false;
in_exception_handler_v = false;
unwind_stack_v = true;}
Binary::Binary(const std::string& impl) : implementation(impl) {}
......
......@@ -5,11 +5,10 @@ class Base_executable {
protected:
static int global_nesting;
static bool in_exception_handler;
static bool in_exception_handler_v;
static bool unwind_stack_v;
static bool collect_excess_thrown;
static unsigned current_exception_count;
static unsigned dropped_catches;
static bool execution_handler_excess_thrown;
public:
......@@ -31,6 +30,7 @@ class Base_executable {
total_execution_time_v.tv_sec = 0;
total_execution_time_v.tv_usec = 0;};
virtual ~Base_executable(void) {}
static bool in_exception_handler(void) {return in_exception_handler_v;}
bool is_running(void) const {return !!executable_nesting;};
static void exception_handler(Error_list& exceptions);
static void catch_blocks(const Argm& argm, Error_list& exceptions);
......@@ -43,6 +43,7 @@ class Base_executable {
struct timeval last_execution_time(void) const {
return last_execution_time_v;};
int last_ret(void) const {return last_return;};
static void replace_error(void);
static void reset(void);
struct timeval total_execution_time(void) const {
return total_execution_time_v;};
......
......@@ -39,9 +39,8 @@ bool Base_executable::execution_handler_excess_thrown = false;
struct timezone Clock::no_timezone_v = {0, 0};
int Base_executable::global_nesting(0);
bool Base_executable::unwind_stack_v(false);
bool Base_executable::in_exception_handler(false);
bool Base_executable::in_exception_handler_v(false);
unsigned Base_executable::current_exception_count(0);
unsigned Base_executable::dropped_catches(0);
std::string Argm::exception_names[Argm::Exception_count] = {
"no exception",
".ambiguous_prototype_dash_dash",
......@@ -53,6 +52,8 @@ std::string Argm::exception_names[Argm::Exception_count] = {
".bad_if_nest",
".binary_does_not_exist",
".binary_not_found",
".break",
".continue",
".dash_dash_argument",
".dash_star_argument",
".directory_not_found",
......@@ -90,6 +91,7 @@ std::string Argm::exception_names[Argm::Exception_count] = {
".not_a_directory",
".not_a_function",
".not_a_number",
".not_catching_exception",
".not_executable",
".not_soon_enough",
".raw_command",
......
......@@ -105,6 +105,7 @@ void internal_init(Error_list& exceptions) {
executable_map.set(new Builtin(".local", b_local));
executable_map.set(new Builtin(".ls", b_ls));
executable_map.set(new Builtin(".nop", b_nop));
executable_map.set(new Builtin(".replace_exception", b_replace_exception));
executable_map.set(new Builtin(".return", b_return));
executable_map.set(new Builtin(".rm_executable", b_rm_executable));
executable_map.set(new Builtin(".scope", b_scope));
......
......@@ -37,6 +37,12 @@
.function_all_flags .binary_not_found name path stack ... {
echo $name : binary not found in $path
echo call stack: $stack$}
.function_all_flags .break stack ... {
echo .break thrown outside of control flow body
echo call stack: $stack$}
.function_all_flags .continue stack ... {
echo .continue thrown outside of control flow body
echo call stack: $stack$}
.function_all_flags .dash_dash_argument args stack ... {
echo optional -- parameter cannot take arguments \( $args \) with call stack: $stack$}
.function_all_flags .dash_star_argument args stack ... {
......@@ -142,6 +148,9 @@
.function_all_flags .not_a_number -- nan stack ... {
echo $nan is not a number
echo call stack: $stack$}
.function_all_flags .not_catching_exception stack ... {
echo .replace_exception called outside of an exception handler
echo call stack: $stack$}
.function_all_flags .not_executable file stack ... {
echo $file is not executable
echo call stack: $stack$}
......@@ -166,30 +175,30 @@
.echo $nl
if_only .var_exists stack {echo call stack: $stack$}
.return -1}
.function_all_flags .sigcont [args ...] {
echo received SIGCONT: $args$; .return -1}
.function_all_flags .sigchld [args ...] {
echo received SIGCHLD: $args$; .return -1}
.function_all_flags .sighup [args ...] {
echo received SIGHUP: $args$; .return -1}
.function_all_flags .sigint [args ...] {
echo received SIGINT: $args$; .return -1}
.function_all_flags .sigquit [args ...] {
echo received SIGQUIT: $args$; .return -1}
.function_all_flags .sigpipe [args ...] {
echo received SIGPIPE: $args$; .return -1}
.function_all_flags .sigterm [args ...] {
echo received SIGTERM: $args$; .return -1}
.function_all_flags .sigtstp [args ...] {
echo received SIGTSTP: $args$; .return -1}
.function_all_flags .siginfo [args ...] {
echo received SIGINFO: $args$; .return -1}
.function_all_flags .sigusr1 [args ...] {
echo received SIGUSR1: $args$; .return -1}
.function_all_flags .sigusr2 [args ...] {
echo received SIGUSR2: $args$; .return -1}
.function_all_flags .sigunknown [args ...] {
echo received unknown unix signal: $args$; .return -1}
.function_all_flags .sigcont {
echo received SIGCONT; .return -1}
.function_all_flags .sigchld {
echo received SIGCHLD; .return -1}
.function_all_flags .sighup {
echo received SIGHUP; .return -1}
.function_all_flags .sigint {
.throw echo $nl received SIGINT}
.function_all_flags .sigquit {
echo received SIGQUIT; .return -1}
.function_all_flags .sigpipe {
echo received SIGPIPE; .return -1}
.function_all_flags .sigterm {
.throw echo $nl received SIGTERM}
.function_all_flags .sigtstp {
echo received SIGTSTP; .return -1}
.function_all_flags .siginfo {
echo received SIGINFO; .return -1}
.function_all_flags .sigusr1 {
echo received SIGUSR1; .return -1}
.function_all_flags .sigusr2 {
echo received SIGUSR2; .return -1}
.function_all_flags .sigunknown {
echo received unknown unix signal; .return -1}
.function_all_flags .shutdown -- args ... {
.nop $args
.combine $nl (now terminating normally) $nl}
......@@ -230,6 +239,11 @@
.function_all_flags af -- [argv ...] {.argfunction}
.function_all_flags c -- text ... {.combine $text$}
.function_all_flags e -- text ... {.echo $text}
.function_all_flags do_while argv ... {
.try_catch_recursive .break {
.nop $argv
.try_catch_recursive .continue {.argfunction}
.while $argv$ {.argfunction}}}
.function_all_flags echo -- text ... {.echo $text; .combine $nl}
.function_all_flags echo-comments cmd ... {
.stepwise $cmd$ {if_only .test_in $1 .nop # #! ## {echo &&&*}}}
......@@ -250,6 +264,20 @@ fn ntimes n {
.while test_var_greater n 0 {
.mapped_argfunction {.argfunction}
.var_subtract n 1}}
.function_all_flags outer_break stack ... {
.replace_exception .break $stack$}
.function_all_flags outer_continue stack ... {
.replace_exception .continue $stack$}
.function_all_flags scope_for var vals ... {
.for $vals$ {
.scope $1 $var {.argfunction}}}
.function_all_flags outer_for var vals ... {
.for $vals$ {
.scope $1 $var {
.try_catch_recursive outer_break outer_continue {.argfunction}}}}
.function_all_flags outer_while argv ... {
.while $argv$ {
.try_catch_recursive outer_break outer_continue {.argfunction}}}
.function_all_flags setf var value {
.if .var_exists $var {.set $var $value}
.else {.global $var $value}}
......@@ -265,7 +293,13 @@ fn whence command {
.try_catch_recursive .function_not_found {
.combine ${.whence_function $command {.argfunction}} $nl}}
fn whichp binary {.which_path $binary $PATH}
.function_all_flags while_and_one_more argv ... {
.try_catch_recursive .break {
.while $argv$ {.argfunction}
.try_catch_recursive .continue {.argfunction}}}
.function_all_flags var_exists -- var {.var_exists $var}
.function_all_flags var_in var set ... {.test_in $$var $set$}
.function_all_flags var_less var N {.test_less $$var $N}
.function_all_flags var_val -- [var ...] {
.if .var_exists var {.for $var$ {.combine $1 \( $$1 \) \ }}
.else {.nop}}
......
......@@ -54,7 +54,7 @@ bool Simple_pattern::match(const std::string& s) const {
if (j == std::string::npos) return false;
j += cur_term.length();}
auto lstart = s.size() - last.size();
if (only_text && j != lstart ||
if ((only_text && j != lstart) ||
j + last.size() > s.size() ||
s.substr(lstart) != last) return false; // C++20 ends_with
else return true;}
......@@ -73,18 +73,18 @@ Entry_pattern::Entry_pattern(const std::string& src) {
for (auto j: temp) options.push_back(Simple_pattern(j));
only_text = options.size() == 1 && options[0].is_only_text();}
// you still can't encapsulate outer_for in a function....
class OuterContinue : public std::exception {};
bool Entry_pattern::match_with_ignore(const Entry_pattern& ignore_pattern,
const std::string& s) const {
for (auto j: options) if (j.match(s))
if (ignore_pattern.specific_match(s, j)) continue;
else return true;
return false;}
// My kingdom for a better continue!
bool Entry_pattern::specific_match(const std::string& s,
const Simple_pattern& competition) const {
for (auto j: options) if (j.match(s) && j.isnt_contained(competition))
return true;
for (auto competition: options) if (competition.match(s))
try {
for (auto j: ignore_pattern.options)
if (j.match(s) && j.isnt_contained(competition)) throw OuterContinue();
return true;}
catch (OuterContinue) {}
else;
return false;}
std::string Entry_pattern::str(void) const {
......@@ -103,8 +103,7 @@ void str_to_entry_pattern_list(const std::string& src,
if (src.empty());
else if (*i == "") {
res.clear();
res.push_back(*i);
;}
res.push_back(*i);}
else if (*i == "..")
if (res.size() > 1) {res.pop_back(); res.pop_back();}
else {res.clear(); ++updir;}
......
This diff is collapsed.
This diff is collapsed.
......@@ -313,6 +313,15 @@ se {se >outfile {e line 1 $nl; e line 2 longer $nl; .echo $nl; e ending}}
.for_each_line x {}
.for_each_line <outfile
.for_each_line <outfile <another A{}
.for_each_line <outfile {
echo current line $*
if_only .test_greater $2 1 {
.collect_errors_except .nop {
.throw .continue
.throw .break
.throw .continue}}
.throw .continue
echo not printed}
.for_each_line <outfile {e line of $# \( $* \) $nl}
/bin/rm outfile
......@@ -421,6 +430,34 @@ x {x {x {x {
.for {e no arguments $1}
.for no_argfunction
.for 1 {e one argument $1}
.for 1 2 3 4 {
echo current arg $1
if_only .test_greater $1 2 {
.collect_errors_except .nop {
.throw .continue
.throw .break
.throw .continue}}
.throw .continue
echo not printed}
fn fIJ outer inner {
.local sum 0
.nop $sum
$outer I 1 2 3 4 5 6 {
if_only_not .test_string_equal $I 1 {.combine $nl}
.combine $I :
$inner J 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 {
if_only .test_less 1 $J {.echo ,}
.echo $J
.set sum ${+ $I $J}
.if .test_less 5 $sum$ {.throw outer_break}
.else_if .test_less 3 $sum$ {.throw outer_continue}
.else {}}}
.echo $nl}
fIJ scope_for scope_for
fIJ outer_for scope_for
fIJ scope_for outer_for
fIJ outer_for outer_for
.else {}
.for 1 2 3 4 {e four arguments $1 $nl}
# .function_all_flags .rm_executable
......@@ -822,19 +859,32 @@ se {.collect_errors_only .function_not_found {
.set_max_collectible_exceptions 7
.get_max_collectible_exceptions
# .getpid .getppid
# .getpid .getppid .sighup
.getpid excess
.getpid {excess argfunc}
.getppid excess
.getppid {excess argfunc}
.fork se {/bin/kill ${.getppid}}
.fork se {/bin/kill ${.getpid}}
se {.fork se {/bin/kill ${.getppid}
echo after the signal in subshell}
echo after the signal in parent}
se {.fork se {/bin/kill ${.getpid}
echo after the signal in subshell}
echo after the signal in parent}
se {.fork se {
/bin/kill -HUP ${.getppid}
echo after the signal in subshell}
echo after the signal in parent}
se {.fork se {
/bin/kill -HUP ${.getpid}
echo after the signal in subshell}
echo after the signal in parent}
.while .return 0 {
echo before signals
/bin/kill -PIPE ${.getpid}
echo after sigpipe
/bin/kill ${.getpid}
echo should not continue beyond SIGTERM}
.while .return 0 {
echo before signals
/bin/kill -USR1 ${.getpid}
echo after sigusr1
/bin/kill -INT ${.getpid}
echo should not continue beyond SIGINT}
.return 0
# .global .local .unset .var_exists
.global
......@@ -1181,6 +1231,13 @@ se {.is_default_error}
.nop {optional argfunc}
.nop 1 2 3 4 5
# .replace_exception
.replace_exception
.replace_exception echo not in exception handler
.try_catch_recursive .replace_exception {
.throw .replace_exception echo now in exception handler}
.return 0
# .return
.return
.return 1 1
......@@ -1350,6 +1407,15 @@ e_after {sa echo hi {.try_catch_recursive ${.internal_functions}$ {&&&args$}}}
a $args$ two; a $args$ three}
wrapper 1 2
.stepwise wrapper 1 2 {d $*}
.stepwise wrapper 1 2 {
echo current line: $*
if_only .test_string_equal $4 two {
.collect_errors_except .nop {
.throw .continue
.throw .break
.throw .continue}}
.throw .continue
echo not printed}
.stepwise wrapper 1 2 {e $* $nl}
# .test_file_exists
......@@ -1570,23 +1636,76 @@ whence .mapped_argfunction {>dummy_file}
.which_path rwsh /usr/bin:.
# .while
.function_all_flags tf N {.test_string_unequal $A $N}
.set A 0
.while {e ARGS}
.while tf 4
.while tf 4 {e printed; .set A 4}
.while tf 4 {e skipped}
.while var_less A 4
.scope 0 A {.while var_less A 4 {echo printed; .throw .break; echo skipped}}
.scope 0 A {.while var_less A 4 {e printed; .set A 4}}
.scope 4 A {.while var_less A 4 {e skipped}}
.while .return 0 {.throw .echo exception within while}
.while silent_throw within condition {e body skipped}
.while .throw .false because {e body skipped}
.set A 0
.while tf 4 {e in .while argfunction $A $nl; .var_add A 1}
.set A 0
.while tf 4 {.if .test_number_equal 0 $A {.set A 1}
.else {.function_all_flags tf N {.throw .false $N}}
e in overwriting argfunction $nl}
.scope 0 A {.while .throw .break {echo condition cannot break}}
.scope 0 A {.while .throw .continue {echo condition cannot continue}}
.scope 0 A {.while var_less A 4 {echo body can break; .throw .break}}
.scope 0 A {.while var_less A 4 {e in .while argfunction $A $nl; .var_add A 1}}
.scope 0 A {.while var_in A 1 2 {echo A is $A; .var_add A 1}}
.scope 0 A {do_while var_in A 1 2 {echo A is $A; .var_add A 1}}
.scope 1 A {.while var_in A 1 2 {echo A is $A; .var_add A 1}}
.scope 1 A {while_and_one_more var_in A 1 2 {echo A is $A; .var_add A 1}}
fn throw_the_kitchen_sink cmd ... {
$cmd$ {
echo early in argfunction $A
.var_add A 1
if_only .test_less $A 3 {.throw .continue}
.collect_errors_except .nop {
.throw .continue
.throw .break
.throw .continue}
echo late in .while argfunction $A}
echo after $cmd$0
.throw .break
echo do not echo after a .break thrown outside control flow}
.scope 0 A {throw_the_kitchen_sink .while var_less A 4}
.scope 0 A {throw_the_kitchen_sink do_while var_less A 4}
.scope 3 A {throw_the_kitchen_sink do_while var_less A 6}
.scope 0 A {throw_the_kitchen_sink while_and_one_more var_less A 4}
.function_all_flags tf N {.test_less $A $N}
.scope 0 A {.while tf 4 {
.if .test_number_equal 0 $A {.set A 1}
.else {.function_all_flags tf N {.throw .false $N}}
echo in overwriting argfunction}}
.set_max_nesting 30
fn IJK outer middle inner {
.scope 0 I {
.local sum 0
.nop $sum
$outer var_less I 5 {
.var_add I 1
.scope 0 J {
$middle var_less J 2 {
.var_add J 1
if_only_not .test_string_equal ${e $I $J} (1 1) {.combine $nl}
.combine $I . $J :
.scope 0 K {
$inner var_less K 30 {
.var_add K 1
if_only .test_less 1 $K {.echo ,}
.echo $K
.set sum ${+ $I + $J $K}
.if .test_less 5 $sum$ {.throw outer_break}
.else_if .test_less 4 $sum$ {.throw outer_continue}
.else {}}}}}}}
.echo $nl}
IJK .while .while .while
IJK outer_while .while .while
IJK .while outer_while .while
IJK .while .while outer_while
IJK outer_while outer_while outer_while
.else {}
.set_max_nesting 20
# .var_add
.set A 4
.var_add
.var_add A 1 2
</