Add cell paths

This commit is contained in:
JT
2021-09-07 10:02:24 +12:00
parent f71b7e89e0
commit 3b7d7861e3
12 changed files with 395 additions and 99 deletions

View File

@ -0,0 +1,19 @@
use super::Expression;
use crate::Span;
#[derive(Debug, Clone)]
pub enum PathMember {
String { val: String, span: Span },
Int { val: usize, span: Span },
}
#[derive(Debug, Clone)]
pub struct CellPath {
pub members: Vec<PathMember>,
}
#[derive(Debug, Clone)]
pub struct FullCellPath {
pub head: Expression,
pub tail: Vec<PathMember>,
}

View File

@ -1,4 +1,4 @@
use super::{Call, Expression, Operator, RangeOperator};
use super::{Call, Expression, FullCellPath, Operator, RangeOperator};
use crate::{BlockId, Signature, Span, VarId};
#[derive(Debug, Clone)]
@ -22,6 +22,7 @@ pub enum Expr {
Table(Vec<Expression>, Vec<Vec<Expression>>),
Keyword(Vec<u8>, Span, Box<Expression>),
String(String), // FIXME: improve this in the future?
FullCellPath(Box<FullCellPath>),
Signature(Box<Signature>),
Garbage,
}

View File

@ -1,5 +1,6 @@
mod block;
mod call;
mod cell_path;
mod expr;
mod expression;
mod operator;
@ -8,6 +9,7 @@ mod statement;
pub use block::*;
pub use call::*;
pub use cell_path::*;
pub use expr::*;
pub use expression::*;
pub use operator::*;

View File

@ -17,4 +17,8 @@ pub enum ShellError {
CantConvert(String, Span),
DivisionByZero(Span),
CannotCreateRange(Span),
AccessBeyondEnd(usize, Span),
AccessBeyondEndOfStream(Span),
IncompatiblePathAccess(String, Span),
CantFindColumn(Span),
}

View File

@ -13,10 +13,10 @@ pub enum SyntaxShape {
String,
/// A dotted path to navigate the table
ColumnPath,
CellPath,
/// A dotted path to navigate the table (including variable)
FullColumnPath,
FullCellPath,
/// Only a numeric (integer or decimal) value is allowed
Number,
@ -76,12 +76,12 @@ impl SyntaxShape {
match self {
SyntaxShape::Any => Type::Unknown,
SyntaxShape::Block => Type::Block,
SyntaxShape::ColumnPath => Type::Unknown,
SyntaxShape::CellPath => Type::Unknown,
SyntaxShape::Duration => Type::Duration,
SyntaxShape::Expression => Type::Unknown,
SyntaxShape::FilePath => Type::FilePath,
SyntaxShape::Filesize => Type::Filesize,
SyntaxShape::FullColumnPath => Type::Unknown,
SyntaxShape::FullCellPath => Type::Unknown,
SyntaxShape::GlobPattern => Type::String,
SyntaxShape::Int => Type::Int,
SyntaxShape::List(x) => {

View File

@ -8,7 +8,7 @@ pub enum Type {
Bool,
String,
Block,
ColumnPath,
CellPath,
Duration,
FilePath,
Filesize,
@ -27,7 +27,7 @@ impl Display for Type {
match self {
Type::Block => write!(f, "block"),
Type::Bool => write!(f, "bool"),
Type::ColumnPath => write!(f, "column path"),
Type::CellPath => write!(f, "cell path"),
Type::Duration => write!(f, "duration"),
Type::FilePath => write!(f, "filepath"),
Type::Filesize => write!(f, "filesize"),

View File

@ -1,6 +1,6 @@
use std::{cell::RefCell, fmt::Debug, rc::Rc};
use crate::ast::RangeInclusion;
use crate::ast::{PathMember, RangeInclusion};
use crate::{span, BlockId, Span, Type};
use crate::ShellError;
@ -364,21 +364,26 @@ impl Value {
}
Value::String { val, .. } => val,
Value::ValueStream { stream, .. } => stream.into_string(),
Value::List { val, .. } => val
.into_iter()
.map(|x| x.into_string())
.collect::<Vec<_>>()
.join(", "),
Value::Table { val, .. } => val
.into_iter()
.map(|x| {
x.into_iter()
.map(|x| x.into_string())
.collect::<Vec<_>>()
.join(", ")
})
.collect::<Vec<_>>()
.join("\n"),
Value::List { val, .. } => format!(
"[{}]",
val.into_iter()
.map(|x| x.into_string())
.collect::<Vec<_>>()
.join(", ")
),
Value::Table { val, headers, .. } => format!(
"[= {} =\n {}]",
headers.join(", "),
val.into_iter()
.map(|x| {
x.into_iter()
.map(|x| x.into_string())
.collect::<Vec<_>>()
.join(", ")
})
.collect::<Vec<_>>()
.join("\n")
),
Value::RowStream {
headers, stream, ..
} => stream.into_string(headers),
@ -393,6 +398,138 @@ impl Value {
span: Span::unknown(),
}
}
pub fn follow_column_path(self, column_path: &[PathMember]) -> Result<Value, ShellError> {
let mut current = self;
for member in column_path {
// FIXME: this uses a few extra clones for simplicity, but there may be a way
// to traverse the path without them
match member {
PathMember::Int {
val: count,
span: origin_span,
} => {
// Treat a numeric path member as `nth <val>`
match &mut current {
Value::List { val, .. } => {
if let Some(item) = val.get(*count) {
current = item.clone();
} else {
return Err(ShellError::AccessBeyondEnd(val.len(), *origin_span));
}
}
Value::ValueStream { stream, .. } => {
if let Some(item) = stream.nth(*count) {
current = item;
} else {
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 => {
return Err(ShellError::IncompatiblePathAccess(
format!("{}", x.get_type()),
*origin_span,
))
}
}
}
PathMember::String {
val,
span: origin_span,
} => match &mut current {
Value::Table {
headers,
val: cells,
span,
} => {
let mut found = false;
for header in headers.iter().enumerate() {
if header.1 == val {
found = true;
let mut column = vec![];
for row in cells {
column.push(row[header.0].clone())
}
current = Value::List {
val: column,
span: *span,
};
break;
}
}
if !found {
return Err(ShellError::CantFindColumn(*origin_span));
}
}
Value::RowStream {
headers,
stream,
span,
} => {
let mut found = false;
for header in headers.iter().enumerate() {
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 {
//FIXME: add "did you mean"
return Err(ShellError::CantFindColumn(*origin_span));
}
}
x => {
return Err(ShellError::IncompatiblePathAccess(
format!("{}", x.get_type()),
*origin_span,
))
}
},
}
}
Ok(current)
}
}
impl PartialEq for Value {