Add transitive use_ir field to Stack and add --use-ir option to nu

This commit is contained in:
Devyn Cairns 2024-06-26 17:28:00 -07:00
parent 73b092bfdc
commit fd0b99554d
No known key found for this signature in database
9 changed files with 102 additions and 34 deletions

View File

@ -1,6 +1,4 @@
use nu_engine::{
command_prelude::*, get_eval_block_with_early_return, get_eval_ir_block, redirect_env,
};
use nu_engine::{command_prelude::*, get_eval_block_with_early_return, redirect_env};
use nu_protocol::{
engine::Closure,
process::{ChildPipe, ChildProcess, ExitStatus},
@ -94,12 +92,13 @@ impl Command for Do {
bind_args_to(&mut callee_stack, &block.signature, rest, head)?;
let eval_block_with_early_return = get_eval_block_with_early_return(engine_state);
let eval_ir_block = get_eval_ir_block(engine_state);
let result = if use_ir {
eval_ir_block(engine_state, &mut callee_stack, block, input)
} else {
eval_block_with_early_return(engine_state, &mut callee_stack, block, input)
};
// Applies to all block evaluation once set true
if use_ir {
caller_stack.use_ir = true;
}
let result = eval_block_with_early_return(engine_state, &mut callee_stack, block, input);
if has_env {
// Merge the block's environment to the current stack

View File

@ -1,3 +1,5 @@
use std::borrow::Cow;
use nu_engine::{command_prelude::*, compile};
use nu_protocol::engine::Closure;
@ -31,7 +33,15 @@ impl Command for ViewIr {
let closure: Closure = call.req(engine_state, stack, 0)?;
let block = engine_state.get_block(closure.block_id);
let ir_block = compile(&StateWorkingSet::new(engine_state), &block)?;
// Use the pre-compiled block if available, otherwise try to compile it
// This helps display the actual compilation error
let ir_block = match &block.ir_block {
Some(ir_block) => Cow::Borrowed(ir_block),
None => Cow::Owned(
compile(&StateWorkingSet::new(engine_state), &block)
.map_err(|err| err.to_shell_error(block.span))?,
),
};
let formatted = format!("{}", ir_block.display(engine_state));
Ok(Value::string(formatted, call.head).into_pipeline_data())

View File

@ -1,6 +1,6 @@
use nu_protocol::{
ast::{
Argument, Block, Call, CellPath, Expr, Expression, ListItem, Math, Operator, PathMember,
Argument, Block, Call, CellPath, Expr, Expression, ListItem, Operator, PathMember,
Pipeline, PipelineRedirection, RecordItem, RedirectionSource, RedirectionTarget,
},
engine::StateWorkingSet,
@ -12,7 +12,7 @@ const BLOCK_INPUT: RegId = RegId(0);
/// Compile Nushell pipeline abstract syntax tree (AST) to internal representation (IR) instructions
/// for evaluation.
pub fn compile(working_set: &StateWorkingSet, block: &Block) -> Result<IrBlock, ShellError> {
pub fn compile(working_set: &StateWorkingSet, block: &Block) -> Result<IrBlock, CompileError> {
let mut builder = BlockBuilder::new();
compile_block(
@ -22,16 +22,13 @@ pub fn compile(working_set: &StateWorkingSet, block: &Block) -> Result<IrBlock,
RedirectModes::default(),
Some(BLOCK_INPUT),
BLOCK_INPUT,
)
.map_err(|err| err.to_shell_error(block.span))?;
)?;
// A complete block has to end with a `return`
builder
.push(
Instruction::Return { src: BLOCK_INPUT }
.into_spanned(block.span.unwrap_or(Span::unknown())),
)
.map_err(|err| err.to_shell_error(block.span))?;
builder.push(
Instruction::Return { src: BLOCK_INPUT }
.into_spanned(block.span.unwrap_or(Span::unknown())),
)?;
Ok(builder.finish())
}
@ -369,7 +366,7 @@ fn compile_expression(
.into_spanned(expr.span),
)?;
}
ListItem::Spread(spread_span, expr) => {
ListItem::Spread(spread_span, _) => {
// Spread the list using list-spread
builder.push(
Instruction::ListSpread {
@ -795,7 +792,7 @@ fn compile_load_env(
/// An internal compiler error, generally means a Nushell bug rather than an issue with user error
/// since parsing and typechecking has already passed.
#[derive(Debug)]
enum CompileError {
pub enum CompileError {
RegisterOverflow,
RegisterUninitialized(RegId),
DataOverflow,
@ -807,8 +804,8 @@ enum CompileError {
}
impl CompileError {
fn to_shell_error(self, mut span: Option<Span>) -> ShellError {
let message = match self {
pub fn message(&self) -> String {
match self {
CompileError::RegisterOverflow => format!("register overflow"),
CompileError::RegisterUninitialized(reg_id) => {
format!("register {reg_id} is uninitialized when used, possibly reused")
@ -821,15 +818,25 @@ impl CompileError {
}
CompileError::Garbage => "encountered garbage, likely due to parse error".into(),
CompileError::UnsupportedOperatorExpression => "unsupported operator expression".into(),
CompileError::AccessEnvByInt(local_span) => {
span = Some(local_span);
"attempted access of $env by integer path".into()
}
CompileError::AccessEnvByInt(_) => "attempted access of $env by integer path".into(),
CompileError::Todo(msg) => {
format!("TODO: {msg}")
}
};
ShellError::IrCompileError { msg: message, span }
}
}
pub fn span(&self) -> Option<Span> {
match self {
CompileError::AccessEnvByInt(span) => Some(*span),
_ => None,
}
}
pub fn to_shell_error(self, span: Option<Span>) -> ShellError {
ShellError::IrCompileError {
msg: self.message(),
span: self.span().or(span),
}
}
}

View File

@ -1,3 +1,4 @@
use crate::eval_ir_block;
#[allow(deprecated)]
use crate::{current_dir, get_config, get_full_help};
use nu_path::expand_path_with;
@ -509,6 +510,11 @@ pub fn eval_block<D: DebugContext>(
block: &Block,
mut input: PipelineData,
) -> Result<PipelineData, ShellError> {
// Remove once IR is the default.
if stack.use_ir {
return eval_ir_block::<D>(engine_state, stack, block, input);
}
D::enter_block(engine_state, block);
let num_pipelines = block.len();

View File

@ -11,8 +11,8 @@ use itertools::Itertools;
use log::trace;
use nu_engine::DIR_VAR_PARSER_INFO;
use nu_protocol::{
ast::*, engine::StateWorkingSet, eval_const::eval_constant, report_error, BlockId, DidYouMean,
Flag, ParseError, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, VarId,
ast::*, engine::StateWorkingSet, eval_const::eval_constant, BlockId, DidYouMean, Flag,
ParseError, ParseWarning, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, VarId,
ENV_VARIABLE_ID, IN_VARIABLE_ID,
};
use std::{
@ -5834,12 +5834,19 @@ pub fn parse_block(
working_set.parse_errors.extend_from_slice(&errors);
}
if !is_subexpression && errors.is_empty() {
// Do not try to compile blocks that are subexpressions, or when we've already had a parse
// failure as that definitely will fail to compile
if !is_subexpression && working_set.parse_errors.is_empty() {
match nu_engine::compile(working_set, &block) {
Ok(ir_block) => {
block.ir_block = Some(ir_block);
}
Err(err) => report_error(working_set, &err),
Err(err) => working_set
.parse_warnings
.push(ParseWarning::IrCompileError {
msg: err.message(),
span,
}),
}
}

View File

@ -47,6 +47,8 @@ pub struct Stack {
pub register_buf_cache: RegisterBufCache,
/// Argument stack for IR evaluation
pub argument_stack: ArgumentStack,
/// Set true to always use IR mode
pub use_ir: bool,
pub recursion_count: u64,
pub parent_stack: Option<Arc<Stack>>,
/// Variables that have been deleted (this is used to hide values from parent stack lookups)
@ -76,6 +78,7 @@ impl Stack {
active_overlays: vec![DEFAULT_OVERLAY_NAME.to_string()],
register_buf_cache: RegisterBufCache::new(),
argument_stack: ArgumentStack::new(),
use_ir: false,
recursion_count: 0,
parent_stack: None,
parent_deletions: vec![],
@ -95,6 +98,7 @@ impl Stack {
active_overlays: parent.active_overlays.clone(),
register_buf_cache: RegisterBufCache::new(),
argument_stack: ArgumentStack::new(),
use_ir: parent.use_ir,
recursion_count: parent.recursion_count,
vars: vec![],
parent_deletions: vec![],
@ -266,6 +270,7 @@ impl Stack {
active_overlays: self.active_overlays.clone(),
register_buf_cache: RegisterBufCache::new(),
argument_stack: ArgumentStack::new(),
use_ir: self.use_ir,
recursion_count: self.recursion_count,
parent_stack: None,
parent_deletions: vec![],
@ -298,6 +303,7 @@ impl Stack {
active_overlays: self.active_overlays.clone(),
register_buf_cache: RegisterBufCache::new(),
argument_stack: ArgumentStack::new(),
use_ir: self.use_ir,
recursion_count: self.recursion_count,
parent_stack: None,
parent_deletions: vec![],

View File

@ -14,12 +14,29 @@ pub enum ParseWarning {
span: Span,
url: String,
},
/// An error occurred with the IR compiler.
///
/// ## Resolution
///
/// The IR compiler is in very early development, so code that can't be compiled is quite
/// expected. If you think it should be working, please report it to us.
#[error("internal compiler error: {msg}")]
#[diagnostic(
help("this is a bug, please report it at https://github.com/nushell/nushell/issues/new along with the code you were compiling if able")
)]
IrCompileError {
msg: String,
#[label = "while compiling this code"]
span: Span,
},
}
impl ParseWarning {
pub fn span(&self) -> Span {
match self {
ParseWarning::DeprecatedWarning { span, .. } => *span,
ParseWarning::IrCompileError { span, .. } => *span,
}
}
}

View File

@ -107,6 +107,7 @@ pub(crate) fn parse_commandline_args(
let error_style: Option<Value> =
call.get_flag(engine_state, &mut stack, "error-style")?;
let no_newline = call.get_named_arg("no-newline");
let use_ir = call.has_flag(engine_state, &mut stack, "use-ir")?;
// ide flags
let lsp = call.has_flag(engine_state, &mut stack, "lsp")?;
@ -251,6 +252,7 @@ pub(crate) fn parse_commandline_args(
table_mode,
error_style,
no_newline,
use_ir,
});
}
}
@ -292,6 +294,7 @@ pub(crate) struct NushellCliArgs {
pub(crate) ide_complete: Option<Value>,
pub(crate) ide_check: Option<Value>,
pub(crate) ide_ast: Option<Spanned<String>>,
pub(crate) use_ir: bool,
}
#[derive(Clone)]
@ -368,6 +371,7 @@ impl Command for Nu {
"start with an alternate environment config file",
None,
)
.switch("use-ir", "EXPERIMENTAL: use the IR evaluation engine on launch", None)
.switch(
"lsp",
"start nu's language server protocol",

View File

@ -26,6 +26,10 @@ pub(crate) fn run_commands(
let mut stack = Stack::new();
let start_time = std::time::Instant::now();
if parsed_nu_cli_args.use_ir {
stack.use_ir = true;
}
// if the --no-config-file(-n) option is NOT passed, load the plugin file,
// load the default env file or custom (depending on parsed_nu_cli_args.env_file),
// and maybe a custom config file (depending on parsed_nu_cli_args.config_file)
@ -144,6 +148,10 @@ pub(crate) fn run_file(
trace!("run_file");
let mut stack = Stack::new();
if parsed_nu_cli_args.use_ir {
stack.use_ir = true;
}
// if the --no-config-file(-n) option is NOT passed, load the plugin file,
// load the default env file or custom (depending on parsed_nu_cli_args.env_file),
// and maybe a custom config file (depending on parsed_nu_cli_args.config_file)
@ -254,6 +262,10 @@ pub(crate) fn run_repl(
let mut stack = Stack::new();
let start_time = std::time::Instant::now();
if parsed_nu_cli_args.use_ir {
stack.use_ir = true;
}
if parsed_nu_cli_args.no_config_file.is_none() {
setup_config(
engine_state,