Skip to content

Commit

Permalink
implement filter selecting
Browse files Browse the repository at this point in the history
  • Loading branch information
suaviloquence committed Aug 6, 2024
1 parent 0903ce9 commit c441316
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 61 deletions.
13 changes: 3 additions & 10 deletions examples/outputs/filter_select.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ expression: result
---
{
"just-just": [
false,
false,
false,
false,
true
"just"
],
"list": [
"my",
Expand All @@ -24,10 +20,7 @@ expression: result
"me"
],
"ms": [
true,
false,
false,
true,
false
"my",
"mother"
]
}
4 changes: 2 additions & 2 deletions examples/scrps/filter_select.scrp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
list: "my very old mother just" | split();
m-words: "my mother mom me" | split();

ms: $list | is_in(list: $m-words)*;
just-just: $list | eq(to: "just")*;
ms: $list | [item: $item | is_in(list: $m-words)];
just-just: $list | [item: $item | eq(to: "just")];
5 changes: 1 addition & 4 deletions grammar.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,8 @@ qualifier -> `?`
filter_list -> `|` filter qualifier filter_list
| ""

value -> leaf
| inline

filter -> ID `(` arg_list `)`
| `[` ID `:` value `]`
| `[` ID `:` leaf filter_list `]`

arg_list -> ID `:` leaf arg_list2
| ""
Expand Down
13 changes: 8 additions & 5 deletions src/frontend/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,13 @@ pub struct Inline<'a> {
pub filters: Option<AstRef<'a, FilterList<'a>>>,
}

