Remove the notion of principal parser

The "principal" parser is the one and only today; in the future we hope to
have multiple parsers to execute fish script in parallel.

Having a globally accessible "principle" parser is suspicious; now we can
get rid of it.
This commit is contained in:
Peter Ammon 2024-06-23 15:14:55 -07:00
parent 631516398e
commit 4557d9fc09
No known key found for this signature in database
4 changed files with 29 additions and 27 deletions

View File

@ -46,7 +46,7 @@ use fish::{
parse_constants::{ParseErrorList, ParseTreeFlags},
parse_tree::ParsedSource,
parse_util::parse_util_detect_errors_in_ast,
parser::{BlockType, Parser},
parser::{BlockType, CancelBehavior, Parser},
path::path_get_config,
printf,
proc::{
@ -65,6 +65,7 @@ use std::fs::File;
use std::mem::MaybeUninit;
use std::os::unix::prelude::*;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::{env, ops::ControlFlow};
@ -591,7 +592,9 @@ fn throwing_main() -> i32 {
ScopeGuard::new((), |()| restore_term_foreground_process_group_for_exit());
let _restore_term = reader_init();
let parser = Parser::principal_parser();
// Construct the root parser!
let env = Rc::new(EnvStack::globals().create_child(true /* dispatches_var_changes */));
let parser: &Parser = &Parser::new(env, CancelBehavior::Clear);
parser.set_syncs_uvars(!opts.no_config);
if !opts.no_exec && !opts.no_config {

View File

@ -25,7 +25,7 @@ use crate::parse_execution::{EndExecutionReason, ParseExecutionContext};
use crate::parse_tree::{parse_source, 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, MainThread};
use crate::threads::assert_is_main_thread;
use crate::util::get_time;
use crate::wait_handle::WaitHandleStore;
use crate::wchar::{wstr, WString, L};
@ -353,6 +353,16 @@ pub type BlockId = usize;
pub type ParserRef = Rc<Parser>;
// Controls the behavior when fish itself receives a signal and there are
// no blocks on the stack.
// The "outermost" parser is responsible for clearing the signal.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum CancelBehavior {
#[default]
Return, // Return the signal to the caller.
Clear, // Clear the signal.
}
pub struct Parser {
/// The current execution context.
execution_context: RefCell<Option<ParseExecutionContext>>,
@ -382,8 +392,8 @@ pub struct Parser {
/// including sending on-variable change events.
syncs_uvars: RelaxedAtomicBool,
/// If set, we are the principal parser.
is_principal: RelaxedAtomicBool,
/// The behavior when fish itself receives a signal and there are no blocks on the stack.
cancel_behavior: CancelBehavior,
/// List of profile items.
profile_items: RefCell<Vec<ProfileItem>>,
@ -393,8 +403,8 @@ pub struct Parser {
}
impl Parser {
/// Create a parser
pub fn new(variables: Rc<EnvStack>, is_principal: bool) -> ParserRef {
/// Create a parser.
pub fn new(variables: Rc<EnvStack>, cancel_behavior: CancelBehavior) -> ParserRef {
let result = Rc::new(Self {
execution_context: RefCell::default(),
job_list: RefCell::default(),
@ -404,7 +414,7 @@ impl Parser {
variables,
library_data: RefCell::new(LibraryData::new()),
syncs_uvars: RelaxedAtomicBool::new(false),
is_principal: RelaxedAtomicBool::new(is_principal),
cancel_behavior,
profile_items: RefCell::default(),
global_event_blocks: AtomicU64::new(0),
});
@ -451,17 +461,6 @@ impl Parser {
.any(|b| b.typ() == BlockType::subst)
}
/// Get the "principal" parser, whatever that is. Can only be called by the main thread.
pub fn principal_parser() -> &'static Parser {
use std::cell::OnceCell;
static PRINCIPAL: MainThread<OnceCell<ParserRef>> = MainThread::new(OnceCell::new());
PRINCIPAL.get().get_or_init(|| {
let dispatches_var_changes = true;
let env = Rc::new(EnvStack::globals().create_child(dispatches_var_changes));
Parser::new(env, true)
})
}
/// Assert that this parser is allowed to execute on the current thread.
pub fn assert_can_execute(&self) {
assert_is_main_thread();
@ -553,14 +552,14 @@ impl Parser {
"Invalid block type"
);
// If fish itself got a cancel signal, then we want to unwind back to the principal parser.
// If we are the principal parser and our block stack is empty, then we want to clear the
// signal.
// If fish itself got a cancel signal, then we want to unwind back to the parser which
// has a Clear cancellation behavior.
// Note this only happens in interactive sessions. In non-interactive sessions, SIGINT will
// cause fish to exit.
let sig = signal_check_cancel();
if sig != 0 {
if self.is_principal.load() && self.block_list.borrow().is_empty() {
if self.cancel_behavior == CancelBehavior::Clear && self.block_list.borrow().is_empty()
{
signal_clear_cancel();
} else {
return EvalRes::new(ProcStatus::from_signal(Signal::new(sig)));

View File

@ -30,7 +30,7 @@ mod wgetopt;
pub mod prelude {
use crate::common::ScopeGuarding;
use crate::env::{env_init, misc_init};
use crate::parser::{Parser, ParserRef};
use crate::parser::{CancelBehavior, Parser, ParserRef};
use crate::reader::reader_init;
use crate::signal::signal_reset_handlers;
pub use crate::tests::env::{PwdEnvironment, TestEnvironment};
@ -52,7 +52,7 @@ pub mod prelude {
impl TestParser {
pub fn new() -> TestParser {
TestParser {
parser: Parser::new(Rc::new(EnvStack::new()), false),
parser: Parser::new(Rc::new(EnvStack::new()), CancelBehavior::default()),
pushed_dirs: RefCell::new(Vec::new()),
}
}

View File

@ -7,7 +7,7 @@ use crate::parse_constants::{
ParseErrorCode, ParseTreeFlags, ParserTestErrorBits, StatementDecoration,
};
use crate::parse_util::{parse_util_detect_errors, parse_util_detect_errors_in_argument};
use crate::parser::Parser;
use crate::parser::{CancelBehavior, Parser};
use crate::reader::{reader_pop, reader_push, reader_reset_interrupted, ReaderConfig};
use crate::signal::{signal_clear_cancel, signal_reset_handlers, signal_set_handlers};
use crate::tests::prelude::*;
@ -716,7 +716,7 @@ fn test_1_cancellation(parser: &Parser, src: &wstr) {
#[serial]
fn test_cancellation() {
let _cleanup = test_init();
let parser = Parser::new(Rc::new(EnvStack::new()), true);
let parser = Parser::new(Rc::new(EnvStack::new()), CancelBehavior::Clear);
reader_push(&parser, L!(""), ReaderConfig::default());
let _pop = ScopeGuard::new((), |()| reader_pop());