Thread a reference to a line counter into parse execution

Simplify Parser by removing the reference to the execution context
This commit is contained in:
Peter Ammon 2024-06-29 14:56:03 -07:00
parent b00ab4673b
commit c212ac95e9
No known key found for this signature in database
3 changed files with 37 additions and 59 deletions

View File

@ -83,23 +83,14 @@ pub struct ExecutionContext {
cancel_signal: RefCell<Option<Signal>>,
// Helper to count lines.
line_counter: RefCell<LineCounter<ast::JobPipeline>>,
// This is shared with the Parser so that the Parser can access the current line.
line_counter: Rc<RefCell<LineCounter<ast::JobPipeline>>>,
/// The block IO chain.
/// For example, in `begin; foo ; end < file.txt` this would have the 'file.txt' IO.
block_io: RefCell<IoChain>,
}
impl ExecutionContext {
pub fn swap(left: &Self, right: Self) -> Self {
left.pstree.swap(&right.pstree);
left.cancel_signal.swap(&right.cancel_signal);
left.line_counter.swap(&right.line_counter);
left.block_io.swap(&right.block_io);
right
}
}
// Report an error, setting $status to `status`. Always returns
// 'end_execution_reason_t::error'.
macro_rules! report_error {
@ -123,12 +114,15 @@ macro_rules! report_error_formatted {
impl<'a> ExecutionContext {
/// Construct a context in preparation for evaluating a node in a tree, with the given block_io.
/// The execution context may access the parser and parent job group (if any) through ctx.
pub fn new(pstree: ParsedSourceRef, block_io: IoChain) -> Self {
let line_counter = pstree.line_counter();
pub fn new(
pstree: ParsedSourceRef,
block_io: IoChain,
line_counter: Rc<RefCell<LineCounter<ast::JobPipeline>>>,
) -> Self {
Self {
pstree: RefCell::new(pstree),
cancel_signal: RefCell::default(),
line_counter: RefCell::new(line_counter),
line_counter,
block_io: RefCell::new(block_io),
}
}

View File

@ -267,4 +267,9 @@ impl<NodeType: Node> LineCounter<NodeType> {
let prev = std::mem::replace(&mut self.node, node_ptr);
unsafe { prev.as_ref() }
}
// Return the source.
pub fn get_source(&self) -> &wstr {
&self.parsed_source.src
}
}

View File

@ -1,6 +1,6 @@
// The fish parser. Contains functions for parsing and evaluating code.
use crate::ast::{Ast, List, Node};
use crate::ast::{self, Ast, List, Node};
use crate::builtins::shared::STATUS_ILLEGAL_CMD;
use crate::common::{
escape_string, scoped_push_replacer, CancelChecker, EscapeFlags, EscapeStringStyle,
@ -22,7 +22,7 @@ use crate::parse_constants::{
SOURCE_LOCATION_UNKNOWN,
};
use crate::parse_execution::{EndExecutionReason, ExecutionContext};
use crate::parse_tree::{parse_source, ParsedSourceRef};
use crate::parse_tree::{parse_source, LineCounter, ParsedSourceRef};
use crate::proc::{job_reap, JobGroupRef, JobList, JobRef, ProcStatus};
use crate::signal::{signal_check_cancel, signal_clear_cancel, Signal};
use crate::threads::assert_is_main_thread;
@ -362,8 +362,9 @@ pub enum CancelBehavior {
}
pub struct Parser {
/// The current execution context.
execution_context: RefCell<Option<ExecutionContext>>,
/// A shared line counter. This is handed out to each execution context
/// so they can communicate the line number back to this Parser.
line_counter: Rc<RefCell<LineCounter<ast::JobPipeline>>>,
/// The jobs associated with this parser.
job_list: RefCell<JobList>,
@ -404,7 +405,7 @@ impl Parser {
/// Create a parser.
pub fn new(variables: Rc<EnvStack>, cancel_behavior: CancelBehavior) -> Parser {
let result = Self {
execution_context: RefCell::default(),
line_counter: Rc::new(RefCell::new(LineCounter::empty())),
job_list: RefCell::default(),
wait_handles: RefCell::new(WaitHandleStore::new()),
block_list: RefCell::default(),
@ -429,10 +430,6 @@ impl Parser {
result
}
fn execution_context(&self) -> Ref<'_, Option<ExecutionContext>> {
self.execution_context.borrow()
}
/// Adds a job to the beginning of the job list.
pub fn job_add(&self, job: JobRef) {
assert!(!job.processes().is_empty());
@ -596,35 +593,23 @@ impl Parser {
let cancel_checker: CancelChecker = Box::new(move || check_cancel_signal().is_some());
op_ctx.cancel_checker = cancel_checker;
// Create and set a new execution context.
let exc = scoped_push_replacer(
|new_value| {
if self.execution_context.borrow().is_none() || new_value.is_none() {
// Outermost node.
std::mem::replace(&mut self.execution_context.borrow_mut(), new_value)
} else {
#[allow(clippy::unnecessary_unwrap)]
Some(ExecutionContext::swap(
self.execution_context.borrow().as_ref().unwrap(),
new_value.unwrap(),
))
}
},
Some(ExecutionContext::new(ps.clone(), block_io.clone())),
);
// Restore the line counter.
let line_counter = Rc::clone(&self.line_counter);
let scoped_line_counter =
scoped_push_replacer(|v| line_counter.replace(v), ps.line_counter());
// Create a new execution context.
let execution_context =
ExecutionContext::new(ps.clone(), block_io.clone(), Rc::clone(&line_counter));
// Check the exec count so we know if anything got executed.
let prev_exec_count = self.libdata().exec_count;
let prev_status_count = self.libdata().status_count;
let reason =
self.execution_context()
.as_ref()
.unwrap()
.eval_node(&op_ctx, node, Some(scope_block));
let reason = execution_context.eval_node(&op_ctx, node, Some(scope_block));
let new_exec_count = self.libdata().exec_count;
let new_status_count = self.libdata().status_count;
ScopeGuarding::commit(exc);
ScopeGuarding::commit(scoped_line_counter);
self.pop_block(scope_block);
job_reap(self, false); // reap again
@ -678,15 +663,7 @@ impl Parser {
///
/// init.fish (line 127): ls|grep pancake
pub fn current_line(&self) -> WString {
if self.execution_context().is_none() {
return WString::new();
};
let Some(source_offset) = self
.execution_context()
.as_ref()
.unwrap()
.get_current_source_offset()
else {
let Some(source_offset) = self.line_counter.borrow_mut().source_offset_of_node() else {
return WString::new();
};
@ -717,7 +694,7 @@ impl Parser {
empty_error.source_start = source_offset;
let mut line_info = empty_error.describe_with_prefix(
&self.execution_context().as_ref().unwrap().get_source(),
self.line_counter.borrow().get_source(),
&prefix,
self.is_interactive(),
skip_caret,
@ -730,11 +707,13 @@ impl Parser {
line_info
}
/// Returns the current line number.
/// Returns the current line number, indexed from 1.
pub fn get_lineno(&self) -> Option<usize> {
self.execution_context()
.as_ref()
.and_then(|ctx| ctx.get_current_line_number())
// The offset is 0 based; the number is 1 based.
self.line_counter
.borrow_mut()
.line_offset_of_node()
.map(|offset| offset + 1)
}
/// Return whether we are currently evaluating a "block" such as an if statement.