#[derive(Debug, Clone)]
pub enum Value<'a> {
Leaf(Leaf<'a>),
Inline(Inline<'a>),
impl<'a> From<Leaf<'a>> for Inline<'a> {
fn from(value: Leaf<'a>) -> Self {
Self {
value,
filters: None,
}
}
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -256,7 +259,7 @@ pub enum Ast<'a> {
},
FilterSelect {
name: &'a str,
value: Value<'a>,
value: Inline<'a>,
},
@flatten[self, .next]
FilterList {
Expand Down
34 changes: 9 additions & 25 deletions src/frontend/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use super::{
ast::{
ArgList, Ast, AstRef, Element, Filter, FilterCall, FilterList, FilterSelect, Inline, Leaf,
Qualifier, RValue, Selector, SelectorCombinator, SelectorList, Statement, StatementList,
Value,
},
scanner::{Lexeme, Scanner, Span, Token},
};
Expand Down Expand Up @@ -192,28 +191,6 @@ impl<'a> Parser<'a> {
Ok(Inline { value, filters })
}

fn parse_value(&mut self) -> Result<Value<'a>> {
let (span, lx) = self.scanner.peek_non_whitespace();

match lx.token {
Token::Less => self.parse_inline().map(Value::Inline),
Token::Dollar | Token::Int | Token::Float | Token::String => {
self.parse_leaf().map(Value::Leaf)
}
_ => Err(ParseError::unexpected(
vec![
Token::Less,
Token::Dollar,
Token::Int,
Token::Float,
Token::String,
],
lx,
span,
)),
}
}

fn parse_selector_list(&mut self) -> Result<Option<AstRef<'a, SelectorList<'a>>>> {
let mut item = self.scanner.peek_non_comment();
if item.1.token == Token::Whitespace {
Expand Down Expand Up @@ -330,9 +307,16 @@ impl<'a> Parser<'a> {
Token::BracketOpen => {
let name = self.try_eat(Token::Id)?.value;
self.try_eat(Token::Colon)?;
let value = self.parse_value()?;
let leaf = self.parse_leaf()?;
let filters = self.parse_filter_list()?;
self.try_eat(Token::BracketClose)?;
Ok(Filter::Select(FilterSelect::new(name, value)))
Ok(Filter::Select(FilterSelect::new(
name,
Inline {
value: leaf,
filters,
},
)))
}
_ => Err(ParseError::unexpected(
vec![Token::Id, Token::BracketOpen],
Expand Down
18 changes: 18 additions & 0 deletions src/interpreter/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,24 @@ fn is_in<'doc>(value: PValue<'doc>, list: Vec<EValue<'doc>>) -> anyhow::Result<P
Ok(Value::Bool(list.contains(&value.into())))
}

#[filter_fn]
fn truthy<'doc>(value: PValue<'doc>) -> anyhow::Result<PValue<'doc>> {
let truthy = match value {
Value::Null => false,
Value::Float(f) => f != 0.,
Value::Int(i) => i != 0,
Value::Bool(b) => b,
Value::String(s) => !s.is_empty(),
Value::List(l) => !l.is_empty(),
Value::Structure(s) => !s.is_empty(),
Value::Extra(Pipeline::Element(_)) => true,
Value::Extra(Pipeline::ListIter(mut i)) => i.next().is_some(),
Value::Extra(Pipeline::StructIter(mut i)) => i.next().is_some(),
};

Ok(Value::Bool(truthy))
}

macro_rules! build_map {
($(
$id: ident,
Expand Down
65 changes: 50 additions & 15 deletions src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use anyhow::Context;
use execution_mode::ExecutionMode;
use reqwest::Url;

use value::{EValue, ListIter};
use value::{EValue, ListIter, PValue};

use crate::frontend::{
ast::{
Expand Down Expand Up @@ -254,21 +254,36 @@ impl<'ast> Interpreter<'ast> {
.into_iter()
.map(|arg| Ok((arg.id, ctx.leaf_to_value(&arg.value)?)))
.collect::<Result<BTreeMap<_, _>>>()?;

match filter.qualifier {
Qualifier::One => filter::dispatch_filter(call.id, value, args, ctx),
Qualifier::Optional if matches!(value, Value::Null) => Ok(Value::Null),
Qualifier::Optional => filter::dispatch_filter(call.id, value, args, ctx),
Qualifier::Collection => value
.try_unwrap::<ListIter>()?
.map(|value| filter::dispatch_filter(call.id, value, args.clone(), ctx))
.collect::<Result<Vec<_>>>()
.map(Value::List),
}
}
ast::Filter::Select(_select) => {
todo!()
qualify(filter.qualifier, value, |value| {
filter::dispatch_filter(call.id, value, args.clone(), ctx)
})
}
ast::Filter::Select(select) => qualify(filter.qualifier, value, |value| {
let ls: ListIter = value.try_unwrap()?;

let mut inner_scope = ElementContext {
element: ctx.element,
variables: Variables::default(),
text: OnceCell::new(),
parent: Some(ctx),
url: ctx.url.clone(),
};

Ok(Value::List(
ls.map(|value| {
let value = EValue::from(value);
inner_scope.set_var(select.name.into(), value.clone())?;

let keep: bool = self
.eval_inline(&select.value, &mut inner_scope)?
.try_unwrap()?;

Ok(keep.then(|| value.into()))
})
.filter_map(Result::transpose)
.collect::<Result<_>>()?,
))
}),
})
.map(EValue::from)
}
Expand All @@ -286,6 +301,26 @@ impl<'ast> Interpreter<'ast> {
}
}

fn qualify<'doc, F>(
qualifier: Qualifier,
value: PValue<'doc>,
mut action: F,
) -> Result<PValue<'doc>>
where
F: FnMut(PValue<'doc>) -> Result<PValue<'doc>>,
{
match qualifier {
Qualifier::One => action(value),
Qualifier::Optional if matches!(value, Value::Null) => Ok(Value::Null),
Qualifier::Optional => action(value),
Qualifier::Collection => value
.try_unwrap::<ListIter>()?
.map(action)
.collect::<Result<Vec<_>>>()
.map(Value::List),
}
}

impl<'ast, 'ctx> ElementContext<'ast, 'ctx> {
pub fn get_var(&self, id: &str) -> anyhow::Result<EValue<'ctx>> {
match id {
Expand Down

0 comments on commit c441316

Please sign in to comment.