aboutsummaryrefslogtreecommitdiffhomepage
path: root/parser.cpp
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2012-09-01 01:46:14 -0700
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2012-09-01 01:46:14 -0700
commitcc1395797e78a26583461333199181b4f8ec0b13 (patch)
tree9f191547887932abe9e6a34d27349085453ed622 /parser.cpp
parent122791646ec7dc421a679cb39cce7a8cbc0a7594 (diff)
First stab at elseif implementation
Diffstat (limited to 'parser.cpp')
-rw-r--r--parser.cpp160
1 files changed, 117 insertions, 43 deletions
diff --git a/parser.cpp b/parser.cpp
index df0677de..b8818a9a 100644
--- a/parser.cpp
+++ b/parser.cpp
@@ -280,7 +280,7 @@ struct block_lookup_entry
/**
The block type id. The legal values are defined in parser.h.
*/
- int type;
+ block_type_t type;
/**
The name of the builtin that creates this type of block, if any.
@@ -356,7 +356,7 @@ static const struct block_lookup_entry block_lookup[]=
}
,
{
- 0, 0, 0
+ (block_type_t)0, 0, 0
}
};
@@ -1402,14 +1402,17 @@ void parser_t::parse_job_argument_list( process_t *p,
*/
skip=1;
- /*
- But if this is in fact a case statement, then it should be evaluated
- */
-
- if( (current_block->type() == SWITCH) && args.at(0).completion == L"case" && p->type == INTERNAL_BUILTIN )
+ /* But if this is in fact a case statement or an elseif statement, then it should be evaluated */
+ block_type_t type = current_block->type();
+ if( type == SWITCH && args.at(0).completion == L"case" && p->type == INTERNAL_BUILTIN )
{
skip=0;
}
+ else if (type == IF && args.at(0).completion == L"elseif")
+ {
+ //skip = 0;
+ printf("SKIP elseif???\n");
+ }
}
if( !skip )
@@ -1701,17 +1704,18 @@ int parser_t::parse_job( process_t *p,
int use_builtin = 1; // May builtins be considered when checking what action this command represents
int use_command = 1; // May commands be considered when checking what action this command represents
int is_new_block=0; // Does this command create a new block?
+ bool unskip = false; // Maybe we are an elseif inside an if block; if so we may want to evaluate this even if the if block is currently set to skip
block_t *prev_block = current_block;
- int prev_tokenizer_pos = current_tokenizer_pos;
+ int prev_tokenizer_pos = current_tokenizer_pos;
current_tokenizer_pos = tok_get_pos( tok );
- while( args.size() == 0 )
+ while( args.empty() )
{
wcstring nxt;
bool has_nxt = false;
- int consumed = 0; // Set to one if the command requires a second command, like e.g. while does
+ bool consumed = false; // Set to one if the command requires a second command, like e.g. while does
int mark; // Use to save the position of the beginning of the token
switch( tok_last_type( tok ))
@@ -1780,7 +1784,7 @@ int parser_t::parse_job( process_t *p,
}
mark = tok_get_pos( tok );
-
+
if( contains( nxt,
L"command",
L"builtin",
@@ -1815,7 +1819,7 @@ int parser_t::parse_job( process_t *p,
tok_next( tok );
}
- consumed=1;
+ consumed = true;
if( nxt == L"command" || nxt == L"builtin" )
{
@@ -1879,7 +1883,7 @@ int parser_t::parse_job( process_t *p,
this->push_block( wb );
}
- consumed=1;
+ consumed = true;
is_new_block=1;
}
@@ -1892,8 +1896,23 @@ int parser_t::parse_job( process_t *p,
ib->tok_pos = mark;
is_new_block=1;
- consumed=1;
+ consumed = true;
}
+ else if( nxt == L"elseif" )
+ {
+ /* Piggyback on the if block, but we need the elseif command */
+ job_set_flag( j, JOB_ELSEIF, 1 );
+ tok_next( tok );
+ consumed = true;
+
+ /* Determine if we need to unskip */
+ if (current_block->type() == IF)
+ {
+ const if_block_t *ib = static_cast<const if_block_t *>(current_block);
+ /* We want to execute this ELSEIF if the IF expression was evaluated, it failed, and so has every other ELSEIF (if any) */
+ unskip = (ib->if_expr_evaluated && ! ib->any_branch_taken);
+ }
+ }
/*
Test if we need another command
@@ -1978,7 +1997,7 @@ int parser_t::parse_job( process_t *p,
If we are not executing the current block, allow
non-existent commands.
*/
- if( current_block->skip )
+ if( current_block->skip && ! unskip)
{
p->actual_cmd.clear();
}
@@ -2243,6 +2262,7 @@ void parser_t::skipped_exec( job_t * j )
{
process_t *p;
+ /* Handle other skipped guys */
for( p = j->first_process; p; p=p->next )
{
if( p->type == INTERNAL_BUILTIN )
@@ -2265,13 +2285,17 @@ void parser_t::skipped_exec( job_t * j )
}
else if( wcscmp( p->argv0(), L"else" )==0)
{
- if( (current_block->type() == IF ) &&
- (static_cast<const if_block_t*>(current_block)->if_expr_evaluated))
- {
- exec( *this, j );
- return;
- }
- }
+ if (current_block->type() == IF)
+ {
+ /* Evaluate this ELSE if the IF expression failed, and so has every ELSEIF (if any) expression thus far */
+ const if_block_t *ib = static_cast<const if_block_t*>(current_block);
+ if (ib->if_expr_evaluated && ! ib->any_branch_taken)
+ {
+ exec( *this, j );
+ return;
+ }
+ }
+ }
else if( wcscmp( p->argv0(), L"case" )==0)
{
if(current_block->type() == SWITCH)
@@ -2285,6 +2309,27 @@ void parser_t::skipped_exec( job_t * j )
job_free( j );
}
+/* Return whether we should skip the current block, if it is an elseif. */
+static bool job_should_skip_elseif(const job_t *job, const block_t *current_block)
+{
+ if (current_block->type() != IF)
+ {
+ /* Not an IF block, so just honor the skip property */
+ return current_block->skip;
+ }
+ else
+ {
+ /* We are an IF block */
+ const if_block_t *ib = static_cast<const if_block_t *>(current_block);
+
+ /* Execute this ELSEIF if the IF expression has been evaluated, it evaluated to false, and all ELSEIFs so far have evaluated to false. */
+ bool execute_elseif = (ib->if_expr_evaluated && ! ib->any_branch_taken);
+
+ /* Invert the sense */
+ return ! execute_elseif;
+ }
+}
+
/**
Evaluates a job from the specified tokenizer. First calls
parse_job to parse the job and then calls exec to execute it.
@@ -2367,7 +2412,24 @@ void parser_t::eval_job( tokenizer *tok )
profile_item->skipped=current_block->skip;
}
- skip = skip || current_block->skip;
+ /* If we're an ELSEIF, then we may want to unskip, if we're skipping because of an IF */
+ if (job_get_flag(j, JOB_ELSEIF))
+ {
+ bool skip_elseif = job_should_skip_elseif(j, current_block);
+
+ /* Record that we're entering an elseif */
+ if (! skip_elseif)
+ {
+ /* We must be an IF block here */
+ assert(current_block->type() == IF);
+ static_cast<if_block_t *>(current_block)->is_elseif_entry = true;
+ }
+
+ /* Record that in the block too. This is similar to what builtin_else does. */
+ current_block->skip = skip_elseif;
+ }
+
+ skip = skip || current_block->skip;
skip = skip || job_get_flag( j, JOB_WILDCARD_ERROR );
skip = skip || job_get_flag( j, JOB_SKIP );
@@ -2405,7 +2467,7 @@ void parser_t::eval_job( tokenizer *tok )
{
case WHILE_TEST_FIRST:
{
- // PCA I added the 'current_block->skip ||' part because we couldn't reliably
+ // PCA I added the 'wb->skip ||' part because we couldn't reliably
// control-C out of loops like this: while test 1 -eq 1; end
wb->skip = wb->skip || proc_get_last_status()!= 0;
wb->status = WHILE_TESTED;
@@ -2416,15 +2478,28 @@ void parser_t::eval_job( tokenizer *tok )
if( current_block->type() == IF )
{
- if_block_t *ib = static_cast<if_block_t *>(current_block);
- if( (! ib->if_expr_evaluated) &&
- (!current_block->skip) )
- {
+ if_block_t *ib = static_cast<if_block_t *>(current_block);
+
+ if (ib->skip)
+ {
+ /* Nothing */
+ }
+ else if (! ib->if_expr_evaluated)
+ {
+ /* Execute the IF */
bool if_result = (proc_get_last_status() == 0);
- ib->if_expr_result = if_result; // store expression result
- current_block->skip = ! if_result; //don't execute if the expresion result was not zero
+ ib->any_branch_taken = if_result;
+ current_block->skip = ! if_result; //don't execute if the expression failed
ib->if_expr_evaluated = true;
- }
+ }
+ else if (ib->is_elseif_entry && ! ib->any_branch_taken)
+ {
+ /* Maybe mark an ELSEIF branch as taken */
+ bool elseif_taken = (proc_get_last_status() == 0);
+ ib->any_branch_taken = elseif_taken;
+ current_block->skip = ! elseif_taken;
+ ib->is_elseif_entry = false;
+ }
}
}
@@ -2625,7 +2700,7 @@ int parser_t::eval( const wcstring &cmdStr, const io_chain_t &io, enum block_typ
/**
\return the block type created by the specified builtin, or -1 on error.
*/
-int parser_get_block_type( const wcstring &cmd )
+block_type_t parser_get_block_type( const wcstring &cmd )
{
int i;
@@ -2636,7 +2711,7 @@ int parser_get_block_type( const wcstring &cmd )
return block_lookup[i].type;
}
}
- return -1;
+ return (block_type_t)-1;
}
/**
@@ -2862,17 +2937,16 @@ int parser_t::test( const wchar_t * buff,
Set to one if a command name has been given for the currently
parsed process specification
*/
- int had_cmd=0;
- int count = 0;
+ int had_cmd=0;
int err=0;
int unfinished = 0;
tokenizer *previous_tokenizer=current_tokenizer;
int previous_pos=current_tokenizer_pos;
- // PCA These statics are terrifying - I have no idea whether and why these variables need to be static
- static int block_pos[BLOCK_MAX_COUNT];
- static int block_type[BLOCK_MAX_COUNT];
+ int block_pos[BLOCK_MAX_COUNT] = {};
+ block_type_t block_type[BLOCK_MAX_COUNT] = {};
+ int count = 0;
int res = 0;
/*
@@ -2933,7 +3007,6 @@ int parser_t::test( const wchar_t * buff,
{
if( !had_cmd )
{
- int is_else;
int mark = tok_get_pos( &tok );
had_cmd = 1;
arg_count=0;
@@ -2989,17 +3062,16 @@ int parser_t::test( const wchar_t * buff,
count--;
tok_set_pos( &tok, mark );
}
-
- is_else = (command == L"else");
/*
Store the block level. This needs to be done
_after_ checking for end commands, but _before_
shecking for block opening commands.
*/
+ bool is_else_or_elseif = (command == L"else" || command == L"elseif");
if( block_level )
{
- block_level[tok_get_pos( &tok )] = count + (is_else?-1:0);
+ block_level[tok_get_pos( &tok )] = count + (is_else_or_elseif?-1:0);
}
/*
@@ -3644,8 +3716,10 @@ block_t::~block_t()
if_block_t::if_block_t() :
if_expr_evaluated(false),
- if_expr_result(false),
+ any_branch_taken(false),
+ is_elseif_entry(false),
else_evaluated(false),
+ if_state(if_state_if),
block_t(IF)
{
}