Эх сурвалжийг харах

Working on formalizing the query language

Cesar Rodas 10 сар өмнө
parent
commit
8f4f5bc6a6

+ 53 - 0
Cargo.lock

@@ -1900,6 +1900,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
 [[package]]
+name = "pest"
+version = "2.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95"
+dependencies = [
+ "memchr",
+ "thiserror",
+ "ucd-trie",
+]
+
+[[package]]
+name = "pest_derive"
+version = "2.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a"
+dependencies = [
+ "pest",
+ "pest_generator",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183"
+dependencies = [
+ "pest",
+ "pest_meta",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.71",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f"
+dependencies = [
+ "once_cell",
+ "pest",
+ "sha2",
+]
+
+[[package]]
 name = "pin-project"
 version = "1.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3012,6 +3057,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
 
 [[package]]
+name = "ucd-trie"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
+
+[[package]]
 name = "unicode-bidi"
 version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3116,6 +3167,8 @@ dependencies = [
  "hmac",
  "num",
  "parking_lot",
+ "pest",
+ "pest_derive",
  "rand",
  "serde",
  "sha2",

+ 2 - 0
utxo/Cargo.toml

@@ -13,6 +13,8 @@ hmac = "0.12.1"
 num = "0.4.3"
 parking_lot = "0.12.2"
 rand = "0.8.5"
+pest = { version = "2.7.10" }
+pest_derive = "2.7.10"
 serde = { version = "1.0.188", features = ["derive"] }
 sha2 = "0.10.7"
 sqlx = { version = "0.7.1", features = [

+ 23 - 0
utxo/src/filter_expr/expr.pest

@@ -0,0 +1,23 @@
+WHITESPACE = _{ " " | "\t" | NEWLINE }
+
+query       = _{ SOI ~ where_clause? ~ limit_clause? ~ order_by_clause? ~ EOI }
+where_clause = { ^"WHERE" ~ expr }
+limit_clause = { ^"LIMIT" ~ number }
+order_by_clause = { ^"ORDER" ~ ^"BY" ~  variable ~ order }
+
+order       = { ^"ASC" | ^"DESC" }
+expr        = _{ logical_expr }
+logical_expr = { logical_term ~ (logical_op ~ logical_term)* }
+logical_term = { not_expr | comparison }
+logical_op   = { ^"AND" |  "&&" | "||" | ^"OR" }
+not_expr     = { (^"NOT" | "!") ~ logical_term }
+comparison   = { sum ~ (comp_op ~ sum)? }
+sum          = { product ~ (add_op ~ product)* }
+product      = { factor ~ (mul_op ~ factor)* }
+factor       = { number | variable | "(" ~ expr ~ ")" }
+comp_op      = { "=" | "!=" | "<" | "<=" | ">" | ">=" }
+add_op       = { "+" | "-" }
+mul_op       = { "*" | "/" }
+variable     = ${ "$" ~ ident ~ (^"." ~ ident)* }
+ident        = @{ASCII_ALPHANUMERIC+}
+number       = @{ "-"? ~ ASCII_DIGIT+ ~ ("." ~ ASCII_DIGIT+)? }

+ 1 - 0
utxo/src/expr/mod.rs → utxo/src/filter_expr/mod.rs

@@ -3,6 +3,7 @@
 #![allow(warnings)]
 
 mod opcode;
+mod parser;
 mod program;
 mod runtime;
 mod value;

+ 0 - 0
utxo/src/expr/opcode.rs → utxo/src/filter_expr/opcode.rs


+ 180 - 0
utxo/src/filter_expr/parser.rs

@@ -0,0 +1,180 @@
+use std::str::FromStr;
+
+// src/main.rs
+use pest::{Parser, Span};
+use pest_derive::Parser;
+
+#[derive(Parser)]
+#[grammar = "src/filter_expr/expr.pest"] // relative path to your .pest file
+struct QueryParser;
+
+#[derive(Clone, PartialEq, Eq, Debug, Copy)]
+pub enum ExprOp {
+    And,
+    Or,
+    Not,
+    Eq,
+    NotEq,
+    Gt,
+    Gte,
+    Lt,
+    Lte,
+    Add,
+    Sub,
+    Mul,
+    Div,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Copy)]
+pub enum Order {
+    Ascending,
+    Descending,
+}
+
+impl FromStr for Order {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "asc" => Ok(Order::Ascending),
+            "desc" => Ok(Order::Descending),
+            _ => Err(()),
+        }
+    }
+}
+
+impl From<&str> for ExprOp {
+    fn from(s: &str) -> Self {
+        match s.to_lowercase().as_str() {
+            "+" => ExprOp::Add,
+            "-" => ExprOp::Sub,
+            "*" => ExprOp::Mul,
+            "/" => ExprOp::Div,
+            "&&" | "and" => ExprOp::And,
+            "||" | "or" => ExprOp::Or,
+            "!" | "not" => ExprOp::Not,
+            "==" | "=" => ExprOp::Eq,
+            "!=" => ExprOp::NotEq,
+            ">" => ExprOp::Gt,
+            ">=" => ExprOp::Gte,
+            "<" => ExprOp::Lt,
+            "<=" => ExprOp::Lte,
+            x => panic!("{} is not a valid Operation", x),
+        }
+    }
+}
+
+#[derive(Debug)]
+enum Expr {
+    Not(Box<Expr>),
+    Op {
+        op: ExprOp,
+        left: Box<Expr>,
+        right: Box<Expr>,
+    },
+    Variable(Vec<String>),
+    Ident(String),
+    Number(i128),
+}
+
+#[derive(Debug)]
+pub struct Query {
+    where_clause: Option<Expr>,
+    limit_clause: Option<i128>,
+    order_by_clause: Option<(Expr, Order)>,
+}
+
+fn parse_expr(pair: pest::iterators::Pair<Rule>) -> Expr {
+    match pair.as_rule() {
+        Rule::logical_expr
+        | Rule::logical_term
+        | Rule::comparison
+        | Rule::sum
+        | Rule::factor
+        | Rule::product => {
+            let mut inner = pair.into_inner();
+            let mut last_expr = None;
+
+            while let Some(first) = inner.next() {
+                let (first_expr, next) = if let Some(first_expr) = last_expr {
+                    (first_expr, Some(first))
+                } else {
+                    (parse_expr(first), inner.next())
+                };
+                last_expr = Some(if let Some(op_pair) = next {
+                    let op = op_pair.as_str().into();
+                    let second = inner.next().unwrap();
+                    let second_expr = parse_expr(second);
+                    Expr::Op {
+                        op,
+                        left: Box::new(first_expr),
+                        right: Box::new(second_expr),
+                    }
+                } else {
+                    first_expr
+                });
+            }
+
+            last_expr.unwrap()
+        }
+        Rule::not_expr => {
+            let mut inner = pair.into_inner();
+            Expr::Not(Box::new(parse_expr(inner.next().unwrap())))
+        }
+        Rule::variable => {
+            let mut inner = pair.into_inner();
+            let mut first = vec![inner.next().unwrap().as_str().to_owned()];
+            while let Some(Expr::Ident(ident)) = inner.next().map(parse_expr) {
+                first.push(ident);
+            }
+            Expr::Variable(first)
+        }
+        Rule::number => Expr::Number(pair.as_str().parse().unwrap()),
+        Rule::ident => Expr::Ident(pair.as_str().to_owned()),
+        x => {
+            panic!("Unexpected rule: {:?} {:?}", x, pair);
+        }
+    }
+}
+
+pub fn parse_query(query: &str) -> Query {
+    let pairs = QueryParser::parse(Rule::query, query).unwrap();
+
+    let mut where_clause = None;
+    let mut limit_clause = None;
+    let mut order_by_clause = None;
+
+    for pair in pairs {
+        match pair.as_rule() {
+            Rule::EOI => {
+                break;
+            }
+            Rule::where_clause => {
+                where_clause = Some(parse_expr(pair.into_inner().next().unwrap()));
+            }
+            Rule::limit_clause => {
+                limit_clause = Some(match parse_expr(pair.into_inner().next().unwrap()) {
+                    Expr::Number(v) => v,
+                    _ => unreachable!(),
+                });
+            }
+            Rule::order_by_clause => {
+                let mut iter = pair.into_inner();
+                let expr = parse_expr(iter.next().unwrap());
+                let order = if let Some(order_by_text) = iter.next() {
+                    order_by_text.as_str().to_lowercase().parse().unwrap()
+                } else {
+                    Order::Ascending
+                };
+                order_by_clause = Some((expr, order));
+            }
+            _ => unreachable!(),
+        }
+    }
+
+    Query {
+        where_clause,
+        limit_clause,
+        order_by_clause,
+    }
+}

+ 19 - 3
utxo/src/expr/program.rs → utxo/src/filter_expr/program.rs

@@ -1,4 +1,4 @@
-use super::{opcode::OpCode, runtime::execute, value::Value, Addr, Error};
+use super::{opcode::OpCode, parser::parse_query, runtime::execute, value::Value, Addr, Error};
 use crate::Transaction;
 
 #[derive(Debug, Clone)]
@@ -15,7 +15,9 @@ pub struct Program {
 }
 
 impl Program {
-    pub fn compile(_program: &str) -> Result<Self, Error> {
+    pub fn new(code: &str) -> Result<Self, Error> {
+        let ast = parse_query(code);
+        panic!("{:#?}", ast);
         todo!()
     }
 
@@ -33,7 +35,21 @@ impl Program {
 #[cfg(test)]
 mod test {
     use super::Program;
-    use crate::expr::{opcode::OpCode, value::Value};
+    use crate::filter_expr::{opcode::OpCode, value::Value};
+
+    #[test]
+    fn parse() {
+        let x = Program::new(
+            r#"
+            WHERE
+                $foo = 3
+                AND $bar
+                AND $foo = 1 + 2 + 3 +  ((1+ 2-3*4-5) * $bar.tx.lol)
+            LIMIT 10
+            ORDER BY $foo DESC
+        "#,
+        );
+    }
 
     #[test]
     fn simple_program() {

+ 0 - 0
utxo/src/expr/runtime.rs → utxo/src/filter_expr/runtime.rs


+ 0 - 0
utxo/src/expr/value.rs → utxo/src/filter_expr/value.rs


+ 1 - 1
utxo/src/lib.rs

@@ -28,8 +28,8 @@ mod asset;
 mod broadcaster;
 mod config;
 mod error;
-mod expr;
 mod filter;
+mod filter_expr;
 mod id;
 mod ledger;
 mod payment;