Switch tables to list/streams of records

This commit is contained in:
JT 2021-09-07 19:07:11 +12:00
parent 2055b83c34
commit b0ab78a767
11 changed files with 75 additions and 165 deletions

View File

@ -23,7 +23,7 @@
- [ ] Source - [ ] Source
- [ ] Autoenv - [ ] Autoenv
- [ ] Externals - [ ] Externals
- [ ] let [first, rest] = [1, 2, 3] - [ ] let [first, rest] = [1, 2, 3] (design question: how do you pattern match a table?)
## Maybe: ## Maybe:
- [ ] default param values? - [ ] default param values?

View File

@ -348,9 +348,9 @@ pub fn report_shell_error(
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
Diagnostic::error() Diagnostic::error()
.with_message("Data cannot be accessed with a column path") .with_message("Data cannot be accessed with a cell path")
.with_labels(vec![Label::primary(diag_file_id, diag_range) .with_labels(vec![Label::primary(diag_file_id, diag_range)
.with_message(format!("{} doesn't support column paths", name))]) .with_message(format!("{} doesn't support cell paths", name))])
} }
ShellError::CantFindColumn(span) => { ShellError::CantFindColumn(span) => {
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;

View File

@ -52,7 +52,7 @@ impl Command for Each {
.into_value_stream(), .into_value_stream(),
span: call.head, span: call.head,
}), }),
Value::List { val, .. } => Ok(Value::ValueStream { Value::List { vals: val, .. } => Ok(Value::ValueStream {
stream: val stream: val
.into_iter() .into_iter()
.map(move |x| { .map(move |x| {
@ -95,8 +95,6 @@ impl Command for Each {
.into_value_stream(), .into_value_stream(),
span: call.head, span: call.head,
}), }),
Value::RowStream { .. } => panic!("iterating row streams is not yet supported"),
Value::Table { .. } => panic!("table iteration not yet supported"),
x => { x => {
//TODO: we need to watch to make sure this is okay //TODO: we need to watch to make sure this is okay
let engine_state = context.engine_state.borrow(); let engine_state = context.engine_state.borrow();

View File

@ -68,8 +68,8 @@ impl Command for For {
.into_value_stream(), .into_value_stream(),
span: call.head, span: call.head,
}), }),
Value::List { val, .. } => Ok(Value::List { Value::List { vals: val, .. } => Ok(Value::List {
val: val vals: val
.into_iter() .into_iter()
.map(move |x| { .map(move |x| {
let engine_state = context.engine_state.borrow(); let engine_state = context.engine_state.borrow();

View File

@ -24,15 +24,7 @@ impl Command for Length {
input: Value, input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> { ) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
match input { match input {
Value::List { val, .. } => { Value::List { vals: val, .. } => {
let length = val.len();
Ok(Value::Int {
val: length as i64,
span: call.head,
})
}
Value::Table { val, .. } => {
let length = val.len(); let length = val.len();
Ok(Value::Int { Ok(Value::Int {
@ -48,14 +40,6 @@ impl Command for Length {
span: call.head, span: call.head,
}) })
} }
Value::RowStream { stream, .. } => {
let length = stream.count();
Ok(Value::Int {
val: length as i64,
span: call.head,
})
}
Value::Nothing { .. } => Ok(Value::Int { Value::Nothing { .. } => Ok(Value::Int {
val: 0, val: 0,
span: call.head, span: call.head,

View File

@ -55,7 +55,7 @@ fn eval_call(context: &EvaluationContext, call: &Call, input: Value) -> Result<V
.var_id .var_id
.expect("Internal error: rest positional parameter lacks var_id"), .expect("Internal error: rest positional parameter lacks var_id"),
Value::List { Value::List {
val: rest_items, vals: rest_items,
span, span,
}, },
) )
@ -133,7 +133,7 @@ pub fn eval_expression(
Expr::FullCellPath(column_path) => { Expr::FullCellPath(column_path) => {
let value = eval_expression(context, &column_path.head)?; let value = eval_expression(context, &column_path.head)?;
value.follow_column_path(&column_path.tail) value.follow_cell_path(&column_path.tail)
} }
Expr::Call(call) => eval_call(context, call, Value::nothing()), Expr::Call(call) => eval_call(context, call, Value::nothing()),
Expr::ExternalCall(_, _) => Err(ShellError::ExternalNotSupported(expr.span)), Expr::ExternalCall(_, _) => Err(ShellError::ExternalNotSupported(expr.span)),
@ -176,7 +176,7 @@ pub fn eval_expression(
output.push(eval_expression(context, expr)?); output.push(eval_expression(context, expr)?);
} }
Ok(Value::List { Ok(Value::List {
val: output, vals: output,
span: expr.span, span: expr.span,
}) })
} }
@ -192,11 +192,14 @@ pub fn eval_expression(
for expr in val { for expr in val {
row.push(eval_expression(context, expr)?); row.push(eval_expression(context, expr)?);
} }
output_rows.push(row); output_rows.push(Value::Record {
cols: output_headers.clone(),
vals: row,
span: expr.span,
});
} }
Ok(Value::Table { Ok(Value::List {
headers: output_headers, vals: output_rows,
val: output_rows,
span: expr.span, span: expr.span,
}) })
} }

View File

@ -1781,7 +1781,7 @@ pub fn parse_table_expression(
Expression { Expression {
expr: Expr::List(vec![]), expr: Expr::List(vec![]),
span, span,
ty: Type::Table, ty: Type::List(Box::new(Type::Unknown)),
}, },
None, None,
), ),
@ -1828,7 +1828,7 @@ pub fn parse_table_expression(
Expression { Expression {
expr: Expr::Table(table_headers, rows), expr: Expr::Table(table_headers, rows),
span, span,
ty: Type::Table, ty: Type::List(Box::new(Type::Unknown)),
}, },
error, error,
) )
@ -1965,7 +1965,7 @@ pub fn parse_value(
// First, check the special-cases. These will likely represent specific values as expressions // First, check the special-cases. These will likely represent specific values as expressions
// and may fit a variety of shapes. // and may fit a variety of shapes.
// //
// We check variable first because immediately following we check for variables with column paths // We check variable first because immediately following we check for variables with cell paths
// which might result in a value that fits other shapes (and require the variable to already be // which might result in a value that fits other shapes (and require the variable to already be
// declared) // declared)
if shape == &SyntaxShape::Variable { if shape == &SyntaxShape::Variable {

View File

@ -29,17 +29,8 @@ impl EvaluationContext {
// TODO: add ctrl-c support // TODO: add ctrl-c support
let value = match value { let value = match value {
Value::RowStream {
headers,
stream,
span,
} => Value::Table {
headers,
val: stream.collect(),
span,
},
Value::ValueStream { stream, span } => Value::List { Value::ValueStream { stream, span } => Value::List {
val: stream.collect(), vals: stream.collect(),
span, span,
}, },
x => x, x => x,

View File

@ -96,7 +96,7 @@ impl SyntaxShape {
SyntaxShape::RowCondition => Type::Bool, SyntaxShape::RowCondition => Type::Bool,
SyntaxShape::Signature => Type::Unknown, SyntaxShape::Signature => Type::Unknown,
SyntaxShape::String => Type::String, SyntaxShape::String => Type::String,
SyntaxShape::Table => Type::Table, SyntaxShape::Table => Type::List(Box::new(Type::Unknown)), // FIXME
SyntaxShape::VarWithOptType => Type::Unknown, SyntaxShape::VarWithOptType => Type::Unknown,
SyntaxShape::Variable => Type::Unknown, SyntaxShape::Variable => Type::Unknown,
} }

View File

@ -15,8 +15,7 @@ pub enum Type {
List(Box<Type>), List(Box<Type>),
Number, Number,
Nothing, Nothing,
Table, Record(Vec<String>, Vec<Type>),
RowStream,
ValueStream, ValueStream,
Unknown, Unknown,
Error, Error,
@ -34,13 +33,12 @@ impl Display for Type {
Type::Float => write!(f, "float"), Type::Float => write!(f, "float"),
Type::Int => write!(f, "int"), Type::Int => write!(f, "int"),
Type::Range => write!(f, "range"), Type::Range => write!(f, "range"),
Type::Record(cols, vals) => write!(f, "record<{}, {:?}>", cols.join(", "), vals),
Type::List(l) => write!(f, "list<{}>", l), Type::List(l) => write!(f, "list<{}>", l),
Type::Nothing => write!(f, "nothing"), Type::Nothing => write!(f, "nothing"),
Type::Number => write!(f, "number"), Type::Number => write!(f, "number"),
Type::String => write!(f, "string"), Type::String => write!(f, "string"),
Type::Table => write!(f, "table"),
Type::ValueStream => write!(f, "value stream"), Type::ValueStream => write!(f, "value stream"),
Type::RowStream => write!(f, "row stream"),
Type::Unknown => write!(f, "unknown"), Type::Unknown => write!(f, "unknown"),
Type::Error => write!(f, "error"), Type::Error => write!(f, "error"),
} }

View File

@ -247,22 +247,17 @@ pub enum Value {
val: String, val: String,
span: Span, span: Span,
}, },
Record {
cols: Vec<String>,
vals: Vec<Value>,
span: Span,
},
ValueStream { ValueStream {
stream: ValueStream, stream: ValueStream,
span: Span, span: Span,
}, },
RowStream {
headers: Vec<String>,
stream: RowStream,
span: Span,
},
List { List {
val: Vec<Value>, vals: Vec<Value>,
span: Span,
},
Table {
headers: Vec<String>,
val: Vec<Vec<Value>>,
span: Span, span: Span,
}, },
Block { Block {
@ -292,10 +287,9 @@ impl Value {
Value::Float { span, .. } => *span, Value::Float { span, .. } => *span,
Value::Range { span, .. } => *span, Value::Range { span, .. } => *span,
Value::String { span, .. } => *span, Value::String { span, .. } => *span,
Value::Record { span, .. } => *span,
Value::List { span, .. } => *span, Value::List { span, .. } => *span,
Value::Table { span, .. } => *span,
Value::Block { span, .. } => *span, Value::Block { span, .. } => *span,
Value::RowStream { span, .. } => *span,
Value::ValueStream { span, .. } => *span, Value::ValueStream { span, .. } => *span,
Value::Nothing { span, .. } => *span, Value::Nothing { span, .. } => *span,
Value::Error { .. } => Span::unknown(), Value::Error { .. } => Span::unknown(),
@ -309,10 +303,9 @@ impl Value {
Value::Float { span, .. } => *span = new_span, Value::Float { span, .. } => *span = new_span,
Value::Range { span, .. } => *span = new_span, Value::Range { span, .. } => *span = new_span,
Value::String { span, .. } => *span = new_span, Value::String { span, .. } => *span = new_span,
Value::RowStream { span, .. } => *span = new_span, Value::Record { span, .. } => *span = new_span,
Value::ValueStream { span, .. } => *span = new_span, Value::ValueStream { span, .. } => *span = new_span,
Value::List { span, .. } => *span = new_span, Value::List { span, .. } => *span = new_span,
Value::Table { span, .. } => *span = new_span,
Value::Block { span, .. } => *span = new_span, Value::Block { span, .. } => *span = new_span,
Value::Nothing { span, .. } => *span = new_span, Value::Nothing { span, .. } => *span = new_span,
Value::Error { .. } => {} Value::Error { .. } => {}
@ -328,12 +321,13 @@ impl Value {
Value::Float { .. } => Type::Float, Value::Float { .. } => Type::Float,
Value::Range { .. } => Type::Range, Value::Range { .. } => Type::Range,
Value::String { .. } => Type::String, Value::String { .. } => Type::String,
Value::Record { cols, vals, .. } => {
Type::Record(cols.clone(), vals.iter().map(|x| x.get_type()).collect())
}
Value::List { .. } => Type::List(Box::new(Type::Unknown)), // FIXME Value::List { .. } => Type::List(Box::new(Type::Unknown)), // FIXME
Value::Table { .. } => Type::Table, // FIXME
Value::Nothing { .. } => Type::Nothing, Value::Nothing { .. } => Type::Nothing,
Value::Block { .. } => Type::Block, Value::Block { .. } => Type::Block,
Value::ValueStream { .. } => Type::ValueStream, Value::ValueStream { .. } => Type::ValueStream,
Value::RowStream { .. } => Type::RowStream,
Value::Error { .. } => Type::Error, Value::Error { .. } => Type::Error,
} }
} }
@ -364,29 +358,21 @@ impl Value {
} }
Value::String { val, .. } => val, Value::String { val, .. } => val,
Value::ValueStream { stream, .. } => stream.into_string(), Value::ValueStream { stream, .. } => stream.into_string(),
Value::List { val, .. } => format!( Value::List { vals: val, .. } => format!(
"[{}]", "[{}]",
val.into_iter() val.into_iter()
.map(|x| x.into_string()) .map(|x| x.into_string())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .join(", ")
), ),
Value::Table { val, headers, .. } => format!( Value::Record { cols, vals, .. } => format!(
"[= {} =\n {}]", "{{{}}}",
headers.join(", "), cols.iter()
val.into_iter() .zip(vals.iter())
.map(|x| { .map(|(x, y)| format!("{}: {}", x, y.clone().into_string()))
x.into_iter()
.map(|x| x.into_string())
.collect::<Vec<_>>()
.join(", ")
})
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n") .join(", ")
), ),
Value::RowStream {
headers, stream, ..
} => stream.into_string(headers),
Value::Block { val, .. } => format!("<Block {}>", val), Value::Block { val, .. } => format!("<Block {}>", val),
Value::Nothing { .. } => String::new(), Value::Nothing { .. } => String::new(),
Value::Error { error } => format!("{:?}", error), Value::Error { error } => format!("{:?}", error),
@ -399,7 +385,7 @@ impl Value {
} }
} }
pub fn follow_column_path(self, column_path: &[PathMember]) -> Result<Value, ShellError> { pub fn follow_cell_path(self, column_path: &[PathMember]) -> Result<Value, ShellError> {
let mut current = self; let mut current = self;
for member in column_path { for member in column_path {
// FIXME: this uses a few extra clones for simplicity, but there may be a way // FIXME: this uses a few extra clones for simplicity, but there may be a way
@ -411,7 +397,7 @@ impl Value {
} => { } => {
// Treat a numeric path member as `nth <val>` // Treat a numeric path member as `nth <val>`
match &mut current { match &mut current {
Value::List { val, .. } => { Value::List { vals: val, .. } => {
if let Some(item) = val.get(*count) { if let Some(item) = val.get(*count) {
current = item.clone(); current = item.clone();
} else { } else {
@ -425,32 +411,6 @@ impl Value {
return Err(ShellError::AccessBeyondEndOfStream(*origin_span)); return Err(ShellError::AccessBeyondEndOfStream(*origin_span));
} }
} }
Value::Table { headers, val, span } => {
if let Some(row) = val.get(*count) {
current = Value::Table {
headers: headers.clone(),
val: vec![row.clone()],
span: *span,
}
} else {
return Err(ShellError::AccessBeyondEnd(val.len(), *origin_span));
}
}
Value::RowStream {
headers,
stream,
span,
} => {
if let Some(row) = stream.nth(*count) {
current = Value::Table {
headers: headers.clone(),
val: vec![row.clone()],
span: *span,
}
} else {
return Err(ShellError::AccessBeyondEndOfStream(*origin_span));
}
}
x => { x => {
return Err(ShellError::IncompatiblePathAccess( return Err(ShellError::IncompatiblePathAccess(
format!("{}", x.get_type()), format!("{}", x.get_type()),
@ -460,28 +420,15 @@ impl Value {
} }
} }
PathMember::String { PathMember::String {
val, val: column_name,
span: origin_span, span: origin_span,
} => match &mut current { } => match &mut current {
Value::Table { Value::Record { cols, vals, .. } => {
headers,
val: cells,
span,
} => {
let mut found = false; let mut found = false;
for header in headers.iter().enumerate() { for col in cols.iter().zip(vals.iter()) {
if header.1 == val { if col.0 == column_name {
current = col.1.clone();
found = true; found = true;
let mut column = vec![];
for row in cells {
column.push(row[header.0].clone())
}
current = Value::List {
val: column,
span: *span,
};
break; break;
} }
} }
@ -490,33 +437,22 @@ impl Value {
return Err(ShellError::CantFindColumn(*origin_span)); return Err(ShellError::CantFindColumn(*origin_span));
} }
} }
Value::RowStream { Value::List { vals, span } => {
headers, let mut output = vec![];
stream, for val in vals {
span, if let Value::Record { cols, vals, .. } = val {
} => { for col in cols.iter().enumerate() {
let mut found = false; if col.1 == column_name {
for header in headers.iter().enumerate() { output.push(vals[col.0].clone());
if header.1 == val { }
found = true;
let mut column = vec![];
for row in stream {
column.push(row[header.0].clone())
} }
current = Value::List {
val: column,
span: *span,
};
break;
} }
} }
if !found { current = Value::List {
//FIXME: add "did you mean" vals: output,
return Err(ShellError::CantFindColumn(*origin_span)); span: *span,
} };
} }
x => { x => {
return Err(ShellError::IncompatiblePathAccess( return Err(ShellError::IncompatiblePathAccess(
@ -844,19 +780,19 @@ impl Value {
val: lhs == rhs, val: lhs == rhs,
span, span,
}), }),
(Value::List { val: lhs, .. }, Value::List { val: rhs, .. }) => Ok(Value::Bool { (Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => Ok(Value::Bool {
val: lhs == rhs, val: lhs == rhs,
span, span,
}), }),
( (
Value::Table { Value::Record {
val: lhs, vals: lhs,
headers: lhs_headers, cols: lhs_headers,
.. ..
}, },
Value::Table { Value::Record {
val: rhs, vals: rhs,
headers: rhs_headers, cols: rhs_headers,
.. ..
}, },
) => Ok(Value::Bool { ) => Ok(Value::Bool {
@ -899,19 +835,19 @@ impl Value {
val: lhs != rhs, val: lhs != rhs,
span, span,
}), }),
(Value::List { val: lhs, .. }, Value::List { val: rhs, .. }) => Ok(Value::Bool { (Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => Ok(Value::Bool {
val: lhs != rhs, val: lhs != rhs,
span, span,
}), }),
( (
Value::Table { Value::Record {
val: lhs, vals: lhs,
headers: lhs_headers, cols: lhs_headers,
.. ..
}, },
Value::Table { Value::Record {
val: rhs, vals: rhs,
headers: rhs_headers, cols: rhs_headers,
.. ..
}, },
) => Ok(Value::Bool { ) => Ok(Value::Bool {