string, glob interpolation
This commit is contained in:
parent
37255bef3c
commit
b63b0217bd
|
@ -157,6 +157,11 @@ impl BlockBuilder {
|
|||
decl_id: _,
|
||||
src_dst: _,
|
||||
} => (),
|
||||
Instruction::StringAppend { src_dst: _, val } => self.free_register(*val)?,
|
||||
Instruction::GlobFrom {
|
||||
src_dst: _,
|
||||
no_expand: _,
|
||||
} => (),
|
||||
Instruction::ListPush { src_dst: _, item } => self.free_register(*item)?,
|
||||
Instruction::ListSpread { src_dst: _, items } => self.free_register(*items)?,
|
||||
Instruction::RecordInsert {
|
||||
|
|
|
@ -21,6 +21,7 @@ pub enum CompileError {
|
|||
RunExternalNotFound,
|
||||
InvalidLhsForAssignment(Span),
|
||||
ModifyImmutableVariable(Span),
|
||||
UnexpectedExpr(&'static str),
|
||||
Todo(&'static str),
|
||||
}
|
||||
|
||||
|
@ -56,6 +57,9 @@ impl CompileError {
|
|||
CompileError::ModifyImmutableVariable(_) => {
|
||||
"attempted to modify immutable variable".into()
|
||||
}
|
||||
CompileError::UnexpectedExpr(msg) => {
|
||||
format!("unexpected expression in this context: {msg}")
|
||||
}
|
||||
CompileError::Todo(msg) => {
|
||||
format!("TODO: {msg}")
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ use super::{
|
|||
use nu_protocol::{
|
||||
ast::{CellPath, Expr, Expression, ListItem, RecordItem},
|
||||
engine::StateWorkingSet,
|
||||
ir::{Instruction, Literal},
|
||||
IntoSpanned, RegId, ENV_VARIABLE_ID,
|
||||
ir::{DataSlice, Instruction, Literal},
|
||||
IntoSpanned, RegId, Type, ENV_VARIABLE_ID,
|
||||
};
|
||||
|
||||
pub(crate) fn compile_expression(
|
||||
|
@ -124,7 +124,7 @@ pub(crate) fn compile_expression(
|
|||
)?;
|
||||
Ok(())
|
||||
}
|
||||
Expr::VarDecl(_) => Err(CompileError::Todo("VarDecl")),
|
||||
Expr::VarDecl(_) => Err(CompileError::UnexpectedExpr("VarDecl")),
|
||||
Expr::Call(call) => {
|
||||
move_in_reg_to_out_reg(builder)?;
|
||||
|
||||
|
@ -135,7 +135,7 @@ pub(crate) fn compile_expression(
|
|||
|
||||
compile_external_call(working_set, builder, head, args, redirect_modes, out_reg)
|
||||
}
|
||||
Expr::Operator(_) => Err(CompileError::Todo("Operator")),
|
||||
Expr::Operator(_) => Err(CompileError::UnexpectedExpr("Operator")),
|
||||
Expr::RowCondition(_) => Err(CompileError::Todo("RowCondition")),
|
||||
Expr::UnaryNot(subexpr) => {
|
||||
drop_input(builder)?;
|
||||
|
@ -354,7 +354,7 @@ pub(crate) fn compile_expression(
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
Expr::Keyword(_) => Err(CompileError::Todo("Keyword")),
|
||||
Expr::Keyword(_) => Err(CompileError::UnexpectedExpr("Keyword")),
|
||||
Expr::ValueWithUnit(_) => Err(CompileError::Todo("ValueWithUnit")),
|
||||
Expr::DateTime(_) => Err(CompileError::Todo("DateTime")),
|
||||
Expr::Filepath(path, no_expand) => {
|
||||
|
@ -430,8 +430,57 @@ pub(crate) fn compile_expression(
|
|||
Expr::ImportPattern(_) => Err(CompileError::Todo("ImportPattern")),
|
||||
Expr::Overlay(_) => Err(CompileError::Todo("Overlay")),
|
||||
Expr::Signature(_) => ignore(builder), // no effect
|
||||
Expr::StringInterpolation(_) => Err(CompileError::Todo("StringInterpolation")),
|
||||
Expr::GlobInterpolation(_, _) => Err(CompileError::Todo("GlobInterpolation")),
|
||||
Expr::StringInterpolation(exprs) | Expr::GlobInterpolation(exprs, _) => {
|
||||
let mut exprs_iter = exprs.iter().peekable();
|
||||
|
||||
if exprs_iter.peek().is_some_and(|e| e.ty == Type::String) {
|
||||
// If the first expression is typed as a string, just take it and build from that
|
||||
compile_expression(
|
||||
working_set,
|
||||
builder,
|
||||
exprs_iter.next().expect("peek() was Some"),
|
||||
redirect_modes.with_capture_out(expr.span),
|
||||
None,
|
||||
out_reg,
|
||||
)?;
|
||||
} else {
|
||||
// Start with an empty string
|
||||
lit(builder, Literal::String(DataSlice::empty()))?;
|
||||
}
|
||||
|
||||
// Compile each expression and append to out_reg
|
||||
for expr in exprs_iter {
|
||||
let scratch_reg = builder.next_register()?;
|
||||
compile_expression(
|
||||
working_set,
|
||||
builder,
|
||||
expr,
|
||||
redirect_modes.with_capture_out(expr.span),
|
||||
None,
|
||||
scratch_reg,
|
||||
)?;
|
||||
builder.push(
|
||||
Instruction::StringAppend {
|
||||
src_dst: out_reg,
|
||||
val: scratch_reg,
|
||||
}
|
||||
.into_spanned(expr.span),
|
||||
)?;
|
||||
}
|
||||
|
||||
// If it's a glob interpolation, change it to a glob
|
||||
if let Expr::GlobInterpolation(_, no_expand) = expr.expr {
|
||||
builder.push(
|
||||
Instruction::GlobFrom {
|
||||
src_dst: out_reg,
|
||||
no_expand,
|
||||
}
|
||||
.into_spanned(expr.span),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Expr::Nothing => lit(builder, Literal::Nothing),
|
||||
Expr::Garbage => Err(CompileError::Garbage),
|
||||
}
|
||||
|
|
|
@ -299,6 +299,31 @@ fn eval_instruction(
|
|||
ctx.put_reg(*src_dst, result);
|
||||
Ok(Continue)
|
||||
}
|
||||
Instruction::StringAppend { src_dst, val } => {
|
||||
let string_value = ctx.collect_reg(*src_dst, *span)?;
|
||||
let operand_value = ctx.collect_reg(*val, *span)?;
|
||||
let string_span = string_value.span();
|
||||
|
||||
let mut string = string_value.into_string()?;
|
||||
let operand = if let Value::String { val, .. } = operand_value {
|
||||
// Small optimization, so we don't have to copy the string *again*
|
||||
val
|
||||
} else {
|
||||
operand_value.to_expanded_string(", ", ctx.engine_state.get_config())
|
||||
};
|
||||
string.push_str(&operand);
|
||||
|
||||
let new_string_value = Value::string(string, string_span);
|
||||
ctx.put_reg(*src_dst, new_string_value.into_pipeline_data());
|
||||
Ok(Continue)
|
||||
}
|
||||
Instruction::GlobFrom { src_dst, no_expand } => {
|
||||
let string_value = ctx.collect_reg(*src_dst, *span)?;
|
||||
let string = string_value.into_string()?;
|
||||
let glob_value = Value::glob(string, *no_expand, *span);
|
||||
ctx.put_reg(*src_dst, glob_value.into_pipeline_data());
|
||||
Ok(Continue)
|
||||
}
|
||||
Instruction::ListPush { src_dst, item } => {
|
||||
let list_value = ctx.collect_reg(*src_dst, *span)?;
|
||||
let item = ctx.collect_reg(*item, *span)?;
|
||||
|
|
|
@ -115,6 +115,13 @@ impl<'a> fmt::Display for FmtInstruction<'a> {
|
|||
let decl = FmtDecl::new(self.engine_state, *decl_id);
|
||||
write!(f, "{:WIDTH$} {decl}, {src_dst}", "call")
|
||||
}
|
||||
Instruction::StringAppend { src_dst, val } => {
|
||||
write!(f, "{:WIDTH$} {src_dst}, {val}", "string-append")
|
||||
}
|
||||
Instruction::GlobFrom { src_dst, no_expand } => {
|
||||
let no_expand = if *no_expand { "no-expand" } else { "expand" };
|
||||
write!(f, "{:WIDTH$} {src_dst}, {no_expand}", "glob-from",)
|
||||
}
|
||||
Instruction::ListPush { src_dst, item } => {
|
||||
write!(f, "{:WIDTH$} {src_dst}, {item}", "list-push")
|
||||
}
|
||||
|
|
|
@ -42,6 +42,13 @@ pub struct DataSlice {
|
|||
pub len: u32,
|
||||
}
|
||||
|
||||
impl DataSlice {
|
||||
/// A data slice that contains no data. This slice is always valid.
|
||||
pub const fn empty() -> DataSlice {
|
||||
DataSlice { start: 0, len: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<DataSlice> for [u8] {
|
||||
type Output = [u8];
|
||||
|
||||
|
@ -90,6 +97,11 @@ pub enum Instruction {
|
|||
/// Make a call. The input is taken from `src_dst`, and the output is placed in `src_dst`,
|
||||
/// overwriting it. The argument stack is used implicitly and cleared when the call ends.
|
||||
Call { decl_id: DeclId, src_dst: RegId },
|
||||
/// Append a value onto the end of a string. Uses `to_expanded_string(", ", ...)` on the value.
|
||||
/// Used for string interpolation literals. Not the same thing as the `++` operator.
|
||||
StringAppend { src_dst: RegId, val: RegId },
|
||||
/// Convert a string into a glob. Used for glob interpolation.
|
||||
GlobFrom { src_dst: RegId, no_expand: bool },
|
||||
/// Push a value onto the end of a list. Used to construct list literals.
|
||||
ListPush { src_dst: RegId, item: RegId },
|
||||
/// Spread a value onto the end of a list. Used to construct list literals.
|
||||
|
|
Loading…
Reference in New Issue
Block a user