diff --git a/issues.md b/issues.md index aa555da..ac2c3ce 100644 --- a/issues.md +++ b/issues.md @@ -9,7 +9,7 @@ - fix double kleene error (see test) - support type selector: `hial './src/tests/rust.rs^rust/:function_item'` -- support assignment (write): `hial './src/tests/rust.rs^rust/:function_item[-1]#label = "modified_fn_name"'` +- support ts write: `hial './src/tests/rust.rs^rust/:function_item[-1]#label = "modified_fn_name"'` - set value on the command line - later: separate api module, used by ffi and dependent crates diff --git a/src/base/extra.rs b/src/base/extra.rs index 0c13a22..237cbc5 100644 --- a/src/base/extra.rs +++ b/src/base/extra.rs @@ -227,6 +227,16 @@ impl CellReaderTrait for CellReader { } } impl CellReader { + pub fn as_file_path(&self) -> Res<&std::path::Path> { + if let DynCellReader::File(ref file_cell) = self.0 { + return file_cell.as_file_path(); + } + if let DynCellReader::Path(ref path_cell) = self.0 { + return path_cell.as_file_path(); + } + userres("this interpretation has no file path") + } + pub fn err(self) -> Res { if let DynCellReader::Error(error) = self.0 { return Err(error); @@ -590,16 +600,6 @@ impl Cell { s } - pub fn as_file_path(&self) -> Res<&std::path::Path> { - if let DynCell::File(ref file_cell) = self.dyn_cell { - return file_cell.as_path(); - } - if let DynCell::Path(ref path_cell) = self.dyn_cell { - return path_cell.as_path(); - } - nores().with_path_res(self.path()) - } - pub fn save(&self, target: Cell) -> Res<()> { if let DynCell::Error(err) = &self.dyn_cell { return Err(err.clone()); diff --git a/src/base/field.rs b/src/base/field.rs index fe1ec6a..bc9f836 100644 --- a/src/base/field.rs +++ b/src/base/field.rs @@ -184,6 +184,12 @@ impl CellReaderTrait for FieldReader { impl CellWriterTrait for FieldWriter { fn set_value(&mut self, value: OwnValue) -> Res<()> { - todo!() // remove this default implementation + match self.ty { + FieldType::Value => self.writer.set_value(value), + FieldType::Label => self.writer.set_label(value), + FieldType::Type => userres("cannot change cell type"), + FieldType::Index => self.writer.set_index(value), + FieldType::Serial => self.writer.set_serial(value), + } } } diff --git a/src/base/intra.rs b/src/base/intra.rs index 199098e..7b06619 100644 --- a/src/base/intra.rs +++ b/src/base/intra.rs @@ -35,12 +35,20 @@ pub trait CellReaderTrait: Debug { } pub trait CellWriterTrait: Debug { - fn set_value(&mut self, value: OwnValue) -> Res<()>; + fn set_index(&mut self, value: OwnValue) -> Res<()> { + todo!() // TODO: remove this default implementation + } fn set_label(&mut self, value: OwnValue) -> Res<()> { todo!() // TODO: remove this default implementation } + fn set_value(&mut self, value: OwnValue) -> Res<()>; + + fn set_serial(&mut self, value: OwnValue) -> Res<()> { + todo!() // TODO: remove this default implementation + } + fn delete(&mut self) -> Res<()> { todo!() // TODO: remove this default implementation } @@ -60,7 +68,7 @@ pub trait GroupTrait: Clone + Debug { // fn get_all<'s, 'a, S: Into>>(&'s self, label: S) -> Res; fn add(&mut self) -> Res<()> { - todo!() // remove this default implementation + todo!() // TODO: remove this default implementation } } diff --git a/src/interpretations/fs.rs b/src/interpretations/fs.rs index 3b4050d..3b0fb50 100644 --- a/src/interpretations/fs.rs +++ b/src/interpretations/fs.rs @@ -129,6 +129,14 @@ impl CellReaderTrait for CellReader { } } +impl CellReader { + pub(crate) fn as_file_path(&self) -> Res<&Path> { + let fileentry = + guard_ok!(&self.group.files[self.pos as usize], err => {return Err(err.clone())}); + Ok(fileentry.path.as_path()) + } +} + impl CellWriterTrait for CellWriter { fn set_value(&mut self, value: OwnValue) -> Res<()> { let string_value = value.to_string(); @@ -184,19 +192,12 @@ impl CellWriterTrait for CellWriter { } } } - - fn set_label(&mut self, value: OwnValue) -> Res<()> { - todo!() // add implementation - } - - fn delete(&mut self) -> Res<()> { - todo!() // add implementation - } } impl Cell { pub(crate) fn from_cell(cell: XCell, _: &str) -> Res { - let path = cell.as_file_path()?; + let r = cell.read(); + let path = r.as_file_path()?; let file_cell = Cell { group: Group { files: Rc::new(vec![read_file(path)]), @@ -218,12 +219,6 @@ impl Cell { }; Ok(new_cell(DynCell::from(file_cell), None)) } - - pub(crate) fn as_path(&self) -> Res<&Path> { - let fileentry = - guard_ok!(&self.group.files[self.pos as usize], err => {return Err(err.clone())}); - Ok(fileentry.path.as_path()) - } } impl CellTrait for Cell { diff --git a/src/interpretations/http.rs b/src/interpretations/http.rs index 35db083..13677dc 100644 --- a/src/interpretations/http.rs +++ b/src/interpretations/http.rs @@ -245,7 +245,7 @@ impl CellReaderTrait for CellReader { impl CellWriterTrait for CellWriter { fn set_value(&mut self, value: OwnValue) -> Res<()> { - todo!() // remove this default implementation + fault("set_value not yet implemented for http") } } diff --git a/src/interpretations/json.rs b/src/interpretations/json.rs index 1e5b124..2c1ea1f 100644 --- a/src/interpretations/json.rs +++ b/src/interpretations/json.rs @@ -81,7 +81,8 @@ impl Cell { (serde_json::from_str(s.as_ref())?, indent) } "fs" => { - let path = cell.as_file_path()?; + let r = cell.read(); + let path = r.as_file_path()?; let indent = detect_file_indentation(path); ( serde_json::from_reader( diff --git a/src/interpretations/path.rs b/src/interpretations/path.rs index 3dd6a35..9455239 100644 --- a/src/interpretations/path.rs +++ b/src/interpretations/path.rs @@ -1,11 +1,14 @@ use std::{ + cell::OnceCell, path::{Path, PathBuf}, - rc::Rc, }; use linkme::distributed_slice; -use crate::base::{Cell as XCell, *}; +use crate::{ + base::{Cell as XCell, *}, + utils::ownrc::{OwnRc, ReadRc, WriteRc}, +}; #[distributed_slice(ELEVATION_CONSTRUCTORS)] static VALUE_TO_PATH: ElevationConstructor = ElevationConstructor { @@ -15,16 +18,13 @@ static VALUE_TO_PATH: ElevationConstructor = ElevationConstructor { }; #[derive(Clone, Debug)] -pub(crate) struct Data(PathBuf, String); - -#[derive(Clone, Debug)] -pub(crate) struct Cell(Rc); +pub(crate) struct Cell(OwnRc); #[derive(Debug)] -pub(crate) struct CellReader(Rc); +pub(crate) struct CellReader(ReadRc, OnceCell); #[derive(Debug)] -pub(crate) struct CellWriter(Rc); +pub(crate) struct CellWriter(WriteRc); impl Cell { pub(crate) fn from_cell(cell: XCell, _: &str) -> Res { @@ -37,7 +37,8 @@ impl Cell { Self::make_cell(PathBuf::from(value), value.to_owned(), Some(cell)) } "fs" => { - let path = cell.as_file_path()?; + let r = cell.read(); + let path = r.as_file_path()?; Self::make_cell( path.to_owned(), path.to_string_lossy().into_owned(), @@ -49,18 +50,17 @@ impl Cell { } fn make_cell(path: PathBuf, string: String, origin: Option) -> Res { - let path_cell = Cell(Rc::new(Data(path, string))); + let path_cell = Cell(OwnRc::new(path)); Ok(new_cell(DynCell::from(path_cell), origin)) } - - pub(crate) fn as_path(&self) -> Res<&Path> { - Ok(self.0 .0.as_path()) - } } impl CellReaderTrait for CellReader { fn value(&self) -> Res { - Ok(Value::Str(&self.0 .1)) + let s = self + .1 + .get_or_init(|| self.0.as_os_str().to_string_lossy().to_string()); + Ok(Value::Str(s)) } fn label(&self) -> Res { @@ -76,9 +76,21 @@ impl CellReaderTrait for CellReader { } } +impl CellReader { + pub(crate) fn as_file_path(&self) -> Res<&Path> { + Ok(self.0.as_path()) + } +} + impl CellWriterTrait for CellWriter { fn set_value(&mut self, value: OwnValue) -> Res<()> { - todo!() // remove this default implementation + match value { + OwnValue::String(s) => { + *(self.0) = PathBuf::from(s); + Ok(()) + } + _ => userres(format!("cannot set fs path to {:?}", value)), + } } } @@ -96,11 +108,20 @@ impl CellTrait for Cell { } fn read(&self) -> Res { - Ok(CellReader(self.0.clone())) + Ok(CellReader( + self.0 + .read() + .ok_or_else(|| lockerr("cannot lock path for reading"))?, + OnceCell::new(), + )) } fn write(&self) -> Res { - Ok(CellWriter(self.0.clone())) + Ok(CellWriter( + self.0 + .write() + .ok_or_else(|| lockerr("cannot lock path for writing"))?, + )) } fn head(&self) -> Res<(Self, Relation)> { diff --git a/src/interpretations/toml.rs b/src/interpretations/toml.rs index 8e05b61..6c5dbf6 100644 --- a/src/interpretations/toml.rs +++ b/src/interpretations/toml.rs @@ -84,7 +84,8 @@ impl Cell { Self::make_cell(value, Some(cell)) } "fs" => { - let path = cell.as_file_path()?; + let r = cell.read(); + let path = r.as_file_path()?; Self::make_cell( &std::fs::read_to_string(path) .map_err(|e| caused(HErrKind::IO, "cannot read file", e))?, diff --git a/src/interpretations/treesitter.rs b/src/interpretations/treesitter.rs index 7306d4a..100e771 100644 --- a/src/interpretations/treesitter.rs +++ b/src/interpretations/treesitter.rs @@ -53,7 +53,8 @@ impl Cell { Self::make_cell(source, lang.to_owned(), Some(cell)) } "fs" => { - let path = cell.as_file_path()?; + let r = cell.read(); + let path = r.as_file_path()?; let source = std::fs::read_to_string(path) .map_err(|e| caused(HErrKind::IO, "cannot read file", e))?; Self::make_cell(source, lang.to_owned(), Some(cell)) @@ -190,7 +191,7 @@ impl CellWriterTrait for Cell { fn set_value(&mut self, value: OwnValue) -> Res<()> { // TODO: not clear how to edit the tree because afterwards // some cursors/cells will be invalid - todo!() + todo!() // TODO: implement this somehow } } diff --git a/src/interpretations/url.rs b/src/interpretations/url.rs index 62bbe65..3a91207 100644 --- a/src/interpretations/url.rs +++ b/src/interpretations/url.rs @@ -70,9 +70,21 @@ impl CellReaderTrait for CellReader { } } +impl CellReader { + pub(crate) fn as_url(&self) -> Res { + Ok(self.0.url.clone()) + } +} + impl CellWriterTrait for CellWriter { fn set_value(&mut self, value: OwnValue) -> Res<()> { - todo!() + match value { + OwnValue::String(s) => { + let url = Url::parse(s.as_str())?; + Ok(()) + } + _ => userres(format!("cannot set url from non-string value {:?}", value)), + } } } diff --git a/src/interpretations/xml.rs b/src/interpretations/xml.rs index fb0598b..9049626 100644 --- a/src/interpretations/xml.rs +++ b/src/interpretations/xml.rs @@ -89,7 +89,8 @@ impl Cell { pub(crate) fn from_cell(cell: XCell, _: &str) -> Res { match cell.interpretation() { "fs" => { - let path = cell.as_file_path()?; + let r = cell.read(); + let path = r.as_file_path()?; let file = File::open(path).map_err(|e| { caused( HErrKind::InvalidFormat, @@ -413,14 +414,14 @@ impl CellReaderTrait for CellReader { Writer::new_with_indent(Cursor::new(Vec::new()), first as u8, indent.len()) }; serialize_node(&mut writer, &nodes[*pos])?; - let ser = String::from_utf8(writer.into_inner().into_inner()).map_err(|e| { + let ser = writer.into_inner().into_inner(); + String::from_utf8(ser).map_err(|e| { caused( HErrKind::InvalidFormat, "invalid utf8 in xml serialization", e, ) - }); - ser + }) } CellReader::Attr { nodes, pos, .. } => match &nodes[*pos] { Attribute::Attribute(k, v) => Ok(format!("{}=\"{}\"", k, v)), diff --git a/src/interpretations/yaml.rs b/src/interpretations/yaml.rs index 0f3d5a9..cd7271a 100644 --- a/src/interpretations/yaml.rs +++ b/src/interpretations/yaml.rs @@ -82,7 +82,8 @@ impl Cell { } "fs" => { let mut source = String::new(); - let path = cell.as_file_path()?; + let r = cell.read(); + let path = r.as_file_path()?; File::open(path) .map_err(|e| { caused( diff --git a/src/pathlang/eval.rs b/src/pathlang/eval.rs index 508b4ac..dbc4b48 100644 --- a/src/pathlang/eval.rs +++ b/src/pathlang/eval.rs @@ -9,21 +9,22 @@ use crate::{ }, }; -macro_rules! eval_debug { - ( - $body:block - ) => { - // $body +macro_rules! ifdebug { + ( $body:expr ) => { + $body }; } #[derive(Clone, Debug)] pub struct EvalIter<'s> { path: Vec>, + // dfs exploration of the cell tree in search of the path stack: Vec, next_max_path_index: usize, } +// a cell that matches the search together with the indices of the path that it matches +// one cell can match multiple indices of the same path (because of doublestars) #[derive(Clone, Debug)] pub struct CellNode { cell: Res, @@ -32,12 +33,15 @@ pub struct CellNode { impl<'s> EvalIter<'s> { pub(crate) fn new(start: Cell, path: Path<'s>) -> EvalIter<'s> { - eval_debug!({ - println!("\n********************************"); - println!("==> path is: {}\n", path) - }); + ifdebug!(println!( + "\nnew EvalIter, path: {:?}:", + path.0 + .iter() + .map(|p| p.to_string()) + .collect::>() + )); let mut path_indices = HashSet::from([0]); - if Self::is_doublestar_match(&start, 0, &path.0) { + if Self::is_doublestar_match(&start, path.0.first()) { path_indices.insert(1); } let start_node = CellNode { @@ -52,47 +56,6 @@ impl<'s> EvalIter<'s> { } } - fn update_next_max_path_index(stack: &[CellNode], next_max_path_index: &mut usize) { - let max_i = stack - .iter() - .map(|cn| cn.path_indices.iter().copied().max().unwrap_or(0)) - .max() - .unwrap_or(0); - if max_i > *next_max_path_index { - *next_max_path_index = max_i; - } - } - - /// returns the minimal path that failed to match - pub fn unmatched_path(&self) -> String { - let mut path = String::new(); - for i in 0..self.next_max_path_index { - path.push_str(self.path[i].to_string().as_str()); - } - if self.next_max_path_index < self.path.len() { - path.push_str(self.path[self.next_max_path_index].to_string().as_str()); - } - path - } - - fn is_doublestar_match(cell: &Cell, path_index: usize, path: &[PathItem<'s>]) -> bool { - eval_debug!({ - // println!( - // "is_doublestar_match: {} index {}", - // cell.debug_string(), - // path_index - // ) - }); - if let Some(path_item) = path.get(path_index) { - if let Some(Selector::DoubleStar) = path_item.selector { - if EvalIter::eval_filters_match(cell, path_item) { - return true; - } - } - } - false - } - fn eval_next(&mut self) -> Option> { while !self.stack.is_empty() { if let Some(cell) = self.pump() { @@ -104,55 +67,62 @@ impl<'s> EvalIter<'s> { } fn pump(&mut self) -> Option> { - eval_debug!({ - println!("----"); - print!("stack:"); - for cn in &self.stack { - print!( - " {} : {:?}", + ifdebug!(println!( + "----\nstack:{}", + self.stack + .iter() + .map(|cn| format!( + " `{}` : {:?}", cn.cell.as_ref().unwrap().debug_string(), cn.path_indices - ); - } - println!(); - }); + )) + .collect::>() + .join("\n") + )); + // pop the last cell match from the stack let CellNode { cell, mut path_indices, - } = guard_some!(self.stack.pop(), { - return None; - }); + } = guard_some!(self.stack.pop(), { return None }); let cell = guard_ok!(cell, err => { debug_err!(err); return None; }); - eval_debug!({ - println!("pump: {} : {:?}", cell.debug_string(), path_indices); - }); + ifdebug!(println!( + "now testing: `{}` : {:?}", + cell.debug_string(), + path_indices + )); + // if there are associated path indices larger than the path length, + // it means that the cell is a match and we should return it if path_indices.iter().any(|i| *i >= self.path.len()) { + ifdebug!(println!( + "found result: `{}`; push back with indices = {:?}", + cell.debug_string(), + path_indices + )); + + // keep smaller path indices for further exploration path_indices.retain(|i| *i < self.path.len()); - eval_debug!({ - println!( - "found result: {}; push back with indices = {:?}", - cell.debug_string(), - path_indices - ); - }); - self.stack.push(CellNode { - cell: Ok(cell.clone()), - path_indices, - }); - Self::update_next_max_path_index(&self.stack, &mut self.next_max_path_index); + if !path_indices.is_empty() { + self.stack.push(CellNode { + cell: Ok(cell.clone()), + path_indices, + }); + Self::update_next_max_path_index(&self.stack, &mut self.next_max_path_index); + } + return Some(Ok(cell)); } let has_relation = |r, path: &[PathItem<'s>]| path_indices.iter().any(|i| path[*i].relation == r); + if has_relation(Relation::Interpretation, &self.path) { - match Self::subgroup(Relation::Interpretation, &cell) { + match cell.elevate().err() { Err(err) => debug_err!(err), Ok(group) => Self::push_interpretations( &group, @@ -165,34 +135,37 @@ impl<'s> EvalIter<'s> { } // output order is reverse: field first, attr second, subs last + // because we put them in a stack for relation in [Relation::Sub, Relation::Attr, Relation::Field] { - if has_relation(relation, &self.path) { - let (star, doublestar) = Self::has_stars(relation, &path_indices, &self.path); - match Self::subgroup(relation, &cell) { - Err(err) => debug_err!(err), - Ok(group) => { - if star || doublestar { - Self::push_by_relation_with_stars( - relation, - &group, - &path_indices, - &self.path, - &mut self.stack, - &mut self.next_max_path_index, - doublestar, - ); - } else { - Self::push_by_relation_without_stars( - relation, - &group, - &path_indices, - &self.path, - &mut self.stack, - &mut self.next_max_path_index, - ) - } - } - } + if !has_relation(relation, &self.path) { + continue; + } + + let (star, doublestar) = Self::has_stars(relation, &path_indices, &self.path); + let group = guard_ok!(Self::subgroup(relation, &cell), err => { + debug_err!(err); + continue; + }); + + if star || doublestar { + Self::push_by_relation_with_stars( + relation, + &group, + &path_indices, + &self.path, + &mut self.stack, + &mut self.next_max_path_index, + doublestar, + ); + } else { + Self::push_by_relation_without_stars( + relation, + &group, + &path_indices, + &self.path, + &mut self.stack, + &mut self.next_max_path_index, + ) } } @@ -234,13 +207,11 @@ impl<'s> EvalIter<'s> { continue; } - eval_debug!({ - println!( - "push interpretation: {} : pathindex={}", - subcell.debug_string(), - path_index + 1 - ); - }); + ifdebug!(println!( + "push interpretation: `{}` : pathindex={}", + subcell.debug_string(), + path_index + 1 + )); stack.push(CellNode { cell: Ok(subcell), path_indices: HashSet::from([path_index + 1]), @@ -273,30 +244,32 @@ impl<'s> EvalIter<'s> { continue; } if Self::accept_subcell(subcell.clone(), path_item) { - eval_debug!({ - println!("match: {} for {}", subcell.debug_string(), path_item); - }); + ifdebug!(println!( + "match: `{}` for {}", + subcell.debug_string(), + path_item + )); if double_stars { accepted_path_indices.insert(*path_index); } accepted_path_indices.insert(*path_index + 1); - if Self::is_doublestar_match(&subcell, *path_index + 1, path) { + if Self::is_doublestar_match(&subcell, path.get(*path_index + 1)) { accepted_path_indices.insert(*path_index + 2); } } else { - eval_debug!({ - println!("no match {} for {}", subcell.debug_string(), path_item); - }); + ifdebug!(println!( + "no match `{}` for {}", + subcell.debug_string(), + path_item + )); } } if !accepted_path_indices.is_empty() { - eval_debug!({ - println!( - "push by star relation: {} : {:?}", - subcell.debug_string(), - accepted_path_indices - ); - }); + ifdebug!(println!( + "push by star relation: `{}` : {:?}", + subcell.debug_string(), + accepted_path_indices + )); stack.push(CellNode { cell: Ok(subcell), path_indices: accepted_path_indices, @@ -306,6 +279,17 @@ impl<'s> EvalIter<'s> { } } + fn is_doublestar_match(cell: &Cell, path_item: Option<&PathItem<'s>>) -> bool { + if let Some(path_item) = path_item { + if let Some(Selector::DoubleStar) = path_item.selector { + if EvalIter::eval_filters_match(cell, path_item) { + return true; + } + } + } + false + } + fn push_by_relation_without_stars( relation: Relation, group: &Group, @@ -337,18 +321,16 @@ impl<'s> EvalIter<'s> { }); if Self::accept_subcell(subcell.clone(), path_item) { accepted_path_indices.insert(path_index + 1); - if Self::is_doublestar_match(&subcell, *path_index + 1, path) { + if Self::is_doublestar_match(&subcell, path.get(*path_index + 1)) { accepted_path_indices.insert(*path_index + 2); } } if !accepted_path_indices.is_empty() { - eval_debug!({ - println!( - "push by non-star relation: {} : {:?}", - subcell.debug_string(), - accepted_path_indices - ); - }); + ifdebug!(println!( + "push by non-star relation: `{}` : {:?}", + subcell.debug_string(), + accepted_path_indices + )); stack.push(CellNode { cell: Ok(subcell), path_indices: accepted_path_indices, @@ -376,13 +358,11 @@ impl<'s> EvalIter<'s> { continue; }); - eval_debug!({ - println!( - "push by field: {} : {:?}", - subcell.debug_string(), - path_index + 1 - ); - }); + ifdebug!(println!( + "push by field: `{}` : {:?}", + subcell.debug_string(), + path_index + 1 + )); stack.push(CellNode { cell: Ok(subcell), path_indices: HashSet::from([path_index + 1]), @@ -406,6 +386,9 @@ impl<'s> EvalIter<'s> { path_indices: &HashSet, path: &[PathItem<'s>], ) -> (bool, bool) { + path_indices + .iter() + .any(|i| path[*i].relation == relation && path[*i].selector == Some(Selector::Star)); let (mut star, mut doublestar) = (false, false); for i in path_indices { if relation == path[*i].relation { @@ -452,38 +435,41 @@ impl<'s> EvalIter<'s> { for filter in &path_item.filters { match EvalIter::eval_bool_expression(subcell.clone(), &filter.expr) { Err(e) => { - eval_debug!({ - println!("verbose eval filter match ERROR"); - }); + ifdebug!(println!("eval_bool_expression failed")); debug_err!(e); return false; } Ok(false) => { - eval_debug!({ - println!("verbose eval filter match FALSE"); - }); + ifdebug!(println!( + "no match of cell `{}` for filter `{}`", + subcell.debug_string(), + filter + )); return false; } Ok(true) => {} } } - eval_debug!({ - println!("eval filter match test is TRUE: {}", subcell.debug_string()); - }); true } fn cell_matches_selector(cell: &Cell, sel: &Selector) -> bool { - eval_debug!({ - println!("cell_matches_selector: selector {:?}; cell {:?}", sel, cell); - }); if *sel == Selector::Star || *sel == Selector::DoubleStar { + ifdebug!(println!( + "cell `{}` matches star selector", + cell.debug_string() + )); return true; } else { match cell.read().err() { Ok(reader) => match reader.label() { Ok(ref k) => { if sel == k { + ifdebug!(println!( + "cell `{}` matches selector {} ", + cell.debug_string(), + sel, + )); return true; } } @@ -496,6 +482,11 @@ impl<'s> EvalIter<'s> { } fn eval_bool_expression(cell: Cell, expr: &Expression<'s>) -> Res { + ifdebug!(println!( + "{{{{\neval_bool_expression cell `{}` for expr `{}`", + cell.debug_string(), + expr + )); let eval_iter_left = Self::new(cell, expr.left.clone()); for cell in eval_iter_left { let cell = guard_ok!(cell, err => { @@ -514,13 +505,16 @@ impl<'s> EvalIter<'s> { continue; }); if Self::eval_expr(op, lvalue, right)? { + ifdebug!(println!("eval_bool_expression true\n}}}}")); return Ok(true); } } } else { + ifdebug!(println!("eval_bool_expression true\n}}}}")); return Ok(true); } } + ifdebug!(println!("eval_bool_expression false\n}}}}")); Ok(false) } @@ -534,6 +528,29 @@ impl<'s> EvalIter<'s> { _ => Ok(false), } } + + fn update_next_max_path_index(stack: &[CellNode], next_max_path_index: &mut usize) { + let max_i = stack + .iter() + .map(|cn| cn.path_indices.iter().copied().max().unwrap_or(0)) + .max() + .unwrap_or(0); + if max_i > *next_max_path_index { + *next_max_path_index = max_i; + } + } + + /// returns the minimal path that failed to match + pub fn unmatched_path(&self) -> String { + let mut path = String::new(); + for i in 0..self.next_max_path_index { + path.push_str(self.path[i].to_string().as_str()); + } + if self.next_max_path_index < self.path.len() { + path.push_str(self.path[self.next_max_path_index].to_string().as_str()); + } + path + } } impl<'s> Iterator for EvalIter<'s> { diff --git a/src/pathlang/path.rs b/src/pathlang/path.rs index cdfda63..033e9a4 100644 --- a/src/pathlang/path.rs +++ b/src/pathlang/path.rs @@ -62,17 +62,22 @@ impl Display for PathStart<'_> { impl Display for Filter<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "[")?; - fmt_path_items(&self.expr.left.0, f)?; - if let Some(op) = self.expr.op { + write!(f, "[{}]", self.expr)?; + Ok(()) + } +} + +impl Display for Expression<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fmt_path_items(&self.left.0, f)?; + if let Some(op) = self.op { write!(f, "{}", op)?; } - match self.expr.right { + match self.right { Some(Value::Str(s)) => write!(f, "'{}'", s)?, Some(v) => write!(f, "{}", v)?, None => {} } - write!(f, "]")?; Ok(()) } } diff --git a/src/tests/search.rs b/src/tests/search.rs index 62737fb..47e0e1e 100644 --- a/src/tests/search.rs +++ b/src/tests/search.rs @@ -1,6 +1,7 @@ use crate::{ base::*, pathlang::path::{Expression, Filter, Path, PathItem}, + pprint::pprint, utils::log::set_verbose, }; @@ -426,9 +427,10 @@ fn search_double_kleene_with_filter() -> Res<()> { set_verbose(true); let root = Cell::from(TREE).be("yaml"); - let eval = str_eval(root.clone(), "/dir1/**")?; - assert_eq!(eval, ["dir1:", "f1:", "size:ø", "dir2:", "f2:", "size:2"]); + // let eval = str_eval(root.clone(), "/dir1/**")?; + // assert_eq!(eval, ["dir1:", "f1:", "size:ø", "dir2:", "f2:", "size:2"]); + pprint(&root, 0, 0); let eval = str_eval(root.clone(), "/dir1/**[/size]")?; // TODO: this assert fails, f1 and f2 should both be returned assert_eq!(eval, ["f1:", "f2:"]);