support parser info

This commit is contained in:
Devyn Cairns 2024-07-01 03:07:23 -07:00
parent 67bd88535c
commit dca2798d4d
No known key found for this signature in database
13 changed files with 109 additions and 46 deletions

View File

@ -61,14 +61,13 @@ impl Command for OverlayUse {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let call = call.assert_ast_call()?; // FIXME
let mut name_arg: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
name_arg.item = trim_quotes_str(&name_arg.item).to_string();
let maybe_origin_module_id =
if let Some(overlay_expr) = call.get_parser_info("overlay_expr") {
if let Some(overlay_expr) = call.get_parser_info(caller_stack, "overlay_expr") {
if let Expr::Overlay(module_id) = &overlay_expr.expr {
module_id
module_id.clone()
} else {
return Err(ShellError::NushellFailedSpanned {
msg: "Not an overlay".to_string(),
@ -111,7 +110,7 @@ impl Command for OverlayUse {
// a) adding a new overlay
// b) refreshing an active overlay (the origin module changed)
let module = engine_state.get_module(*module_id);
let module = engine_state.get_module(module_id);
// Evaluate the export-env block (if any) and keep its environment
if let Some(block_id) = module.env_block {
@ -119,7 +118,7 @@ impl Command for OverlayUse {
&name_arg.item,
engine_state,
caller_stack,
get_dirs_var_from_call(call),
get_dirs_var_from_call(caller_stack, call),
)?;
let block = engine_state.get_block(block_id);

View File

@ -54,11 +54,10 @@ This command is a parser keyword. For details, check:
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let call = call.assert_ast_call()?; // FIXME
let Some(Expression {
expr: Expr::ImportPattern(import_pattern),
..
}) = call.get_parser_info("import_pattern")
}) = call.get_parser_info(caller_stack, "import_pattern")
else {
return Err(ShellError::GenericError {
error: "Unexpected import".into(),
@ -69,6 +68,9 @@ This command is a parser keyword. For details, check:
});
};
// Necessary so that we can modify the stack.
let import_pattern = import_pattern.clone();
if let Some(module_id) = import_pattern.head.id {
// Add constants
for var_id in &import_pattern.constants {
@ -100,7 +102,7 @@ This command is a parser keyword. For details, check:
&module_arg_str,
engine_state,
caller_stack,
get_dirs_var_from_call(call),
get_dirs_var_from_call(caller_stack, call),
)?;
let maybe_parent = maybe_file_path
.as_ref()

View File

@ -48,12 +48,9 @@ impl Command for BytesBuild {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let call = call.assert_ast_call()?; // FIXME
let mut output = vec![];
for val in call.rest_iter_flattened(0, |expr| {
let eval_expression = get_eval_expression(engine_state);
eval_expression(engine_state, stack, expr)
})? {
let eval_expression = get_eval_expression(engine_state);
for val in call.rest_iter_flattened(engine_state, stack, eval_expression, 0)? {
let val_span = val.span();
match val {
Value::Binary { mut val, .. } => output.append(&mut val),

View File

@ -45,7 +45,6 @@ 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
@ -57,7 +56,7 @@ impl Command for SourceEnv {
&source_filename.item,
engine_state,
caller_stack,
get_dirs_var_from_call(call),
get_dirs_var_from_call(caller_stack, call),
)? {
PathBuf::from(&path)
} else {

View File

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

View File

@ -293,7 +293,21 @@ impl CallExt for ir::Call {
stack: &mut Stack,
name: &str,
) -> Result<T, ShellError> {
todo!("req_parser_info is not yet implemented on ir::Call")
// FIXME: this depends on the AST evaluator. We can fix this by making the parser info an
// enum rather than using expressions. It's not clear that evaluation of this is ever really
// needed.
if let Some(expr) = self.get_parser_info(stack, name) {
let expr = expr.clone();
let stack = &mut stack.use_call_arg_out_dest();
let result = eval_expression::<WithoutDebug>(engine_state, stack, &expr)?;
FromValue::from_value(result)
} else {
Err(ShellError::CantFindColumn {
col_name: name.into(),
span: None,
src_span: self.head,
})
}
}
fn has_positional_args(&self, stack: &Stack, starting_pos: usize) -> bool {

View File

@ -1,8 +1,8 @@
use crate::ClosureEvalOnce;
use nu_path::canonicalize_with;
use nu_protocol::{
ast::{Call, Expr},
engine::{EngineState, Stack, StateWorkingSet},
ast::Expr,
engine::{Call, EngineState, Stack, StateWorkingSet},
Config, ShellError, Span, Value, VarId,
};
use std::{
@ -244,15 +244,15 @@ 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 {
Some(id)
} else {
None
}
})
pub fn get_dirs_var_from_call(stack: &Stack, call: &Call) -> Option<VarId> {
call.get_parser_info(stack, DIR_VAR_PARSER_INFO)
.and_then(|x| {
if let Expr::Var(id) = x.expr {
Some(id)
} else {
None
}
})
}
/// This helper function is used to find files during eval

View File

@ -662,7 +662,11 @@ fn eval_call(
.argument_stack
.get_args(*args_base, args_len)
.into_iter()
.fold(head, |span, arg| span.append(arg.span()));
.fold(head, |span, arg| {
arg.span()
.map(|arg_span| span.append(arg_span))
.unwrap_or(span)
});
let call = Call {
decl_id,
head,

View File

@ -189,7 +189,9 @@ fn ir_call_to_extern_call(
extern_call.add_argument(stack, name_arg);
extern_call.add_argument(stack, val_arg);
}
a @ (engine::Argument::Positional { .. } | engine::Argument::Spread { .. }) => {
a @ (engine::Argument::Positional { .. }
| engine::Argument::Spread { .. }
| engine::Argument::ParserInfo { .. }) => {
let argument = a.clone();
extern_call.add_argument(stack, argument);
}

View File

@ -19,6 +19,8 @@ impl MatchPattern {
pub enum Pattern {
Record(Vec<(String, MatchPattern)>),
List(Vec<MatchPattern>),
// TODO: it would be nice if this didn't depend on AST
// maybe const evaluation can get us to a Value instead?
Value(Expression),
Variable(VarId),
Or(Vec<MatchPattern>),

View File

@ -1,19 +1,14 @@
use std::sync::Arc;
use crate::{ir::DataSlice, Span, Value};
use crate::{ast::Expression, ir::DataSlice, Span, Value};
/// Represents a fully evaluated argument to a call.
#[derive(Debug, Clone)]
pub enum Argument {
/// A positional argument
Positional {
span: Span,
val: Value,
},
Spread {
span: Span,
vals: Value,
},
Positional { span: Span, val: Value },
/// A spread argument, e.g. `...$args`
Spread { span: Span, vals: Value },
/// A named argument with no value, e.g. `--flag`
Flag {
data: Arc<[u8]>,
@ -27,17 +22,27 @@ pub enum Argument {
span: Span,
val: Value,
},
/// Information generated by the parser for use by certain keyword commands
ParserInfo {
data: Arc<[u8]>,
name: DataSlice,
// TODO: rather than `Expression`, this would probably be best served by a specific enum
// type for this purpose.
expr: Box<Expression>,
},
}
impl Argument {
/// The span encompassing the argument's usage within the call, distinct from the span of the
/// actual value of the argument.
pub fn span(&self) -> Span {
pub fn span(&self) -> Option<Span> {
match self {
Argument::Positional { span, .. } => *span,
Argument::Spread { span, .. } => *span,
Argument::Flag { span, .. } => *span,
Argument::Named { span, .. } => *span,
Argument::Positional { span, .. } => Some(*span),
Argument::Spread { span, .. } => Some(*span),
Argument::Flag { span, .. } => Some(*span),
Argument::Named { span, .. } => Some(*span),
// Because `ParserInfo` is generated, its span shouldn't be used
Argument::ParserInfo { .. } => None,
}
}
}

View File

@ -1,4 +1,7 @@
use crate::{ast, ir, DeclId, FromValue, ShellError, Span, Value};
use crate::{
ast::{self, Expression},
ir, DeclId, FromValue, ShellError, Span, Value,
};
use super::{EngineState, Stack, StateWorkingSet};
@ -117,6 +120,16 @@ impl Call<'_> {
}
}
/// Get a parser info argument by name.
pub fn get_parser_info<'a>(&'a self, stack: &'a Stack, name: &str) -> Option<&'a Expression> {
match &self.inner {
CallImpl::AstRef(call) => call.get_parser_info(name),
CallImpl::AstBox(call) => call.get_parser_info(name),
CallImpl::IrRef(call) => call.get_parser_info(stack, name),
CallImpl::IrBox(call) => call.get_parser_info(stack, name),
}
}
/// Evaluator-agnostic implementation of `rest_iter_flattened()`. Evaluates or gets all of the
/// positional and spread arguments, flattens spreads, and then returns one list of values.
pub fn rest_iter_flattened(

View File

@ -1,6 +1,7 @@
use std::sync::Arc;
use crate::{
ast::Expression,
engine::{self, Argument, Stack},
DeclId, ShellError, Span, Spanned, Value,
};
@ -55,6 +56,7 @@ impl Call {
Span::new(past.start, self.span.end)
}
/// The number of named arguments, with or without values.
pub fn named_len(&self, stack: &Stack) -> usize {
self.arguments(stack)
.iter()
@ -62,6 +64,7 @@ impl Call {
.count()
}
/// Iterate through named arguments, with or without values.
pub fn named_iter<'a>(
&'a self,
stack: &'a Stack,
@ -97,6 +100,8 @@ impl Call {
)
}
/// Get a named argument's value by name. Returns [`None`] for named arguments with no value as
/// well.
pub fn get_named_arg<'a>(&self, stack: &'a Stack, flag_name: &str) -> Option<&'a Value> {
// Optimized to avoid str::from_utf8()
self.arguments(stack)
@ -115,6 +120,7 @@ impl Call {
.flatten()
}
/// The number of positional arguments, excluding spread arguments.
pub fn positional_len(&self, stack: &Stack) -> usize {
self.arguments(stack)
.iter()
@ -122,6 +128,7 @@ impl Call {
.count()
}
/// Iterate through positional arguments. Does not include spread arguments.
pub fn positional_iter<'a>(&self, stack: &'a Stack) -> impl Iterator<Item = &'a Value> {
self.arguments(stack).iter().filter_map(|arg| match arg {
Argument::Positional { val, .. } => Some(val),
@ -129,6 +136,7 @@ impl Call {
})
}
/// Get a positional argument by index. Does not include spread arguments.
pub fn positional_nth<'a>(&self, stack: &'a Stack, index: usize) -> Option<&'a Value> {
self.positional_iter(stack).nth(index)
}
@ -150,6 +158,8 @@ impl Call {
.skip(start)
}
/// Returns all of the positional arguments including and after `start`, with spread arguments
/// flattened into a single `Vec`.
pub fn rest_iter_flattened(
&self,
stack: &Stack,
@ -174,6 +184,20 @@ impl Call {
Ok(acc)
}
/// Get a parser info argument by name.
pub fn get_parser_info<'a>(&self, stack: &'a Stack, name: &str) -> Option<&'a Expression> {
self.arguments(stack)
.iter()
.find_map(|argument| match argument {
Argument::ParserInfo {
data,
name: name_slice,
expr,
} if &data[*name_slice] == name.as_bytes() => Some(expr.as_ref()),
_ => None,
})
}
/// Returns a span encompassing the entire call.
pub fn span(&self) -> Span {
self.span
@ -197,7 +221,9 @@ impl CallBuilder {
self.inner.args_base = stack.argument_stack.get_base();
}
self.inner.args_len += 1;
self.inner.span = self.inner.span.append(argument.span());
if let Some(span) = argument.span() {
self.inner.span = self.inner.span.append(span);
}
stack.argument_stack.push(argument);
self
}