instructions: list-spread, record-spread

This commit is contained in:
Devyn Cairns 2024-06-26 16:56:09 -07:00
parent ed59f80b35
commit 73b092bfdc
No known key found for this signature in database
4 changed files with 88 additions and 30 deletions

View File

@ -344,42 +344,37 @@ fn compile_expression(
},
)?;
for item in items {
// Compile the expression of the item / spread
let reg = builder.next_register()?;
let expr = match item {
ListItem::Item(expr) | ListItem::Spread(_, expr) => expr,
};
compile_expression(
working_set,
builder,
expr,
redirect_modes.with_capture_out(expr.span),
None,
reg,
)?;
match item {
ListItem::Item(expr) => {
// Add each item using push-list
let item_reg = builder.next_register()?;
compile_expression(
working_set,
builder,
expr,
redirect_modes.with_capture_out(expr.span),
None,
item_reg,
)?;
ListItem::Item(_) => {
// Add each item using list-push
builder.push(
Instruction::ListPush {
src_dst: out_reg,
item: item_reg,
item: reg,
}
.into_spanned(expr.span),
)?;
}
ListItem::Spread(spread_span, expr) => {
// Implement a spread as a ++ binary operation
let rhs_reg = builder.next_register()?;
compile_expression(
working_set,
builder,
expr,
redirect_modes.with_capture_out(expr.span),
None,
rhs_reg,
)?;
// Spread the list using list-spread
builder.push(
Instruction::BinaryOp {
lhs_dst: out_reg,
op: Operator::Math(Math::Append),
rhs: rhs_reg,
Instruction::ListSpread {
src_dst: out_reg,
items: reg,
}
.into_spanned(*spread_span),
)?;
@ -471,7 +466,7 @@ fn compile_expression(
working_set,
builder,
key,
redirect_modes.with_capture_out(expr.span),
redirect_modes.with_capture_out(key.span),
None,
key_reg,
)?;
@ -479,7 +474,7 @@ fn compile_expression(
working_set,
builder,
val,
redirect_modes.with_capture_out(expr.span),
redirect_modes.with_capture_out(val.span),
None,
val_reg,
)?;
@ -493,7 +488,23 @@ fn compile_expression(
)?;
}
RecordItem::Spread(spread_span, expr) => {
return Err(CompileError::Todo("Record with spread"))
// Spread the expression using record-spread
let reg = builder.next_register()?;
compile_expression(
working_set,
builder,
expr,
redirect_modes.with_capture_out(expr.span),
None,
reg,
)?;
builder.push(
Instruction::RecordSpread {
src_dst: out_reg,
items: reg,
}
.into_spanned(*spread_span),
)?;
}
}
}
@ -923,6 +934,7 @@ impl BlockBuilder {
src_dst: _,
} => (),
Instruction::ListPush { src_dst: _, item } => self.free_register(*item)?,
Instruction::ListSpread { src_dst: _, items } => self.free_register(*items)?,
Instruction::RecordInsert {
src_dst: _,
key,
@ -931,6 +943,7 @@ impl BlockBuilder {
self.free_register(*key)?;
self.free_register(*val)?;
}
Instruction::RecordSpread { src_dst: _, items } => self.free_register(*items)?,
Instruction::BinaryOp {
lhs_dst: _,
op: _,

View File

@ -304,6 +304,20 @@ fn eval_instruction(
ctx.put_reg(*src_dst, Value::list(list, list_span).into_pipeline_data());
Ok(Continue)
}
Instruction::ListSpread { src_dst, items } => {
let list_value = ctx.collect_reg(*src_dst, *span)?;
let items = ctx.collect_reg(*items, *span)?;
let list_span = list_value.span();
let items_span = items.span();
let mut list = list_value.into_list()?;
list.extend(
items
.into_list()
.map_err(|_| ShellError::CannotSpreadAsList { span: items_span })?,
);
ctx.put_reg(*src_dst, Value::list(list, list_span).into_pipeline_data());
Ok(Continue)
}
Instruction::RecordInsert { src_dst, key, val } => {
let record_value = ctx.collect_reg(*src_dst, *span)?;
let key = ctx.collect_reg(*key, *span)?;
@ -317,6 +331,25 @@ fn eval_instruction(
);
Ok(Continue)
}
Instruction::RecordSpread { src_dst, items } => {
let record_value = ctx.collect_reg(*src_dst, *span)?;
let items = ctx.collect_reg(*items, *span)?;
let record_span = record_value.span();
let items_span = items.span();
let mut record = record_value.into_record()?;
// Not using .extend() here because it doesn't handle duplicates
for (key, val) in items
.into_record()
.map_err(|_| ShellError::CannotSpreadAsRecord { span: items_span })?
{
record.insert(key, val);
}
ctx.put_reg(
*src_dst,
Value::record(record, record_span).into_pipeline_data(),
);
Ok(Continue)
}
Instruction::BinaryOp { lhs_dst, op, rhs } => binary_op(ctx, *lhs_dst, op, *rhs, *span),
Instruction::FollowCellPath { src_dst, path } => {
let data = ctx.take_reg(*src_dst);

View File

@ -115,9 +115,15 @@ impl<'a> fmt::Display for FmtInstruction<'a> {
Instruction::ListPush { src_dst, item } => {
write!(f, "{:WIDTH$} {src_dst}, {item}", "list-push")
}
Instruction::ListSpread { src_dst, items } => {
write!(f, "{:WIDTH$} {src_dst}, {items}", "list-spread")
}
Instruction::RecordInsert { src_dst, key, val } => {
write!(f, "{:WIDTH$} {src_dst}, {key}, {val}", "record-insert")
}
Instruction::RecordSpread { src_dst, items } => {
write!(f, "{:WIDTH$} {src_dst}, {items}", "record-spread")
}
Instruction::BinaryOp { lhs_dst, op, rhs } => {
write!(f, "{:WIDTH$} {lhs_dst}, {op:?}, {rhs}", "binary-op")
}

View File

@ -90,12 +90,18 @@ pub enum Instruction {
Call { decl_id: DeclId, src_dst: RegId },
/// Push a value onto the end of a list. Used to construct list literals.
ListPush { src_dst: RegId, item: RegId },
/// Insert a key-value pair into a record. Any existing value for the key is overwritten.
/// Spread a value onto the end of a list. Used to construct list literals.
ListSpread { src_dst: RegId, items: RegId },
/// Insert a key-value pair into a record. Used to construct record literals. Any existing value
/// for the key is overwritten.
RecordInsert {
src_dst: RegId,
key: RegId,
val: RegId,
},
/// Spread a record onto a record. Used to construct record literals. Any existing value for the
/// key is overwritten.
RecordSpread { src_dst: RegId, items: RegId },
/// Do a binary operation on `lhs_dst` (left) and `rhs` (right) and write the result to
/// `lhs_dst`.
BinaryOp {