string, glob interpolation
This commit is contained in:
parent
37255bef3c
commit
b63b0217bd
|
@ -157,6 +157,11 @@ impl BlockBuilder {
|
||||||
decl_id: _,
|
decl_id: _,
|
||||||
src_dst: _,
|
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::ListPush { src_dst: _, item } => self.free_register(*item)?,
|
||||||
Instruction::ListSpread { src_dst: _, items } => self.free_register(*items)?,
|
Instruction::ListSpread { src_dst: _, items } => self.free_register(*items)?,
|
||||||
Instruction::RecordInsert {
|
Instruction::RecordInsert {
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub enum CompileError {
|
||||||
RunExternalNotFound,
|
RunExternalNotFound,
|
||||||
InvalidLhsForAssignment(Span),
|
InvalidLhsForAssignment(Span),
|
||||||
ModifyImmutableVariable(Span),
|
ModifyImmutableVariable(Span),
|
||||||
|
UnexpectedExpr(&'static str),
|
||||||
Todo(&'static str),
|
Todo(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +57,9 @@ impl CompileError {
|
||||||
CompileError::ModifyImmutableVariable(_) => {
|
CompileError::ModifyImmutableVariable(_) => {
|
||||||
"attempted to modify immutable variable".into()
|
"attempted to modify immutable variable".into()
|
||||||
}
|
}
|
||||||
|
CompileError::UnexpectedExpr(msg) => {
|
||||||
|
format!("unexpected expression in this context: {msg}")
|
||||||
|
}
|
||||||
CompileError::Todo(msg) => {
|
CompileError::Todo(msg) => {
|
||||||
format!("TODO: {msg}")
|
format!("TODO: {msg}")
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ use super::{
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{CellPath, Expr, Expression, ListItem, RecordItem},
|
ast::{CellPath, Expr, Expression, ListItem, RecordItem},
|
||||||
engine::StateWorkingSet,
|
engine::StateWorkingSet,
|
||||||
ir::{Instruction, Literal},
|
ir::{DataSlice, Instruction, Literal},
|
||||||
IntoSpanned, RegId, ENV_VARIABLE_ID,
|
IntoSpanned, RegId, Type, ENV_VARIABLE_ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn compile_expression(
|
pub(crate) fn compile_expression(
|
||||||
|
@ -124,7 +124,7 @@ pub(crate) fn compile_expression(
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Expr::VarDecl(_) => Err(CompileError::Todo("VarDecl")),
|
Expr::VarDecl(_) => Err(CompileError::UnexpectedExpr("VarDecl")),
|
||||||
Expr::Call(call) => {
|
Expr::Call(call) => {
|
||||||
move_in_reg_to_out_reg(builder)?;
|
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)
|
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::RowCondition(_) => Err(CompileError::Todo("RowCondition")),
|
||||||
Expr::UnaryNot(subexpr) => {
|
Expr::UnaryNot(subexpr) => {
|
||||||
drop_input(builder)?;
|
drop_input(builder)?;
|
||||||
|
@ -354,7 +354,7 @@ pub(crate) fn compile_expression(
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Expr::Keyword(_) => Err(CompileError::Todo("Keyword")),
|
Expr::Keyword(_) => Err(CompileError::UnexpectedExpr("Keyword")),
|
||||||
Expr::ValueWithUnit(_) => Err(CompileError::Todo("ValueWithUnit")),
|
Expr::ValueWithUnit(_) => Err(CompileError::Todo("ValueWithUnit")),
|
||||||
Expr::DateTime(_) => Err(CompileError::Todo("DateTime")),
|
Expr::DateTime(_) => Err(CompileError::Todo("DateTime")),
|
||||||
Expr::Filepath(path, no_expand) => {
|
Expr::Filepath(path, no_expand) => {
|
||||||
|
@ -430,8 +430,57 @@ pub(crate) fn compile_expression(
|
||||||
Expr::ImportPattern(_) => Err(CompileError::Todo("ImportPattern")),
|
Expr::ImportPattern(_) => Err(CompileError::Todo("ImportPattern")),
|
||||||
Expr::Overlay(_) => Err(CompileError::Todo("Overlay")),
|
Expr::Overlay(_) => Err(CompileError::Todo("Overlay")),
|
||||||
Expr::Signature(_) => ignore(builder), // no effect
|
Expr::Signature(_) => ignore(builder), // no effect
|
||||||
Expr::StringInterpolation(_) => Err(CompileError::Todo("StringInterpolation")),
|
Expr::StringInterpolation(exprs) | Expr::GlobInterpolation(exprs, _) => {
|
||||||
Expr::GlobInterpolation(_, _) => Err(CompileError::Todo("GlobInterpolation")),
|
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::Nothing => lit(builder, Literal::Nothing),
|
||||||
Expr::Garbage => Err(CompileError::Garbage),
|
Expr::Garbage => Err(CompileError::Garbage),
|
||||||
}
|
}
|
||||||
|
|
|
@ -299,6 +299,31 @@ fn eval_instruction(
|
||||||
ctx.put_reg(*src_dst, result);
|
ctx.put_reg(*src_dst, result);
|
||||||
Ok(Continue)
|
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 } => {
|
Instruction::ListPush { src_dst, item } => {
|
||||||
let list_value = ctx.collect_reg(*src_dst, *span)?;
|
let list_value = ctx.collect_reg(*src_dst, *span)?;
|
||||||
let item = ctx.collect_reg(*item, *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);
|
let decl = FmtDecl::new(self.engine_state, *decl_id);
|
||||||
write!(f, "{:WIDTH$} {decl}, {src_dst}", "call")
|
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 } => {
|
Instruction::ListPush { src_dst, item } => {
|
||||||
write!(f, "{:WIDTH$} {src_dst}, {item}", "list-push")
|
write!(f, "{:WIDTH$} {src_dst}, {item}", "list-push")
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,13 @@ pub struct DataSlice {
|
||||||
pub len: u32,
|
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] {
|
impl std::ops::Index<DataSlice> for [u8] {
|
||||||
type Output = [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`,
|
/// 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.
|
/// overwriting it. The argument stack is used implicitly and cleared when the call ends.
|
||||||
Call { decl_id: DeclId, src_dst: RegId },
|
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.
|
/// Push a value onto the end of a list. Used to construct list literals.
|
||||||
ListPush { src_dst: RegId, item: RegId },
|
ListPush { src_dst: RegId, item: RegId },
|
||||||
/// Spread a value onto the end of a list. Used to construct list literals.
|
/// Spread a value onto the end of a list. Used to construct list literals.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user