Skip to content

Commit

Permalink
accept or expressions and type selectors in path
Browse files Browse the repository at this point in the history
  • Loading branch information
emanueldima committed Mar 14, 2024
1 parent f54db3b commit 6f31d53
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 67 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The path API can be seen as an generalization of the concepts behind xpath, json

:warning: Hial is **currently under construction.** Some things don't work yet and some things will change.


### What can it do?

##### 1. Select or search for pieces of data in a structured way.
Expand All @@ -34,6 +35,7 @@ Print all services with inaccessible images in a Docker compose file:
# shell
hial './config.yaml^yaml/services/*[ /image^split[":"]/[0]^http[HEAD]@status/code>=400 ]'
# 🚧 todo: split interpretation (regex[( ([^:]*): )*]
# 🚧 todo: get from docker image to docker url
```

```rust
Expand All @@ -54,7 +56,7 @@ Print the structure of a rust file (struct, enum, type, functions) as a tree:
```bash
hial './src/tests/rust.rs^rust/**[#type^split["_"]/[-1]=="item"]/*[name|parameters|return_type]'
# 🚧 todo: search results as tree
# 🚧 todo: boolean filter combinator
# 🚧 todo: split interpretation
```

##### 2. Modify data selected as above.
Expand All @@ -78,6 +80,7 @@ Change the user's docker configuration:
```bash
# shell
hial '~/.docker/config.json^json/auths/docker.io/username = "newuser"'
# 🚧 todo: assign operator
```
```rust
// rust
Expand All @@ -93,7 +96,7 @@ Copy a string from some json object entry which is embedded in a zip file, into

```bash
# shell
hial 'copy ./assets.zip^zip/data.json^json/meshes/sphere ./src/assets/sphere.rs^rust/**[#type=="let_declaration"][/pattern=sphere]/value'
hial 'copy ./assets.zip^zip/data.json^json/meshes/sphere ./src/assets/sphere.rs^rust/**[:let_declaration][/pattern=sphere]/value'
# 🚧 todo: support copy
# 🚧 todo: support zip
# 🚧 todo: /**[filter] should match leaves only
Expand All @@ -103,7 +106,7 @@ Split a markdown file into sections and put each in a separate file:

