omg it compiles

This commit is contained in:
Devyn Cairns 2024-06-18 21:39:42 -07:00
parent acee591003
commit b0d24ea434
24 changed files with 111 additions and 140 deletions

View File

@ -39,6 +39,8 @@ impl Command for TimeIt {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
// FIXME: get working with IR. I don't think this should actually be so AST dependent
let call = call.assert_ast_call()?;
let command_to_run = call.positional_nth(0);
// Get the start time after all other computation has been done.

View File

@ -30,6 +30,7 @@ impl Command for ExportEnv {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let call = call.assert_ast_call()?; // FIXME
let block_id = call
.positional_nth(0)
.expect("checked through parser")

View File

@ -35,6 +35,7 @@ impl Command for SourceEnv {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let call = call.assert_ast_call()?; // FIXME
let source_filename: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
// Note: this hidden positional is the block_id that corresponded to the 0th position

View File

@ -103,7 +103,7 @@ impl Command for Du {
let current_dir = current_dir(engine_state, stack)?;
let paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
let paths = if call.rest_iter(0).count() == 0 {
let paths = if !call.has_positional_args(stack, 0) {
None
} else {
Some(paths)

View File

@ -110,7 +110,7 @@ impl Command for Ls {
};
let pattern_arg = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
let input_pattern_arg = if call.rest_iter(0).count() == 0 {
let input_pattern_arg = if !call.has_positional_args(stack, 0) {
None
} else {
Some(pattern_arg)

View File

@ -57,7 +57,7 @@ impl Command for Open {
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
let eval_block = get_eval_block(engine_state);
if paths.is_empty() && call.rest_iter(0).next().is_none() {
if paths.is_empty() && !call.has_positional_args(stack, 0) {
// try to use path from pipeline input if there were no positional or spread args
let (filename, span) = match input {
PipelineData::Value(val, ..) => {

View File

@ -4,10 +4,8 @@ use nu_engine::get_eval_block;
use nu_engine::{command_prelude::*, current_dir};
use nu_path::expand_path_with;
use nu_protocol::{
ast::{Expr, Expression},
byte_stream::copy_with_interrupt,
process::ChildPipe,
ByteStreamSource, DataSource, OutDest, PipelineMetadata,
ast, byte_stream::copy_with_interrupt, process::ChildPipe, ByteStreamSource, DataSource,
OutDest, PipelineMetadata,
};
use std::{
fs::File,
@ -70,24 +68,6 @@ impl Command for Save {
let append = call.has_flag(engine_state, stack, "append")?;
let force = call.has_flag(engine_state, stack, "force")?;
let progress = call.has_flag(engine_state, stack, "progress")?;
let out_append = if let Some(Expression {
expr: Expr::Bool(out_append),
..
}) = call.get_parser_info("out-append")
{
*out_append
} else {
false
};
let err_append = if let Some(Expression {
expr: Expr::Bool(err_append),
..
}) = call.get_parser_info("err-append")
{
*err_append
} else {
false
};
let span = call.head;
#[allow(deprecated)]
@ -110,14 +90,7 @@ impl Command for Save {
PipelineData::ByteStream(stream, metadata) => {
check_saving_to_source_file(metadata.as_ref(), &path, stderr_path.as_ref())?;
let (file, stderr_file) = get_files(
&path,
stderr_path.as_ref(),
append,
out_append,
err_append,
force,
)?;
let (file, stderr_file) = get_files(&path, stderr_path.as_ref(), append, force)?;
let size = stream.known_size();
let ctrlc = engine_state.ctrlc.clone();
@ -222,14 +195,7 @@ impl Command for Save {
stderr_path.as_ref(),
)?;
let (mut file, _) = get_files(
&path,
stderr_path.as_ref(),
append,
out_append,
err_append,
force,
)?;
let (mut file, _) = get_files(&path, stderr_path.as_ref(), append, force)?;
for val in ls {
file.write_all(&value_to_bytes(val)?)
.map_err(|err| ShellError::IOError {
@ -259,14 +225,7 @@ impl Command for Save {
input_to_bytes(input, Path::new(&path.item), raw, engine_state, stack, span)?;
// Only open file after successful conversion
let (mut file, _) = get_files(
&path,
stderr_path.as_ref(),
append,
out_append,
err_append,
force,
)?;
let (mut file, _) = get_files(&path, stderr_path.as_ref(), append, force)?;
file.write_all(&bytes).map_err(|err| ShellError::IOError {
msg: err.to_string(),
@ -398,7 +357,8 @@ fn convert_to_extension(
let eval_block = get_eval_block(engine_state);
eval_block(engine_state, stack, block, input)
} else {
decl.run(engine_state, stack, &Call::new(span), input)
let call = ast::Call::new(span);
decl.run(engine_state, stack, &(&call).into(), input)
}
} else {
Ok(input)
@ -474,19 +434,17 @@ fn get_files(
path: &Spanned<PathBuf>,
stderr_path: Option<&Spanned<PathBuf>>,
append: bool,
out_append: bool,
err_append: bool,
force: bool,
) -> Result<(File, Option<File>), ShellError> {
// First check both paths
let (path, path_span) = prepare_path(path, append || out_append, force)?;
let (path, path_span) = prepare_path(path, append, force)?;
let stderr_path_and_span = stderr_path
.as_ref()
.map(|stderr_path| prepare_path(stderr_path, append || err_append, force))
.map(|stderr_path| prepare_path(stderr_path, append, force))
.transpose()?;
// Only if both files can be used open and possibly truncate them
let file = open_file(path, path_span, append || out_append)?;
let file = open_file(path, path_span, append)?;
let stderr_file = stderr_path_and_span
.map(|(stderr_path, stderr_path_span)| {
@ -499,7 +457,7 @@ fn get_files(
inner: vec![],
})
} else {
open_file(stderr_path, stderr_path_span, append || err_append)
open_file(stderr_path, stderr_path_span, append)
}
})
.transpose()?;

View File

@ -1,6 +1,6 @@
use dialoguer::Input;
use nu_engine::{command_prelude::*, get_eval_expression};
use nu_protocol::{ast::Expr, FromValue, NuGlob};
use nu_protocol::{FromValue, NuGlob};
use std::{
error::Error,
path::{Path, PathBuf},
@ -92,42 +92,19 @@ pub fn is_older(src: &Path, dst: &Path) -> Option<bool> {
/// Get rest arguments from given `call`, starts with `starting_pos`.
///
/// It's similar to `call.rest`, except that it always returns NuGlob. And if input argument has
/// Type::Glob, the NuGlob is unquoted, which means it's required to expand.
/// It's similar to `call.rest`, except that it always returns NuGlob.
pub fn get_rest_for_glob_pattern(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
starting_pos: usize,
) -> Result<Vec<Spanned<NuGlob>>, ShellError> {
let mut output = vec![];
let eval_expression = get_eval_expression(engine_state);
for result in call.rest_iter_flattened(starting_pos, |expr| {
let result = eval_expression(engine_state, stack, expr);
match result {
Err(e) => Err(e),
Ok(result) => {
let span = result.span();
// convert from string to quoted string if expr is a variable
// or string interpolation
match result {
Value::String { val, .. }
if matches!(
&expr.expr,
Expr::FullCellPath(_) | Expr::StringInterpolation(_)
) =>
{
// should not expand if given input type is not glob.
Ok(Value::glob(val, expr.ty != Type::Glob, span))
}
other => Ok(other),
}
}
}
})? {
output.push(FromValue::from_value(result)?);
}
Ok(output)
call.rest_iter_flattened(engine_state, stack, eval_expression, starting_pos)?
.into_iter()
// This used to be much more complex, but I think `FromValue` should be able to handle the
// nuance here.
.map(FromValue::from_value)
.collect()
}

View File

@ -149,27 +149,27 @@ pub fn transpose(
if !args.rest.is_empty() && args.header_row {
return Err(ShellError::IncompatibleParametersSingle {
msg: "Can not provide header names and use `--header-row`".into(),
span: call.get_named_arg("header-row").expect("has flag").span,
span: call.get_flag_span(stack, "header-row").expect("has flag"),
});
}
if !args.header_row && args.keep_all {
return Err(ShellError::IncompatibleParametersSingle {
msg: "Can only be used with `--header-row`(`-r`)".into(),
span: call.get_named_arg("keep-all").expect("has flag").span,
span: call.get_flag_span(stack, "keep-all").expect("has flag"),
});
}
if !args.header_row && args.keep_last {
return Err(ShellError::IncompatibleParametersSingle {
msg: "Can only be used with `--header-row`(`-r`)".into(),
span: call.get_named_arg("keep-last").expect("has flag").span,
span: call.get_flag_span(stack, "keep-last").expect("has flag"),
});
}
if args.keep_all && args.keep_last {
return Err(ShellError::IncompatibleParameters {
left_message: "can't use `--keep-last` at the same time".into(),
left_span: call.get_named_arg("keep-last").expect("has flag").span,
left_span: call.get_flag_span(stack, "keep-last").expect("has flag"),
right_message: "because of `--keep-all`".into(),
right_span: call.get_named_arg("keep-all").expect("has flag").span,
right_span: call.get_flag_span(stack, "keep-all").expect("has flag"),
});
}

View File

@ -1,7 +1,6 @@
use nu_engine::{CallExt, ClosureEval};
use nu_protocol::{
ast::Call,
engine::{Closure, EngineState, Stack},
engine::{Call, Closure, EngineState, Stack},
IntoPipelineData, PipelineData, ShellError, Span, Value,
};

View File

@ -676,7 +676,7 @@ Operating system commands:
}
};
let output = heavy_lifting(code, escape, osc, call)?;
let output = heavy_lifting(code, escape, osc, stack, call)?;
Ok(Value::string(output, call.head).into_pipeline_data())
}
@ -710,26 +710,30 @@ Operating system commands:
}
};
let output = heavy_lifting(code, escape, osc, call)?;
let output = heavy_lifting(code, escape, osc, &Stack::new(), call)?;
Ok(Value::string(output, call.head).into_pipeline_data())
}
}
fn heavy_lifting(code: Value, escape: bool, osc: bool, call: &Call) -> Result<String, ShellError> {
fn heavy_lifting(
code: Value,
escape: bool,
osc: bool,
stack: &Stack,
call: &Call,
) -> Result<String, ShellError> {
let param_is_string = matches!(code, Value::String { .. });
if escape && osc {
return Err(ShellError::IncompatibleParameters {
left_message: "escape".into(),
left_span: call
.get_named_arg("escape")
.expect("Unexpected missing argument")
.span,
.get_flag_span(stack, "escape")
.expect("Unexpected missing argument"),
right_message: "osc".into(),
right_span: call
.get_named_arg("osc")
.expect("Unexpected missing argument")
.span,
.get_flag_span(stack, "osc")
.expect("Unexpected missing argument"),
});
}
let code_string = if param_is_string {
@ -741,10 +745,7 @@ fn heavy_lifting(code: Value, escape: bool, osc: bool, call: &Call) -> Result<St
if (escape || osc) && (param_is_valid_string) {
let code_vec: Vec<char> = code_string.chars().collect();
if code_vec[0] == '\\' {
let span = match call.get_flag_expr("escape") {
Some(expr) => expr.span,
None => call.head,
};
let span = call.get_flag_span(stack, "escape").unwrap_or(call.head);
return Err(ShellError::TypeMismatch {
err_message: "no need for escape characters".into(),

View File

@ -58,7 +58,7 @@ impl Command for IsTerminal {
_ => {
return Err(ShellError::IncompatibleParametersSingle {
msg: "Only one stream may be checked".into(),
span: Span::merge_many(call.arguments.iter().map(|arg| arg.span())),
span: call.arguments_span(),
});
}
};

View File

@ -84,27 +84,26 @@ impl Command for Kill {
{
return Err(ShellError::IncompatibleParameters {
left_message: "force".to_string(),
left_span: call
.get_named_arg("force")
.ok_or_else(|| ShellError::GenericError {
left_span: call.get_flag_span(stack, "force").ok_or_else(|| {
ShellError::GenericError {
error: "Flag error".into(),
msg: "flag force not found".into(),
span: Some(call.head),
help: None,
inner: vec![],
})?
.span,
}
})?,
right_message: "signal".to_string(),
right_span: Span::merge(
call.get_named_arg("signal")
.ok_or_else(|| ShellError::GenericError {
call.get_flag_span(stack, "signal").ok_or_else(|| {
ShellError::GenericError {
error: "Flag error".into(),
msg: "flag signal not found".into(),
span: Some(call.head),
help: None,
inner: vec![],
})?
.span,
}
})?,
signal_span,
),
});

View File

@ -17,8 +17,7 @@ pub use str_::*;
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{EngineState, Stack, StateWorkingSet},
engine::{Call, EngineState, Stack, StateWorkingSet},
ShellError,
};

View File

@ -62,7 +62,7 @@ On Windows based systems, Nushell will wait for the command to finish and then e
command.envs(envs);
// Configure args.
let args = crate::eval_arguments_from_call(engine_state, stack, call)?;
let args = crate::eval_arguments_from_call(engine_state, stack, call.assert_ast_call()?)?;
command.args(args.into_iter().map(|s| s.item));
// Execute the child process, replacing/terminating the current process

View File

@ -87,7 +87,7 @@ impl Command for NuCheck {
&path_str.item,
engine_state,
stack,
get_dirs_var_from_call(call),
get_dirs_var_from_call(call.assert_ast_call()?), // FIXME
) {
Ok(path) => {
if let Some(path) = path {

View File

@ -1,7 +1,7 @@
use nu_cmd_base::hook::eval_hook;
use nu_engine::{command_prelude::*, env_to_strings, get_eval_expression};
use nu_protocol::{
ast::{Expr, Expression},
ast::{self, Expr, Expression},
did_you_mean,
process::ChildProcess,
ByteStream, NuGlob, OutDest,
@ -45,6 +45,9 @@ impl Command for External {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
// FIXME: this currently only works with AST calls, but I think #13089 totally fixes that
// so if I can merge that, this should just work fine
let call = call.assert_ast_call()?;
let cwd = engine_state.cwd(Some(stack))?;
// Evaluate the command name in the same way the arguments are evaluated. Since this isn't
@ -236,7 +239,7 @@ fn remove_quotes(s: &str) -> Cow<'_, str> {
pub fn eval_arguments_from_call(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
call: &ast::Call,
) -> Result<Vec<Spanned<String>>, ShellError> {
let ctrlc = &engine_state.ctrlc;
let cwd = engine_state.cwd(Some(stack))?;

View File

@ -1,10 +1,5 @@
use nu_protocol::record;
use nu_protocol::Value;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Type,
};
use nu_engine::command_prelude::*;
use nu_protocol::{record, Value};
#[derive(Clone)]
pub struct UName;

View File

@ -339,7 +339,7 @@ fn get_theme_flag(
struct CmdInput<'a> {
engine_state: &'a EngineState,
stack: &'a mut Stack,
call: &'a Call,
call: &'a Call<'a>,
data: PipelineData,
}
@ -347,7 +347,7 @@ impl<'a> CmdInput<'a> {
fn new(
engine_state: &'a EngineState,
stack: &'a mut Stack,
call: &'a Call,
call: &'a Call<'a>,
data: PipelineData,
) -> Self {
Self {

View File

@ -4,7 +4,7 @@ use nu_protocol::{
debugger::WithoutDebug,
engine::{self, EngineState, Stack, StateWorkingSet},
eval_const::eval_constant,
ir, FromValue, ShellError, Value,
ir, FromValue, ShellError, Span, Value,
};
pub trait CallExt {
@ -23,6 +23,9 @@ pub trait CallExt {
name: &str,
) -> Result<Option<T>, ShellError>;
/// Efficiently get the span of a flag argument
fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span>;
fn rest<T: FromValue>(
&self,
engine_state: &EngineState,
@ -107,6 +110,10 @@ impl CallExt for ast::Call {
}
}
fn get_flag_span(&self, _stack: &Stack, name: &str) -> Option<Span> {
self.get_named_arg(name).map(|arg| arg.span)
}
fn rest<T: FromValue>(
&self,
engine_state: &EngineState,
@ -224,6 +231,11 @@ impl CallExt for ir::Call {
}
}
fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span> {
self.named_iter(stack)
.find_map(|(i_name, _)| (i_name.item == name).then_some(i_name.span))
}
fn rest<T: FromValue>(
&self,
_engine_state: &EngineState,
@ -317,6 +329,10 @@ impl CallExt for engine::Call<'_> {
proxy!(self.get_flag(engine_state, stack, name))
}
fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span> {
proxy!(self.get_flag_span(stack, name))
}
fn rest<T: FromValue>(
&self,
engine_state: &EngineState,

View File

@ -244,6 +244,7 @@ pub fn path_str(
}
pub const DIR_VAR_PARSER_INFO: &str = "dirs_var";
// FIXME: this should be possible on IR calls
pub fn get_dirs_var_from_call(call: &Call) -> Option<VarId> {
call.get_parser_info(DIR_VAR_PARSER_INFO).and_then(|x| {
if let Expr::Var(id) = x.expr {

View File

@ -1,7 +1,7 @@
use nu_parser::*;
use nu_protocol::{
ast::{Argument, Call, Expr, ExternalArgument, PathMember, Range},
engine::{Command, EngineState, Stack, StateWorkingSet},
ast::{Argument, Expr, ExternalArgument, PathMember, Range},
engine::{Call, Command, EngineState, Stack, StateWorkingSet},
ParseError, PipelineData, ShellError, Signature, Span, SyntaxShape,
};
use rstest::rstest;
@ -1507,10 +1507,7 @@ mod range {
#[cfg(test)]
mod input_types {
use super::*;
use nu_protocol::{
ast::{Argument, Call},
Category, PipelineData, ShellError, Type,
};
use nu_protocol::{ast::Argument, engine::Call, Category, PipelineData, ShellError, Type};
#[derive(Clone)]
pub struct LsTest;

View File

@ -21,6 +21,18 @@ pub enum CallImpl<'a> {
}
impl Call<'_> {
/// Returns a new AST call with the given span. This is often used by commands that need an
/// empty call to pass to a command. It's not easily possible to add anything to this.
pub fn new(span: Span) -> Self {
// this is using the boxed variant, which isn't so efficient... but this is only temporary
// anyway.
Call {
head: span,
decl_id: 0,
inner: CallImpl::AstBox(Box::new(ast::Call::new(span))),
}
}
/// Convert the `Call` from any lifetime into `'static`, by cloning the data within onto the
/// heap.
pub fn to_owned(&self) -> Call<'static> {
@ -85,6 +97,16 @@ impl Call<'_> {
.rest_const(working_set, starting_pos)
}
/// Returns a span covering the call's arguments.
pub fn arguments_span(&self) -> Span {
match &self.inner {
CallImpl::AstRef(call) => call.arguments_span(),
CallImpl::AstBox(call) => call.arguments_span(),
CallImpl::IrRef(call) => call.arguments_span(),
CallImpl::IrBox(call) => call.arguments_span(),
}
}
/// Returns a span covering the whole call.
pub fn span(&self) -> Span {
match &self.inner {

View File

@ -175,7 +175,7 @@ impl CallBuilder {
self.inner.args_base = stack.argument_stack.get_base();
}
self.inner.args_len += 1;
self.inner.span.end = self.inner.span.end.max(argument.span().end);
self.inner.span = self.inner.span.append(argument.span());
stack.argument_stack.push(argument);
self
}