aboutsummaryrefslogtreecommitdiffhomepage
path: root/parser.cpp
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2013-12-20 14:37:40 -0800
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2013-12-20 14:37:40 -0800
commit739e529416c3917d1e75d1a41850762a327e6ea9 (patch)
tree6d6bcf6fd73e1990e0ccdf8875c94c9fb34b05b9 /parser.cpp
parent384987cd5b9bcc2fd194c8343e00c1931c174a73 (diff)
Initial flailing around trying to adopt new parser for actual execution
Diffstat (limited to 'parser.cpp')
-rw-r--r--parser.cpp442
1 files changed, 438 insertions, 4 deletions
diff --git a/parser.cpp b/parser.cpp
index 0d47e5e1..cd43b41a 100644
--- a/parser.cpp
+++ b/parser.cpp
@@ -1633,6 +1633,110 @@ void parser_t::parse_job_argument_list(process_t *p,
}
*/
+#if 0
+process_t *parser_t::create_boolean_process(job_t *job, const parse_node_t &bool_statement, const parser_context_t &ctx)
+{
+ // Handle a boolean statement
+ bool skip_job = false;
+ assert(bool_statement.type == symbol_boolean_statement);
+ switch (specific_statement.production_idx)
+ {
+ // These magic numbers correspond to productions for boolean_statement
+ case 0:
+ // AND. Skip if the last job failed.
+ skip_job = (proc_get_last_status() != 0);
+ break;
+
+ case 1:
+ // OR. Skip if the last job succeeded.
+ skip_job = (proc_get_last_status() == 0);
+ break;
+
+ case 2:
+ // NOT. Negate it.
+ job_set_flag(job, JOB_NEGATE, !job_get_flag(job, JOB_NEGATE));
+ break;
+
+ default:
+ {
+ fprintf(stderr, "Unexpected production in boolean statement\n");
+ PARSER_DIE();
+ break;
+ }
+ }
+
+ process_t *result = NULL;
+ if (! skip_job)
+ {
+ const parse_node_t &subject = *ctx.tree.get_child(bool_statement, 1, symbol_statement);
+ result = this->create_job_process(job, subject, ctx);
+ }
+ return result;
+}
+
+/* Returns a process_t allocated with new. It's the caller's responsibility to delete it (!) */
+process_t *parser_t::create_job_process(job_t *job, const parse_node_t &statement_node, const parser_context_t &ctx)
+{
+ assert(statement_node.type == symbol_statement);
+ assert(statement_node.child_count == 1);
+
+ // We may skip this job entirely, e.g. with an 'and' statement
+ bool skip_job = false;
+
+ // Get the "specific statement" which is boolean / block / if / switch / decorated
+ const parse_node_t &specific_statement = *ctx.tree.get_child(statement_node, 0);
+
+ process_t *result = NULL;
+
+ switch (specific_statement.type)
+ {
+ case symbol_boolean_statement:
+ {
+ result = this->create_boolean_process(job, specific_statement, ctx);
+ break;
+ }
+
+ case symbol_block_statement:
+ {
+ const parse_node_t &header = *ctx.tree.get_child(specific_statement, 0, symbol_block_header);
+ const parse_node_t &specific_header = *ctx.tree.get_child(header, 0);
+ switch (specific_header.type)
+ {
+ case symbol_for_header:
+ result = this->create_for_process(job, specific_header, specific_statement, ctx);
+ break;
+
+ case symbol_while_header:
+ result = this->create_while_process(job, specific_header, specific_statement, ctx);
+ break;
+
+ case symbol_function_header:
+ // No process is associated with creating a function
+ // TODO: create the darn function!
+ result = NULL;
+ break;
+
+ case symbol_begin_header:
+
+ break;
+
+ default:
+ fprintf(stderr, "Unexpected header type\n");
+ PARSER_DIE();
+ break;
+ }
+ }
+ }
+
+ // expand_one command
+ // handle booleans (and, not, or)
+ // set INTERNAL_EXEC
+ // implicit CD
+
+ return proc;
+}
+#endif
+
/**
Fully parse a single job. Does not call exec on it, but any command substitutions in the job will be executed.
@@ -1642,9 +1746,7 @@ void parser_t::parse_job_argument_list(process_t *p,
f
\return 1 on success, 0 on error
*/
-int parser_t::parse_job(process_t *p,
- job_t *j,
- tokenizer_t *tok)
+int parser_t::parse_job(process_t *p, job_t *j, tokenizer_t *tok)
{
std::vector<completion_t> args; // The list that will become the argv array for the program
int use_function = 1; // May functions be considered when checking what action this command represents
@@ -2336,6 +2438,206 @@ static bool job_should_skip_elseif(const job_t *job, const block_t *current_bloc
}
/**
+ Evaluates a job from a node tree.
+*/
+
+#if 0
+void parser_t::eval_job(const parse_node_t &job_node, const parser_context_t &ctx)
+{
+ assert(job_node.type == symbol_job);
+ this->job_start_pos = (int)job_node.source_start;
+
+ // Get terminal modes
+ struct termios tmodes = {};
+ if (get_is_interactive())
+ {
+ if (tcgetattr(STDIN_FILENO, &tmodes))
+ {
+ // need real error handling here
+ wperror(L"tcgetattr");
+ return;
+ }
+ }
+
+ /* Track whether we had an error */
+ bool process_errored = false;
+
+ /* Profiling support */
+ long long t1 = 0, t2 = 0, t3 = 0;
+ const bool do_profile = profile;
+ profile_item_t *profile_item = NULL;
+ if (do_profile)
+ {
+ profile_item = new profile_item_t();
+ profile_item->skipped = 1;
+ profile_items.push_back(profile_item);
+ t1 = get_time();
+ }
+
+ job_t *j = this->job_create();
+ job_set_flag(j, JOB_FOREGROUND, 1);
+ job_set_flag(j, JOB_TERMINAL, job_get_flag(j, JOB_CONTROL));
+ job_set_flag(j, JOB_TERMINAL, job_get_flag(j, JOB_CONTROL) \
+ && (!is_subshell && !is_event));
+ job_set_flag(j, JOB_SKIP_NOTIFICATION, is_subshell \
+ || is_block \
+ || is_event \
+ || (!get_is_interactive()));
+
+ current_block->job = j;
+
+ /* Tell the job what its command is */
+ j->set_command(job_node.get_source(ctx.src));
+
+ /* Construct process_t structures for every statement in the job */
+ const parse_node_t *statement_node = ctx.tree.get_child(job_node, 0, symbol_statement);
+ assert(statement_node != NULL);
+
+ /* Create the process (may fail!) */
+ j->first_process = this->create_job_process(j, *statement_node, ctx);
+ if (j->first_process == NULL)
+ process_errored = true;
+
+ /* Construct process_ts for job continuations (pipelines), by walking the list until we hit the terminal (empty) job continuationf */
+ const parse_node_t *job_cont = ctx.tree.get_child(job_node, 1, symbol_job_continuation);
+ process_t *last_process = j->first_process;
+ while (! process_errored && job_cont != NULL && job_cont->child_count > 0)
+ {
+ assert(job_cont->type == symbol_job_continuation);
+
+ /* Get the statement node and make a process from it */
+ const parse_node_t *statement_node = ctx.tree.get_child(*job_cont, 1, symbol_statement);
+ assert(statement_node != NULL);
+
+ /* Store the new process (and maybe with an error) */
+ last_process->next = this->create_job_process(j, *statement_node, ctx);
+ if (last_process->next == NULL)
+ process_errored = true;
+
+ /* Link the process and get the next continuation */
+ last_process = last_process->next;
+ job_cont = ctx.tree.get_child(*job_cont, 2, symbol_job_continuation);
+ }
+
+ bool skip = false;
+ if (this->parse_job(j->first_process, j, job_node, ctx) && j->first_process->get_argv())
+ {
+ if (do_profile)
+ {
+ t2 = get_time();
+ profile_item->cmd = j->command();
+ profile_item->skipped=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);
+
+ if (!skip)
+ {
+ int was_builtin = 0;
+ if (j->first_process->type==INTERNAL_BUILTIN && !j->first_process->next)
+ was_builtin = 1;
+ scoped_push<int> tokenizer_pos_push(&current_tokenizer_pos, job_begin_pos);
+ exec_job(*this, j);
+
+ /* Only external commands require a new fishd barrier */
+ if (!was_builtin)
+ set_proc_had_barrier(false);
+ }
+ else
+ {
+ this->skipped_exec(j);
+ }
+
+ if (do_profile)
+ {
+ t3 = get_time();
+ profile_item->level=eval_level;
+ profile_item->parse = (int)(t2-t1);
+ profile_item->exec=(int)(t3-t2);
+ }
+
+ if (current_block->type() == WHILE)
+ {
+ while_block_t *wb = static_cast<while_block_t *>(current_block);
+ switch (wb->status)
+ {
+ case WHILE_TEST_FIRST:
+ {
+ // 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;
+ }
+ break;
+ }
+ }
+
+ if (current_block->type() == IF)
+ {
+ 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->any_branch_taken = if_result;
+
+ /* Don't execute if the expression failed */
+ current_block->skip = ! if_result;
+ 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;
+ }
+ }
+
+ }
+ else
+ {
+ /*
+ This job could not be properly parsed. We free it
+ instead, and set the status to 1. This should be
+ rare, since most errors should be detected by the
+ ahead of time validator.
+ */
+ job_free(j);
+
+ proc_set_last_status(1);
+ }
+ current_block->job = 0;
+ break;
+}
+#endif
+
+/**
Evaluates a job from the specified tokenizer. First calls
parse_job to parse the job and then calls exec to execute it.
@@ -2575,12 +2877,144 @@ void parser_t::eval_job(tokenizer_t *tok)
}
+#if 0
+int parser_t::eval2(const wcstring &cmd_str, const io_chain_t &io, enum block_type_t block_type)
+{
+ parser_context_t mut_ctx;
+ mut_ctx.src = cmd_str;
+
+ /* Parse the tree */
+ if (! parse_t::parse(cmd_str, parse_flag_none, &mut_ctx.tree, NULL))
+ {
+ return 1;
+ }
+
+ /* Make a const version for safety's sake */
+ const parser_context_t &ctx = mut_ctx;
+
+ CHECK_BLOCK(1);
+
+ /* Record the current chain so we can put it back later */
+ scoped_push<io_chain_t> block_io_push(&block_io, io);
+ scoped_push<wcstring_list_t> forbidden_function_push(&forbidden_function);
+ const size_t forbid_count = forbidden_function.size();
+ const block_t *start_current_block = current_block;
+
+ /* Do some stuff I haven't figured out yet */
+ job_reap(0);
+
+ /* Only certain blocks are allowed */
+ if ((block_type != TOP) &&
+ (block_type != SUBST))
+ {
+ debug(1,
+ INVALID_SCOPE_ERR_MSG,
+ parser_t::get_block_desc(block_type));
+ bugreport();
+ return 1;
+ }
+
+ eval_level++;
+
+ this->push_block(new scope_block_t(block_type));
+
+ error_code = 0;
+
+ event_fire(NULL);
+
+ /* Execute the top job list */
+ assert(! ctx.tree.empty());
+ const parse_node_t *job_list = &ctx.tree.at(0);
+ assert(job_list->type == symbol_job_list);
+ while (job_list != NULL)
+ {
+ // These correspond to the three productions of job_list
+ // Try pulling out a job
+ const parse_node_t *job = NULL;
+ switch (job_list->production_idx)
+ {
+ case 0: // empty
+ job_list = NULL;
+ break;
+
+ case 1: //job, job_list
+ job = ctx.tree.get_child(*job_list, 0, symbol_job);
+ job_list = ctx.tree.get_child(*job_list, 1, symbol_job_list);
+ break;
+
+ case 2: //blank line, job_list
+ job = NULL;
+ job_list = ctx.tree.get_child(*job_list, 1, symbol_job_list);
+ break;
+
+ default: //if we get here, it means more productions have been added to job_list, which is bad
+ PARSER_DIE();
+ }
+
+ if (job != NULL)
+ {
+ this->eval_job(*job, ctx);
+ }
+ }
+
+ parser_t::pop_block();
+
+ while (start_current_block != current_block)
+ {
+ if (current_block == 0)
+ {
+ debug(0,
+ _(L"End of block mismatch. Program terminating."));
+ bugreport();
+ FATAL_EXIT();
+ break;
+ }
+
+ if ((!error_code) && (!exit_status()) && (!proc_get_last_status()))
+ {
+
+ //debug( 2, L"Status %d\n", proc_get_last_status() );
+
+ debug(1,
+ L"%ls", parser_t::get_block_desc(current_block->type()));
+ debug(1,
+ BLOCK_END_ERR_MSG);
+ fwprintf(stderr, L"%ls", parser_t::current_line());
+
+ const wcstring h = builtin_help_get(*this, L"end");
+ if (h.size())
+ fwprintf(stderr, L"%ls", h.c_str());
+ break;
+
+ }
+ parser_t::pop_block();
+ }
+
+ this->print_errors_stderr();
+
+ while (forbidden_function.size() > forbid_count)
+ parser_t::allow_function();
+
+ /*
+ Restore previous eval state
+ */
+ eval_level--;
+
+ int code=error_code;
+ error_code=0;
+
+ job_reap(0);
+
+ return code;
+}
+#endif
+
int parser_t::eval(const wcstring &cmd_str, const io_chain_t &io, enum block_type_t block_type)
{
const wchar_t * const cmd = cmd_str.c_str();
size_t forbid_count;
int code;
- block_t *start_current_block = current_block;
+ const block_t *start_current_block = current_block;
/* Record the current chain so we can put it back later */
scoped_push<io_chain_t> block_io_push(&block_io, io);