```bash
# shell
`hial 'copy ./book.md^md/*[#type=="heading1"][as x] ./{label(x)}.md'
`hial 'copy ./book.md^md/*[:heading1][as x] ./{label(x)}.md'
# 🚧 todo: support copy
# 🚧 todo: support markdown
# 🚧 todo: support interpolation in destination
Expand Down Expand Up @@ -225,7 +228,7 @@ Examples:
- `./src/main.rs@size` is the size of this file (the `size` attribute of the file).
- `./src/main.rs^rust` represents the rust AST tree.
- `./src/main.rs^rust/*[#type=='function_item']` are all the top-level cells representing functions in the `main.rs` rust file.
- `./src/main.rs^rust/*[:function_item]` are all the top-level cells representing functions in the `main.rs` rust file.
- `http://api.github.com` is a url cell.
- `http://api.github.com^http` is the http response of a GET request to this url.
Expand Down
5 changes: 1 addition & 4 deletions doc/issues.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
# List of Todos and other Issues

- support ~
- support type selector: `hial './src/tests/rust.rs^rust/*[:function_item]'`
- add http interpretation params: method=HEAD, accept=""
- add split(":") and regex interpretations
- /*[name|parameters|return_type] ??
- set value on the command line: '/username = "newuser"'
- https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/rustfmt/src/lib.rs^http^rust does not work
- '**[filter]' must be work as '**/*[filter]' (filter to be applied only on leaves)
- support rust/ts write: `hial './src/tests/rust.rs^rust/*[:function_item].label = "modified_fn_name"'`
- add http interpretation params: method=HEAD, accept=""
- support zip, markdown
- support 'copy source destination'
- support ^json^tree^xml
Expand Down
45 changes: 38 additions & 7 deletions src/search/parse.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{api::*, guard_ok, search::path::*, search::url::*};
use nom::character::complete::space0;
use nom::error::VerboseErrorKind;
use nom::multi::separated_list1;
use nom::{
branch::alt,
bytes::complete::{escaped, tag},
Expand Down Expand Up @@ -174,20 +175,45 @@ fn filter(input: &str) -> NomRes<&str, Filter> {
fn expression(input: &str) -> NomRes<&str, Expression> {
context(
"expression",
tuple((path_items, opt(tuple((operation, value))))),
tuple((
space0,
separated_list1(tag("|"), alt((type_expression, ternary_expression))),
)),
)(input)
.map(|(next_input, res)| {
(
next_input,
if res.1.len() == 1 {
res.1.into_iter().next().unwrap()
} else {
Expression::Or { expressions: res.1 }
},
)
})
}

fn ternary_expression(input: &str) -> NomRes<&str, Expression> {
context(
"ternary expression",
tuple((path_items, space0, opt(tuple((operation, space0, rvalue))))),
)(input)
.map(|(next_input, res)| {
(
next_input,
Expression {
Expression::Ternary {
left: res.0,
op: res.1.map(|x| x.0),
right: res.1.map(|x| x.1),
op: res.2.map(|x| x.0),
right: res.2.map(|x| x.2),
},
)
})
}

fn type_expression(input: &str) -> NomRes<&str, Expression> {
context("type expression", tuple((tag(":"), identifier_code_points)))(input)
.map(|(next_input, res)| (next_input, Expression::Type { ty: res.1 }))
}

fn interpretation_param(input: &str) -> NomRes<&str, InterpretationParam> {
context(
"interpretation parameter",
Expand All @@ -197,7 +223,7 @@ fn interpretation_param(input: &str) -> NomRes<&str, InterpretationParam> {
space0,
identifier_code_points,
space0,
opt(tuple((tag("="), space0, value))),
opt(tuple((tag("="), space0, rvalue))),
)),
tag("]"),
),
Expand Down Expand Up @@ -226,8 +252,13 @@ fn relation(input: &str) -> NomRes<&str, char> {
.map(|(next_input, res)| (next_input, res.chars().next().unwrap()))
}

fn value(input: &str) -> NomRes<&str, Value> {
context("value", alt((value_string, value_uint)))(input)
fn rvalue(input: &str) -> NomRes<&str, Value> {
context("value", alt((value_ident, value_string, value_uint)))(input)
}

fn value_ident(input: &str) -> NomRes<&str, Value> {
context("value ident", identifier_code_points)(input)
.map(|(next_input, res)| (next_input, Value::Str(res)))
}

fn value_string(input: &str) -> NomRes<&str, Value> {
Expand Down
81 changes: 46 additions & 35 deletions src/search/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,39 @@ pub(crate) struct Filter<'a> {
}

#[derive(Clone, Debug, PartialEq)]
pub(crate) struct Expression<'a> {
pub(crate) left: Path<'a>,
pub(crate) op: Option<&'a str>,
pub(crate) right: Option<Value<'a>>,
pub(crate) enum Expression<'a> {
Ternary {
left: Path<'a>,
op: Option<&'a str>,
right: Option<Value<'a>>,
},
Type {
ty: &'a str,
},
Or {
expressions: Vec<Expression<'a>>,
},
}

impl Display for Path<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fmt_path_items(&self.0, f)?;
for it in &self.0 {
write!(f, "{}", it)?;
}
Ok(())
}
}

impl Display for PathItem<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fmt_path_item(self, f)?;
match self {
PathItem::Elevation(e) => {
write!(f, "{}", e)?;
}
PathItem::Normal(n) => {
write!(f, "{}", n)?;
}
}
Ok(())
}
}
Expand Down Expand Up @@ -120,14 +137,27 @@ impl Display for Filter<'_> {

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.right {
Some(Value::Str(s)) => write!(f, "'{}'", s)?,
Some(v) => write!(f, "{}", v)?,
None => {}
match self {
Expression::Ternary { left, op, right } => {
write!(f, "{}", left)?;
if let Some(op) = op {
write!(f, "{}", op)?;
}
if let Some(right) = right {
write!(f, "{:?}", right)?;
}
}
Expression::Type { ty } => {
write!(f, ":{}", ty)?;
}
Expression::Or { expressions } => {
for (i, expr) in expressions.iter().enumerate() {
write!(f, "{}", expr)?;
if i < expressions.len() - 1 {
write!(f, "|")?;
}
}
}
}
Ok(())
}
Expand All @@ -148,7 +178,7 @@ pub(crate) struct DisplayRefPath<'a, 'b: 'a, 'c: 'b>(pub(crate) &'c Vec<&'b Path
impl<'a, 'b: 'a, 'c: 'b> Display for DisplayRefPath<'a, 'b, 'c> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for it in self.0 {
fmt_path_item(it, f)?
write!(f, "{}", it)?;
}
Ok(())
}
Expand All @@ -159,31 +189,12 @@ pub(crate) struct DisplayPath<'a, 'b: 'a>(pub(crate) &'b Vec<PathItem<'a>>);
impl<'a, 'b: 'a> Display for DisplayPath<'a, 'b> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for it in self.0 {
fmt_path_item(it, f)?
write!(f, "{}", it)?;
}
Ok(())
}
}

