pak_db/query/
pql.rs

1
2use std::{iter::Peekable, marker::PhantomData};
3
4use logos::{Lexer, Logos};
5
6use crate::{error::{PakResult, PqlError, PqlResult}, group::DeserializeGroup, query::{PakQuery, PakQueryExpression, PakQueryIntersection, PakQueryUnion}, value::PakValue};
7
8//==============================================================================================
9//        PQL Tokens
10//==============================================================================================
11
12#[derive(Logos, Debug, PartialEq, PartialOrd, Clone)]
13#[logos(skip r"[ \t\n\f]+")] // Ignore this regex pattern between tokens
14pub enum PqlToken {
15    #[token("=")]
16    Eq,
17    #[token("<")]
18    Less,
19    #[token("<=")]
20    LessEq,
21    #[token(">")]
22    Greater,
23    #[token(">=")]
24    GreaterEq,
25    #[token("<-")]
26    Contains,
27    #[token("|")]
28    Or,
29    #[token("&")]
30    And,
31    #[regex("|[ ]+\\(")]
32    GroupStartOr,
33    #[regex("&[ ]+\\(")]
34    GroupStartAnd,
35    #[token("(")]
36    GroupStart,
37    #[token(")")]
38    GroupEnd,
39    #[regex("[a-zA-Z_]([a-zA-Z0-9_-]+)?", text)]
40    Text(String),
41    #[regex("[0-9]+", int)]
42    #[regex("[0-9]+i", int)]
43    Int(i64),
44    #[regex("[0-9]+u", uint)]
45    Uint(u64),
46    #[regex("[0-9]+.[0-9]+", float)]
47    #[regex("[0-9]+f", float)]
48    Float(f64),
49}
50
51impl PqlToken {
52    pub fn get_text(&self) -> Option<&String> {
53        let PqlToken::Text(text) = self else { return None };
54        Some(text)
55    }
56}
57
58fn text(lex : &mut Lexer<PqlToken>) -> Option<String> {
59    Some(lex.slice().to_string())
60}
61
62fn int(lex : &mut Lexer<PqlToken>) -> Option<i64> {
63    lex.slice().parse().ok()
64}
65
66fn uint(lex : &mut Lexer<PqlToken>) -> Option<u64> {
67    lex.slice().parse().ok()
68}
69
70fn float(lex : &mut Lexer<PqlToken>) -> Option<f64> {
71    lex.slice().parse().ok()
72}
73
74
75//==============================================================================================
76//        Parse Function
77//==============================================================================================
78
79pub fn pql<T : DeserializeGroup + 'static>(source : &str) -> PakResult<Box<dyn PakQueryExpression<T>>> {
80    if source.starts_with("all") || source.is_empty() {
81        return Ok(Box::new(PakQuery::All));
82    }
83    let mut lexer = Lexer::new(source).peekable();
84    match PqlQuery::parse(&mut lexer) {
85        Ok(query) => {Ok(query.eval())},
86        Err(error) => Err(error.into()),
87    }
88}
89
90//==============================================================================================
91//        Pql parsing Common
92//==============================================================================================
93
94type TokenResult = Result<PqlToken, ()>;
95
96fn next_is<I : Iterator<Item =TokenResult>>(lexer : &mut Peekable<I>, token : &PqlToken) -> PqlResult<bool> {
97    let Some(Ok(t)) = lexer.peek() else { return Err(PqlError::EndOfFile.into()) };
98    Ok(token == t)
99}
100
101fn next_is_or_end<I : Iterator<Item =TokenResult>>(lexer : &mut Peekable<I>, token : &PqlToken) -> PqlResult<bool> {
102    let Some(Ok(t)) = lexer.peek() else { return Ok(false) };
103    Ok(token == t)
104}
105
106//==============================================================================================
107//        Query
108//==============================================================================================
109
110#[derive(Debug, Clone)]
111enum PqlQuery {
112    Expression(Box<PqlExpression>),
113    Group(Box<PqlGroup>)
114}
115
116impl PqlQuery {
117    fn parse<I : Iterator<Item =TokenResult>>(lexer : &mut Peekable<I>) -> PqlResult<PqlQuery> {
118        let group = PqlGroup::parse(lexer);
119        match group {
120            Ok(group) => return Ok(PqlQuery::Group(Box::new(group))),
121            Err(PqlError::NoMatch) => {},
122            Err(err) => return Err(err), 
123        };
124        let expression = PqlExpression::parse(lexer);
125        match expression {
126            Ok(expression) => Ok(PqlQuery::Expression(Box::new(expression))),
127            Err(err) => Err(err),
128        }
129    }
130    
131    fn eval<T : DeserializeGroup + 'static>(self) -> Box<dyn PakQueryExpression<T>> {
132        match self {
133            PqlQuery::Expression(pql_expression) => pql_expression.eval(),
134            PqlQuery::Group(pql_group) => pql_group.eval(),
135        }
136    }
137}
138
139
140//==============================================================================================
141//        PqlGroup
142//==============================================================================================
143
144#[derive(Debug, Clone)]
145struct PqlGroup(PqlQuery);
146
147impl PqlGroup {
148    fn parse<I : Iterator<Item =TokenResult>>(lexer : &mut Peekable<I>) -> PqlResult<PqlGroup> {
149        if !next_is_or_end(lexer, &PqlToken::GroupStart)? { return Err(PqlError::NoMatch) }
150        lexer.next();
151        let query = PqlQuery::parse(lexer)?;
152        if !next_is(lexer, &PqlToken::GroupEnd)? { return Err(PqlError::UnexpectedToken(lexer.next().unwrap().unwrap(), ")".to_string())) }
153        lexer.next();
154        Ok(PqlGroup(query))
155    }
156    
157    fn eval<T : DeserializeGroup + 'static>(self) -> Box<dyn PakQueryExpression<T>> {
158        self.0.eval()
159    }
160}
161
162
163//==============================================================================================
164//        Expression
165//==============================================================================================
166
167#[derive(Debug, Clone)]
168struct PqlExpression {
169    first : PqlStatement,
170    second : Option<(PqlToken, PqlQuery)>
171}
172
173impl PqlExpression {
174    fn parse<I : Iterator<Item =TokenResult>>(lexer : &mut Peekable<I>) -> PqlResult<PqlExpression> {
175        let first = PqlStatement::parse(lexer)?;
176        if !(next_is_or_end(lexer, &PqlToken::Or)? || next_is_or_end(lexer, &PqlToken::And)?) { return Ok(PqlExpression { first, second: None }) }
177        let Some(Ok(op)) = lexer.next() else { return Ok(PqlExpression { first, second: None })};
178        let second = PqlQuery::parse(lexer)?;
179        Ok(PqlExpression { first, second : Some((op, second)) })
180    }
181    
182    fn eval<T : DeserializeGroup + 'static>(self) -> Box<dyn PakQueryExpression<T>> {
183        let first = self.first.eval::<T>();
184        if let Some((op, second)) = self.second {
185            let second = second.eval();
186            match op {
187                PqlToken::And => Box::new(PakQueryIntersection::new(first, second)),
188                PqlToken::Or => Box::new(PakQueryUnion::new(first, second)),
189                _ => unreachable!()
190            }
191        } else {
192            Box::new(first)
193        }
194    }
195}
196
197
198//==============================================================================================
199//        Statement
200//==============================================================================================
201
202#[derive(Debug, Clone)]
203struct PqlStatement {
204    key : String,
205    op : PqlToken,
206    value : PakValue
207}
208
209impl PqlStatement {
210    fn parse<I : Iterator<Item =TokenResult>>(lexer : &mut Peekable<I>) -> PqlResult<PqlStatement> {
211        let key = parse_text(lexer)?;
212        let op = parse_statement_op(lexer)?;
213        let value = parse_value(lexer)?;
214        Ok(PqlStatement { key, op, value })
215    }
216    
217    fn eval<T : DeserializeGroup + 'static>(self) -> Box<dyn PakQueryExpression<T>> {
218        let query = match self.op {
219            PqlToken::Eq => PakQuery::Equal(self.key, self.value, PhantomData),
220            PqlToken::Less => PakQuery::LessThan(self.key, self.value),
221            PqlToken::LessEq => PakQuery::LessThanEqual(self.key, self.value),
222            PqlToken::Greater => PakQuery::GreaterThan(self.key, self.value),
223            PqlToken::GreaterEq => PakQuery::GreaterThanEqual(self.key, self.value),
224            PqlToken::Contains => PakQuery::Contains(self.key, self.value),
225            _ => unreachable!()
226        };
227        Box::new(query)
228    }
229}
230
231
232//==============================================================================================
233//        Value Parse
234//==============================================================================================
235
236fn parse_value<I : Iterator<Item =TokenResult>>(lexer : &mut Peekable<I>) -> PqlResult<PakValue> {
237    if !check_value(lexer)? { return Err(PqlError::NoMatch) }
238    let Some(Ok(first_text)) = lexer.next() else { return Err(PqlError::EndOfFile) };
239    let value = match first_text {
240        PqlToken::Text(value) => PakValue::String(value),
241        PqlToken::Int(value) => PakValue::Int(value),
242        PqlToken::Uint(value) => PakValue::Uint(value),
243        PqlToken::Float(value) => PakValue::Float(value.to_bits()),
244        _ => { unreachable!() }
245    };
246    Ok(value)
247}
248
249fn check_value<I : Iterator<Item =TokenResult>>(lexer : &mut Peekable<I>) -> PqlResult<bool> {
250    let Some(Ok(next)) = lexer.peek() else { return Err(PqlError::EndOfFile) };
251    Ok(matches!(next, PqlToken::Text(_) | PqlToken::Float(_) | PqlToken::Int(_) | PqlToken::Uint(_)))
252}
253
254//==============================================================================================
255//        Text
256//==============================================================================================
257
258fn parse_text<I : Iterator<Item =TokenResult>>(lexer : &mut Peekable<I>) -> PqlResult<String> {
259    if !check_text(lexer)? { return Err(PqlError::NoMatch) }
260    let Some(Ok(first_text)) = lexer.next() else { return Err(PqlError::EndOfFile) };
261    let Some(text) = first_text.get_text() else { return Err(PqlError::NoMatch) };
262    Ok(text.clone())
263}
264
265fn check_text<I : Iterator<Item =TokenResult>>(lexer : &mut Peekable<I>) -> PqlResult<bool> {
266    let Some(Ok(next)) = lexer.peek() else { return Err(PqlError::EndOfFile) };
267    Ok(matches!(next, PqlToken::Text(_)))
268}
269
270//==============================================================================================
271//        Statement Op
272//==============================================================================================
273
274fn parse_statement_op<I : Iterator<Item =TokenResult>>(lexer : &mut Peekable<I>) -> PqlResult<PqlToken> {
275    if !check_statement_op(lexer)? { return Err(PqlError::NoMatch) }
276    Ok(lexer.next().unwrap().unwrap())
277}
278
279fn check_statement_op<I : Iterator<Item =TokenResult>>(lexer : &mut Peekable<I>) -> PqlResult<bool> {
280    let Some(Ok(next)) = lexer.peek() else { return Err(PqlError::EndOfFile) };
281    Ok(matches!(next, PqlToken::Eq | PqlToken::Less | PqlToken::Greater | PqlToken::LessEq | PqlToken::GreaterEq | PqlToken::Contains))
282}
283
284
285#[cfg(test)]
286mod test {
287    use logos::Lexer;
288
289    use crate::{index::PakIndexIdentifier, query::pql::{PqlExpression, PqlGroup, PqlQuery, PqlStatement, PqlToken}, test::{Person, Pet, alice_smith, bob_johnson, build_data_base, jane_doe, john_doe, john_jacob}, value::PakValue};
290
291    #[test]
292    fn pql_parse_query() {
293        let pql = "(age >= 25 | name = John) & last_name = Doe & personallity_traits <- Patient";
294        let mut lexer = Lexer::<PqlToken>::new(pql).peekable();
295        let query = PqlQuery::parse(&mut lexer);
296        assert!(query.is_ok())
297    }
298    
299    #[test]
300    fn pql_parse_statement() {
301        let pql = "age <= 25";
302        let mut lexer = Lexer::<PqlToken>::new(pql).peekable();
303        let stmt = PqlStatement::parse(&mut lexer).unwrap();
304        assert_eq!(stmt.key, "age");
305        assert_eq!(stmt.op, PqlToken::LessEq);
306        assert_eq!(stmt.value, PakValue::Int(25));
307    }
308    
309    #[test]
310    fn pql_parse_expression() {
311        let pql = "age <= 20 | name >= J";
312        let mut lexer = Lexer::<PqlToken>::new(pql).peekable();
313        let expr = PqlExpression::parse(&mut lexer).unwrap();
314        assert_eq!(expr.first.key, "age");
315        assert_eq!(expr.first.op, PqlToken::LessEq);
316        assert_eq!(expr.first.value, PakValue::Int(20));
317    }
318    
319    #[test]
320    fn pql_parse_group() {
321        let pql = "(age <= 20 | name >= J)";
322        let mut lexer = Lexer::<PqlToken>::new(pql).peekable();
323        let expr = PqlGroup::parse(&mut lexer).unwrap();
324        assert!(matches!(expr, PqlGroup(PqlQuery::Expression(_))));
325    }
326    
327    #[test]
328    fn pql_compare() {
329        let (pak, _, _) = build_data_base();
330        let pql = "personallity_traits <- Patient";
331        let query = "personallity_traits".contains_value("Patient");
332        let people = pak.query_pql::<(Person,)>(pql).unwrap();
333        let other_people = pak.query::<(Person,)>(query).unwrap();
334        assert_eq!(people.len(), other_people.len());
335        assert!(people.iter().all(|person| person.personallity_traits.contains(&crate::test::PersonalityTrait::Patient)));
336        assert!(other_people.iter().all(|person| person.personallity_traits.contains(&crate::test::PersonalityTrait::Patient)));
337    }
338    
339    #[test]
340    fn pql_all() {
341        let (pak, _, _) = build_data_base();
342        let pql = "all";
343        let (people, pets) = pak.query_pql::<(Person, Pet)>(pql).unwrap();
344        
345        assert_eq!(people.len(), 7);
346        assert_eq!(pets.len(), 3);
347        
348        assert!(people.contains(&john_doe()));
349        assert!(people.contains(&jane_doe()));
350        assert!(people.contains(&alice_smith()));
351        assert!(people.contains(&john_jacob()));
352        assert!(people.contains(&bob_johnson()));
353    }
354}