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 { 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 { match item {
ListItem::Item(expr) => { ListItem::Item(_) => {
// Add each item using push-list // Add each item using list-push
let item_reg = builder.next_register()?;
compile_expression(
working_set,
builder,
expr,
redirect_modes.with_capture_out(expr.span),
None,
item_reg,
)?;
builder.push( builder.push(
Instruction::ListPush { Instruction::ListPush {
src_dst: out_reg, src_dst: out_reg,
item: item_reg, item: reg,
} }
.into_spanned(expr.span), .into_spanned(expr.span),
)?; )?;
} }
ListItem::Spread(spread_span, expr) => { ListItem::Spread(spread_span, expr) => {
// Implement a spread as a ++ binary operation // Spread the list using list-spread
let rhs_reg = builder.next_register()?;
compile_expression(
working_set,
builder,
expr,
redirect_modes.with_capture_out(expr.span),
None,
rhs_reg,
)?;
builder.push( builder.push(
Instruction::BinaryOp { Instruction::ListSpread {
lhs_dst: out_reg, src_dst: out_reg,
op: Operator::Math(Math::Append), items: reg,
rhs: rhs_reg,
} }
.into_spanned(*spread_span), .into_spanned(*spread_span),
)?; )?;
@ -471,7 +466,7 @@ fn compile_expression(
working_set, working_set,
builder, builder,
key, key,
redirect_modes.with_capture_out(expr.span), redirect_modes.with_capture_out(key.span),
None, None,
key_reg, key_reg,
)?; )?;
@ -479,7 +474,7 @@ fn compile_expression(
working_set, working_set,
builder, builder,
val, val,
redirect_modes.with_capture_out(expr.span), redirect_modes.with_capture_out(val.span),
None, None,
val_reg, val_reg,
)?; )?;
@ -493,7 +488,23 @@ fn compile_expression(
)?; )?;
} }
RecordItem::Spread(spread_span, expr) => { 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: _, src_dst: _,
} => (), } => (),
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::RecordInsert { Instruction::RecordInsert {
src_dst: _, src_dst: _,
key, key,
@ -931,6 +943,7 @@ impl BlockBuilder {
self.free_register(*key)?; self.free_register(*key)?;
self.free_register(*val)?; self.free_register(*val)?;
} }
Instruction::RecordSpread { src_dst: _, items } => self.free_register(*items)?,
Instruction::BinaryOp { Instruction::BinaryOp {
lhs_dst: _, lhs_dst: _,
op: _, op: _,

View File

@ -304,6 +304,20 @@ fn eval_instruction(
ctx.put_reg(*src_dst, Value::list(list, list_span).into_pipeline_data()); ctx.put_reg(*src_dst, Value::list(list, list_span).into_pipeline_data());
Ok(Continue) 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 } => { Instruction::RecordInsert { src_dst, key, val } => {
let record_value = ctx.collect_reg(*src_dst, *span)?; let record_value = ctx.collect_reg(*src_dst, *span)?;
let key = ctx.collect_reg(*key, *span)?; let key = ctx.collect_reg(*key, *span)?;
@ -317,6 +331,25 @@ fn eval_instruction(
); );
Ok(Continue) 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::BinaryOp { lhs_dst, op, rhs } => binary_op(ctx, *lhs_dst, op, *rhs, *span),
Instruction::FollowCellPath { src_dst, path } => { Instruction::FollowCellPath { src_dst, path } => {
let data = ctx.take_reg(*src_dst); 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 } => { Instruction::ListPush { src_dst, item } => {
write!(f, "{:WIDTH$} {src_dst}, {item}", "list-push") 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 } => { Instruction::RecordInsert { src_dst, key, val } => {
write!(f, "{:WIDTH$} {src_dst}, {key}, {val}", "record-insert") 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 } => { Instruction::BinaryOp { lhs_dst, op, rhs } => {
write!(f, "{:WIDTH$} {lhs_dst}, {op:?}, {rhs}", "binary-op") 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 }, Call { decl_id: DeclId, src_dst: RegId },
/// 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 },
/// 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 { RecordInsert {
src_dst: RegId, src_dst: RegId,
key: RegId, key: RegId,
val: 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 /// Do a binary operation on `lhs_dst` (left) and `rhs` (right) and write the result to
/// `lhs_dst`. /// `lhs_dst`.
BinaryOp { BinaryOp {