diff --git a/parse_execution.cpp b/parse_execution.cpp index 726f29207..15c7de5f2 100644 --- a/parse_execution.cpp +++ b/parse_execution.cpp @@ -50,7 +50,7 @@ static wcstring profiling_cmd_name_for_redirectable_block(const parse_node_t &no return result; } -parse_execution_context_t::parse_execution_context_t(const parse_node_tree_t &t, const wcstring &s, parser_t *p, int initial_eval_level) : tree(t), src(s), parser(p), eval_level(initial_eval_level) +parse_execution_context_t::parse_execution_context_t(const parse_node_tree_t &t, const wcstring &s, parser_t *p, int initial_eval_level) : tree(t), src(s), parser(p), eval_level(initial_eval_level), executing_node_idx(NODE_OFFSET_INVALID), cached_lineno_offset(0), cached_lineno_count(0) { } @@ -1302,6 +1302,9 @@ parse_execution_result_t parse_execution_context_t::run_1_job(const parse_node_t /* Increment the eval_level for the duration of this command */ scoped_push saved_eval_level(&eval_level, eval_level + 1); + + /* Save the node index */ + scoped_push saved_node_offset(&executing_node_idx, this->get_offset(job_node)); /* Profiling support */ long long start_time = 0, parse_time = 0, exec_time = 0; @@ -1525,3 +1528,58 @@ parse_execution_result_t parse_execution_context_t::eval_node_at_offset(node_off return status; } + +int parse_execution_context_t::get_current_line_number() +{ + /* If we're not executing anything, return -1 */ + if (this->executing_node_idx == NODE_OFFSET_INVALID) + { + return -1; + } + + /* If for some reason we're executing a node without source, return -1 */ + const parse_node_t &node = tree.at(this->executing_node_idx); + if (! node.has_source()) + { + return -1; + } + + /* Count the number of newlines, leveraging our cache */ + const size_t offset = tree.at(this->executing_node_idx).source_start; + assert(offset <= src.size()); + + /* Easy hack to handle 0 */ + if (offset == 0) + { + return 1; + } + + /* We want to return (one plus) the number of newlines at offsets less than the given offset. cached_lineno_count is the number of newlines at indexes less than cached_lineno_offset. */ + const wchar_t *str = src.c_str(); + if (offset > cached_lineno_offset) + { + size_t i; + for (i = cached_lineno_offset; str[i] != L'\0' && i < offset; i++) + { + /* Add one for every newline we find in the range [cached_lineno_offset, offset) */ + if (str[i] == L'\n') + { + cached_lineno_count++; + } + } + cached_lineno_offset = i; //note: i, not offset, in case offset is beyond the length of the string + } + else if (offset < cached_lineno_offset) + { + /* Subtract one for every newline we find in the range [offset, cached_lineno_offset) */ + for (size_t i = offset; i < cached_lineno_offset; i++) + { + if (str[i] == L'\n') + { + cached_lineno_count--; + } + } + cached_lineno_offset = offset; + } + return cached_lineno_count + 1; +} diff --git a/parse_execution.h b/parse_execution.h index 8ab5c5d6e..91c32c99b 100644 --- a/parse_execution.h +++ b/parse_execution.h @@ -39,6 +39,13 @@ private: //parse_error_list_t errors; int eval_level; + + /* The currently executing node index, used to indicate the line number */ + node_offset_t executing_node_idx; + + /* Cached line number information */ + size_t cached_lineno_offset; + int cached_lineno_count; /* No copying allowed */ parse_execution_context_t(const parse_execution_context_t&); @@ -105,6 +112,9 @@ public: /* Returns the current eval level */ int current_eval_level() const { return eval_level; } + + /* Returns the current line number. Not const since it touches cached_lineno_offset */ + int get_current_line_number(); /* Start executing at the given node offset. Returns 0 if there was no error, 1 if there was an error */ parse_execution_result_t eval_node_at_offset(node_offset_t offset, const block_t *associated_block, const io_chain_t &io); diff --git a/parser.cpp b/parser.cpp index 0a4f004ae..3febef097 100644 --- a/parser.cpp +++ b/parser.cpp @@ -928,6 +928,16 @@ const wchar_t *parser_t::is_function() const int parser_t::get_lineno() const { + if (parser_use_ast()) + { + int lineno = -1; + if (! execution_contexts.empty()) + { + lineno = execution_contexts.back()->get_current_line_number(); + } + return lineno; + } + int lineno; if (! current_tokenizer || ! tok_string(current_tokenizer)) diff --git a/tests/test9.in b/tests/test9.in index 32a537f78..a3a48151a 100644 --- a/tests/test9.in +++ b/tests/test9.in @@ -79,4 +79,9 @@ for $var1 in 1 2 3 end echo +# Test status -n +eval 'status -n +status -n +status -n' + false diff --git a/tests/test9.out b/tests/test9.out index e44b26044..d42618630 100644 --- a/tests/test9.out +++ b/tests/test9.out @@ -10,3 +10,6 @@ Foop Doop Testing for loop 123 +1 +2 +3