list, record, table expression support
This commit is contained in:
parent
7890b3f27a
commit
ed59f80b35
|
@ -1,7 +1,7 @@
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{
|
ast::{
|
||||||
Argument, Block, Call, CellPath, Expr, Expression, Operator, PathMember, Pipeline,
|
Argument, Block, Call, CellPath, Expr, Expression, ListItem, Math, Operator, PathMember,
|
||||||
PipelineRedirection, RedirectionSource, RedirectionTarget,
|
Pipeline, PipelineRedirection, RecordItem, RedirectionSource, RedirectionTarget,
|
||||||
},
|
},
|
||||||
engine::StateWorkingSet,
|
engine::StateWorkingSet,
|
||||||
ir::{DataSlice, Instruction, IrBlock, Literal, RedirectMode},
|
ir::{DataSlice, Instruction, IrBlock, Literal, RedirectMode},
|
||||||
|
@ -36,7 +36,7 @@ pub fn compile(working_set: &StateWorkingSet, block: &Block) -> Result<IrBlock,
|
||||||
Ok(builder.finish())
|
Ok(builder.finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Clone)]
|
||||||
struct RedirectModes {
|
struct RedirectModes {
|
||||||
out: Option<Spanned<RedirectMode>>,
|
out: Option<Spanned<RedirectMode>>,
|
||||||
err: Option<Spanned<RedirectMode>>,
|
err: Option<Spanned<RedirectMode>>,
|
||||||
|
@ -49,6 +49,13 @@ impl RedirectModes {
|
||||||
err: None,
|
err: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn with_capture_out(&self, span: Span) -> Self {
|
||||||
|
RedirectModes {
|
||||||
|
out: Some(RedirectMode::Capture.into_spanned(span)),
|
||||||
|
err: self.err.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_block(
|
fn compile_block(
|
||||||
|
@ -328,9 +335,170 @@ fn compile_expression(
|
||||||
Expr::Block(block_id) => lit(builder, Literal::Block(*block_id)),
|
Expr::Block(block_id) => lit(builder, Literal::Block(*block_id)),
|
||||||
Expr::Closure(block_id) => lit(builder, Literal::Closure(*block_id)),
|
Expr::Closure(block_id) => lit(builder, Literal::Closure(*block_id)),
|
||||||
Expr::MatchBlock(_) => Err(CompileError::Todo("MatchBlock")),
|
Expr::MatchBlock(_) => Err(CompileError::Todo("MatchBlock")),
|
||||||
Expr::List(_) => Err(CompileError::Todo("List")),
|
Expr::List(items) => {
|
||||||
Expr::Table(_) => Err(CompileError::Todo("Table")),
|
// Guess capacity based on items (does not consider spread as more than 1)
|
||||||
Expr::Record(_) => Err(CompileError::Todo("Record")),
|
lit(
|
||||||
|
builder,
|
||||||
|
Literal::List {
|
||||||
|
capacity: items.len(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
for item in items {
|
||||||
|
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,
|
||||||
|
)?;
|
||||||
|
builder.push(
|
||||||
|
Instruction::ListPush {
|
||||||
|
src_dst: out_reg,
|
||||||
|
item: 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,
|
||||||
|
)?;
|
||||||
|
builder.push(
|
||||||
|
Instruction::BinaryOp {
|
||||||
|
lhs_dst: out_reg,
|
||||||
|
op: Operator::Math(Math::Append),
|
||||||
|
rhs: rhs_reg,
|
||||||
|
}
|
||||||
|
.into_spanned(*spread_span),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Expr::Table(table) => {
|
||||||
|
lit(
|
||||||
|
builder,
|
||||||
|
Literal::List {
|
||||||
|
capacity: table.rows.len(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Evaluate the columns
|
||||||
|
let column_registers = table
|
||||||
|
.columns
|
||||||
|
.iter()
|
||||||
|
.map(|column| {
|
||||||
|
let reg = builder.next_register()?;
|
||||||
|
compile_expression(
|
||||||
|
working_set,
|
||||||
|
builder,
|
||||||
|
column,
|
||||||
|
redirect_modes.with_capture_out(column.span),
|
||||||
|
None,
|
||||||
|
reg,
|
||||||
|
)?;
|
||||||
|
Ok(reg)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<RegId>, CompileError>>()?;
|
||||||
|
|
||||||
|
// Build records for each row
|
||||||
|
for row in table.rows.iter() {
|
||||||
|
let row_reg = builder.next_register()?;
|
||||||
|
builder.load_literal(
|
||||||
|
row_reg,
|
||||||
|
Literal::Record {
|
||||||
|
capacity: table.columns.len(),
|
||||||
|
}
|
||||||
|
.into_spanned(expr.span),
|
||||||
|
)?;
|
||||||
|
for (column_reg, item) in column_registers.iter().zip(row.iter()) {
|
||||||
|
let column_reg = builder.clone_reg(*column_reg, item.span)?;
|
||||||
|
let item_reg = builder.next_register()?;
|
||||||
|
compile_expression(
|
||||||
|
working_set,
|
||||||
|
builder,
|
||||||
|
item,
|
||||||
|
redirect_modes.with_capture_out(item.span),
|
||||||
|
None,
|
||||||
|
item_reg,
|
||||||
|
)?;
|
||||||
|
builder.push(
|
||||||
|
Instruction::RecordInsert {
|
||||||
|
src_dst: out_reg,
|
||||||
|
key: column_reg,
|
||||||
|
val: item_reg,
|
||||||
|
}
|
||||||
|
.into_spanned(item.span),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the column registers, since they aren't needed anymore
|
||||||
|
for reg in column_registers {
|
||||||
|
builder.free_register(reg)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Expr::Record(items) => {
|
||||||
|
lit(
|
||||||
|
builder,
|
||||||
|
Literal::Record {
|
||||||
|
capacity: items.len(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for item in items {
|
||||||
|
match item {
|
||||||
|
RecordItem::Pair(key, val) => {
|
||||||
|
// Add each item using record-insert
|
||||||
|
let key_reg = builder.next_register()?;
|
||||||
|
let val_reg = builder.next_register()?;
|
||||||
|
compile_expression(
|
||||||
|
working_set,
|
||||||
|
builder,
|
||||||
|
key,
|
||||||
|
redirect_modes.with_capture_out(expr.span),
|
||||||
|
None,
|
||||||
|
key_reg,
|
||||||
|
)?;
|
||||||
|
compile_expression(
|
||||||
|
working_set,
|
||||||
|
builder,
|
||||||
|
val,
|
||||||
|
redirect_modes.with_capture_out(expr.span),
|
||||||
|
None,
|
||||||
|
val_reg,
|
||||||
|
)?;
|
||||||
|
builder.push(
|
||||||
|
Instruction::RecordInsert {
|
||||||
|
src_dst: out_reg,
|
||||||
|
key: key_reg,
|
||||||
|
val: val_reg,
|
||||||
|
}
|
||||||
|
.into_spanned(expr.span),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
RecordItem::Spread(spread_span, expr) => {
|
||||||
|
return Err(CompileError::Todo("Record with spread"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Expr::Keyword(_) => Err(CompileError::Todo("Keyword")),
|
Expr::Keyword(_) => Err(CompileError::Todo("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")),
|
||||||
|
@ -757,9 +925,12 @@ impl BlockBuilder {
|
||||||
Instruction::ListPush { src_dst: _, item } => self.free_register(*item)?,
|
Instruction::ListPush { src_dst: _, item } => self.free_register(*item)?,
|
||||||
Instruction::RecordInsert {
|
Instruction::RecordInsert {
|
||||||
src_dst: _,
|
src_dst: _,
|
||||||
key: _,
|
key,
|
||||||
val,
|
val,
|
||||||
} => self.free_register(*val)?,
|
} => {
|
||||||
|
self.free_register(*key)?;
|
||||||
|
self.free_register(*val)?;
|
||||||
|
}
|
||||||
Instruction::BinaryOp {
|
Instruction::BinaryOp {
|
||||||
lhs_dst: _,
|
lhs_dst: _,
|
||||||
op: _,
|
op: _,
|
||||||
|
@ -829,6 +1000,13 @@ impl BlockBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clone a register with a `clone` instruction.
|
||||||
|
fn clone_reg(&mut self, src: RegId, span: Span) -> Result<RegId, CompileError> {
|
||||||
|
let dst = self.next_register()?;
|
||||||
|
self.push(Instruction::Clone { dst, src }.into_spanned(span))?;
|
||||||
|
Ok(dst)
|
||||||
|
}
|
||||||
|
|
||||||
/// Consume the builder and produce the final [`IrBlock`].
|
/// Consume the builder and produce the final [`IrBlock`].
|
||||||
fn finish(self) -> IrBlock {
|
fn finish(self) -> IrBlock {
|
||||||
IrBlock {
|
IrBlock {
|
||||||
|
|
|
@ -306,10 +306,11 @@ fn eval_instruction(
|
||||||
}
|
}
|
||||||
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 val = ctx.collect_reg(*val, *span)?;
|
let val = ctx.collect_reg(*val, *span)?;
|
||||||
let record_span = record_value.span();
|
let record_span = record_value.span();
|
||||||
let mut record = record_value.into_record()?;
|
let mut record = record_value.into_record()?;
|
||||||
record.insert(ctx.get_str(*key, *span)?, val);
|
record.insert(key.coerce_into_string()?, val);
|
||||||
ctx.put_reg(
|
ctx.put_reg(
|
||||||
*src_dst,
|
*src_dst,
|
||||||
Value::record(record, record_span).into_pipeline_data(),
|
Value::record(record, record_span).into_pipeline_data(),
|
||||||
|
|
|
@ -116,7 +116,6 @@ impl<'a> fmt::Display for FmtInstruction<'a> {
|
||||||
write!(f, "{:WIDTH$} {src_dst}, {item}", "list-push")
|
write!(f, "{:WIDTH$} {src_dst}, {item}", "list-push")
|
||||||
}
|
}
|
||||||
Instruction::RecordInsert { src_dst, key, val } => {
|
Instruction::RecordInsert { src_dst, key, val } => {
|
||||||
let key = FmtData(self.data, *key);
|
|
||||||
write!(f, "{:WIDTH$} {src_dst}, {key}, {val}", "record-insert")
|
write!(f, "{:WIDTH$} {src_dst}, {key}, {val}", "record-insert")
|
||||||
}
|
}
|
||||||
Instruction::BinaryOp { lhs_dst, op, rhs } => {
|
Instruction::BinaryOp { lhs_dst, op, rhs } => {
|
||||||
|
|
|
@ -93,7 +93,7 @@ pub enum Instruction {
|
||||||
/// Insert a key-value pair into a record. Any existing value for the key is overwritten.
|
/// Insert a key-value pair into a record. Any existing value for the key is overwritten.
|
||||||
RecordInsert {
|
RecordInsert {
|
||||||
src_dst: RegId,
|
src_dst: RegId,
|
||||||
key: DataSlice,
|
key: RegId,
|
||||||
val: RegId,
|
val: 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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user