fn fmt_path_items(path_items: &Vec<PathItem>, f: &mut Formatter<'_>) -> std::fmt::Result {
for it in path_items {
fmt_path_item(it, f)?
}
Ok(())
}

fn fmt_path_item(path_item: &PathItem, f: &mut Formatter<'_>) -> std::fmt::Result {
match path_item {
PathItem::Elevation(e) => {
write!(f, "{}", e)?;
}
PathItem::Normal(n) => {
write!(f, "{}", n)?;
}
}
Ok(())
}

impl<'a> PathStart<'a> {
pub fn eval(&self) -> Res<Xell> {
match self {
Expand Down
34 changes: 28 additions & 6 deletions src/search/searcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ impl<'s> Searcher<'s> {

fn eval_filters_match(subcell: &Xell, path_item: &NormalPathItem) -> bool {
for filter in &path_item.filters {
match Searcher::eval_bool_expression(subcell.clone(), &filter.expr) {
match Searcher::eval_expression(subcell.clone(), &filter.expr) {
Err(e) => {
ifdebug!(println!("eval_bool_expression failed"));
debug_err!(e);
Expand All @@ -394,9 +394,31 @@ impl<'s> Searcher<'s> {
true
}

fn eval_bool_expression(cell: Xell, expr: &Expression<'s>) -> Res<bool> {
fn eval_expression(cell: Xell, expr: &Expression<'s>) -> Res<bool> {
match expr {
Expression::Ternary { left, op, right } => {
Self::eval_ternary_expression(cell, left.clone(), *op, *right)
}
Expression::Type { ty } => cell.read().ty().map(|t| t == *ty),
Expression::Or { expressions } => {
for expr in expressions {
if Self::eval_expression(cell.clone(), expr)? {
return Ok(true);
}
}
Ok(false)
}
}
}

fn eval_ternary_expression(
cell: Xell,
left: Path<'s>,
op: Option<&'s str>,
right: Option<Value<'s>>,
) -> Res<bool> {
ifdebug!(println!(
"{{{{\neval_bool_expression cell `{}` for expr `{}`",
"{{{{\neval_ternary_expression cell `{}` for expr `{}`",
cell.debug_string(),
expr
));
Expand All @@ -412,14 +434,14 @@ impl<'s> Searcher<'s> {
}
}

let eval_iter_left = Self::new(cell, expr.left.clone());
let eval_iter_left = Self::new(cell, left);
for cell in eval_iter_left {
let cell = guard_ok!(cell, err => {
debug_err!(err);
continue;
});
if let Some(op) = expr.op {
if let Some(right) = expr.right {
if let Some(op) = op {
if let Some(right) = right {
let reader = guard_ok!(cell.read().err(), err => {
debug_err!(err);
continue;
Expand Down
15 changes: 7 additions & 8 deletions src/search/url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,15 +253,12 @@ where
T: InputTakeAtPosition,
<T as InputTakeAtPosition>::Item: AsChar,
{
let accept =
|c: char| c == '-' || c == '_' || c == '.' || c == ':' || c == '*' || c.is_alphanum();
i.split_at_position1_complete(
|item| {
let char_item = item.as_char();
!(char_item == '-'
|| char_item == '_'
|| char_item == '.'
|| char_item == ':'
|| char_item == '*'
|| char_item.is_alphanum())
!accept(char_item)
},
ErrorKind::AlphaNumeric,
)
Expand All @@ -272,10 +269,11 @@ where
T: InputTakeAtPosition,
<T as InputTakeAtPosition>::Item: AsChar,
{
let accept = |c: char| c == '-' || c == '.' || c.is_alphanum();
i.split_at_position1_complete(
|item| {
let char_item = item.as_char();
!(char_item == '-' || char_item.is_alphanum() || char_item == '.')
!accept(char_item)
// ... actual ascii code points and url encoding...: https://infra.spec.whatwg.org/#ascii-code-point
},
ErrorKind::AlphaNumeric,
Expand All @@ -287,10 +285,11 @@ where
T: InputTakeAtPosition,
<T as InputTakeAtPosition>::Item: AsChar,
{
let accept = |c: char| c == '_' || c.is_alphanumeric();
i.split_at_position1_complete(
|item| {
let char_item = item.as_char();
!(char_item.is_alphanum())
!accept(char_item)
},
ErrorKind::AlphaNumeric,
)
Expand Down
Loading

0 comments on commit 6f31d53

Please sign in to comment.