#1 Query language

Ouvert
cesar veut fusionner 30 commits à partir de cesar/expr-engine vers cesar/main

+ 166 - 44
Cargo.lock

@@ -265,7 +265,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -357,7 +357,7 @@ dependencies = [
  "heck 0.4.1",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -451,7 +451,7 @@ dependencies = [
  "proc-macro-crate",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
  "syn_derive",
 ]
 
@@ -485,15 +485,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "bytes"
-version = "1.6.0"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
 
 [[package]]
 name = "cc"
-version = "1.1.0"
+version = "1.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8"
+checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052"
 
 [[package]]
 name = "cfg-if"
@@ -554,7 +554,7 @@ dependencies = [
  "heck 0.5.0",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -598,6 +598,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
 
 [[package]]
+name = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
 name = "core-foundation"
 version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -722,7 +728,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "regex",
- "syn 2.0.70",
+ "syn 2.0.71",
  "synthez",
 ]
 
@@ -763,9 +769,11 @@ version = "0.99.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce"
 dependencies = [
+ "convert_case",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "rustc_version",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -1025,7 +1033,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -1090,7 +1098,7 @@ dependencies = [
  "quote",
  "serde",
  "serde_json",
- "syn 2.0.70",
+ "syn 2.0.71",
  "textwrap",
  "thiserror",
  "typed-builder",
@@ -1249,9 +1257,9 @@ dependencies = [
 
 [[package]]
 name = "http-body"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
 dependencies = [
  "bytes",
  "http",
@@ -1484,7 +1492,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "regex",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -1663,6 +1671,30 @@ dependencies = [
 ]
 
 [[package]]
+name = "num"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
+dependencies = [
+ "num-bigint",
+ "num-complex",
+ "num-integer",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
 name = "num-bigint-dig"
 version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1680,6 +1712,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "num-complex"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
 name = "num-integer"
 version = "0.1.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1700,6 +1741,17 @@ dependencies = [
 ]
 
 [[package]]
+name = "num-rational"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
+dependencies = [
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
 name = "num-traits"
 version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1757,7 +1809,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -1802,7 +1854,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall 0.5.2",
+ "redox_syscall 0.5.3",
  "smallvec",
  "windows-targets 0.52.6",
 ]
@@ -1856,6 +1908,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"
@@ -1872,7 +1969,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -2053,9 +2150,9 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.5.2"
+version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
+checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
 dependencies = [
  "bitflags 2.6.0",
 ]
@@ -2122,6 +2219,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
 
 [[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
 name = "rustix"
 version = "0.37.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2193,14 +2299,14 @@ dependencies = [
  "heck 0.4.1",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
 name = "security-framework"
-version = "2.11.0"
+version = "2.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
 dependencies = [
  "bitflags 2.6.0",
  "core-foundation",
@@ -2211,15 +2317,21 @@ dependencies = [
 
 [[package]]
 name = "security-framework-sys"
-version = "2.11.0"
+version = "2.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7"
+checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
 dependencies = [
  "core-foundation-sys",
  "libc",
 ]
 
 [[package]]
+name = "semver"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+
+[[package]]
 name = "serde"
 version = "1.0.204"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2236,7 +2348,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -2336,7 +2448,7 @@ checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -2632,9 +2744,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.70"
+version = "2.0.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16"
+checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2650,7 +2762,7 @@ dependencies = [
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -2671,7 +2783,7 @@ version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a3d2c2202510a1e186e63e596d9318c91a8cbe85cd1a56a7be0c333e5f59ec8d"
 dependencies = [
- "syn 2.0.70",
+ "syn 2.0.71",
  "synthez-codegen",
  "synthez-core",
 ]
@@ -2682,7 +2794,7 @@ version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f724aa6d44b7162f3158a57bccd871a77b39a4aef737e01bcdff41f4772c7746"
 dependencies = [
- "syn 2.0.70",
+ "syn 2.0.71",
  "synthez-core",
 ]
 
@@ -2695,7 +2807,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "sealed",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -2742,22 +2854,22 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "1.0.61"
+version = "1.0.62"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
+checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.61"
+version = "1.0.62"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
+checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -2806,7 +2918,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -2910,7 +3022,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -2958,7 +3070,7 @@ checksum = "29a3151c41d0b13e3d011f98adc24434560ef06673a155a6c7f66b9879eecce2"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]
@@ -2968,6 +3080,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"
@@ -3068,8 +3186,12 @@ dependencies = [
  "borsh",
  "chrono",
  "cucumber",
+ "derive_more",
  "futures",
  "hmac",
+ "num",
+ "pest",
+ "pest_derive",
  "rand",
  "serde",
  "sha2",
@@ -3133,7 +3255,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
  "wasm-bindgen-shared",
 ]
 
@@ -3167,7 +3289,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -3403,7 +3525,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.71",
 ]
 
 [[package]]

+ 4 - 0
utxo/Cargo.toml

@@ -8,9 +8,13 @@ async-trait = "0.1.73"
 bech32 = "0.11.0"
 borsh = { version = "1.3.1", features = ["derive", "bytes", "de_strict_order"] }
 chrono = { version = "0.4.31", features = ["serde"] }
+derive_more = "0.99.17"
 futures = { version = "0.3.30", optional = true }
 hmac = "0.12.1"
+num = "0.4.3"
 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 = [

+ 8 - 1
utxo/src/amount.rs

@@ -68,7 +68,14 @@ impl TryInto<Amount> for AnyAmount {
 /// The `cents` and `Asset` must be used to store amounts in the storage
 /// layer. Float or string representations should be used to display
 #[derive(
-    Clone, Debug, Eq, PartialEq, Deserialize, borsh::BorshSerialize, borsh::BorshDeserialize,
+    Clone,
+    Debug,
+    Eq,
+    PartialEq,
+    PartialOrd,
+    Deserialize,
+    borsh::BorshSerialize,
+    borsh::BorshDeserialize,
 )]
 pub struct Amount {
     asset: Asset,

+ 3 - 1
utxo/src/asset.rs

@@ -8,7 +8,9 @@ use std::{
     str::FromStr,
 };
 
-#[derive(Debug, Hash, PartialEq, Eq, Clone, borsh::BorshSerialize, borsh::BorshDeserialize)]
+#[derive(
+    Debug, Hash, PartialEq, PartialOrd, Eq, Clone, borsh::BorshSerialize, borsh::BorshDeserialize,
+)]
 /// An asset type
 pub struct Asset {
     /// The name of the asset

+ 237 - 0
utxo/src/filter_expr/compiler/mod.rs

@@ -0,0 +1,237 @@
+use super::{
+    expr::{Expr, ExprOp},
+    opcode::OpCode,
+    value::Value,
+    Addr, Error, Register,
+};
+use std::collections::HashMap;
+
+pub struct Compiler<'a> {
+    expr: &'a Expr,
+    current_register: Register,
+    labels: Addr,
+}
+
+mod optimizations;
+
+impl<'a> Compiler<'a> {
+    pub fn new(expr: &'a Expr) -> Self {
+        Self {
+            expr,
+            current_register: 0.into(),
+            labels: 0.into(),
+        }
+    }
+
+    fn next_label(&mut self) -> Addr {
+        *self.labels += 1;
+        self.labels
+    }
+
+    fn next_register(&mut self) -> Register {
+        *self.current_register += 1;
+        self.current_register
+    }
+
+    fn compile_expr(
+        &mut self,
+        expr: &'a Expr,
+        exit_label: Addr,
+        return_to: Register,
+    ) -> Result<Vec<OpCode>, Error> {
+        Ok(match expr {
+            Expr::Variable(name) => vec![OpCode::LOAD_EXTERNAL(return_to, name.clone())],
+            Expr::Bool(value) => vec![OpCode::LOAD(return_to, Value::Bool(*value).into())],
+            Expr::String(string) => vec![OpCode::LOAD(
+                return_to,
+                Value::String(string.clone()).into(),
+            )],
+            Expr::Number(number) => vec![OpCode::LOAD(return_to, Value::Number(*number).into())],
+            Expr::Op { op, terms } => {
+                let mut opcodes = vec![];
+                let current_exit_label = self.next_label();
+
+                match op {
+                    ExprOp::Not => {
+                        if terms.len() != 1 {
+                            return Err(Error::UnexpectedExprState);
+                        }
+                        let mut opcodes = terms
+                            .first()
+                            .map(|expr| self.compile_expr(expr, exit_label, return_to))
+                            .ok_or(Error::UnexpectedExprState)??;
+                        opcodes.push(OpCode::NOT(return_to, return_to));
+                    }
+                    ExprOp::Eq => {
+                        let first = self.next_register();
+                        let other = self.next_register();
+
+                        let terms = terms
+                            .iter()
+                            .enumerate()
+                            .map(|(idx, term)| {
+                                self.compile_expr(
+                                    term,
+                                    current_exit_label,
+                                    if idx == 0 { first } else { other },
+                                )
+                            })
+                            .collect::<Result<Vec<_>, _>>()?;
+                        let mut iter = terms.into_iter();
+                        let mut term_opcodes = iter.next().ok_or(Error::MissingNextRule)?;
+                        opcodes.append(&mut term_opcodes);
+
+                        if iter
+                            .map(|mut term_codes| {
+                                opcodes.append(&mut term_codes);
+                                opcodes.push(OpCode::EQ(return_to, first, other));
+                                opcodes.push(OpCode::JNE(return_to, exit_label));
+                                1
+                            })
+                            .sum::<usize>()
+                            == 0
+                        {
+                            return Err(Error::MissingNextRule);
+                        }
+
+                        let _ = opcodes.pop();
+                    }
+                    ExprOp::Add | ExprOp::Mul | ExprOp::Div | ExprOp::Sub => {
+                        let term_return = self.next_register();
+                        let terms = terms
+                            .iter()
+                            .enumerate()
+                            .map(|(idx, term)| {
+                                self.compile_expr(term, current_exit_label, term_return)
+                            })
+                            .collect::<Result<Vec<_>, _>>()?;
+
+                        let mut iter = terms.into_iter();
+                        let mut term_opcodes = iter.next().ok_or(Error::MissingNextRule)?;
+                        opcodes.append(&mut term_opcodes);
+                        opcodes.push(OpCode::MOV(return_to, term_return));
+
+                        for mut term_opcodes in iter {
+                            opcodes.append(&mut term_opcodes);
+                            opcodes.push(match op {
+                                ExprOp::Add => OpCode::ADD(return_to, return_to, term_return),
+                                ExprOp::Mul => OpCode::MUL(return_to, return_to, term_return),
+                                ExprOp::Div => OpCode::DIV(return_to, return_to, term_return),
+                                ExprOp::Sub => OpCode::SUB(return_to, return_to, term_return),
+                                _ => unreachable!(),
+                            });
+                        }
+
+                        return Ok(opcodes);
+                    }
+                    ExprOp::Or | ExprOp::And => {
+                        opcodes = vec![OpCode::LOAD(return_to, Value::Bool(false).into())];
+
+                        let terms = terms
+                            .iter()
+                            .enumerate()
+                            .map(|(idx, term)| {
+                                self.compile_expr(term, current_exit_label, return_to)
+                            })
+                            .collect::<Result<Vec<_>, _>>()?;
+
+                        let mut iter = terms.into_iter();
+
+                        for mut term_opcodes in iter.into_iter() {
+                            opcodes.append(&mut term_opcodes);
+                            match op {
+                                ExprOp::Or => opcodes.push(OpCode::JEQ(return_to, exit_label)),
+                                ExprOp::And => opcodes.push(OpCode::JNE(return_to, exit_label)),
+                                _ => unreachable!(),
+                            };
+                        }
+                    }
+                    x => panic!("unimplemented {:#?}", x),
+                }
+
+                opcodes.push(OpCode::LABEL(current_exit_label));
+                opcodes
+            }
+        })
+    }
+
+    pub fn optimize(opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
+        vec![
+            optimizations::remove_unused_load,
+            optimizations::remove_redundant_load_and_mov,
+            optimizations::calculate_static_values,
+            optimizations::remove_useless_jumps,
+            optimizations::remove_dead_code,
+        ]
+        .into_iter()
+        .fold((opcodes, false), |(opcodes, has_optimized), f| {
+            let (opcodes, optimized) = f(opcodes);
+            (opcodes, has_optimized || optimized)
+        })
+    }
+
+    /// Returns a list of Addresses that are labeled in the opcodes list. This function is required
+    /// to resolve any jump in the compiler
+    pub fn labels_to_addr(opcodes: &[OpCode]) -> HashMap<Addr, (Addr, Addr)> {
+        let mut pos_without_labels = 0;
+        opcodes
+            .iter()
+            .enumerate()
+            .map(|(actual_pos, opcode)| match opcode {
+                OpCode::LABEL(label) => (opcode, pos_without_labels, actual_pos),
+                _ => {
+                    pos_without_labels = pos_without_labels + 1usize;
+                    (opcode, pos_without_labels - 1, actual_pos)
+                }
+            })
+            .filter_map(|(opcode, pos_without_label, pos)| match opcode {
+                OpCode::LABEL(id) => Some((*id, (pos_without_label.into(), pos.into()))),
+                _ => None,
+            })
+            .collect::<HashMap<_, _>>()
+    }
+
+    pub fn resolve_label_to_addr(opcodes: Vec<OpCode>) -> Result<Vec<OpCode>, Error> {
+        let labels_to_addr = Self::labels_to_addr(&opcodes);
+
+        opcodes
+            .into_iter()
+            .filter(|opcode| !matches!(opcode, OpCode::LABEL(_)))
+            .map(|opcode| {
+                // Rewrite JMP to not use labels but instead addresses
+                Ok(match opcode {
+                    OpCode::JMP(label) => OpCode::JMP(
+                        labels_to_addr
+                            .get(&label)
+                            .map(|addr| addr.0)
+                            .ok_or(Error::UnknownLabel(*label))?,
+                    ),
+                    OpCode::JEQ(register, label) => OpCode::JEQ(
+                        register,
+                        labels_to_addr
+                            .get(&label)
+                            .map(|addr| addr.0)
+                            .ok_or(Error::UnknownLabel(*label))?,
+                    ),
+                    OpCode::JNE(register, label) => OpCode::JNE(
+                        register,
+                        labels_to_addr
+                            .get(&label)
+                            .map(|addr| addr.0)
+                            .ok_or(Error::UnknownLabel(*label))?,
+                    ),
+                    opcode => opcode,
+                })
+            })
+            .collect()
+    }
+
+    pub fn compile(mut self) -> Result<Vec<OpCode>, Error> {
+        let program_exit = self.next_label();
+        let return_to = self.next_register();
+        let mut opcodes = self.compile_expr(self.expr, program_exit, return_to)?;
+        opcodes.push(OpCode::LABEL(program_exit));
+        opcodes.push(OpCode::HLT(return_to));
+        Ok(opcodes)
+    }
+}

+ 104 - 0
utxo/src/filter_expr/compiler/optimizations/calculate_static_values.rs

@@ -0,0 +1,104 @@
+use crate::filter_expr::{opcode::OpCode, Value};
+use std::{collections::HashMap, rc::Rc};
+
+/// Executes operations that can be performed at compile time, and sets the initial_register to
+/// the result. This is useful for optimizing the program, by executing at `compile` time as
+/// much as possible, when the terms of the opcodes are not dependent on the runtime state.
+pub fn calculate_static_values(mut opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
+    let mut register = HashMap::new();
+    let mut has_changed = false;
+
+    opcodes.iter_mut().for_each(|opcode| {
+        match &opcode {
+            OpCode::LOAD(dst, value) => {
+                register.insert(*dst, value.clone());
+            }
+            OpCode::JMP(_) | OpCode::HLT(_) | OpCode::LABEL(_) => {}
+            OpCode::MOV(dst, src) => {
+                register.remove(dst);
+                if let Some(value) = register.remove(src) {
+                    *opcode = OpCode::LOAD(*dst, value);
+                    has_changed = true;
+                }
+            }
+            OpCode::LOAD_EXTERNAL(dst, _)
+            | OpCode::AND(dst, _, _)
+            | OpCode::OR(dst, _, _)
+            | OpCode::NOT(dst, _)
+            | OpCode::MOD(dst, _, _)
+            | OpCode::NE(dst, _, _)
+            | OpCode::GT(dst, _, _)
+            | OpCode::LT(dst, _, _)
+            | OpCode::NEG(dst, _)
+            | OpCode::SHL(dst, _, _)
+            | OpCode::XOR(dst, _, _)
+            | OpCode::SHR(dst, _, _)
+            | OpCode::GE(dst, _, _)
+            | OpCode::LE(dst, _, _)
+            | OpCode::CPY(dst, _) => {
+                register.remove(dst);
+            }
+            OpCode::JEQ(reg, addr) => {
+                if register.get(reg).map(|x| x.is_true()).unwrap_or_default() {
+                    *opcode = OpCode::JMP(*addr);
+                    has_changed = true;
+                }
+            }
+            OpCode::JNE(reg, addr) => {
+                if register.get(reg).map(|x| x.is_false()).unwrap_or_default() {
+                    *opcode = OpCode::JMP(*addr);
+                    has_changed = true;
+                }
+            }
+            OpCode::EQ(dst, reg1, reg2) => {
+                let value1 = if let Some(value) = register.remove(reg1) {
+                    value.clone()
+                } else {
+                    register.remove(dst);
+                    return;
+                };
+                let value2 = if let Some(value) = register.get(reg2) {
+                    value.clone()
+                } else {
+                    register.remove(dst);
+                    return;
+                };
+                let result = value1 == value2;
+                *opcode = OpCode::LOAD(*dst, Rc::new(result.into()));
+                has_changed = true;
+            }
+            OpCode::MUL(dst, reg1, reg2)
+            | OpCode::SUB(dst, reg1, reg2)
+            | OpCode::DIV(dst, reg1, reg2)
+            | OpCode::ADD(dst, reg1, reg2) => {
+                let number1 = match register.get(reg1).map(|value| value.as_number()) {
+                    Some(Ok(number)) => number,
+                    _ => {
+                        register.remove(dst);
+                        return;
+                    }
+                };
+                let number2 = match register.get(reg2).map(|value| value.as_number()) {
+                    Some(Ok(number)) => number,
+                    _ => {
+                        register.remove(dst);
+                        return;
+                    }
+                };
+                register.remove(dst);
+
+                if let Some(calculated_value) = match opcode {
+                    OpCode::ADD(_, _, _) => number1.checked_add(number2).map(Value::Number),
+                    OpCode::MUL(_, _, _) => number1.checked_mul(number2).map(Value::Number),
+                    OpCode::SUB(_, _, _) => number1.checked_sub(number2).map(Value::Number),
+                    OpCode::DIV(_, _, _) => number1.checked_div(number2).map(Value::Number),
+                    _ => None,
+                } {
+                    *opcode = OpCode::LOAD(*dst, calculated_value.into());
+                    has_changed = true;
+                }
+            }
+        };
+    });
+    (opcodes, has_changed)
+}

+ 10 - 0
utxo/src/filter_expr/compiler/optimizations/mod.rs

@@ -0,0 +1,10 @@
+mod calculate_static_values;
+mod remove_dead_code;
+mod remove_redundant_load_and_mov;
+mod remove_unused_load;
+mod remove_useless_jumps;
+
+pub use self::{
+    calculate_static_values::*, remove_dead_code::*, remove_redundant_load_and_mov::*,
+    remove_unused_load::*, remove_useless_jumps::*,
+};

+ 90 - 0
utxo/src/filter_expr/compiler/optimizations/remove_dead_code.rs

@@ -0,0 +1,90 @@
+use crate::filter_expr::{compiler::Compiler, opcode::OpCode};
+use std::collections::HashSet;
+
+/// Remove OpCodes that are not executed.
+///
+/// This function simulates the Jumps the code may have and attempts to execute all possible
+/// branches, regardless of the actual values, keeping track of executed opcodes.
+///
+/// Non executed opcodes are then removed from the list of opcodes.
+#[inline]
+pub fn remove_dead_code(mut opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
+    let total = opcodes.len();
+    // list of opcodes that have been executed
+    let mut executed_opcodes = vec![0; total];
+
+    // current opcode position
+    let mut current = 0;
+
+    let mut future_jumps = HashSet::new();
+    // to avoid infinite loop, since the jumps are unconditional, we need to keep track the jumps
+    let mut jumped_at = HashSet::new();
+
+    // convert labels to address for the current list of opcodes
+    let label_to_addr = Compiler::labels_to_addr(&opcodes);
+
+    loop {
+        match if let Some(opcode) = opcodes.get(current) {
+            executed_opcodes[current] += 1;
+            opcode
+        } else {
+            break;
+        } {
+            OpCode::HLT(_) => {
+                let mut has_jumped = false;
+                for jump_to in future_jumps.iter() {
+                    if !jumped_at.contains(jump_to) {
+                        jumped_at.insert(*jump_to);
+                        has_jumped = true;
+                        current = *jump_to;
+                        break;
+                    }
+                }
+
+                if !has_jumped {
+                    break;
+                }
+            }
+            OpCode::JMP(to) => {
+                let to = label_to_addr
+                    .get(to)
+                    .map(|addr| addr.1)
+                    .expect("Invalid label");
+                if !jumped_at.contains(&to) {
+                    jumped_at.insert(*to);
+                    current = *to;
+                } else {
+                    current += 1;
+                }
+            }
+            OpCode::JEQ(_, to) | OpCode::JNE(_, to) => {
+                let to = label_to_addr
+                    .get(to)
+                    .map(|addr| addr.1)
+                    .expect("Invalid label");
+                future_jumps.insert(*to);
+                current += 1;
+            }
+            _ => {
+                current += 1;
+            }
+        }
+    }
+
+    let total_opcodes = opcodes.len();
+    let new_opcodes = opcodes
+        .into_iter()
+        .enumerate()
+        .filter_map(|(idx, opcode)| {
+            if executed_opcodes[idx] == 0 {
+                None
+            } else {
+                Some(opcode)
+            }
+        })
+        .collect::<Vec<_>>();
+
+    let new_opcodes_total = new_opcodes.len();
+
+    (new_opcodes, total_opcodes != new_opcodes_total)
+}

+ 30 - 0
utxo/src/filter_expr/compiler/optimizations/remove_redundant_load_and_mov.rs

@@ -0,0 +1,30 @@
+use crate::filter_expr::opcode::OpCode;
+
+/// Convert:
+///     LOAD reg var
+///     MOV target reg
+/// To:
+///     LOAD target var
+pub fn remove_redundant_load_and_mov(mut opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
+    let old_total_opcodes = opcodes.len();
+    let new_opcodes = opcodes.into_iter().fold(Vec::new(), |mut acc, current| {
+        if let OpCode::MOV(target, from) = &current {
+            if let Some(last) = acc.last_mut() {
+                match last {
+                    OpCode::LOAD(reg, _) => {
+                        if reg == from {
+                            *reg = *target;
+                            return acc;
+                        }
+                    }
+                    _ => {}
+                }
+            }
+        }
+        acc.push(current);
+        acc
+    });
+    let new_total_opcodes = new_opcodes.len();
+
+    (new_opcodes, old_total_opcodes != new_total_opcodes)
+}

+ 148 - 0
utxo/src/filter_expr/compiler/optimizations/remove_unused_load.rs

@@ -0,0 +1,148 @@
+use crate::filter_expr::opcode::OpCode;
+use std::collections::HashMap;
+
+/// Remove loaded values that are not read by any opcode. This is useful for reducing the size
+/// of the opcodes to be executed
+pub fn remove_unused_load(mut opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
+    // list of opcodes where the register are accessed
+    let used_registers = opcodes
+        .iter()
+        .enumerate()
+        .map(|(pos, opcode)| match opcode {
+            OpCode::LOAD(_, _)
+            | OpCode::LOAD_EXTERNAL(_, _)
+            | OpCode::LABEL(_)
+            | OpCode::JMP(_) => vec![],
+            OpCode::NOT(_, reg1)
+            | OpCode::CPY(_, reg1)
+            | OpCode::MOV(_, reg1)
+            | OpCode::SHL(_, reg1, _)
+            | OpCode::SHR(_, reg1, _)
+            | OpCode::NEG(_, reg1)
+            | OpCode::HLT(reg1)
+            | OpCode::JNE(reg1, _)
+            | OpCode::JEQ(reg1, _) => {
+                vec![(*reg1, pos)]
+            }
+            OpCode::ADD(_, reg1, reg2)
+            | OpCode::GE(_, reg1, reg2)
+            | OpCode::LE(_, reg1, reg2)
+            | OpCode::MUL(_, reg1, reg2)
+            | OpCode::NE(_, reg1, reg2)
+            | OpCode::DIV(_, reg1, reg2)
+            | OpCode::MOD(_, reg1, reg2)
+            | OpCode::XOR(_, reg1, reg2)
+            | OpCode::SUB(_, reg1, reg2)
+            | OpCode::AND(_, reg1, reg2)
+            | OpCode::OR(_, reg1, reg2)
+            | OpCode::EQ(_, reg1, reg2)
+            | OpCode::GT(_, reg1, reg2)
+            | OpCode::LT(_, reg1, reg2) => {
+                vec![(*reg1, pos), (*reg2, pos)]
+            }
+        })
+        .flatten()
+        .fold(HashMap::new(), |mut acc, (reg, pos)| {
+            acc.entry(reg).or_insert_with(Vec::new).push(pos);
+            acc
+        });
+
+    // a list of registers that are loaded or overwritten
+    let loaded_registers = opcodes
+        .iter()
+        .enumerate()
+        .filter_map(|(pos, opcode)| match opcode {
+            OpCode::LOAD(dst, _) | OpCode::LOAD_EXTERNAL(dst, _) => Some((*dst, pos)),
+            OpCode::ADD(dst, _, _)
+            | OpCode::NOT(dst, _)
+            | OpCode::CPY(dst, _)
+            | OpCode::MOV(dst, _)
+            | OpCode::SHL(dst, _, _)
+            | OpCode::SHR(dst, _, _)
+            | OpCode::NEG(dst, _)
+            | OpCode::GE(dst, _, _)
+            | OpCode::LE(dst, _, _)
+            | OpCode::MUL(dst, _, _)
+            | OpCode::NE(dst, _, _)
+            | OpCode::DIV(dst, _, _)
+            | OpCode::MOD(dst, _, _)
+            | OpCode::XOR(dst, _, _)
+            | OpCode::SUB(dst, _, _)
+            | OpCode::AND(dst, _, _)
+            | OpCode::OR(dst, _, _)
+            | OpCode::EQ(dst, _, _)
+            | OpCode::GT(dst, _, _)
+            | OpCode::LT(dst, _, _) => Some((*dst, pos)),
+            _ => None,
+        })
+        .fold(HashMap::new(), |mut acc, (reg, pos)| {
+            acc.entry(reg).or_insert_with(Vec::new).push(pos);
+            acc
+        });
+
+    let old_total_opcodes = opcodes.len();
+    let old_opcodes = opcodes.clone();
+
+    // remove unused registers. If the register has not been read by any opcode, then it can be
+    // removed.
+    let new_opcodes = opcodes
+        .into_iter()
+        .enumerate()
+        .filter_map(|(pos, opcode)| match &opcode {
+            OpCode::LOAD(dst, _) | OpCode::LOAD_EXTERNAL(dst, _) => {
+                // get the address where the register is reloaded.
+                //
+                // The main goal is to check if readed before it is reloaded. If it is not readed
+                // then it is redundant and can be removed.
+                let next_load = loaded_registers
+                    .get(dst)
+                    .expect("Invalid register")
+                    .iter()
+                    .fold(None, |mut next, loaded_at| {
+                        if next.is_none() && *loaded_at > pos {
+                            next = Some(*loaded_at);
+                        }
+                        next
+                    });
+
+                if let Some(next_load) = next_load {
+                    if old_opcodes[pos..next_load].iter().any(|opcode| {
+                        matches!(
+                            opcode,
+                            OpCode::JEQ(_, _) | OpCode::JMP(_) | OpCode::JNE(_, _)
+                        )
+                    }) {
+                        // Between the current LOAD and the next time is updated, either with
+                        // another LOAD or another OpCode that updates this register there is a
+                        // jump, making it too complex to determine right now if the register is
+                        // used or not, it is safer just to keep it until further analysis is
+                        // implemented
+                        return Some(opcode);
+                    }
+                }
+
+                if let Some(readed_at) = used_registers.get(dst) {
+                    if readed_at.iter().any(|readed_at| {
+                        if let Some(next_load) = next_load {
+                            *readed_at <= next_load && *readed_at >= pos
+                        } else if *readed_at > pos {
+                            true
+                        } else {
+                            false
+                        }
+                    }) {
+                        Some(opcode)
+                    } else {
+                        None
+                    }
+                } else {
+                    None
+                }
+            }
+            _ => Some(opcode),
+        })
+        .collect::<Vec<_>>();
+
+    let new_total_opcodes = new_opcodes.len();
+    (new_opcodes, old_total_opcodes != new_total_opcodes)
+}

+ 35 - 0
utxo/src/filter_expr/compiler/optimizations/remove_useless_jumps.rs

@@ -0,0 +1,35 @@
+use crate::filter_expr::{compiler::Compiler, opcode::OpCode};
+
+/// Remove useless jumps
+///
+/// Useless jumps are any kind of jump that is just going to the next line.
+pub fn remove_useless_jumps(mut opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
+    // `pos` is the position of the opcode in the new list of opcodes, where the LABELS are
+    // ignored and not counted
+    let mut pos: usize = 0;
+    let label_to_addr = Compiler::labels_to_addr(&opcodes);
+    let old_total_opcodes = opcodes.len();
+    let new_opcodes = opcodes
+        .into_iter()
+        .map(|opcode| match &opcode {
+            OpCode::LABEL(_) => (pos, opcode),
+            _ => {
+                pos += 1;
+                (pos - 1, opcode)
+            }
+        })
+        .filter_map(|(pos, opcode)| match &opcode {
+            OpCode::JEQ(_, addr) | OpCode::JMP(addr) | OpCode::JNE(_, addr) => {
+                if label_to_addr.get(addr).map(|x| x.0) == pos.checked_add(1).map(|r| r.into()) {
+                    None
+                } else {
+                    Some(opcode)
+                }
+            }
+            _ => Some(opcode),
+        })
+        .collect::<Vec<_>>();
+    let new_total_opcodes = new_opcodes.len();
+
+    (new_opcodes, old_total_opcodes != new_total_opcodes)
+}

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

@@ -0,0 +1,36 @@
+COMMENT    = { "//" ~ (!NEWLINE ~ ANY)* ~ NEWLINE }
+WHITESPACE = _{ " " | "\t" | NEWLINE }
+
+init         = _{ SOI ~ expr ~ EOI }
+expr         = { logical_expr }
+logical_expr = { logical_term ~ (logical_op ~ logical_term)* }
+logical_term = { not_expr | comparison }
+logical_op   = { ^"AND" |  "&&" | "||" | ^"OR" }
+not_expr     = { (^"NOT" | "!") ~ logical_expr }
+comparison   = { sum ~ (comp_op ~ sum)? }
+sum          = { product ~ (add_op ~ product)* }
+product      = { factor ~ (mul_op ~ factor)* }
+factor       = { number | string | bool | variable  |  "(" ~ logical_expr ~ ")" }
+comp_op      = { "=" | "!=" | "<" | "<=" | ">" | ">=" }
+add_op       = { "+" | "-" }
+mul_op       = { "*" | "/" }
+
+// string
+string = ${ quote ~ inner_str ~ quote }
+quote = { "\"" }
+inner_str = @{ (!("\"" | "\\") ~ ANY)* ~ (escape ~ inner_str)? }
+escape = @{ "\\" ~ ("\"" | "\\" | "r" | "n" | "t" | "0" | "'" | code ) }
+code = @{ "x" ~ hex_digit{2} }
+hex_digit = @{ '0'..'9' | 'a'..'f' | 'A'..'F' }
+
+
+// variable
+variable     = ${ ident ~ (^"." ~ ident)* }
+ident        = @{  ("_" | alpha) ~ ("_" | alpha_num)* }
+alpha        = _{ 'a'..'z' | 'A'..'Z' }
+alpha_num    = _{ alpha | '0'..'9' }
+
+bool        = @ { ^"TRUE" | ^"FALSE" }
+
+// number
+number       = @{ "-"? ~ ASCII_DIGIT+ ~ ("." ~ ASCII_DIGIT+)? }

+ 168 - 0
utxo/src/filter_expr/expr.rs

@@ -0,0 +1,168 @@
+use crate::id;
+
+use super::Error;
+use std::{ops::Deref, str::FromStr};
+
+#[derive(Clone, PartialEq, Eq, Debug, Copy)]
+/// Expression operators
+///
+/// The operators are used to compare values in the expression language.
+pub enum ExprOp {
+    And,
+    Or,
+    Eq,
+    NotEq,
+    Gt,
+    Gte,
+    Not,
+    Lt,
+    Lte,
+    Add,
+    Sub,
+    Mul,
+    Div,
+}
+
+impl From<&str> for ExprOp {
+    fn from(s: &str) -> Self {
+        match s.to_lowercase().as_str() {
+            "+" => ExprOp::Add,
+            "-" => ExprOp::Sub,
+            "*" => ExprOp::Mul,
+            "!" => ExprOp::Not,
+            "/" => ExprOp::Div,
+            "&&" | "and" => ExprOp::And,
+            "||" | "or" => ExprOp::Or,
+            "==" | "=" => ExprOp::Eq,
+            "!=" => ExprOp::NotEq,
+            ">" => ExprOp::Gt,
+            ">=" => ExprOp::Gte,
+            "<" => ExprOp::Lt,
+            "<=" => ExprOp::Lte,
+            x => panic!("{} is not a valid Operation", x),
+        }
+    }
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Copy)]
+/// Order
+///
+/// The order is used to sort the results of a query.
+pub enum Order {
+    Ascending,
+    Descending,
+}
+
+impl FromStr for Order {
+    type Err = Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s.to_lowercase().as_str() {
+            "asc" => Ok(Order::Ascending),
+            "desc" => Ok(Order::Descending),
+            s => Err(Error::InvalidOrder(s.to_owned())),
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+/// Expression
+///
+/// This is the main expression type that is used to represent the AST of the expression.
+///
+/// It can be a simple variable, a string, a number, an identifier, a unary operation or a binary
+/// operation.
+pub enum Expr {
+    Op { op: ExprOp, terms: Vec<Box<Expr>> },
+    Variable(Variable),
+    String(String),
+    Bool(bool),
+    Number(i128),
+}
+
+impl Expr {
+    /// Takes the expression, traverses it and rearranges it to a more optimal form.
+    ///
+    /// The main goal is to execute the cheaper terms first leaving the most expensive to the last.
+    pub fn rearrange_expression(self) -> Self {
+        let can_be_rearranged = self.can_be_rearranged();
+        match self {
+            Expr::Op { op, terms } => {
+                let mut terms = terms.into_iter().map(|x| (x.cost(), x)).collect::<Vec<_>>();
+                if can_be_rearranged {
+                    terms.sort_by(|a, b| a.0.cmp(&b.0));
+                }
+
+                Expr::Op {
+                    op,
+                    terms: terms
+                        .into_iter()
+                        .map(|x| x.1.rearrange_expression().into())
+                        .collect::<Vec<_>>(),
+                }
+            }
+            x => x,
+        }
+    }
+
+    /// Checks if the expression can be rearranged.
+    pub fn can_be_rearranged(&self) -> bool {
+        match self {
+            Expr::Op { op, terms } => {
+                matches!(
+                    op,
+                    ExprOp::Add | ExprOp::Or | ExprOp::And | ExprOp::Eq | ExprOp::NotEq
+                )
+            }
+            _ => false,
+        }
+    }
+
+    /// Cost associated with the expression.
+    pub fn cost(&self) -> usize {
+        match self {
+            Expr::Op { op, terms } => {
+                let mut cost = 0;
+                for term in terms {
+                    cost += term.cost();
+                }
+                cost
+            }
+            Expr::Variable(_) => 1_000,
+            Expr::String(_) => 1,
+            Expr::Bool(_) => 1,
+            Expr::Number(_) => 1,
+        }
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Hash, PartialOrd, Eq)]
+/// Variable
+///
+/// A variable is a list of strings that represent a path to a value in the data.
+pub struct Variable(Vec<String>);
+
+impl Deref for Variable {
+    type Target = [String];
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl From<Vec<String>> for Variable {
+    fn from(v: Vec<String>) -> Self {
+        Variable(v)
+    }
+}
+
+impl From<&str> for Variable {
+    fn from(s: &str) -> Self {
+        Variable(vec![s.to_owned()])
+    }
+}
+
+impl Variable {
+    pub fn add_part(&mut self, part: String) {
+        self.0.push(part);
+    }
+}

+ 127 - 0
utxo/src/filter_expr/filter.rs

@@ -0,0 +1,127 @@
+use super::{
+    compiler::Compiler,
+    expr::{Expr, ExprOp, Variable},
+    opcode::OpCode,
+    parser::parse,
+    runtime::execute,
+    value::{Value, ValueOrRef},
+    Addr, Error, Register,
+};
+use crate::Transaction;
+use std::{collections::HashMap, rc::Rc};
+
+#[derive(Debug, Clone)]
+pub struct Filter {
+    /// Query
+    expr: Option<Expr>,
+    /// List of external variables
+    variables: Vec<Variable>,
+    /// Unoptimized opcodes
+    dbg_opcodes: Vec<OpCode>,
+    /// Opcodes with human readable labels
+    opcodes: Vec<OpCode>,
+    /// The list of opcodes that make up the program, the labels has been converted into addresses
+    opcodes_to_execute: Vec<OpCode>,
+    /// The address of the initial opcode
+    initial_opcode_addr: Addr,
+    /// The state of the register
+    pub(crate) initial_register: HashMap<Register, ValueOrRef>,
+}
+
+impl Filter {
+    pub fn new(code: &str) -> Result<Self, Error> {
+        let expr = parse(code)?;
+        let opcodes = expr.as_ref().map_or_else(
+            || {
+                Ok(vec![
+                    OpCode::LOAD(0.into(), Rc::new(true.into())),
+                    OpCode::HLT(0.into()),
+                ])
+            },
+            |expr| Compiler::new(expr).compile(),
+        )?;
+
+        let instance = Self {
+            expr,
+            dbg_opcodes: opcodes.clone(),
+            opcodes: opcodes.clone(),
+            initial_opcode_addr: 0.into(),
+            variables: opcodes
+                .iter()
+                .filter_map(|x| match x {
+                    OpCode::LOAD_EXTERNAL(_, name) => Some(name.clone()),
+                    _ => None,
+                })
+                .collect(),
+            opcodes_to_execute: Compiler::resolve_label_to_addr(opcodes)?,
+            initial_register: HashMap::new(),
+        };
+        Ok(instance.optimize())
+    }
+
+    /// Optimizes the program
+    pub fn optimize(mut self) -> Self {
+        let mut new_opcodes = self.opcodes;
+        loop {
+            let (new_opcodes_, has_changed) = Compiler::optimize(new_opcodes);
+            new_opcodes = new_opcodes_;
+            if !has_changed {
+                break;
+            }
+        }
+
+        self.opcodes = new_opcodes;
+        self.initial_register.clear();
+        self.opcodes_to_execute = Compiler::resolve_label_to_addr(self.opcodes.clone()).unwrap();
+
+        for (pos, opcode) in self.opcodes.iter().enumerate() {
+            // Execute all the LOAD opcodes until we find a non-LOAD opcode
+            // and store the initial state of the registers
+            match opcode {
+                OpCode::LOAD(r, v) => {
+                    self.initial_register
+                        .insert(*r, ValueOrRef::WeakRef(Rc::downgrade(v)));
+                }
+                _ => {
+                    self.initial_opcode_addr = pos.into();
+                    break;
+                }
+            }
+        }
+
+        self
+    }
+
+    /// Returns a human readable version of the compiled program (generated op-codes)
+    pub fn debug(&self) -> String {
+        self.dbg_opcodes
+            .iter()
+            .map(|x| match x {
+                OpCode::HLT(_) | OpCode::LABEL(_) => x.to_string(),
+                x => format!("\t{}", x.to_string()),
+            })
+            .collect::<Vec<_>>()
+            .join("\n")
+    }
+
+    pub fn dump(&self) -> String {
+        self.opcodes_to_execute
+            .iter()
+            .enumerate()
+            .map(|(pos, opcode)| format!("{}: {}", pos, opcode.to_string()))
+            .collect::<Vec<_>>()
+            .join("\n")
+    }
+
+    pub fn execute(
+        &self,
+        external_variables: &HashMap<Variable, ValueOrRef>,
+    ) -> Result<Value, Error> {
+        execute(
+            external_variables,
+            &self.opcodes_to_execute,
+            self.initial_opcode_addr,
+            self.initial_register.clone(),
+        )
+    }
+}

+ 100 - 0
utxo/src/filter_expr/mod.rs

@@ -0,0 +1,100 @@
+//! Expression module
+
+#![allow(warnings)]
+use parser::Rule;
+use std::num::ParseIntError;
+
+mod compiler;
+mod expr;
+mod filter;
+mod opcode;
+mod parser;
+mod runtime;
+mod value;
+
+#[cfg(test)]
+mod tests;
+
+pub use self::{filter::Filter, value::Value};
+
+#[derive(
+    Debug,
+    Clone,
+    Copy,
+    PartialEq,
+    Eq,
+    Hash,
+    derive_more::Display,
+    derive_more::Deref,
+    derive_more::DerefMut,
+    derive_more::From,
+    derive_more::Into,
+)]
+pub struct Register(usize);
+
+#[derive(
+    Debug,
+    Clone,
+    Copy,
+    PartialEq,
+    Eq,
+    Hash,
+    derive_more::Display,
+    derive_more::Deref,
+    derive_more::DerefMut,
+    derive_more::From,
+    derive_more::Into,
+)]
+pub struct Addr(usize);
+
+impl Addr {
+    pub fn next(&mut self) {
+        self.0 += 1;
+    }
+
+    pub fn jump_to(&mut self, addr: &Addr) {
+        self.0 = addr.0;
+    }
+}
+
+#[derive(thiserror::Error, Debug, PartialEq)]
+pub enum Error {
+    #[error("Variable not found: {0}")]
+    VariableNotFound(String),
+
+    #[error("Program is out of bound")]
+    OutOfBoundaries,
+
+    #[error("Register {0} is out of boundaries")]
+    RegisterOutOfBoundaries(Register),
+
+    #[error("Empty registers: {0}")]
+    EmptyRegister(Register),
+
+    #[error("Value is not a number")]
+    NotANumber,
+
+    #[error("Math overflow")]
+    Overflow,
+
+    #[error("Unexpected rule: {0:?}")]
+    UnexpectedRule(Rule),
+
+    #[error("Parsing error: {0:?}")]
+    ParsingError(#[from] pest::error::Error<Rule>),
+
+    #[error("Internal parsing error: Missing next rule")]
+    MissingNextRule,
+
+    #[error("Error parsing number: {0}")]
+    ParseIntError(#[from] ParseIntError),
+
+    #[error("Invalid order: {0}")]
+    InvalidOrder(String),
+
+    #[error("Unknown label: {0}")]
+    UnknownLabel(usize),
+
+    #[error("Unexpected expression state")]
+    UnexpectedExprState,
+}

+ 168 - 0
utxo/src/filter_expr/opcode.rs

@@ -0,0 +1,168 @@
+use super::{expr::Variable, value::Value, Addr, Register};
+use std::rc::Rc;
+
+#[derive(Clone, Debug, PartialEq)]
+/// OpCode for a register based virtual machine
+///
+/// The OpCode is a simple enum that represents the operations that can be performed on the register
+/// based Virtual Machine.
+pub enum OpCode {
+    // Data Movement Instructions
+    /// LOAD <destination> <value>
+    /// Load a value into a register.
+    LOAD(Register, Rc<Value>),
+
+    /// Request an external source for a given information
+    /// LOAD <destination> <value>
+    /// Load the <value> variable name from an external source which is unknown at compile time.
+    /// The value is stored in the given register
+    LOAD_EXTERNAL(Register, Variable),
+
+    /// CPY <destination> <source>
+    /// Copy the value from the source register to the destination register.
+    CPY(Register, Register),
+
+    /// MOV <destination> <source>
+    /// Move the value from the source register to the destination register.
+    MOV(Register, Register),
+
+    // Arithmetic Operations
+    /// ADD <destination> <source1> <source2>
+    /// Add the values in source1 and source2, store the result in destination.
+    ADD(Register, Register, Register),
+
+    /// SUB <destination> <source1> <source2>
+    /// Subtract the value in source2 from the value in source1, store the result in destination.
+    SUB(Register, Register, Register),
+
+    /// MUL <destination> <source1> <source2>
+    /// Multiply the values in source1 and source2, store the result in destination.
+    MUL(Register, Register, Register),
+
+    /// DIV <destination> <source1> <source2>
+    /// Divide the value in source1 by the value in source2, store the result in destination.
+    DIV(Register, Register, Register),
+
+    /// MOD <destination> <source1> <source2>
+    /// Calculate the remainder of source1 divided by source2, store the result in destination.
+    MOD(Register, Register, Register),
+
+    /// NEG <destination> <source>
+    /// Negate the value in the source register, store the result in destination.
+    NEG(Register, Register),
+
+    // Logical Operations
+    /// AND <destination> <source1> <source2>
+    /// Perform a bitwise AND on the values in source1 and source2, store the result in
+    /// destination.
+    AND(Register, Register, Register),
+
+    /// OR <destination> <source1> <source2>
+    /// Perform a bitwise OR on the values in source1 and source2, store the result in destination.
+    OR(Register, Register, Register),
+
+    /// XOR <destination> <source1> <source2>
+    /// Perform a bitwise XOR on the values in source1 and source2, store the result in
+    /// destination.
+    XOR(Register, Register, Register),
+
+    /// NOT <destination> <source>
+    /// Perform a bitwise NOT on the value in the source register, store the result in destination.
+    NOT(Register, Register),
+
+    /// SHL <destination> <source> <value>
+    /// Shift the value in the source register left by the specified number of bits, store the
+    /// result in destination.
+    SHL(Register, Register, Value),
+
+    /// SHR <destination> <source> <value>
+    /// Shift the value in the source register right by the specified number of bits, store the
+    /// result in destination.
+    SHR(Register, Register, Value),
+
+    /// EQ <destination> <source1> <source2>
+    /// Check if source1 is equal to source2, store the result in destination (1 if true, 0 if
+    /// false).
+    EQ(Register, Register, Register),
+
+    /// NE <destination> <source1> <source2>
+    /// Check if source1 is not equal to source2, store the result in destination (1 if true, 0 if
+    /// false).
+    NE(Register, Register, Register),
+
+    /// LT <destination> <source1> <source2>
+    /// Check if source1 is less than source2, store the result in destination (1 if true, 0 if
+    /// false).
+    LT(Register, Register, Register),
+
+    /// LE <destination> <source1> <source2>
+    /// Check if source1 is less than or equal to source2, store the result in destination (1 if
+    /// true, 0 if false).
+    LE(Register, Register, Register),
+
+    /// GT <destination> <source1> <source2>
+    /// Check if source1 is greater than source2, store the result in destination (1 if true, 0 if
+    /// false).
+    GT(Register, Register, Register),
+
+    /// GE <destination> <source1> <source2>
+    /// Check if source1 is greater than or equal to source2, store the result in destination (1 if
+    /// true, 0 if false).
+    GE(Register, Register, Register),
+
+    // Branching Operations
+    /// LABEL <address>
+    /// Adds a label to the this part of the code
+    LABEL(Addr),
+
+    /// JMP <address>
+    /// Jump to the specified address. Addresses are custom tags through a LABEL.
+    JMP(Addr),
+
+    /// JEQ <register> <address>
+    /// Jump to the address if the value in the register is true (non-zero).
+    JEQ(Register, Addr),
+
+    /// JNE <register> <address>
+    /// Jump to the address if the value in the register is false (zero).
+    JNE(Register, Addr),
+
+    // Control Operation
+    /// HLT
+    /// Halt the execution.
+    HLT(Register),
+}
+
+impl ToString for OpCode {
+    fn to_string(&self) -> String {
+        match self {
+            OpCode::LOAD(r, v) => format!("LOAD {:?} {:?}", r, v),
+            OpCode::LOAD_EXTERNAL(r, v) => format!("LOAD_EXTERNAL {:?} {:?}", r, v),
+            OpCode::CPY(r1, r2) => format!("CPY {:?} {:?}", r1, r2),
+            OpCode::MOV(r1, r2) => format!("MOV {:?} {:?}", r1, r2),
+            OpCode::ADD(r1, r2, r3) => format!("ADD {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::SUB(r1, r2, r3) => format!("SUB {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::MUL(r1, r2, r3) => format!("MUL {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::DIV(r1, r2, r3) => format!("DIV {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::MOD(r1, r2, r3) => format!("MOD {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::NEG(r1, r2) => format!("NEG {:?} {:?}", r1, r2),
+            OpCode::AND(r1, r2, r3) => format!("AND {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::OR(r1, r2, r3) => format!("OR {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::XOR(r1, r2, r3) => format!("XOR {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::NOT(r1, r2) => format!("NOT {:?} {:?}", r1, r2),
+            OpCode::SHL(r1, r2, v) => format!("SHL {:?} {:?} {:?}", r1, r2, v),
+            OpCode::SHR(r1, r2, v) => format!("SHR {:?} {:?} {:?}", r1, r2, v),
+            OpCode::EQ(r1, r2, r3) => format!("EQ {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::NE(r1, r2, r3) => format!("NE {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::LT(r1, r2, r3) => format!("LT {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::LE(r1, r2, r3) => format!("LE {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::GT(r1, r2, r3) => format!("GT {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::GE(r1, r2, r3) => format!("GE {:?} {:?} {:?}", r1, r2, r3),
+            OpCode::LABEL(a) => format!("LABEL {:?}:", a),
+            OpCode::JMP(a) => format!("JMP {:?}", a),
+            OpCode::JEQ(r, a) => format!("JEQ {:?} {:?}", r, a),
+            OpCode::JNE(r, a) => format!("JNE {:?} {:?}", r, a),
+            OpCode::HLT(r) => format!("HLT {:?}", r),
+        }
+    }
+}

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

@@ -0,0 +1,158 @@
+use super::{
+    expr::{Expr, ExprOp, Order, Variable},
+    Error,
+};
+use pest::{Parser, Span};
+use pest_derive::Parser;
+use std::str::FromStr;
+
+#[derive(Parser)]
+#[grammar = "src/filter_expr/expr.pest"] // relative path to your .pest file
+struct QueryParser;
+
+/// Borrowed from https://github.com/pest-parser/pest/blob/master/meta/src/parser.rs#L687C2-L741C2
+fn unescape(string: &str) -> Option<String> {
+    let mut result = String::new();
+    let mut chars = string.chars();
+
+    loop {
+        match chars.next() {
+            Some('\\') => match chars.next()? {
+                '"' => result.push('"'),
+                '\\' => result.push('\\'),
+                'r' => result.push('\r'),
+                'n' => result.push('\n'),
+                't' => result.push('\t'),
+                '0' => result.push('\0'),
+                '\'' => result.push('\''),
+                'x' => {
+                    let string: String = chars.clone().take(2).collect();
+                    if string.len() != 2 {
+                        return None;
+                    }
+                    for _ in 0..string.len() {
+                        chars.next()?;
+                    }
+                    let value = u8::from_str_radix(&string, 16).ok()?;
+                    result.push(char::from(value));
+                }
+                'u' => {
+                    if chars.next()? != '{' {
+                        return None;
+                    }
+                    let string: String = chars.clone().take_while(|c| *c != '}').collect();
+                    if string.len() < 2 || 6 < string.len() {
+                        return None;
+                    }
+                    for _ in 0..string.len() + 1 {
+                        chars.next()?;
+                    }
+                    let value = u32::from_str_radix(&string, 16).ok()?;
+                    result.push(char::from_u32(value)?);
+                }
+                _ => return None,
+            },
+            Some(c) => result.push(c),
+            None => return Some(result),
+        };
+    }
+}
+
+fn parse_expr(pair: pest::iterators::Pair<Rule>) -> Result<Expr, Error> {
+    Ok(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().ok_or(Error::MissingNextRule)?;
+                    let second_expr = parse_expr(second)?;
+                    Expr::Op {
+                        op,
+                        terms: vec![Box::new(first_expr), Box::new(second_expr)],
+                    }
+                } else {
+                    first_expr
+                });
+            }
+
+            last_expr.ok_or(Error::MissingNextRule)?
+        }
+        Rule::string => {
+            let mut inner = pair.into_inner();
+            let _ = inner.next();
+
+            Expr::String(
+                inner
+                    .next()
+                    .map(|x| unescape(x.as_str()))
+                    .flatten()
+                    .expect("invalid escape"),
+            )
+        }
+        Rule::not_expr => {
+            let mut inner = pair.into_inner();
+            Expr::Op {
+                op: ExprOp::Not,
+                terms: vec![Box::new(parse_expr(
+                    inner.next().ok_or(Error::MissingNextRule)?,
+                )?)],
+            }
+        }
+        Rule::variable => {
+            let mut inner = pair.into_inner();
+            let mut variable: Variable = vec![inner
+                .next()
+                .ok_or(Error::MissingNextRule)?
+                .as_str()
+                .to_owned()]
+            .into();
+            while let Some(Ok(Expr::String(ident))) = inner.next().map(parse_expr) {
+                variable.add_part(ident);
+            }
+            Expr::Variable(variable)
+        }
+        Rule::number => Expr::Number(pair.as_str().parse()?),
+        Rule::ident => Expr::String(pair.as_str().to_owned()),
+        Rule::bool => Expr::Bool(pair.as_str().trim().to_lowercase() == "true"),
+        rule => return Err(Error::UnexpectedRule(rule)),
+    })
+}
+
+/// Parses a string a returns a result wrappign a Query struct
+pub fn parse(query: &str) -> Result<Option<Expr>, Error> {
+    let pairs = QueryParser::parse(Rule::init, query)?;
+
+    for pair in pairs {
+        match pair.as_rule() {
+            Rule::EOI => {
+                break;
+            }
+            Rule::expr => {
+                return Ok(Some(
+                    pair.into_inner()
+                        .next()
+                        .map(parse_expr)
+                        .ok_or(Error::MissingNextRule)??
+                        .rearrange_expression(),
+                ));
+            }
+            Rule::COMMENT => {}
+            _ => unreachable!(),
+        }
+    }
+
+    Ok(None)
+}

+ 205 - 0
utxo/src/filter_expr/runtime.rs

@@ -0,0 +1,205 @@
+use super::{
+    expr::Variable,
+    opcode::OpCode,
+    value::{Value, ValueOrRef},
+    Addr, Error, Register,
+};
+use crate::Transaction;
+use num::CheckedAdd;
+use std::{collections::HashMap, ops::Deref};
+
+macro_rules! get {
+    ($r:expr,$pos:expr) => {
+        ($r.get($pos)
+            .ok_or_else(|| Error::RegisterOutOfBoundaries($pos.to_owned()))?)
+    };
+}
+
+macro_rules! remove {
+    ($r:expr,$pos:expr) => {
+        ($r.remove($pos)
+            .ok_or_else(|| Error::RegisterOutOfBoundaries($pos.to_owned()))?)
+    };
+}
+
+macro_rules! set {
+    ($r:expr, $pos:expr, $new_value:expr) => {
+        (if let Some(previous_value) = $r.get_mut($pos) {
+            *previous_value = $new_value;
+        } else {
+            $r.insert(($pos).to_owned(), $new_value);
+        })
+    };
+}
+
+#[inline]
+/// Execute the program
+///
+/// Executes the program passing the external variables and the initial registers.
+///
+/// The external variables is a reference to a hashmap that contains the
+/// external variables, if the program attempts to load a variable that does not
+/// exists will return an error immediately.
+///
+/// The initial registers is a hashmap that contains the initial state of the
+/// registers, this is a tool available for the compiler to execute the initial
+/// LOAD, if it makes sense, and pass over the initial state of the registers to
+/// the program, avoiding executing the same opcodes over and over
+pub fn execute(
+    external_variables: &HashMap<Variable, ValueOrRef>,
+    code: &[OpCode],
+    start_at: Addr,
+    initial_registers: HashMap<Register, ValueOrRef>,
+) -> Result<Value, Error> {
+    let mut execution: Addr = start_at;
+    let mut registers = initial_registers;
+
+    loop {
+        match code.get(*execution).ok_or(Error::OutOfBoundaries)? {
+            OpCode::LABEL(_addr) => {}
+            OpCode::LOAD_EXTERNAL(dst, name) => {
+                let value = external_variables
+                    .get(name)
+                    .ok_or(Error::VariableNotFound((*name).join(".")))?;
+
+                set!(registers, dst, value.clone());
+            }
+            OpCode::LOAD(dst, ref val) => set!(registers, dst, val.into()),
+            OpCode::CPY(dst, reg2) => {
+                let value = get!(registers, reg2).clone();
+                set!(registers, dst, value);
+            }
+            OpCode::MOV(dst, reg2) => {
+                let previous_value = remove!(registers, reg2);
+                set!(registers, dst, previous_value);
+            }
+            OpCode::ADD(dst, a, b) => {
+                let new_value = get!(registers, a)
+                    .checked_add(get!(registers, b))
+                    .ok_or(Error::Overflow)?
+                    .into();
+                set!(registers, dst, new_value);
+            }
+            OpCode::SUB(dst, reg1, reg2) => {
+                let num1 = get!(registers, reg1).as_number()?;
+                let num2 = get!(registers, reg2).as_number()?;
+                set!(
+                    registers,
+                    dst,
+                    num1.checked_sub(num2)
+                        .map(|number| Value::Number(number).into())
+                        .ok_or(Error::Overflow)?
+                );
+            }
+            OpCode::AND(dst, reg2, reg3) => {
+                let new_value = Value::Bool(
+                    get!(registers, reg2).as_boolean()? && get!(registers, reg3).as_boolean()?,
+                )
+                .into();
+                set!(registers, dst, new_value);
+            }
+            OpCode::MOD(dst, reg1, reg2) => {
+                let num1 = get!(registers, reg1).as_number()?;
+                let num2 = get!(registers, reg2).as_number()?;
+                set!(
+                    registers,
+                    dst,
+                    num1.checked_rem(num2)
+                        .map(|number| Value::Number(number).into())
+                        .ok_or(Error::Overflow)?
+                );
+            }
+            OpCode::NEG(dst, reg1) => {
+                let num1 = get!(registers, reg1).as_number()?;
+                set!(
+                    registers,
+                    dst,
+                    num1.checked_neg()
+                        .map(|number| Value::Number(number).into())
+                        .ok_or(Error::Overflow)?
+                );
+            }
+            OpCode::GE(dst, reg1, reg2) => {
+                let new_value = Value::Bool(get!(registers, reg1) >= get!(registers, reg2)).into();
+                set!(registers, dst, new_value);
+            }
+            OpCode::GT(dst, reg1, reg2) => {
+                let new_value = Value::Bool(get!(registers, reg1) > get!(registers, reg2)).into();
+                set!(registers, dst, new_value);
+            }
+            OpCode::LE(dst, reg1, reg2) => {
+                let new_value = Value::Bool(get!(registers, reg1) <= get!(registers, reg2)).into();
+                set!(registers, dst, new_value);
+            }
+            OpCode::LT(dst, reg1, reg2) => {
+                let new_value = Value::Bool(get!(registers, reg1) < get!(registers, reg2)).into();
+                set!(registers, dst, new_value);
+            }
+            OpCode::EQ(dst, reg1, reg2) => {
+                let new_value = Value::Bool(get!(registers, reg1) == get!(registers, reg2)).into();
+                set!(registers, dst, new_value);
+            }
+            OpCode::NE(dst, reg1, reg2) => {
+                let new_value = Value::Bool(get!(registers, reg1) != get!(registers, reg2)).into();
+                set!(registers, dst, new_value);
+            }
+            OpCode::DIV(dst, reg1, reg2) => {
+                let num1 = get!(registers, reg1).as_number()?;
+                let num2 = get!(registers, reg2).as_number()?;
+                set!(
+                    registers,
+                    dst,
+                    num1.checked_div(num2)
+                        .map(|number| Value::Number(number).into())
+                        .ok_or(Error::Overflow)?
+                );
+            }
+            OpCode::MUL(dst, reg1, reg2) => {
+                let num1 = get!(registers, reg1).as_number()?;
+                let num2 = get!(registers, reg2).as_number()?;
+                set!(
+                    registers,
+                    dst,
+                    num1.checked_mul(num2)
+                        .map(|number| Value::Number(number).into())
+                        .ok_or(Error::Overflow)?
+                );
+            }
+            OpCode::OR(dst1, reg2, reg3) => {
+                todo!()
+            }
+            OpCode::XOR(dst, reg2, reg3) => {
+                todo!()
+            }
+            OpCode::NOT(dst, reg2) => {
+                let new_value = Value::Bool(!get!(registers, reg2).as_boolean()?).into();
+                set!(registers, dst, new_value);
+            }
+            OpCode::JMP(addr) => {
+                execution.jump_to(addr);
+                continue;
+            }
+            OpCode::JEQ(reg, addr) => {
+                if get!(registers, reg).as_boolean()? {
+                    execution.jump_to(addr);
+                    continue;
+                }
+            }
+            OpCode::JNE(reg, addr) => {
+                if !get!(registers, reg).as_boolean()? {
+                    execution.jump_to(addr);
+                    continue;
+                }
+            }
+            OpCode::HLT(return_register) => {
+                return registers
+                    .remove(return_register)
+                    .map(|x| (*x).clone())
+                    .ok_or_else(|| Error::EmptyRegister(return_register.to_owned()));
+            }
+            x => panic!("Unimplemented opcode: {:?}", x),
+        }
+
+        execution.next();
+    }
+}

+ 10 - 0
utxo/src/filter_expr/tests/always_true.expr

@@ -0,0 +1,10 @@
+/// Test that the optimizer can rearrange the expression to be less expensive, which is always true
+/// because the cheaper expression is always true (1 = 1) which is evaluated at compile time, since
+/// the expression is an OR, the whole expression is true and it makes no further sense to continue
+/// evaluating
+(
+    foo = 3 + 2 * 4 / 2 * 298210 + bar
+    AND 25 = 5*five
+)
+OR false
+OR  1 = 1

+ 98 - 0
utxo/src/filter_expr/tests/mod.rs

@@ -0,0 +1,98 @@
+use super::{expr::Variable, Error, Filter, Value};
+use crate::filter_expr::value::ValueOrRef;
+use std::collections::HashMap;
+
+fn external_variables<K: Into<Variable>, V: Into<Value>>(
+    external_variables: Vec<(K, V)>,
+) -> HashMap<Variable, ValueOrRef> {
+    external_variables
+        .into_iter()
+        .map(|(k, v)| (k.into(), ValueOrRef::Value(v.into())))
+        .collect()
+}
+
+fn testsuite<K: Into<Variable>, V: Into<Value>, R: Into<Value>>(
+    code: &str,
+    variables: Vec<(K, V)>,
+    ret: Result<R, Error>,
+) -> Filter {
+    let filter = Filter::new(code).expect("valid filter");
+    let variables = external_variables(variables);
+    println!("{}\n\n{}", filter.debug(), filter.dump());
+    assert_eq!(filter.execute(&variables), ret.map(|x| x.into()));
+    filter
+}
+
+#[test]
+fn or_() {
+    testsuite(
+        include_str!("or.expr"),
+        vec![("foo", 2), ("bar", 3)],
+        Ok(false),
+    );
+    testsuite(
+        include_str!("or.expr"),
+        vec![("foo", 1), ("bar", 3)],
+        Ok(true),
+    );
+    testsuite(
+        include_str!("or.expr"),
+        vec![("foo", 2), ("bar", 2)],
+        Ok(true),
+    );
+}
+
+#[test]
+fn program_1() {
+    testsuite(
+        include_str!("program1.expr"),
+        vec![("external", 5)],
+        Ok(true),
+    );
+    testsuite(
+        include_str!("program1.expr"),
+        vec![("external", 4)],
+        Ok(false),
+    );
+}
+
+#[test]
+fn nested() {
+    let vm = testsuite(
+        include_str!("nested.expr"),
+        vec![("foo", 0), ("bar", 0), ("five", 5)],
+        Ok(false),
+    );
+
+    assert_eq!(
+        vm.execute(&external_variables(vec![
+            ("foo", 1192844),
+            ("bar", 1),
+            ("five", 4),
+        ])),
+        Ok(false.into())
+    );
+
+    assert_eq!(
+        vm.execute(&external_variables(vec![
+            ("foo", 1192844),
+            ("bar", 1),
+            ("five", 5),
+        ])),
+        Ok(true.into())
+    );
+}
+
+#[test]
+fn missing_external_variable() {
+    testsuite::<_, _, bool>(
+        include_str!("nested.expr"),
+        vec![("foo", 1192844), ("bar", 1)],
+        Err(Error::VariableNotFound("five".to_owned())),
+    );
+}
+
+#[test]
+fn rearrange_expr_from_less_expensive() {
+    testsuite::<&str, i128, _>(include_str!("always_true.expr"), vec![], Ok(true));
+}

+ 1 - 0
utxo/src/filter_expr/tests/nested.expr

@@ -0,0 +1 @@
+foo = 3 + 2 * 4 / 2 * 298210 + bar AND 25 * 27 / 3/ 3 /3 = 5*five

+ 1 - 0
utxo/src/filter_expr/tests/or.expr

@@ -0,0 +1 @@
+foo = 1 OR bar = 2

+ 1 - 0
utxo/src/filter_expr/tests/program1.expr

@@ -0,0 +1 @@
+5 + 1 = external + 1 AND 5 = external

+ 188 - 0
utxo/src/filter_expr/value.rs

@@ -0,0 +1,188 @@
+use crate::{
+    filter_expr::{
+        expr::{Expr, Variable},
+        Error,
+    },
+    payment::PaymentTo,
+    AccountId, Amount, Asset, PaymentFrom, RevId, Status, Tag, TxId, Type,
+};
+use chrono::{DateTime, Utc};
+use num::CheckedAdd;
+use std::{
+    ops::{Add, Deref},
+    rc::{Rc, Weak},
+};
+
+#[derive(Clone, Debug, PartialEq, PartialOrd)]
+pub enum Value {
+    /// Not a value
+    Nil,
+    /// Amount
+    Amount(Amount),
+    /// Asset
+    Asset(Asset),
+    /// Account Type
+    AccountId(AccountId),
+    /// Transaction ID
+    TxId(TxId),
+    /// Type
+    Type(Type),
+    /// Revision Id
+    RevId(RevId),
+    /// Status
+    Status(Status),
+    /// Tag
+    Tag(Tag),
+    /// Datetime
+    Date(DateTime<Utc>),
+    /// String
+    String(String),
+    /// Integer number
+    Number(i128),
+    /// PaymentFrom
+    From(Vec<PaymentFrom>),
+    /// Payment Recipient
+    To(Vec<PaymentTo>),
+    /// Tags
+    Tags(Vec<Tag>),
+    /// Bool values
+    Bool(bool),
+    /// A dynamic variable name
+    Variable(Variable),
+}
+
+impl From<&Variable> for Value {
+    fn from(value: &Variable) -> Self {
+        Self::Variable(value.clone())
+    }
+}
+
+impl From<bool> for Value {
+    fn from(x: bool) -> Self {
+        Value::Bool(x)
+    }
+}
+
+impl From<i128> for Value {
+    fn from(x: i128) -> Self {
+        Value::Number(x)
+    }
+}
+
+impl Value {
+    /// Attempts to convert the value as a boolean if possible or returns an error.
+    pub fn as_boolean(&self) -> Result<bool, Error> {
+        match self {
+            Value::Bool(b) => Ok(*b),
+            Value::Nil => Ok(false),
+            Value::Number(x) => Ok(*x > 0),
+            /// TODO: Throw an error . Add types for convertion errors
+            _ => panic!("Cannot convert to bool"),
+        }
+    }
+
+    /// Checks if the value is TRUE, any other value will return FALSE.
+    pub fn is_true(&self) -> bool {
+        match self {
+            Value::Bool(b) => *b,
+            _ => false,
+        }
+    }
+
+    /// Checks if the value is FALSE, any other value will return FALSE.
+    pub fn is_false(&self) -> bool {
+        match self {
+            Value::Bool(b) => *b == false,
+            _ => false,
+        }
+    }
+
+    pub fn as_number(&self) -> Result<i128, Error> {
+        match self {
+            Value::Number(x) => Ok(*x),
+            Value::String(x) => x.parse().map_err(|_| Error::NotANumber),
+            _ => Err(Error::NotANumber),
+        }
+    }
+}
+
+impl Add for Value {
+    type Output = Self;
+
+    fn add(self, other: Self) -> Self {
+        Value::Number(
+            self.as_number()
+                .expect("Not a number")
+                .checked_add(other.as_number().expect("Not a number"))
+                .expect("Overflow"),
+        )
+    }
+}
+
+impl CheckedAdd for Value {
+    fn checked_add(&self, other: &Self) -> Option<Self> {
+        self.as_number()
+            .ok()?
+            .checked_add(other.as_number().ok()?)
+            .map(Value::Number)
+    }
+}
+
+#[derive(Debug, Clone)]
+/// Value or reference to a value.
+///
+/// This is a sorf of attempt to avoid cloning from the source code to the
+/// registers, specially when the value is being defined in the source code,
+/// instead
+/// a Weak reference is being used to avoid cloning the value.
+pub enum ValueOrRef {
+    WeakRef(Weak<Value>),
+    Value(Value),
+}
+
+impl Deref for ValueOrRef {
+    type Target = Value;
+
+    fn deref(&self) -> &Self::Target {
+        match self {
+            ValueOrRef::WeakRef(value) => {
+                if let Some(rc_value) = value.upgrade() {
+                    unsafe { &*(Rc::into_raw(rc_value) as *const Value) }
+                } else {
+                    &Value::Nil
+                }
+            }
+            ValueOrRef::Value(v) => v,
+        }
+    }
+}
+
+impl PartialEq for ValueOrRef {
+    fn eq(&self, other: &Self) -> bool {
+        self.deref() == other.deref()
+    }
+}
+
+impl PartialOrd for ValueOrRef {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        self.partial_cmp(other)
+    }
+}
+
+impl From<Rc<Value>> for ValueOrRef {
+    fn from(value: Rc<Value>) -> Self {
+        ValueOrRef::WeakRef(Rc::downgrade(&value))
+    }
+}
+
+impl From<&Rc<Value>> for ValueOrRef {
+    fn from(value: &Rc<Value>) -> Self {
+        ValueOrRef::WeakRef(Rc::downgrade(&value))
+    }
+}
+
+impl From<Value> for ValueOrRef {
+    fn from(value: Value) -> Self {
+        ValueOrRef::Value(value)
+    }
+}

+ 1 - 0
utxo/src/lib.rs

@@ -29,6 +29,7 @@ mod broadcaster;
 mod config;
 mod error;
 mod filter;
+mod filter_expr;
 mod id;
 mod ledger;
 mod payment;

+ 2 - 0
utxo/src/payment.rs

@@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
     Debug,
     Eq,
     PartialEq,
+    PartialOrd,
     Deserialize,
     Serialize,
     borsh::BorshDeserialize,
@@ -26,6 +27,7 @@ pub struct PaymentTo {
     Eq,
     PartialEq,
     Deserialize,
+    PartialOrd,
     Serialize,
     borsh::BorshDeserialize,
     borsh::BorshSerialize,