diff --git a/Cargo.toml b/Cargo.toml index b096adf3e06..e3bf5d6f271 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -154,6 +154,7 @@ futures = { version = "0.3.31", default-features = fa futures-channel = { version = "0.3.31", default-features = false } futures-core = { version = "0.3.31", default-features = false } futures-io = { version = "0.3.31", default-features = false } +futures-lite = { version = "2.6.1" } futures-sink = { version = "0.3.31", default-features = false } futures-util = { version = "0.3.31", default-features = false } glob = { version = "0.3.3" } @@ -249,6 +250,8 @@ smallvec = { version = "2.0.0-alpha.11", default-featu smol_str = { version = "0.3.4" } sort-package-json = { version = "0.0.12" } specta = { version = "2.0.0-rc.22", default-features = false } +sqruff-lib = { version = "0.37.3" } +sqruff-lib-core = { version = "0.37.3" } stacker = { version = "0.1.22", default-features = false } supports-color = { version = "3.0.2", default-features = false } supports-unicode = { version = "3.0.0", default-features = false } diff --git a/libs/@local/graph/api/Cargo.toml b/libs/@local/graph/api/Cargo.toml index 8099f3985e1..71f179cdc65 100644 --- a/libs/@local/graph/api/Cargo.toml +++ b/libs/@local/graph/api/Cargo.toml @@ -44,7 +44,7 @@ hash-graph-type-defs = { workspace = true } hash-graph-validation = { workspace = true } hashql-ast = { workspace = true } hashql-diagnostics = { workspace = true, features = ["serde", "render"] } -hashql-eval = { workspace = true, features = ["graph"] } +hashql-eval = { workspace = true } hashql-hir = { workspace = true } hashql-syntax-jexpr = { workspace = true } type-system = { workspace = true, features = ["utoipa"] } diff --git a/libs/@local/hashql/ast/src/lowering/node_renumberer.rs b/libs/@local/hashql/ast/src/lowering/node_renumberer.rs index d1f3c1857d0..cda3d37fa62 100644 --- a/libs/@local/hashql/ast/src/lowering/node_renumberer.rs +++ b/libs/@local/hashql/ast/src/lowering/node_renumberer.rs @@ -26,6 +26,7 @@ impl Visitor<'_> for NodeRenumberer { } impl Default for NodeRenumberer { + #[inline] fn default() -> Self { Self::new() } diff --git a/libs/@local/hashql/ast/src/lowering/sanitizer.rs b/libs/@local/hashql/ast/src/lowering/sanitizer.rs index b394d1eca65..1be747aba94 100644 --- a/libs/@local/hashql/ast/src/lowering/sanitizer.rs +++ b/libs/@local/hashql/ast/src/lowering/sanitizer.rs @@ -204,6 +204,7 @@ impl<'heap> Visitor<'heap> for Sanitizer { } impl Default for Sanitizer { + #[inline] fn default() -> Self { Self::new() } diff --git a/libs/@local/hashql/compiletest/Cargo.toml b/libs/@local/hashql/compiletest/Cargo.toml index 65b99360e5e..1d4784fd3cc 100644 --- a/libs/@local/hashql/compiletest/Cargo.toml +++ b/libs/@local/hashql/compiletest/Cargo.toml @@ -11,7 +11,7 @@ version.workspace = true hashql-ast = { workspace = true, public = true } hashql-core = { workspace = true, public = true } hashql-diagnostics = { workspace = true, features = ["render"], public = true } -hashql-eval = { workspace = true, features = ["graph"], public = true } +hashql-eval = { workspace = true, public = true } hashql-hir = { workspace = true, public = true } hashql-mir = { workspace = true, public = true } hashql-syntax-jexpr = { workspace = true, public = true } diff --git a/libs/@local/hashql/compiletest/src/suite/eval_graph_read_entity.rs b/libs/@local/hashql/compiletest/src/suite/eval_graph_read_entity.rs deleted file mode 100644 index 98c6db52680..00000000000 --- a/libs/@local/hashql/compiletest/src/suite/eval_graph_read_entity.rs +++ /dev/null @@ -1,127 +0,0 @@ -use core::fmt::Write as _; - -use hashql_ast::node::expr::Expr; -use hashql_core::{ - collections::FastHashMap, - module::ModuleRegistry, - pretty::{Formatter, RenderOptions}, - r#type::{TypeFormatterOptions, environment::Environment}, - value::{self, List, Opaque, Primitive, Struct, Value}, -}; -use hashql_eval::graph::read::{FilterSlice, GraphReadCompiler}; -use hashql_hir::{ - context::HirContext, - intern::Interner, - node::NodeData, - pretty::{NodeFormatter, NodeFormatterOptions}, - visit::Visitor as _, -}; - -use super::{RunContext, Suite, SuiteDiagnostic}; -use crate::suite::common::{Header, process_status}; - -pub(crate) struct EvalGraphReadEntitySuite; - -impl Suite for EvalGraphReadEntitySuite { - fn name(&self) -> &'static str { - "eval/graph/read/entity" - } - - fn description(&self) -> &'static str { - "Entity read operations in graph evaluation" - } - - fn run<'heap>( - &self, - RunContext { - heap, diagnostics, .. - }: RunContext<'_, 'heap>, - mut expr: Expr<'heap>, - ) -> Result { - let mut environment = Environment::new(heap); - let registry = ModuleRegistry::new(&environment); - let interner = Interner::new(heap); - let mut context = HirContext::new(&interner, ®istry); - - let mut output = String::new(); - - let result = hashql_ast::lowering::lower( - heap.intern_symbol("::main"), - &mut expr, - &environment, - ®istry, - ); - let types = process_status(diagnostics, result)?; - - let node = process_status(diagnostics, NodeData::from_ast(expr, &mut context, &types))?; - let node = process_status( - diagnostics, - hashql_hir::lower::lower(node, &types, &mut environment, &mut context), - )?; - - let formatter = Formatter::new(heap); - let mut formatter = NodeFormatter::new( - &formatter, - &environment, - &context, - NodeFormatterOptions { - r#type: TypeFormatterOptions::terse(), - }, - ); - - let _ = writeln!( - output, - "{}\n\n{}", - Header::new("HIR"), - formatter.render(node, RenderOptions::default().with_plain()), - ); - - let user_id_value = Value::Opaque(Opaque::new( - heap.intern_symbol("::graph::types::knowledge::entity::EntityUuid"), - Value::Opaque(Opaque::new( - heap.intern_symbol("::core::uuid::Uuid"), - Value::Primitive(Primitive::String(value::String::new( - heap.intern_symbol("e2851dbb-7376-4959-9bca-f72cafc4448f"), - ))), - )), - )); - - let mut inputs = FastHashMap::default(); - inputs.insert( - heap.intern_symbol("example_integer"), - Value::Primitive(Primitive::Integer(value::Integer::new_unchecked( - heap.intern_symbol("42"), - ))), - ); - inputs.insert(heap.intern_symbol("user_id"), user_id_value.clone()); - inputs.insert( - heap.intern_symbol("user"), - Value::Struct(Struct::from_fields( - heap, - [(heap.intern_symbol("id"), user_id_value.clone())], - )), - ); - inputs.insert( - heap.intern_symbol("user_ids"), - Value::List(List::from_values([user_id_value])), - ); - - let mut compiler = GraphReadCompiler::new(heap, &inputs); - compiler.visit_node(node); - let residual = process_status(diagnostics, compiler.finish())?; - - let FilterSlice::Entity { range } = residual.output[&node.id].clone(); - - let filters = residual.filters.entity(range); - - #[expect(clippy::use_debug)] - let _ = writeln!( - output, - "\n{}\n\n{:#?}", - Header::new("Entity Filter"), - filters - ); - - Ok(output) - } -} diff --git a/libs/@local/hashql/compiletest/src/suite/mod.rs b/libs/@local/hashql/compiletest/src/suite/mod.rs index ba00443e0f7..69edab286f1 100644 --- a/libs/@local/hashql/compiletest/src/suite/mod.rs +++ b/libs/@local/hashql/compiletest/src/suite/mod.rs @@ -9,7 +9,6 @@ mod ast_lowering_special_form_expander; mod ast_lowering_type_definition_extractor; mod ast_lowering_type_extractor; pub(crate) mod common; -mod eval_graph_read_entity; mod eval_postgres; mod hir_lower_alias_replacement; mod hir_lower_checking; @@ -50,8 +49,7 @@ use self::{ ast_lowering_sanitizer::AstLoweringSanitizerSuite, ast_lowering_special_form_expander::AstLoweringSpecialFormExpanderSuite, ast_lowering_type_definition_extractor::AstLoweringTypeDefinitionExtractorSuite, - ast_lowering_type_extractor::AstLoweringTypeExtractorSuite, - eval_graph_read_entity::EvalGraphReadEntitySuite, eval_postgres::EvalPostgres, + ast_lowering_type_extractor::AstLoweringTypeExtractorSuite, eval_postgres::EvalPostgres, hir_lower_alias_replacement::HirLowerAliasReplacementSuite, hir_lower_checking::HirLowerTypeCheckingSuite, hir_lower_ctor::HirLowerCtorSuite, hir_lower_graph_hoisting::HirLowerGraphHoistingSuite, @@ -152,7 +150,6 @@ const SUITES: &[&dyn Suite] = &[ &AstLoweringSpecialFormExpanderSuite, &AstLoweringTypeDefinitionExtractorSuite, &AstLoweringTypeExtractorSuite, - &EvalGraphReadEntitySuite, &EvalPostgres, &HirLowerAliasReplacementSuite, &HirLowerCtorSuite, diff --git a/libs/@local/hashql/core/src/value/dict.rs b/libs/@local/hashql/core/src/value/dict.rs deleted file mode 100644 index fcd6368c89f..00000000000 --- a/libs/@local/hashql/core/src/value/dict.rs +++ /dev/null @@ -1,327 +0,0 @@ -use rpds::RedBlackTreeMap; - -use super::Value; - -/// A persistent key-value mapping. -/// -/// All operations return new [`Dict`] instances without modifying the original. -/// -/// # Examples -/// -/// ``` -/// use hashql_core::{ -/// heap::Heap, -/// value::{Integer, Primitive, String}, -/// value::{Dict, Value}, -/// }; -/// -/// let heap = Heap::new(); -/// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); -/// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); -/// -/// // Create a dict from key-value pairs -/// let person = Dict::from_entries([ -/// (string("name"), string("Alice")), -/// (string("age"), integer("30")) -/// ]); -/// -/// // Access values -/// if let Some(name) = person.get(&string("name")) { -/// println!("Name: {:?}", name); -/// } -/// -/// // Insert returns a new dict -/// let updated_person = person.insert(string("email"), string("alice@example.com")); -/// -/// assert_eq!(person.len(), 2); // Original unchanged -/// assert_eq!(updated_person.len(), 3); // New dict has additional entry -/// ``` -#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] -pub struct Dict<'heap> { - // We use a `RedBlackTreeMap` here, because otherwise the type would be unhashable, another - // possibility would be to use an immutable chunkmap. - values: RedBlackTreeMap, Value<'heap>>, -} - -impl<'heap> Dict<'heap> { - /// Creates a new [`Dict`] from key-value pairs. - /// - /// If duplicate keys are provided, the last value for each key is retained. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Integer, Primitive, String}, - /// value::{Dict, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); - /// - /// let entries = [ - /// (string("b"), integer("2")), - /// (string("a"), integer("1")), - /// (string("c"), integer("3")), - /// ]; - /// - /// let dict = Dict::from_entries(entries); - /// assert_eq!(dict.len(), 3); - /// // Dict contains all entries - /// ``` - pub fn from_entries(entries: impl IntoIterator, Value<'heap>)>) -> Self { - Self { - values: entries.into_iter().collect(), - } - } - - /// Returns the value for the given key. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Primitive, String}, - /// value::{Dict, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let key = string("username"); - /// let value = string("alice"); - /// let dict = Dict::from_entries([(key.clone(), value.clone())]); - /// - /// assert_eq!(dict.get(&key), Some(&value)); - /// assert_eq!(dict.get(&string("nonexistent")), None); - /// ``` - #[must_use] - pub fn get(&self, key: &Value<'heap>) -> Option<&Value<'heap>> { - self.values.get(key) - } - - /// Returns both the stored key and value for the given key. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Primitive, String}, - /// value::{Dict, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let dict = Dict::from_entries([ - /// (string("config"), string("enabled")) - /// ]); - /// - /// if let Some((stored_key, stored_value)) = dict.get_key_value(&string("config")) { - /// assert_eq!(stored_key, &string("config")); - /// assert_eq!(stored_value, &string("enabled")); - /// } - /// ``` - #[must_use] - pub fn get_key_value(&self, key: &Value<'heap>) -> Option<(&Value<'heap>, &Value<'heap>)> { - self.values.get_key_value(key) - } - - /// Returns a new [`Dict`] with the given key-value pair inserted. - /// - /// If the key already exists, its value is replaced. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Integer, Primitive, String}, - /// value::{Dict, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); - /// - /// let original = Dict::from_entries([ - /// (string("a"), integer("1")) - /// ]); - /// - /// let updated = original.insert(string("b"), integer("2")); - /// - /// assert_eq!(original.len(), 1); // Original unchanged - /// assert_eq!(updated.len(), 2); // New dict has both entries - /// assert!(updated.contains_key(&string("b"))); - /// ``` - #[must_use] - pub fn insert(&self, key: Value<'heap>, value: Value<'heap>) -> Self { - Self { - values: self.values.insert(key, value), - } - } - - /// Returns a new [`Dict`] with the given key removed. - /// - /// If the key doesn't exist, returns an equivalent dict. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Integer, Primitive, String}, - /// value::{Dict, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); - /// - /// let original = Dict::from_entries([ - /// (string("a"), integer("1")), - /// (string("b"), integer("2")), - /// ]); - /// - /// let updated = original.remove(&string("a")); - /// - /// assert_eq!(original.len(), 2); // Original unchanged - /// assert_eq!(updated.len(), 1); // New dict has one less entry - /// assert!(!updated.contains_key(&string("a"))); - /// ``` - #[must_use] - pub fn remove(&self, key: &Value<'heap>) -> Self { - let values = self.values.remove(key); - - Self { values } - } - - /// Returns `true` if the dict contains the given key. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Primitive, String}, - /// value::{Dict, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let dict = Dict::from_entries([(string("key1"), string("value1"))]); - /// - /// assert!(dict.contains_key(&string("key1"))); - /// assert!(!dict.contains_key(&string("key2"))); - /// ``` - #[must_use] - pub fn contains_key(&self, key: &Value<'heap>) -> bool { - self.values.contains_key(key) - } - - /// Returns the number of key-value pairs in the dict. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Integer, Primitive, String}, - /// value::{Dict, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); - /// - /// let empty_dict = Dict::from_entries([]); - /// assert_eq!(empty_dict.len(), 0); - /// - /// let dict = Dict::from_entries([ - /// (string("a"), integer("1")), - /// (string("b"), integer("2")), - /// ]); - /// assert_eq!(dict.len(), 2); - /// ``` - #[must_use] - pub fn len(&self) -> usize { - self.values.size() - } - - /// Returns `true` if the dict contains no key-value pairs. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Primitive, String}, - /// value::{Dict, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let empty_dict = Dict::from_entries([]); - /// assert!(empty_dict.is_empty()); - /// - /// let dict = Dict::from_entries([ - /// (string("key"), string("value")), - /// ]); - /// assert!(!dict.is_empty()); - /// ``` - #[must_use] - pub fn is_empty(&self) -> bool { - self.values.is_empty() - } - - /// Returns an iterator over the key-value pairs. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Integer, Primitive, String}, - /// value::{Dict, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); - /// - /// let dict = Dict::from_entries([ - /// (string("name"), string("Alice")), - /// (string("age"), integer("30")), - /// (string("city"), string("Boston")), - /// ]); - /// - /// // Iterate over all key-value pairs - /// let pairs: Vec<_> = dict.iter().collect(); - /// assert_eq!(pairs.len(), 3); - /// - /// // Check that we can find specific entries - /// assert!(pairs.iter().any(|(k, v)| **k == string("name") && **v == string("Alice"))); - /// - /// // Use with for loop - /// for (key, value) in dict.iter() { - /// println!("{:?}: {:?}", key, value); - /// } - /// ``` - pub fn iter(&self) -> impl Iterator, &Value<'heap>)> { - self.values.iter() - } -} - -impl<'heap> FromIterator<(Value<'heap>, Value<'heap>)> for Dict<'heap> { - fn from_iter, Value<'heap>)>>(iter: T) -> Self { - Self { - values: iter.into_iter().collect(), - } - } -} diff --git a/libs/@local/hashql/core/src/value/list.rs b/libs/@local/hashql/core/src/value/list.rs deleted file mode 100644 index ccaa1f74bfa..00000000000 --- a/libs/@local/hashql/core/src/value/list.rs +++ /dev/null @@ -1,279 +0,0 @@ -use super::Value; - -/// A persistent sequence of values. -/// -/// All operations return new [`List`] instances without modifying the original. -/// -/// # Examples -/// -/// ``` -/// use hashql_core::{ -/// heap::Heap, -/// value::{List, Primitive, String, Value}, -/// }; -/// -/// let heap = Heap::new(); -/// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); -/// -/// // Create a list of user IDs -/// let user_ids = List::from_values([ -/// string("user_123"), -/// string("user_456"), -/// string("user_789"), -/// ]); -/// -/// // Add a new user ID (returns new list) -/// let extended_ids = user_ids.push(string("user_999")); -/// -/// assert_eq!(user_ids.len(), 3); // Original unchanged -/// assert_eq!(extended_ids.len(), 4); // New list has additional element -/// -/// // Access elements by index -/// if let Some(first_id) = extended_ids.get(0) { -/// println!("First user: {:?}", first_id); -/// } -/// ``` -#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] -pub struct List<'heap> { - values: rpds::Vector>, -} - -impl<'heap> List<'heap> { - /// Creates a new [`List`] from values. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{List, Primitive, String, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let values = vec![ - /// string("first"), - /// string("second"), - /// string("third"), - /// ]; - /// - /// let list = List::from_values(values); - /// assert_eq!(list.len(), 3); - /// assert_eq!(list.get(0), Some(&string("first"))); - /// ``` - pub fn from_values(values: impl IntoIterator>) -> Self { - Self { - values: values.into_iter().collect(), - } - } - - /// Returns a new [`List`] with the given value appended. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{List, Primitive, String, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let original = List::from_values([string("a"), string("b")]); - /// let extended = original.push(string("c")); - /// - /// assert_eq!(original.len(), 2); // Original unchanged - /// assert_eq!(extended.len(), 3); // New list has additional element - /// assert_eq!(extended.get(2), Some(&string("c"))); - /// ``` - #[must_use] - pub fn push(&self, value: Value<'heap>) -> Self { - Self { - values: self.values.push_back(value), - } - } - - /// Returns a new [`List`] with the last element removed. - /// - /// # Returns - /// - /// [`None`] if the list is empty. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{List, Primitive, String, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let original = List::from_values([string("a"), string("b")]); - /// let popped = original.pop().unwrap(); - /// - /// assert_eq!(original.len(), 2); // Original unchanged - /// assert_eq!(popped.len(), 1); // New list has one less element - /// assert_eq!(popped.get(0), Some(&string("a"))); - /// - /// let empty = List::from_values([]); - /// assert_eq!(empty.pop(), None); - /// ``` - #[must_use] - pub fn pop(&self) -> Option { - let values = self.values.drop_last()?; - - Some(Self { values }) - } - - /// Returns a reference to the value at the given index. - /// - /// # Returns - /// - /// [`None`] if the index is out of bounds. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{List, Primitive, String, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let list = List::from_values([string("zero"), string("one"), string("two")]); - /// - /// assert_eq!(list.get(0), Some(&string("zero"))); - /// assert_eq!(list.get(1), Some(&string("one"))); - /// assert_eq!(list.get(10), None); // Out of bounds - /// ``` - #[must_use] - pub fn get(&self, index: usize) -> Option<&Value<'heap>> { - self.values.get(index) - } - - /// Returns a new [`List`] with the value at the given index replaced. - /// - /// # Returns - /// - /// [`None`] if the index is out of bounds. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{List, Primitive, String, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let original = List::from_values([string("old_value"), string("keep_this")]); - /// - /// let updated = original.set(0, string("new_value")).unwrap(); - /// - /// assert_eq!(original.get(0), Some(&string("old_value"))); // Original unchanged - /// assert_eq!(updated.get(0), Some(&string("new_value"))); - /// assert_eq!(updated.get(1), Some(&string("keep_this"))); // Other values preserved - /// - /// // Out of bounds returns None - /// assert_eq!(original.set(10, string("invalid")), None); - /// ``` - #[must_use] - pub fn set(&self, index: usize, value: Value<'heap>) -> Option { - let values = self.values.set(index, value)?; - - Some(Self { values }) - } - - /// Returns the number of elements in the list. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{List, Primitive, String, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let empty_list = List::from_values([]); - /// assert_eq!(empty_list.len(), 0); - /// - /// let list = List::from_values([string("a"), string("b"), string("c")]); - /// assert_eq!(list.len(), 3); - /// ``` - #[must_use] - pub fn len(&self) -> usize { - self.values.len() - } - - /// Returns `true` if the list contains no elements. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{List, Primitive, String, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let empty_list = List::from_values([]); - /// assert!(empty_list.is_empty()); - /// - /// let list = List::from_values([string("element")]); - /// assert!(!list.is_empty()); - /// ``` - #[must_use] - pub fn is_empty(&self) -> bool { - self.values.is_empty() - } - - /// Returns an iterator over the values. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{List, Primitive, String, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let list = List::from_values([string("first"), string("second"), string("third")]); - /// - /// // Iterate over all values - /// let values: Vec<_> = list.iter().collect(); - /// assert_eq!(values.len(), 3); - /// assert_eq!(values[0], &string("first")); - /// - /// // Use with for loop - /// for (index, value) in list.iter().enumerate() { - /// println!("Element {}: {:?}", index, value); - /// } - /// ``` - pub fn iter(&self) -> impl Iterator> { - self.values.iter() - } -} - -impl<'heap> FromIterator> for List<'heap> { - fn from_iter>>(iter: T) -> Self { - Self { - values: iter.into_iter().collect(), - } - } -} diff --git a/libs/@local/hashql/core/src/value/mod.rs b/libs/@local/hashql/core/src/value/mod.rs index f8b0cc393bf..6a38edf3a9e 100644 --- a/libs/@local/hashql/core/src/value/mod.rs +++ b/libs/@local/hashql/core/src/value/mod.rs @@ -1,293 +1,3 @@ -mod dict; -mod list; - -mod opaque; mod primitive; -mod r#struct; -mod tuple; - -pub use self::{ - dict::Dict, - list::List, - opaque::Opaque, - primitive::{Float, Integer, Primitive, String}, - r#struct::{Struct, StructError}, - tuple::{Tuple, TupleError}, -}; -use crate::symbol::Symbol; - -/// Errors that can occur when accessing fields on values. -#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)] -pub enum FieldAccessError<'heap> { - /// A struct field access error. - Struct(StructError<'heap>), - /// A tuple field access error. - Tuple(TupleError<'heap>), - /// The value type does not support field access. - #[display("Cannot access field `{_1}` on `{_0}`")] - UnableToAccess(&'static str, Symbol<'heap>), -} - -impl core::error::Error for FieldAccessError<'_> {} - -/// Errors that can occur when accessing values by index. -#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)] -pub enum IndexAccessError { - /// The provided index type is not valid for list access. - #[display("Unable to access list with index type `{_0}`")] - InvalidListIndexType(&'static str), - /// The requested key was not found in the collection. - #[display("Key not found")] - KeyNotFound, - /// The value type does not support index access. - #[display("Unable to index `{_0}`")] - UnableToAccess(&'static str), -} - -impl core::error::Error for IndexAccessError {} - -/// A value in HashQL. -/// -/// Values are immutable and can be primitives (null, boolean, integer, float, string) or -/// collections (struct, tuple, list, dict, opaque). -/// -/// # Examples -/// -/// ``` -/// use hashql_core::{ -/// heap::Heap, -/// value::{Dict, Integer, List, Primitive, String, Struct, Tuple, Value}, -/// }; -/// -/// let heap = Heap::new(); -/// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); -/// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); -/// # let boolean = |value: bool| Value::Primitive(Primitive::Boolean(value)); -/// -/// // Primitive values -/// let number = integer("42"); -/// let text = string("hello"); -/// let flag = boolean(true); -/// -/// // Collections -/// let list = Value::List(List::from_values([number.clone(), text.clone()])); -/// let tuple = Value::Tuple(Tuple::from_values([flag.clone(), number.clone()])); -/// -/// // Structured data -/// let person = Value::Struct(Struct::from_fields( -/// &heap, -/// [ -/// (heap.intern_symbol("name"), text.clone()), -/// (heap.intern_symbol("age"), number.clone()), -/// ], -/// )); -/// -/// let config = Value::Dict(Dict::from_entries([ -/// (string("debug"), flag), -/// (string("port"), number), -/// ])); -/// ``` -#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, derive_more::From)] -pub enum Value<'heap> { - /// A primitive literal value (null, boolean, integer, float, or string). - Primitive(Primitive<'heap>), - /// A structured value with named fields. - Struct(Struct<'heap>), - /// A fixed-size sequence of values accessed by position. - Tuple(Tuple<'heap>), - /// A variable-size sequence of values. - List(List<'heap>), - /// A key-value mapping. - Dict(Dict<'heap>), - /// An opaque nominal value with a name and a value. - Opaque(Opaque<'heap>), -} - -impl<'heap> Value<'heap> { - /// Returns the type name of this value. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Integer, Primitive, String, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); - /// - /// let number = integer("42"); - /// assert_eq!(number.type_name(), "integer"); - /// - /// let text = string("hello"); - /// assert_eq!(text.type_name(), "string"); - /// - /// let flag = Value::Primitive(Primitive::Boolean(true)); - /// assert_eq!(flag.type_name(), "boolean"); - /// ``` - #[must_use] - pub const fn type_name(&self) -> &'static str { - match self { - Self::Primitive(Primitive::Null) => "null", - Self::Primitive(Primitive::Boolean(_)) => "boolean", - Self::Primitive(Primitive::Integer(_)) => "integer", - Self::Primitive(Primitive::Float(_)) => "float", - Self::Primitive(Primitive::String(_)) => "string", - Self::Struct(_) => "struct", - Self::Tuple(_) => "tuple", - Self::List(_) => "list", - Self::Dict(_) => "dict", - Self::Opaque(_) => "opaque", - } - } - - /// Accesses a field by symbol name. - /// - /// For structs, accesses named fields. For tuples, parses the symbol as an integer index. - /// - /// # Errors - /// - /// Returns an error if the field doesn't exist or the value type doesn't support field access. - /// - /// # Examples - /// - /// ``` - /// # #![feature(assert_matches)] - /// # use core::assert_matches; - /// use hashql_core::{ - /// heap::Heap, - /// value::{FieldAccessError, Integer, Primitive, String, Struct, Tuple, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); - /// - /// // Struct field access - /// let person = Value::Struct(Struct::from_fields( - /// &heap, - /// [(heap.intern_symbol("name"), string("Alice"))], - /// )); - /// let name_field = heap.intern_symbol("name"); - /// assert_eq!( - /// person.access_by_field(name_field).unwrap(), - /// &string("Alice") - /// ); - /// - /// // Tuple field access (using string index) - /// let point = Value::Tuple(Tuple::from_values([integer("1"), integer("2")])); - /// let index_0 = heap.intern_symbol("0"); - /// assert_eq!(point.access_by_field(index_0).unwrap(), &integer("1")); - /// - /// // Error case - field access on primitive - /// let number = integer("42"); - /// let field = heap.intern_symbol("invalid"); - /// assert_matches!( - /// number.access_by_field(field), - /// Err(FieldAccessError::UnableToAccess("integer", _)) - /// ); - /// ``` - pub fn access_by_field(&self, field: Symbol<'heap>) -> Result<&Self, FieldAccessError<'heap>> { - match self { - Self::Struct(r#struct) => r#struct.get(field).map_err(FieldAccessError::Struct), - Self::Tuple(tuple) => tuple.get(field).map_err(FieldAccessError::Tuple), - Self::Opaque(opaque) => opaque.value().access_by_field(field), - Self::Primitive(_) | Self::List(_) | Self::Dict(_) => { - Err(FieldAccessError::UnableToAccess(self.type_name(), field)) - } - } - } - - /// Accesses an element by index or key. - /// - /// For lists, the index must be an integer. For dicts, any value can be used as a key. - /// - /// # Errors - /// - /// Returns an error if the index type is invalid or the value type doesn't support indexing. - /// - /// # Examples - /// - /// ``` - /// # #![feature(assert_matches)] - /// # use core::assert_matches; - /// use hashql_core::{ - /// heap::Heap, - /// value::{Dict, IndexAccessError, Integer, List, Primitive, String, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); - /// - /// // List index access - /// let list = Value::List(List::from_values([ - /// string("first"), - /// string("second"), - /// ])); - /// let index = integer("0"); - /// assert_eq!( - /// list.access_by_index(&index).unwrap(), - /// Some(&string("first")) - /// ); - /// - /// // Dict key access - /// let dict = Value::Dict(Dict::from_entries([ - /// (string("key"), string("value")) - /// ])); - /// let key = string("key"); - /// assert_eq!( - /// dict.access_by_index(&key).unwrap(), - /// Some(&string("value")) - /// ); - /// - /// // Error case - invalid index type for list - /// let invalid_index = string("not_a_number"); - /// assert_matches!( - /// list.access_by_index(&invalid_index), - /// Err(IndexAccessError::InvalidListIndexType(_)) - /// ); - /// - /// // Error case - index access on primitive - /// let number = integer("42"); - /// assert_matches!( - /// number.access_by_index(&index), - /// Err(IndexAccessError::UnableToAccess("integer")) - /// ); - /// ``` - pub fn access_by_index(&self, index: &Self) -> Result, IndexAccessError> { - match self { - Value::List(list) => { - let integer = match index { - &Self::Primitive(Primitive::Integer(integer)) => integer, - Self::Primitive(Primitive::Float(float)) - if let Some(integer) = float.as_integer() => - { - integer - } - Self::Primitive(_) - | Self::Struct(_) - | Self::Tuple(_) - | Self::List(_) - | Self::Dict(_) - | Self::Opaque(_) => { - return Err(IndexAccessError::InvalidListIndexType(index.type_name())); - } - }; - - let Some(index) = integer.as_usize() else { - return Ok(None); - }; - Ok(list.get(index)) - } - Value::Dict(dict) => Ok(dict.get(index)), - Value::Opaque(opaque) => opaque.value().access_by_index(index), - Value::Primitive(_) | Value::Struct(_) | Value::Tuple(_) => { - Err(IndexAccessError::UnableToAccess(self.type_name())) - } - } - } -} +pub use self::primitive::{Float, Integer, Primitive, String}; diff --git a/libs/@local/hashql/core/src/value/opaque.rs b/libs/@local/hashql/core/src/value/opaque.rs deleted file mode 100644 index 6804498635c..00000000000 --- a/libs/@local/hashql/core/src/value/opaque.rs +++ /dev/null @@ -1,102 +0,0 @@ -use alloc::rc::Rc; - -use super::Value; -use crate::symbol::Symbol; - -/// A nominal type that wraps a value with a type name. -/// -/// Opaque values create distinct types from the same underlying representation. -/// Two opaque values are equal only if both their names and wrapped values are equal. -/// -/// # Examples -/// -/// ``` -/// use hashql_core::{ -/// heap::Heap, -/// value::{Integer, Primitive, String}, -/// symbol::Symbol, -/// value::{Opaque, Value}, -/// }; -/// -/// let heap = Heap::new(); -/// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); -/// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); -/// -/// let user_id = Opaque::new(heap.intern_symbol("UserId"), string("user_12345")); -/// let score = Opaque::new(heap.intern_symbol("Score"), integer("95")); -/// -/// assert_eq!(user_id.name(), heap.intern_symbol("UserId")); -/// assert_eq!(user_id.value(), &string("user_12345")); -/// ``` -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Opaque<'heap> { - name: Symbol<'heap>, - value: Rc>, -} - -impl<'heap> Opaque<'heap> { - /// Creates a new nominal type with the given name and value. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Opaque, Primitive, String, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let email = Opaque::new(heap.intern_symbol("Email"), string("alice@example.com")); - /// # let _email = email; - /// ``` - pub fn new(name: Symbol<'heap>, value: impl Into>>) -> Self { - Self { - name, - value: value.into(), - } - } - - /// Returns the type name of this nominal type. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Opaque, Primitive, String, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let username = Opaque::new(heap.intern_symbol("Username"), string("alice")); - /// assert_eq!(username.name(), heap.intern_symbol("Username")); - /// ``` - #[must_use] - pub const fn name(&self) -> Symbol<'heap> { - self.name - } - - /// Returns the wrapped value. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Integer, Opaque, Primitive, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); - /// - /// let temperature = Opaque::new(heap.intern_symbol("Temperature"), integer("72")); - /// assert_eq!(temperature.value(), &integer("72")); - /// ``` - #[must_use] - pub fn value(&self) -> &Value<'heap> { - &self.value - } -} diff --git a/libs/@local/hashql/core/src/value/struct.rs b/libs/@local/hashql/core/src/value/struct.rs deleted file mode 100644 index e42d24d7c0f..00000000000 --- a/libs/@local/hashql/core/src/value/struct.rs +++ /dev/null @@ -1,291 +0,0 @@ -use core::ops::Index; - -use super::{Tuple, Value}; -use crate::{ - collections::SmallVec, - heap::{Heap, TransferInto as _}, - symbol::Symbol, -}; - -/// Errors that can occur when working with [`Struct`] fields. -#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::Display)] -pub enum StructError<'heap> { - /// The requested field was not found in the struct. - #[display("Field `{_0}` not found")] - FieldNotFound(Symbol<'heap>), -} - -impl core::error::Error for StructError<'_> {} - -/// A named tuple with field-based access to values. -/// -/// Fields can be accessed by name. The internal order of fields is not guaranteed. -/// -/// # Examples -/// -/// ``` -/// use hashql_core::{ -/// heap::Heap, -/// value::{Integer, Primitive, String, Struct, Value}, -/// symbol::Symbol, -/// }; -/// -/// let heap = Heap::new(); -/// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); -/// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); -/// -/// // Create a struct representing a person -/// let name_field = heap.intern_symbol("name"); -/// let age_field = heap.intern_symbol("age"); -/// let email_field = heap.intern_symbol("email"); -/// -/// let person = Struct::from_fields( -/// &heap, -/// [ -/// ( -/// name_field, -/// string("Alice"), -/// ), -/// ( -/// age_field, -/// integer("30"), -/// ), -/// ( -/// email_field, -/// string("alice@example.com"), -/// ), -/// ], -/// ); -/// -/// // Access fields by name -/// assert_eq!( -/// person.get(name_field).unwrap(), -/// &string("Alice") -/// ); -/// assert_eq!( -/// person.get(age_field).unwrap(), -/// &integer("30") -/// ); -/// -/// // Using index syntax -/// assert_eq!( -/// person[name_field], -/// string("Alice") -/// ); -/// ``` -#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] -pub struct Struct<'heap> { - /// Field names associated with the underlying tuple of values. - fields: &'heap [Symbol<'heap>], - values: Tuple<'heap>, -} - -impl<'heap> Struct<'heap> { - /// Creates a new [`Struct`] from field-value pairs. - /// - /// # Panics - /// - /// Panics if there are duplicate fields if debug assertions are enabled. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Float, Integer, Primitive, String, Struct, Value}, - /// symbol::Symbol, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); - /// # let float = |value: &'static str| Value::Primitive(Primitive::Float(Float::new_unchecked(heap.intern_symbol(value)))); - /// - /// let fields = [ - /// (heap.intern_symbol("id"), integer("42")), - /// (heap.intern_symbol("name"), string("Product")), - /// (heap.intern_symbol("price"), float("19.99")), - /// ]; - /// - /// let product = Struct::from_fields(&heap, fields); - /// assert_eq!(product.get(heap.intern_symbol("id")).unwrap(), &integer("42")); - /// ``` - pub fn from_fields( - heap: &'heap Heap, - fields: impl IntoIterator, Value<'heap>)>, - ) -> Self { - let fields = fields.into_iter(); - - let (fields, values): (SmallVec<_>, SmallVec<_>) = fields.collect(); - - // This is an assert, as previous stages in the compilation should have ensured that there - // are no duplicate fields. - if cfg!(debug_assertions) { - let mut seen = crate::collections::fast_hash_set_with_capacity(fields.len()); - for field in &fields { - assert!(seen.insert(*field), "Duplicate field: {field}"); - } - } - - let fields = fields.transfer_into(heap); - let values = Tuple::from_values(values); - - Self { fields, values } - } - - /// Returns the value for the given field. - /// - /// # Errors - /// - /// Returns [`StructError::FieldNotFound`] if the field doesn't exist. - /// - /// # Examples - /// - /// ``` - /// # #![feature(assert_matches)] - /// # use core::assert_matches; - /// use hashql_core::{ - /// heap::Heap, - /// value::{Integer, Primitive, String, Struct, StructError, Value}, - /// symbol::Symbol, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); - /// - /// let name_field = heap.intern_symbol("name"); - /// let age_field = heap.intern_symbol("age"); - /// - /// let person = Struct::from_fields( - /// &heap, - /// [ - /// (name_field, string("Bob")), - /// (age_field, integer("25")), - /// ], - /// ); - /// - /// // Successful field access - /// assert_eq!(person.get(name_field), Ok(&string("Bob"))); - /// - /// // Field not found - /// let unknown_field = heap.intern_symbol("unknown"); - /// assert_matches!( - /// person.get(unknown_field), - /// Err(StructError::FieldNotFound(field)) if field == unknown_field - /// ); - /// ``` - pub fn get(&self, field: Symbol<'heap>) -> Result<&Value<'heap>, StructError<'heap>> { - let Some(position) = self.fields.iter().position(|name| *name == field) else { - return Err(StructError::FieldNotFound(field)); - }; - - Ok(&self.values[position]) - } - - /// Returns the number of fields in the struct. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Integer, Primitive, String, Struct, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); - /// - /// let empty_struct = Struct::from_fields(&heap, []); - /// assert_eq!(empty_struct.len(), 0); - /// - /// let person = Struct::from_fields( - /// &heap, - /// [ - /// (heap.intern_symbol("name"), string("Alice")), - /// (heap.intern_symbol("age"), integer("30")), - /// (heap.intern_symbol("city"), string("Boston")), - /// ], - /// ); - /// assert_eq!(person.len(), 3); - /// ``` - #[must_use] - pub const fn len(&self) -> usize { - self.fields.len() - } - - /// Returns `true` if the struct contains no fields. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Primitive, String, Struct, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let empty_struct = Struct::from_fields(&heap, []); - /// assert!(empty_struct.is_empty()); - /// - /// let person = Struct::from_fields( - /// &heap, - /// [(heap.intern_symbol("name"), string("Alice"))], - /// ); - /// assert!(!person.is_empty()); - /// ``` - #[must_use] - pub const fn is_empty(&self) -> bool { - self.fields.is_empty() - } - - /// Returns an iterator over the field-value pairs. - /// - /// The order is unspecified but stable. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Integer, Primitive, String, Struct, Value}, - /// symbol::Symbol, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// # let integer = |value: &'static str| Value::Primitive(Primitive::Integer(Integer::new_unchecked(heap.intern_symbol(value)))); - /// - /// let person = Struct::from_fields( - /// &heap, - /// [ - /// (heap.intern_symbol("name"), string("Alice")), - /// (heap.intern_symbol("age"), integer("30")), - /// (heap.intern_symbol("city"), string("Boston")), - /// ], - /// ); - /// - /// // Iterate over all field-value pairs - /// for (field, value) in person.iter() { - /// println!("{}: {:?}", field, value); - /// } - /// - /// // Collect into a vector - /// let pairs: Vec<_> = person.iter().collect(); - /// assert_eq!(pairs.len(), 3); - /// ``` - pub fn iter(&self) -> impl Iterator, &Value<'heap>)> { - self.fields.iter().copied().zip(self.values.iter()) - } -} - -impl<'heap> Index> for Struct<'heap> { - type Output = Value<'heap>; - - fn index(&self, index: Symbol<'heap>) -> &Self::Output { - self.get(index).expect("struct field not found") - } -} diff --git a/libs/@local/hashql/core/src/value/tuple.rs b/libs/@local/hashql/core/src/value/tuple.rs deleted file mode 100644 index 2a1d9f44f64..00000000000 --- a/libs/@local/hashql/core/src/value/tuple.rs +++ /dev/null @@ -1,220 +0,0 @@ -use alloc::rc::Rc; -use core::{num::ParseIntError, ops::Index}; - -use super::Value; -use crate::symbol::Symbol; - -/// Errors that can occur when working with [`Tuple`] field access. -#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)] -pub enum TupleError<'heap> { - /// The provided symbol could not be parsed as a valid integer index. - #[display("`{_0}` is not a valid integer: {_1}")] - InvalidInteger(Symbol<'heap>, ParseIntError), - /// The provided index is out of bounds for the tuple. - #[display("`{_0}` is out of bounds, the tuple has {_1} elements")] - OutOfBounds(Symbol<'heap>, usize), -} - -impl core::error::Error for TupleError<'_> {} - -/// A fixed-size collection of values accessed by position. -/// -/// Tuples store values where each element's position has semantic meaning. -/// Elements are accessed by index and tuples cannot be modified after creation. -/// -/// # Examples -/// -/// ``` -/// use hashql_core::{ -/// heap::Heap, -/// value::{Float, Primitive, Tuple, Value}, -/// }; -/// -/// let heap = Heap::new(); -/// # let float = |value: &'static str| Value::Primitive(Primitive::Float(Float::new_unchecked(heap.intern_symbol(value)))); -/// -/// // A 3D point represented as a tuple -/// let point = Tuple::from_values([float("1.23"), float("4.56"), float("7.89")]); -/// -/// // Access elements by index -/// assert_eq!(point[0], float("1.23")); -/// assert_eq!(point[1], float("4.56")); -/// assert_eq!(point.len(), 3); -/// ``` -#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] -pub struct Tuple<'heap> { - values: Rc<[Value<'heap>]>, -} - -impl<'heap> Tuple<'heap> { - /// Creates a new [`Tuple`] from values. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Primitive, String, Tuple, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let values = [ - /// string("red"), - /// string("green"), - /// string("blue"), - /// ]; - /// - /// let color_tuple = Tuple::from_values(values); - /// assert_eq!(color_tuple.len(), 3); - /// assert_eq!(color_tuple[0], string("red")); - /// ``` - pub fn from_values(values: impl IntoIterator>) -> Self { - Self { - values: values.into_iter().collect(), - } - } - - /// Returns the value at the given index. - /// - /// The symbol must represent a valid non-negative integer that is within - /// the bounds of the tuple. - /// - /// # Errors - /// - /// Returns an error if the symbol is not a valid integer or is out of bounds. - /// - /// # Examples - /// - /// ``` - /// # #![feature(assert_matches)] - /// # use core::assert_matches; - /// use hashql_core::{ - /// heap::Heap, - /// value::{Primitive, String, Tuple, TupleError, Value}, - /// symbol::Symbol, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let tuple = Tuple::from_values([string("first"), string("second")]); - /// - /// // Valid index access - /// let index_0 = heap.intern_symbol("0"); - /// assert_eq!(tuple.get(index_0).unwrap(), &string("first")); - /// - /// // Invalid integer - /// let invalid = heap.intern_symbol("not_a_number"); - /// assert_matches!( - /// tuple.get(invalid), - /// Err(TupleError::InvalidInteger(_, _)) - /// ); - /// - /// // Out of bounds - /// let out_of_bounds = heap.intern_symbol("10"); - /// assert_matches!( - /// tuple.get(out_of_bounds), - /// Err(TupleError::OutOfBounds(_, _)) - /// ); - /// ``` - pub fn get(&self, field: Symbol<'heap>) -> Result<&Value<'heap>, TupleError<'heap>> { - let index = field - .as_str() - .parse::() - .map_err(|error| TupleError::InvalidInteger(field, error))?; - - self.values - .get(index) - .ok_or_else(|| TupleError::OutOfBounds(field, self.values.len())) - } - - /// Returns the number of elements in the tuple. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Primitive, String, Tuple, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let empty_tuple = Tuple::from_values([]); - /// assert_eq!(empty_tuple.len(), 0); - /// - /// let tuple = Tuple::from_values([string("a"), string("b"), string("c")]); - /// assert_eq!(tuple.len(), 3); - /// ``` - #[must_use] - pub fn len(&self) -> usize { - self.values.len() - } - - /// Returns `true` if the tuple contains no elements. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Primitive, String, Tuple, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let empty_tuple = Tuple::from_values([]); - /// assert!(empty_tuple.is_empty()); - /// - /// let tuple = Tuple::from_values([string("element")]); - /// assert!(!tuple.is_empty()); - /// ``` - #[must_use] - pub fn is_empty(&self) -> bool { - self.values.is_empty() - } - - /// Returns an iterator over the values. - /// - /// # Examples - /// - /// ``` - /// use hashql_core::{ - /// heap::Heap, - /// value::{Primitive, String, Tuple, Value}, - /// }; - /// - /// let heap = Heap::new(); - /// # let string = |value: &'static str| Value::Primitive(Primitive::String(String::new(heap.intern_symbol(value)))); - /// - /// let tuple = Tuple::from_values([string("first"), string("second"), string("third")]); - /// - /// for (index, value) in tuple.iter().enumerate() { - /// println!("Element {}: {:?}", index, value); - /// } - /// - /// assert_eq!( - /// tuple.iter().collect::>(), - /// [ - /// &string("first"), - /// &string("second"), - /// &string("third") - /// ] - /// ); - /// ``` - pub fn iter(&self) -> impl Iterator> { - self.values.iter() - } -} - -impl<'heap> Index for Tuple<'heap> { - type Output = Value<'heap>; - - fn index(&self, index: usize) -> &Self::Output { - &self.values[index] - } -} diff --git a/libs/@local/hashql/eval/Cargo.toml b/libs/@local/hashql/eval/Cargo.toml index 8d865a81ac9..a7fed3cb1e2 100644 --- a/libs/@local/hashql/eval/Cargo.toml +++ b/libs/@local/hashql/eval/Cargo.toml @@ -18,41 +18,38 @@ hashql-mir = { workspace = true, public = true } # Private workspace dependencies hashql-core = { workspace = true } -type-system = { workspace = true, optional = true } # Private third-party dependencies -bytes.workspace = true -derive_more = { workspace = true, features = ["display"] } -futures-lite = "2.6.1" -postgres-protocol.workspace = true -postgres-types = { workspace = true, features = ["uuid-1"] } -serde = { workspace = true } -serde_json = { workspace = true, features = ["raw_value"] } -simple-mermaid = { workspace = true } -tokio.workspace = true -tokio-postgres.workspace = true -tokio-util = { workspace = true, features = ["rt"] } -url.workspace = true -uuid.workspace = true +bytes = { workspace = true } +derive_more = { workspace = true, features = ["display"] } +futures-lite = { workspace = true } +postgres-protocol = { workspace = true } +postgres-types = { workspace = true, features = ["uuid-1"] } +serde = { workspace = true } +serde_json = { workspace = true, features = ["raw_value"] } +simple-mermaid = { workspace = true } +tokio = { workspace = true } +tokio-postgres = { workspace = true } +tokio-util = { workspace = true, features = ["rt"] } +url = { workspace = true } +uuid = { workspace = true } [dev-dependencies] -error-stack.workspace = true -hash-graph-authorization = { workspace = true } -hash-graph-store.workspace = true -hash-graph-test-data.workspace = true -hashql-compiletest = { workspace = true } -hashql-diagnostics = { workspace = true, features = ["render"] } -insta = { workspace = true } -libtest-mimic = { workspace = true } -regex = { workspace = true } -similar-asserts = { workspace = true } -sqruff-lib = "0.37.3" -sqruff-lib-core = "0.37.3" -testcontainers = { workspace = true, features = ["reusable-containers"] } -testcontainers-modules = { workspace = true, features = ["postgres"] } - -[features] -graph = ["dep:hash-graph-store", "dep:type-system"] +error-stack = { workspace = true } +hash-graph-authorization = { workspace = true } +hash-graph-store = { workspace = true } +hash-graph-test-data = { workspace = true } +hashql-compiletest = { workspace = true } +hashql-diagnostics = { workspace = true, features = ["render"] } +insta = { workspace = true } +libtest-mimic = { workspace = true } +regex = { workspace = true } +similar-asserts = { workspace = true } +sqruff-lib = { workspace = true } +sqruff-lib-core = { workspace = true } +testcontainers = { workspace = true, features = ["reusable-containers"] } +testcontainers-modules = { workspace = true, features = ["postgres"] } +type-system = { workspace = true } [lints] workspace = true diff --git a/libs/@local/hashql/eval/package.json b/libs/@local/hashql/eval/package.json index e6f969e8c66..80d1565235f 100644 --- a/libs/@local/hashql/eval/package.json +++ b/libs/@local/hashql/eval/package.json @@ -10,7 +10,6 @@ "test:unit": "mise run test:unit @rust/hashql-eval" }, "dependencies": { - "@blockprotocol/type-system-rs": "workspace:*", "@rust/hash-graph-postgres-store": "workspace:*", "@rust/hash-graph-store": "workspace:*", "@rust/hashql-core": "workspace:*", @@ -19,6 +18,7 @@ "@rust/hashql-mir": "workspace:*" }, "devDependencies": { + "@blockprotocol/type-system-rs": "workspace:*", "@rust/error-stack": "workspace:*", "@rust/hash-graph-authorization": "workspace:*", "@rust/hash-graph-test-data": "workspace:*" diff --git a/libs/@local/hashql/eval/src/graph/error.rs b/libs/@local/hashql/eval/src/graph/error.rs deleted file mode 100644 index 2739f9ce016..00000000000 --- a/libs/@local/hashql/eval/src/graph/error.rs +++ /dev/null @@ -1,26 +0,0 @@ -use alloc::borrow::Cow; - -use hashql_diagnostics::category::DiagnosticCategory; - -use super::read::error::GraphReadCompilerDiagnosticCategory; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum GraphCompilerDiagnosticCategory { - Read(GraphReadCompilerDiagnosticCategory), -} - -impl DiagnosticCategory for GraphCompilerDiagnosticCategory { - fn id(&self) -> Cow<'_, str> { - Cow::Borrowed("graph") - } - - fn name(&self) -> Cow<'_, str> { - Cow::Borrowed("Graph") - } - - fn subcategory(&self) -> Option<&dyn DiagnosticCategory> { - match self { - Self::Read(read) => Some(read), - } - } -} diff --git a/libs/@local/hashql/eval/src/graph/mod.rs b/libs/@local/hashql/eval/src/graph/mod.rs deleted file mode 100644 index 6c733dc3f6c..00000000000 --- a/libs/@local/hashql/eval/src/graph/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod error; -pub mod read; diff --git a/libs/@local/hashql/eval/src/graph/read/convert.rs b/libs/@local/hashql/eval/src/graph/read/convert.rs deleted file mode 100644 index c86fd1642e4..00000000000 --- a/libs/@local/hashql/eval/src/graph/read/convert.rs +++ /dev/null @@ -1,87 +0,0 @@ -use alloc::borrow::Cow; - -use hash_graph_store::filter::Parameter; -use hashql_core::value::{Primitive, Value}; -use type_system::knowledge::PropertyValue; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, derive_more::Display)] -pub(crate) enum ConversionError { - #[display("dictionary keys must be strings")] - DictKeyNotString, -} - -fn value_to_string(value: &Value<'_>) -> Option { - match value { - Value::Primitive(Primitive::String(string)) => Some(string.as_str().to_owned()), - Value::Opaque(opaque) => value_to_string(opaque.value()), - Value::Primitive(_) - | Value::Struct(_) - | Value::Tuple(_) - | Value::List(_) - | Value::Dict(_) => None, - } -} - -fn value_to_property_value(value: &Value<'_>) -> Result { - match value { - Value::Primitive(Primitive::Null) => Ok(PropertyValue::Null), - &Value::Primitive(Primitive::Boolean(bool)) => Ok(PropertyValue::Bool(bool)), - Value::Primitive(Primitive::Integer(integer)) => { - Ok(PropertyValue::Number(integer.as_real())) - } - Value::Primitive(Primitive::Float(float)) => Ok(PropertyValue::Number(float.as_real())), - Value::Primitive(Primitive::String(string)) => { - Ok(PropertyValue::String(string.as_str().to_owned())) - } - Value::Struct(r#struct) => r#struct - .iter() - .map(|(key, value)| { - let key = key.as_str().to_owned(); - let value = value_to_property_value(value)?; - - Ok((key, value)) - }) - .try_collect() - .map(PropertyValue::Object), - Value::Tuple(tuple) => tuple - .iter() - .map(value_to_property_value) - .try_collect() - .map(PropertyValue::Array), - Value::List(list) => list - .iter() - .map(value_to_property_value) - .try_collect() - .map(PropertyValue::Array), - Value::Dict(dict) => dict - .iter() - .map(|(key, value)| { - let key = value_to_string(key).ok_or(ConversionError::DictKeyNotString)?; - let value = value_to_property_value(value)?; - - Ok((key, value)) - }) - .try_collect() - .map(PropertyValue::Object), - Value::Opaque(opaque) => value_to_property_value(opaque.value()), - } -} - -pub(super) fn convert_value_to_parameter<'heap>( - value: &Value<'heap>, -) -> Result, ConversionError> { - match value { - &Value::Primitive(Primitive::Boolean(bool)) => Ok(Parameter::Boolean(bool)), - Value::Primitive(Primitive::Integer(integer)) => Ok(Parameter::Decimal(integer.as_real())), - Value::Primitive(Primitive::Float(float)) => Ok(Parameter::Decimal(float.as_real())), - Value::Primitive(Primitive::String(string)) => { - Ok(Parameter::Text(Cow::Borrowed(string.as_symbol().unwrap()))) - } - Value::Primitive(Primitive::Null) - | Value::Struct(_) - | Value::Tuple(_) - | Value::List(_) - | Value::Dict(_) => value_to_property_value(value).map(Parameter::Any), - Value::Opaque(opaque) => convert_value_to_parameter(opaque.value()), - } -} diff --git a/libs/@local/hashql/eval/src/graph/read/error.rs b/libs/@local/hashql/eval/src/graph/read/error.rs deleted file mode 100644 index 49377a8be2c..00000000000 --- a/libs/@local/hashql/eval/src/graph/read/error.rs +++ /dev/null @@ -1,667 +0,0 @@ -use alloc::borrow::Cow; -use core::fmt::Debug; - -use hashql_core::{ - span::{SpanId, Spanned}, - symbol::Ident, - value::{FieldAccessError, IndexAccessError}, -}; -use hashql_diagnostics::{ - Diagnostic, DiagnosticIssues, Label, Status, - category::{DiagnosticCategory, TerminalDiagnosticCategory}, - diagnostic::Message, - severity::Severity, -}; -use hashql_hir::node::{branch::Branch, operation::BinOp, variable::QualifiedVariable}; - -use super::{FilterCompilerContext, convert::ConversionError}; - -pub type GraphReadCompilerDiagnostic = - Diagnostic; -pub type GraphReadCompilerIssues = - DiagnosticIssues; -pub type GraphReadCompilerStatus = Status; - -const VALUE_PARAMETER_CONVERSION: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "value-parameter-conversion", - name: "Cannot convert value to graph parameter", -}; - -const PATH_CONVERSION: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "path-conversion", - name: "Cannot query against complex object", -}; - -const QUALIFIED_VARIABLE_UNSUPPORTED: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "qualified-variable-unsupported", - name: "Qualified variables not supported", -}; - -const TYPE_CONSTRUCTOR_UNSUPPORTED: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "type-constructor-unsupported", - name: "Type constructors not supported as values", -}; - -const BINARY_OPERATION_UNSUPPORTED: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "binary-operation-unsupported", - name: "Binary operations not supported in this context", -}; - -const PATH_INDEXING_UNSUPPORTED: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "path-indexing-unsupported", - name: "Indexing through traversal paths not supported", -}; - -const FIELD_ACCESS_INTERNAL_ERROR: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "field-access-internal-error", - name: "Internal error during field access", -}; - -const INDEX_ACCESS_INTERNAL_ERROR: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "index-access-internal-error", - name: "Internal error during index access", -}; - -const PATH_TRAVERSAL_INTERNAL_ERROR: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "path-traversal-internal-error", - name: "Internal error during path traversal", -}; - -const CALL_UNSUPPORTED: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "call-unsupported", - name: "Function calls not supported", -}; - -const CLOSURE_UNSUPPORTED: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "closure-unsupported", - name: "Closures not supported", -}; - -const NESTED_GRAPH_READ_UNSUPPORTED: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "nested-graph-read-unsupported", - name: "Nested graph read operations not supported", -}; - -const BRANCH_UNSUPPORTED: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "branch-unsupported", - name: "Branch construct unsupported", -}; - -const PATH_IN_DATA_CONSTRUCT_UNSUPPORTED: TerminalDiagnosticCategory = TerminalDiagnosticCategory { - id: "path-in-data-construct-unsupported", - name: "Path in data construct unsupported", -}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum GraphReadCompilerDiagnosticCategory { - ValueParameterConversion, - PathConversion, - QualifiedVariableUnsupported, - TypeConstructorUnsupported, - BinaryOperationUnsupported, - PathIndexingUnsupported, - FieldAccessInternalError, - IndexAccessInternalError, - PathTraversalInternalError, - CallUnsupported, - ClosureUnsupported, - NestedGraphReadUnsupported, - BranchUnsupported, - PathInDataConstructUnsupported, -} - -impl DiagnosticCategory for GraphReadCompilerDiagnosticCategory { - fn id(&self) -> Cow<'_, str> { - Cow::Borrowed("graph-read-compiler") - } - - fn name(&self) -> Cow<'_, str> { - Cow::Borrowed("Graph Read Compiler") - } - - fn subcategory(&self) -> Option<&dyn DiagnosticCategory> { - match self { - Self::ValueParameterConversion => Some(&VALUE_PARAMETER_CONVERSION), - Self::PathConversion => Some(&PATH_CONVERSION), - Self::QualifiedVariableUnsupported => Some(&QUALIFIED_VARIABLE_UNSUPPORTED), - Self::TypeConstructorUnsupported => Some(&TYPE_CONSTRUCTOR_UNSUPPORTED), - Self::BinaryOperationUnsupported => Some(&BINARY_OPERATION_UNSUPPORTED), - Self::PathIndexingUnsupported => Some(&PATH_INDEXING_UNSUPPORTED), - Self::FieldAccessInternalError => Some(&FIELD_ACCESS_INTERNAL_ERROR), - Self::IndexAccessInternalError => Some(&INDEX_ACCESS_INTERNAL_ERROR), - Self::PathTraversalInternalError => Some(&PATH_TRAVERSAL_INTERNAL_ERROR), - Self::CallUnsupported => Some(&CALL_UNSUPPORTED), - Self::ClosureUnsupported => Some(&CLOSURE_UNSUPPORTED), - Self::NestedGraphReadUnsupported => Some(&NESTED_GRAPH_READ_UNSUPPORTED), - Self::BranchUnsupported => Some(&BRANCH_UNSUPPORTED), - Self::PathInDataConstructUnsupported => Some(&PATH_IN_DATA_CONSTRUCT_UNSUPPORTED), - } - } -} - -// TODO: test once https://linear.app/hash/issue/H-4603/enable-dict-literal-construct lands -pub(super) fn value_parameter_conversion_error( - context: FilterCompilerContext, - value_span: SpanId, - error: ConversionError, -) -> GraphReadCompilerDiagnostic { - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::ValueParameterConversion, - Severity::Error, - ) - .primary(Label::new( - value_span, - format!("Cannot convert value to graph parameter: {error}"), - )); - - diagnostic.labels.push(Label::new( - context.span, - "... within this filter expression", - )); - - diagnostic.add_message(Message::help( - "Graph parameters require valid JSON-compatible values. Dictionary keys must be strings, \ - as non-string keys cannot be properly serialized. Ensure all object keys in your data \ - are strings.", - )); - - diagnostic.add_message(Message::note( - "This error may indicate a data modeling issue. Entities should be JSON-compliant with \ - string keys. If you're seeing this error, the data structure you're trying to convert is \ - not serializable into JSON.", - )); - - diagnostic -} - -pub(super) fn path_conversion_error( - context: FilterCompilerContext, - path_span: SpanId, -) -> GraphReadCompilerDiagnostic { - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::PathConversion, - Severity::Error, - ) - .primary(Label::new( - path_span, - "Cannot query against this complex object", - )); - - diagnostic.labels.push(Label::new( - context.span, - "... within this filter expression", - )); - - diagnostic.add_message(Message::help( - "Filter expressions can only query against simple scalar properties that map to database \ - columns, not complex objects. Use individual properties of the object instead (e.g., \ - `entity.metadata.record_id.entity_id.entity_uuid` instead of \ - `entity.metadata.record_id`).", - )); - - diagnostic.add_message(Message::note( - "This is a temporary limitation of the current query compiler. Support for querying \ - against complex objects in filter expressions is being tracked in \ - https://linear.app/hash/issue/H-4911/hashql-allow-for-querying-against-complex-objects.", - )); - - diagnostic -} - -// In *theory* there's no way to hit this, as any value that's a qualified variable has already been -// resolved to a type-constructor, or intrinsic. This is just here as a sanity check until the -// proper module system lands. -#[coverage(off)] -pub(super) fn qualified_variable_unsupported( - context: FilterCompilerContext, - variable: &QualifiedVariable, - span: SpanId, -) -> GraphReadCompilerDiagnostic { - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::QualifiedVariableUnsupported, - Severity::Error, - ) - .primary(Label::new( - span, - format!( - "Qualified variable `{}` not supported here", - variable.name() - ), - )); - - diagnostic.labels.push(Label::new( - context.span, - "... within this filter expression", - )); - - diagnostic.add_message(Message::help(format!( - "Qualified variables like `{}` are not currently supported in filter expressions. Use \ - local variables or direct parameter references instead.", - variable.name() - ))); - - diagnostic.add_message(Message::note( - "Qualified variables are not yet supported. Implementation of a proper module system is \ - being tracked in https://linear.app/hash/issue/H-4912/hashql-implement-modules.", - )); - - diagnostic -} - -pub(super) fn type_constructor_unsupported( - context: FilterCompilerContext, - span: SpanId, -) -> GraphReadCompilerDiagnostic { - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::TypeConstructorUnsupported, - Severity::Error, - ) - .primary(Label::new( - span, - Cow::Borrowed("Cannot use constructor as value here"), - )); - - diagnostic.labels.push(Label::new( - context.span, - "... within this filter expression", - )); - - diagnostic.add_message(Message::help( - "Constructor functions cannot currently be used as first-class values in filter \ - expressions. You can still call constructors to create values (e.g., `SomeType(x)`), but \ - you cannot use the constructor itself in comparisons or pass it as an argument within \ - filter contexts.", - )); - - diagnostic.add_message(Message::note( - "This is a current limitation of the filter expression compiler. Constructors work as \ - first-class values elsewhere in the language, and support for this in filter expressions \ - is being tracked in https://linear.app/hash/issue/H-4913/hashql-implement-vm.", - )); - - diagnostic -} - -pub(super) fn binary_operation_unsupported( - context: FilterCompilerContext, - op: Spanned, -) -> GraphReadCompilerDiagnostic { - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::BinaryOperationUnsupported, - Severity::Error, - ) - .primary(Label::new( - op.span, - format!("Operation `{}` not supported here", op.value.as_str()), - )); - - diagnostic.labels.push(Label::new( - context.span, - "... within this filter expression", - )); - - diagnostic.add_message(Message::help(format!( - "The `{0}` operation can only be used at the top level of filter conditions, not as an \ - operand in other operations. For example, `(a {0} b) == c` is not allowed, but `(a {0} \ - b) && (c == d)` is valid.", - op.value.as_str(), - ))); - - diagnostic.add_message(Message::note( - "This is an intentional current limitation to keep expressions simple, but there are \ - plans to remove this restriction in the future to allow more complex expressions. \ - Progress on this enhancement is tracked in \ - https://linear.app/hash/issue/H-4911/hashql-allow-for-querying-against-complex-objects.", - )); - - diagnostic -} - -// TODO: requires https://linear.app/hash/issue/H-4603/enable-dict-literal-construct or -// https://linear.app/hash/issue/H-4870/implement-properties-traversal-primitive -pub(super) fn path_indexing_unsupported( - context: FilterCompilerContext, - expr_span: SpanId, - index_span: SpanId, -) -> GraphReadCompilerDiagnostic { - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::PathIndexingUnsupported, - Severity::Error, - ) - .primary(Label::new(index_span, "Cannot use computed value as index")); - - diagnostic - .labels - .push(Label::new(expr_span, "... when indexing this value")); - - diagnostic.labels.push(Label::new( - context.span, - "... within this filter expression", - )); - - diagnostic.add_message(Message::help( - "Dynamic indexing using database values is not currently supported in filter expressions. \ - Use a literal value like `[\"key\"]` or `[0]` instead of computed values like \ - `[entity.id]`. This limitation exists because such operations are complex to translate \ - into database queries.", - )); - - diagnostic.add_message(Message::note( - "This is a temporary limitation of the database query compiler. Support for dynamic \ - indexing using computed values in filter expressions is being tracked in \ - https://linear.app/hash/issue/H-4914/hashql-support-indexing-into-collections-based-on-query-paths.", - )); - - diagnostic -} - -#[coverage(off)] -pub(crate) fn field_access_internal_error( - expr_span: SpanId, - field: &Ident, - error: &FieldAccessError, -) -> GraphReadCompilerDiagnostic { - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::FieldAccessInternalError, - Severity::Bug, - ) - .primary(Label::new( - field.span, - format!("Field access for `{field}` failed unexpectedly"), - )); - - diagnostic - .labels - .push(Label::new(expr_span, "... on this value")); - - diagnostic.add_message(Message::help( - "This is an internal compiler error. The field access should have been validated by the \ - type checker, but the operation failed during compilation. Please report this as a bug \ - with the code that triggered this error.", - )); - - diagnostic.add_message(Message::note( - "This error indicates a bug in the type checker or compiler. The field access was \ - expected to succeed based on type information, but failed during evaluation.", - )); - - diagnostic.add_message(Message::note(format!( - "Internal error that occurred: {error}" - ))); - - diagnostic -} - -#[coverage(off)] -pub(crate) fn index_access_internal_error( - expr_span: SpanId, - index_span: SpanId, - error: &IndexAccessError, -) -> GraphReadCompilerDiagnostic { - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::IndexAccessInternalError, - Severity::Bug, - ) - .primary(Label::new(index_span, "Index access failed unexpectedly")); - - diagnostic - .labels - .push(Label::new(expr_span, "... on this value")); - - diagnostic.add_message(Message::help( - "This is an internal compiler error. The index access should have been validated by the \ - type checker, but the operation failed during compilation. Please report this as a bug \ - with the code that triggered this error.", - )); - - diagnostic.add_message(Message::note( - "This error indicates a bug in the type checker or compiler. The index access was \ - expected to succeed based on type information, but failed during evaluation.", - )); - - diagnostic.add_message(Message::note(format!( - "Internal error that occurred: {error}" - ))); - - diagnostic -} - -#[coverage(off)] -pub(crate) fn path_traversal_internal_error

( - expr_span: SpanId, - access_span: SpanId, - path: Option<&P>, -) -> GraphReadCompilerDiagnostic -where - P: Debug, -{ - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::PathTraversalInternalError, - Severity::Bug, - ) - .primary(Label::new( - access_span, - "Path traversal failed unexpectedly", - )); - - diagnostic - .labels - .push(Label::new(expr_span, "... on this value")); - - diagnostic.add_message(Message::help( - "This is an internal compiler error. The path traversal should have been validated by the \ - type checker, but failed during compilation. This indicates a mismatch between the type \ - checker's expectations and the actual path structure. Please report this as a bug with \ - the code that triggered this error.", - )); - - diagnostic.add_message(Message::note( - "This error suggests that the partial query path code was not properly adjusted to match \ - the type checker's validation. This is a compiler implementation bug.", - )); - - diagnostic.add_message(Message::note(format!( - "The path you were trying to access: {path:?}" - ))); - - diagnostic -} - -pub(super) fn call_unsupported( - context: FilterCompilerContext, - call_span: SpanId, -) -> GraphReadCompilerDiagnostic { - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::CallUnsupported, - Severity::Error, - ) - .primary(Label::new(call_span, "Function call not supported here")); - - diagnostic.labels.push(Label::new( - context.span, - "... within this filter expression", - )); - - diagnostic.add_message(Message::help( - "Filter expressions do not currently support function calls. Move the function call \ - outside the filter expression, assign the result to a variable, and use that variable in \ - the filter instead.", - )); - - diagnostic.add_message(Message::note( - "Function calls in filter expressions are not yet implemented. This feature may be added \ - in future versions for specific categories of pure functions. Progress is tracked in \ - https://linear.app/hash/issue/H-4913/hashql-implement-vm.", - )); - - diagnostic -} - -pub(super) fn closure_unsupported( - context: FilterCompilerContext, - closure_span: SpanId, -) -> GraphReadCompilerDiagnostic { - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::ClosureUnsupported, - Severity::Error, - ) - .primary(Label::new( - closure_span, - "Closure definition not supported here", - )); - - diagnostic.labels.push(Label::new( - context.span, - "... within this filter expression", - )); - - diagnostic.add_message(Message::help( - "Filter expressions do not currently support closure definitions. Move the closure \ - outside the filter expression, assign the result to a variable, and use that variable in \ - the filter instead.", - )); - - diagnostic.add_message(Message::note( - "Closures in filter expressions are not yet implemented. This is a current limitation \ - that is being tracked in https://linear.app/hash/issue/H-4913/hashql-implement-vm.", - )); - - diagnostic -} - -pub(super) fn nested_graph_read_unsupported( - context: FilterCompilerContext, - graph_span: SpanId, -) -> GraphReadCompilerDiagnostic { - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::NestedGraphReadUnsupported, - Severity::Error, - ) - .primary(Label::new( - graph_span, - "Nested graph read operations not supported here", - )); - - diagnostic.labels.push(Label::new( - context.span, - "... within this filter expression", - )); - - diagnostic.add_message(Message::help( - "Filter expressions do not support nested graph operations. Use a separate query for the \ - nested graph read and pass the result to this filter expression.", - )); - - diagnostic.add_message(Message::note( - "Nested graph reads in filter expressions are not yet implemented. This is a current \ - limitation that is being tracked in \ - https://linear.app/hash/issue/H-4913/hashql-implement-vm.", - )); - - diagnostic -} - -/// Context type for branch unsupported diagnostics. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum BranchContext { - /// Branch in a filter boolean expression context. - Filter, - /// Branch in a filter expression context. - FilterExpression, -} - -/// Creates a diagnostic for unsupported branch constructs in filter contexts. -/// -/// This diagnostic is emitted when conditional statements (if/else) are encountered -/// in filter contexts where they are not supported. -/// -/// # Arguments -/// -/// * `context` - The filter compilation context containing the parent filter span -/// * `branch` - The branch construct that is not supported -/// * `branch_context` - Whether this is in a filter or filter expression context -/// -/// # Returns -/// -/// A diagnostic indicating that branch constructs are not supported in this context, -/// with guidance on how to restructure the code. -pub(super) fn branch_unsupported( - branch: &Branch, - span: SpanId, - branch_context: BranchContext, -) -> GraphReadCompilerDiagnostic { - // Create specific primary message based on branch type - let primary_message = match branch { - Branch::If(_) => "conditional expressions are not supported in filter contexts", - }; - - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::BranchUnsupported, - Severity::Error, - ) - .primary(Label::new(span, primary_message)); - - diagnostic.add_message(Message::help( - "rewrite the logic to avoid conditional statements", - )); - - diagnostic.add_message(Message::note( - "conditional statements (`if`/`else`) are not supported by the current query compiler. \ - This is a fundamental limitation that will be addressed in the next-generation compiler", - )); - - match branch_context { - BranchContext::Filter => { - diagnostic.add_message(Message::note( - "consider using boolean operators (`&&`, `||`, `!`) to combine conditions \ - directly instead of `if` statements", - )); - } - BranchContext::FilterExpression => { - diagnostic.add_message(Message::note( - "conditional logic must be handled outside the query or using other language \ - constructs", - )); - } - } - - diagnostic -} - -/// Creates a diagnostic for unsupported path expressions within data constructs. -/// -/// This diagnostic is emitted when path expressions are encountered within data -/// constructs like tuples, lists, dictionaries, or structs, where they are not -/// supported by the current query compiler. -/// -/// # Arguments -/// -/// * `path_span` - The span of the path expression within the data construct -/// * `construct_name` - The name of the data construct containing the path (e.g., "tuple", "list") -/// -/// # Returns -/// -/// A diagnostic indicating that path expressions are not supported within the -/// specified data construct, with guidance on alternative approaches. -pub(super) fn path_in_data_construct_unsupported(path_span: SpanId) -> GraphReadCompilerDiagnostic { - let mut diagnostic = Diagnostic::new( - GraphReadCompilerDiagnosticCategory::PathInDataConstructUnsupported, - Severity::Error, - ) - .primary(Label::new( - path_span, - "path expressions are not supported in data constructs", - )); - - diagnostic.add_message(Message::help( - "rewrite the logic to avoid path expressions in data constructs", - )); - - diagnostic.add_message(Message::note( - "path expressions within complex data constructs (tuples, lists, dictionaries, structs) \ - are not supported by the current query compiler. This is a fundamental limitation that \ - will be addressed in the next-generation compiler", - )); - - diagnostic -} diff --git a/libs/@local/hashql/eval/src/graph/read/filter.rs b/libs/@local/hashql/eval/src/graph/read/filter.rs deleted file mode 100644 index 9dd14148bb1..00000000000 --- a/libs/@local/hashql/eval/src/graph/read/filter.rs +++ /dev/null @@ -1,255 +0,0 @@ -use core::fmt::Debug; - -use hash_graph_store::filter::{Filter, FilterExpression, Parameter, QueryRecord}; -use hashql_core::value::Primitive; -use hashql_hir::node::{ - Node, - branch::{Branch, If}, - call::{Call, PointerKind}, - data::Data, - kind::NodeKind, - r#let::{Binding, Let}, - operation::{BinOp, BinaryOperation, Operation, TypeAssertion, TypeOperation}, - thunk::Thunk, - variable::{LocalVariable, Variable}, -}; - -use super::{ - CompilationError, FilterCompilerContext, GraphReadCompiler, - error::qualified_variable_unsupported, path::CompleteQueryPath, sink::FilterSink, -}; - -/// Checks if a [`Node`] represents a boolean literal with the specified value. -/// -/// This helper function is used to optimize conditional expressions when one -/// branch is a literal boolean value. -/// -/// # Examples -/// -/// ```ignore -/// // Returns true if the node represents `true` -/// assert!(is_bool_literal(true_node, true)); -/// // Returns false if the node represents `false` when checking for `true` -/// assert!(!is_bool_literal(false_node, true)); -/// ``` -fn is_bool_literal(node: Node<'_>, value: bool) -> bool { - matches!( - node.kind, - NodeKind::Data(Data::Primitive(Primitive::Boolean(constant))) if constant == value - ) -} - -impl<'env, 'heap: 'env> GraphReadCompiler<'env, 'heap> { - #[expect(clippy::too_many_lines, reason = "match statement")] - pub(super) fn compile_filter( - &mut self, - context: FilterCompilerContext, - node: Node<'heap>, - sink: &mut FilterSink<'_, 'heap, R>, - ) -> Result<(), CompilationError> - where - R: QueryRecord: CompleteQueryPath<'heap, PartialQueryPath: Debug> + Clone>, - { - match node.kind { - NodeKind::Variable(Variable::Local(LocalVariable { id, arguments: _ })) => { - debug_assert_ne!( - id.value, context.param_id, - "typecheck should have caught this, cannot just return the entity itself." - ); - - let value = self.locals[&id.value]; - self.compile_filter(context, value, sink) - } - NodeKind::Variable(Variable::Qualified(qualified)) => { - self.diagnostics.push(qualified_variable_unsupported( - context, &qualified, node.span, - )); - - Err(CompilationError) - } - NodeKind::Let(Let { bindings, body }) => { - for Binding { - span: _, - binder, - value, - } in bindings - { - self.locals.insert(binder.id, *value); - } - - let filter = self.compile_filter(context, body, sink); - - for Binding { - span: _, - binder, - value: _, - } in bindings - { - self.locals.remove(&binder.id); - } - - filter - } - NodeKind::Operation(Operation::Binary(BinaryOperation { op, left, right })) => { - let func = match op.value { - BinOp::And => { - let mut sink = sink.and(); - - let (left, right) = ( - self.compile_filter(context, left, &mut sink), - self.compile_filter(context, right, &mut sink), - ); - - // We defer the error handling to make sure we gather every diagnostic - left?; - right?; - - return Ok(()); - } - BinOp::Or => { - let mut sink = sink.or(); - - let (left, right) = ( - self.compile_filter(context, left, &mut sink), - self.compile_filter(context, right, &mut sink), - ); - - // We defer the error handling to make sure we gather every diagnostic - left?; - right?; - - return Ok(()); - } - BinOp::Eq => Filter::Equal, - BinOp::Ne => Filter::NotEqual, - BinOp::Lt => Filter::Less, - BinOp::Lte => Filter::LessOrEqual, - BinOp::Gt => Filter::Greater, - BinOp::Gte => Filter::GreaterOrEqual, - }; - - let (left, right) = ( - self.compile_filter_expr(context, left) - .and_then(|expr| expr.finish(context, &mut self.diagnostics)), - self.compile_filter_expr(context, right) - .and_then(|expr| expr.finish(context, &mut self.diagnostics)), - ); - - sink.push(func(left?, right?)); - Ok(()) - } - // Unary are currently not supported, so we can skip them - NodeKind::Operation(Operation::Type(TypeOperation::Assertion(TypeAssertion { - value, - r#type: _, - force: _, - }))) => self.compile_filter(context, value, sink), - NodeKind::Thunk(Thunk { body }) => self.compile_filter(context, body, sink), - NodeKind::Call(Call { - kind: PointerKind::Thin, - function, - arguments: _, - }) => { - // A thin call is a call to a thunk, so simply redirect to the thunks body - self.compile_filter(context, function, sink) - } - // If we came to this match arm using these nodes, then that means that the filter - // must have evaluated to a boolean expression. Therefore we can just check if the - // expression evaluates to true. - NodeKind::Operation( - Operation::Type(TypeOperation::Constructor(..)) | Operation::Input(..), - ) - | NodeKind::Data(_) - | NodeKind::Access(_) - | NodeKind::Call(_) - | NodeKind::Closure(_) - | NodeKind::Graph(_) => { - sink.push(Filter::Equal( - self.compile_filter_expr(context, node)? - .finish(context, &mut self.diagnostics)?, - FilterExpression::Parameter { - parameter: Parameter::Boolean(true), - convert: None, - }, - )); - - Ok(()) - } - // Conditional expressions (`if A then B else C`) are transformed into equivalent - // boolean logic expressions. We apply optimizations for common literal patterns: - // - // 1. `if A then true else C` → `A || C` - // 2. `if A then B else false` → `A && B` - // 3. General case: `if A then B else C` → `(A && B) || (!A && C)` - // - // The general transformation preserves semantics: - // - When A is true: `(true && B) || (false && C)` = `B || false` = `B` - // - When A is false: `(false && B) || (true && C)` = `false || C` = `C` - NodeKind::Branch(Branch::If(If { test, then, r#else })) => { - if is_bool_literal(then, true) { - // Optimization: `if A then true else C` → `A || C` - // When A is true, result is true; when A is false, result is C - let mut sink = sink.or(); - - let test = self.compile_filter(context, test, &mut sink); - let r#else = self.compile_filter(context, r#else, &mut sink); - - // Delayed to ensure that we get all diagnostics possible - test?; - r#else?; - } else if is_bool_literal(r#else, false) { - // Optimization: `if A then B else false` → `A && B` - // When A is true, result is B; when A is false, result is false - let mut sink = sink.and(); - - let test = self.compile_filter(context, test, &mut sink); - let then = self.compile_filter(context, then, &mut sink); - - // Delayed to ensure that we get all diagnostics possible - test?; - then?; - } else { - // General case: `if A then B else C` → `(A && B) || (!A && C)` - - // Compile each branch into separate filter vectors to enable reuse - // of the test condition in both the positive and negative forms - let mut test_filter = Vec::new(); - let mut test_sink = FilterSink::::And(&mut test_filter); - let test_result = self.compile_filter(context, test, &mut test_sink); - - let mut then_filter = Vec::new(); - let mut then_sink = FilterSink::::And(&mut then_filter); - let then_result = self.compile_filter(context, then, &mut then_sink); - - let mut else_filter = Vec::new(); - let mut else_sink = FilterSink::::And(&mut else_filter); - let else_result = self.compile_filter(context, r#else, &mut else_sink); - - // Build left side: `A && B` - let mut left = Vec::with_capacity(test_filter.len() + then_filter.len()); - left.extend_from_slice(&test_filter); - left.extend(then_filter); - let left = Filter::All(left); - - // Build right side: `!A && C` - let mut right = Vec::with_capacity(1 + else_filter.len()); - right.push(Filter::Not(Box::new(Filter::All(test_filter)))); - right.extend(else_filter); - let right = Filter::All(right); - - // Combine with OR: `(A && B) || (!A && C)` - let mut sink = sink.or(); - sink.push(left); - sink.push(right); - - // Defer error handling to collect all diagnostics before failing - test_result?; - then_result?; - else_result?; - } - - Ok(()) - } - } - } -} diff --git a/libs/@local/hashql/eval/src/graph/read/filter_expr.rs b/libs/@local/hashql/eval/src/graph/read/filter_expr.rs deleted file mode 100644 index 2bab9bc0d4b..00000000000 --- a/libs/@local/hashql/eval/src/graph/read/filter_expr.rs +++ /dev/null @@ -1,715 +0,0 @@ -use alloc::borrow::Cow; -use core::fmt::Debug; - -use hash_graph_store::filter::{FilterExpression, QueryRecord}; -use hashql_core::{ - span::SpanId, - value::{self, Opaque, Primitive, Value}, -}; -use hashql_hir::node::{ - Node, - access::{Access, FieldAccess, IndexAccess}, - call::{Call, PointerKind}, - data::{Data, DictField}, - graph::Graph, - kind::NodeKind, - r#let::{Binding, Let}, - operation::{ - BinOp, BinaryOperation, InputOp, InputOperation, Operation, TypeAssertion, TypeConstructor, - TypeOperation, - }, - thunk::Thunk, - variable::{LocalVariable, Variable}, -}; - -use super::{ - CompilationError, FilterCompilerContext, GraphReadCompiler, - convert::convert_value_to_parameter, - error::{ - BranchContext, GraphReadCompilerIssues, branch_unsupported, call_unsupported, - closure_unsupported, field_access_internal_error, nested_graph_read_unsupported, - path_conversion_error, path_in_data_construct_unsupported, path_indexing_unsupported, - path_traversal_internal_error, qualified_variable_unsupported, - value_parameter_conversion_error, - }, - path::{CompleteQueryPath, PartialQueryPath, traverse_into_field, traverse_into_index}, -}; -use crate::graph::read::error::{ - binary_operation_unsupported, index_access_internal_error, type_constructor_unsupported, -}; - -pub(crate) enum IntermediateExpression<'env, 'heap, P> { - Value { - value: Cow<'env, Value<'heap>>, - span: SpanId, - }, - Path { - path: Option

, - span: SpanId, - }, -} - -impl<'heap, P> IntermediateExpression<'_, 'heap, P> { - pub(crate) fn finish( - self, - context: FilterCompilerContext, - diagnostics: &mut GraphReadCompilerIssues, - ) -> Result, CompilationError> - where - R: QueryRecord: CompleteQueryPath<'heap, PartialQueryPath = P>>, - P: PartialQueryPath<'heap, QueryPath = R::QueryPath<'heap>>, - { - match self { - Self::Value { value, span } => { - let parameter = match convert_value_to_parameter(&value) { - Ok(value) => value, - Err(error) => { - diagnostics.push(value_parameter_conversion_error(context, span, error)); - return Err(CompilationError); - } - }; - - Ok(FilterExpression::Parameter { - parameter, - convert: None, - }) - } - Self::Path { path: None, span } => { - diagnostics.push(path_conversion_error(context, span)); - Err(CompilationError) - } - Self::Path { - path: Some(path), - span, - } => { - let Some(path) = path.finish() else { - diagnostics.push(path_conversion_error(context, span)); - return Err(CompilationError); - }; - - Ok(FilterExpression::Path { path }) - } - } - } -} - -impl<'env, 'heap: 'env> GraphReadCompiler<'env, 'heap> { - fn compile_filter_expr_data

( - &mut self, - context: FilterCompilerContext, - span: SpanId, - data: &'heap Data<'heap>, - ) -> Result, CompilationError> - where - P: PartialQueryPath<'heap> + Debug, - { - match data { - Data::Primitive(literal) => Ok(IntermediateExpression::Value { - value: Cow::Owned(Value::Primitive(*literal)), - span, - }), - Data::Tuple(tuple) => { - let mut values = Vec::with_capacity(tuple.fields.len()); - for (index, field) in tuple.fields.iter().enumerate() { - let Ok(IntermediateExpression::Value { value, span: _ }) = - self.compile_filter_expr::(context.with_current_span(span), *field) - else { - // `!` ensures that no `IntermediateExpression::Path` will be returned - continue; - }; - - if values.len() != index { - // Previous iteration failed, so pushing (and thereby potentially - // reallocating) is pointless. - continue; - } - - values.push(value.into_owned()); - } - - if values.len() != tuple.fields.len() { - return Err(CompilationError); - } - - Ok(IntermediateExpression::Value { - value: Cow::Owned(Value::Tuple(value::Tuple::from_values(values))), - span, - }) - } - Data::Struct(r#struct) => { - let mut fields = Vec::with_capacity(r#struct.fields.len()); - for (index, field) in r#struct.fields.iter().enumerate() { - let Ok(IntermediateExpression::Value { value, span: _ }) = - self.compile_filter_expr::(context.with_current_span(span), field.value) - else { - // `!` ensures that no `IntermediateExpression::Path` will be returned - continue; - }; - - if fields.len() != index { - // Previous iteration failed, so pushing (and thereby potentially - // reallocating) is pointless. - continue; - } - - fields.push((field.name.value, value.into_owned())); - } - - if fields.len() != r#struct.fields.len() { - return Err(CompilationError); - } - - Ok(IntermediateExpression::Value { - value: Cow::Owned(Value::Struct(value::Struct::from_fields(self.heap, fields))), - span, - }) - } - Data::List(list) => { - let mut values = Vec::with_capacity(list.elements.len()); - for (index, element) in list.elements.iter().enumerate() { - let Ok(IntermediateExpression::Value { value, span: _ }) = - self.compile_filter_expr::(context.with_current_span(span), *element) - else { - // `!` ensures that no `IntermediateExpression::Path` will be returned - continue; - }; - - if values.len() != index { - // Previous iteration failed, so pushing (and thereby potentially - // reallocating) is pointless. - continue; - } - - values.push(value.into_owned()); - } - - if values.len() != list.elements.len() { - return Err(CompilationError); - } - - Ok(IntermediateExpression::Value { - value: Cow::Owned(Value::List(value::List::from_values(values))), - span, - }) - } - Data::Dict(dict) => { - let mut entries = Vec::with_capacity(dict.fields.len()); - - for (index, DictField { key, value }) in dict.fields.iter().enumerate() { - let key = self.compile_filter_expr::(context.with_current_span(span), *key); - let value = - self.compile_filter_expr::(context.with_current_span(span), *value); - - // We delay destructing here, so that we can gather errors for both keys and - // values - let ( - Ok(IntermediateExpression::Value { - value: key, - span: _, - }), - Ok(IntermediateExpression::Value { value, span: _ }), - ) = (key, value) - else { - continue; - }; - - if entries.len() != index { - // Previous iteration failed, so pushing (and thereby potentially - // reallocating) is pointless. - continue; - } - - entries.push((key.into_owned(), value.into_owned())); - } - - if entries.len() != dict.fields.len() { - return Err(CompilationError); - } - - Ok(IntermediateExpression::Value { - value: Cow::Owned(Value::Dict(value::Dict::from_entries(entries))), - span, - }) - } - } - } - - fn compile_filter_expr_variable

( - &mut self, - context: FilterCompilerContext, - span: SpanId, - variable: &'heap Variable<'heap>, - ) -> Result, CompilationError> - where - P: PartialQueryPath<'heap> + Debug, - { - match variable { - &Variable::Local(LocalVariable { id, arguments: _ }) - if id.value == context.param_id => - { - if P::UNSUPPORTED { - self.diagnostics - .push(path_in_data_construct_unsupported(span)); - - return Err(CompilationError); - } - - Ok(IntermediateExpression::Path { path: None, span }) - } - Variable::Local(LocalVariable { id, arguments: _ }) => { - let value = self.locals[&id.value]; - self.compile_filter_expr(context.with_current_span(span), value) - } - Variable::Qualified(qualified_variable) => { - self.diagnostics.push(qualified_variable_unsupported( - context, - qualified_variable, - span, - )); - - Err(CompilationError) - } - } - } - - fn compile_filter_expr_let( - &mut self, - Let { bindings, body }: &'heap Let<'heap>, - recurse: impl FnOnce(&mut Self, Node<'heap>) -> T, - ) -> T { - for Binding { - span: _, - binder, - value, - } in bindings - { - self.locals.insert(binder.id, *value); - } - - let result = recurse(self, *body); - - for Binding { - span: _, - binder, - value: _, - } in bindings - { - self.locals.remove(&binder.id); - } - - result - } - - fn compile_filter_expr_operation_type

( - &mut self, - context: FilterCompilerContext, - span: SpanId, - operation: &'heap TypeOperation<'heap>, - ) -> Result, CompilationError> - where - P: PartialQueryPath<'heap> + Debug, - { - match operation { - TypeOperation::Assertion(TypeAssertion { - value, - r#type: _, - force: _, - }) => self.compile_filter_expr(context, *value), - &TypeOperation::Constructor(TypeConstructor { name: _ }) => { - self.diagnostics - .push(type_constructor_unsupported(context, span)); - - Err(CompilationError) - } - } - } - - fn compile_filter_expr_operation_binary

( - &mut self, - context: FilterCompilerContext, - &BinaryOperation { - op, - left: _, - right: _, - }: &'heap BinaryOperation<'heap>, - ) -> Result, CompilationError> - where - P: PartialQueryPath<'heap>, - { - match op.value { - BinOp::And - | BinOp::Or - | BinOp::Eq - | BinOp::Ne - | BinOp::Lt - | BinOp::Lte - | BinOp::Gt - | BinOp::Gte => { - self.diagnostics - .push(binary_operation_unsupported(context, op)); - - Err(CompilationError) - } - } - } - - fn compile_filter_expr_operation_input

( - &self, - span: SpanId, - InputOperation { op, name }: &'heap InputOperation<'heap>, - ) -> IntermediateExpression<'env, 'heap, P> - where - P: PartialQueryPath<'heap> + Debug, - { - match op.value { - InputOp::Exists => { - let exists = self.inputs.contains_key(&name.value); - - IntermediateExpression::Value { - value: Cow::Owned(value::Value::Primitive(value::Primitive::Boolean(exists))), - span, - } - } - InputOp::Load { .. } => { - let value = &self.inputs[&name.value]; - - IntermediateExpression::Value { - value: Cow::Borrowed(value), - span, - } - } - } - } - - fn compile_filter_expr_operation

( - &mut self, - context: FilterCompilerContext, - span: SpanId, - operation: &'heap Operation<'heap>, - ) -> Result, CompilationError> - where - P: PartialQueryPath<'heap> + Debug, - { - match operation { - Operation::Type(r#type) => { - self.compile_filter_expr_operation_type(context, span, r#type) - } - Operation::Binary(binary) => self.compile_filter_expr_operation_binary(context, binary), - Operation::Input(input) => Ok(self.compile_filter_expr_operation_input(span, input)), - } - } - - fn compile_filter_expr_access_field

( - &mut self, - context: FilterCompilerContext, - span: SpanId, - FieldAccess { - expr: expr_node, - field, - }: &'heap FieldAccess<'heap>, - ) -> Result, CompilationError> - where - P: PartialQueryPath<'heap> + Debug, - { - let expr = self.compile_filter_expr::

(context, *expr_node)?; - - let output = match expr { - IntermediateExpression::Value { value, span: _ } => { - let value = match value { - Cow::Borrowed(value) => value.access_by_field(field.value).map(Cow::Borrowed), - Cow::Owned(value) => { - value.access_by_field(field.value).cloned().map(Cow::Owned) - } - }; - - let value = match value { - Ok(value) => value, - Err(error) => { - self.diagnostics.push(field_access_internal_error( - expr_node.span, - field, - &error, - )); - return Err(CompilationError); - } - }; - - IntermediateExpression::Value { value, span } - } - - IntermediateExpression::Path { path, span: _ } => { - let path = match traverse_into_field(path, self.heap, field.value) { - Ok(path) => path, - Err(path) => { - if path.is_none() && P::UNSUPPORTED { - self.diagnostics - .push(path_in_data_construct_unsupported(span)); - - return Err(CompilationError); - } - - self.diagnostics.push(path_traversal_internal_error( - expr_node.span, - field.span, - path.as_ref(), - )); - - return Err(CompilationError); - } - }; - - IntermediateExpression::Path { - path: Some(path), - span, - } - } - }; - - Ok(output) - } - - fn compile_filter_expr_access_index

( - &mut self, - context: FilterCompilerContext, - span: SpanId, - IndexAccess { - expr: expr_node, - index: index_node, - }: &'heap IndexAccess<'heap>, - ) -> Result, CompilationError> - where - P: PartialQueryPath<'heap> + Debug, - { - let expr = self.compile_filter_expr::

(context, *expr_node); - let index = self.compile_filter_expr::

(context, *index_node); - - let (expr, index) = (expr?, index?); - - let index = match index { - IntermediateExpression::Value { value, span: _ } => value, - IntermediateExpression::Path { path: _, span: _ } => { - self.diagnostics.push(path_indexing_unsupported( - context, - expr_node.span, - index_node.span, - )); - - return Err(CompilationError); - } - }; - - let output = match expr { - IntermediateExpression::Value { value, span: _ } => { - let value = match value { - Cow::Borrowed(value) => value - .access_by_index(&index) - .map(|value| value.map(Cow::Borrowed)), - Cow::Owned(value) => value - .access_by_index(&index) - .map(|value| value.cloned().map(Cow::Owned)), - }; - - let value = match value { - Ok(value) => value, - Err(error) => { - self.diagnostics.push(index_access_internal_error( - expr_node.span, - index_node.span, - &error, - )); - return Err(CompilationError); - } - }; - - let value = value.unwrap_or_else(|| Cow::Owned(Value::Primitive(Primitive::Null))); - - IntermediateExpression::Value { value, span } - } - IntermediateExpression::Path { path, span: _ } => { - let path = match traverse_into_index(path, self.heap, index) { - Ok(path) => path, - Err(path) => { - if path.is_none() && P::UNSUPPORTED { - self.diagnostics - .push(path_in_data_construct_unsupported(span)); - - return Err(CompilationError); - } - - self.diagnostics.push(path_traversal_internal_error( - expr_node.span, - index_node.span, - path.as_ref(), - )); - - return Err(CompilationError); - } - }; - - IntermediateExpression::Path { - path: Some(path), - span, - } - } - }; - - Ok(output) - } - - fn compile_filter_expr_access

( - &mut self, - context: FilterCompilerContext, - span: SpanId, - access: &'heap Access<'heap>, - ) -> Result, CompilationError> - where - P: PartialQueryPath<'heap> + Debug, - { - match access { - Access::Field(field) => self.compile_filter_expr_access_field(context, span, field), - Access::Index(index) => self.compile_filter_expr_access_index(context, span, index), - } - } - - fn compile_filter_expr_call_ctor( - &mut self, - context: FilterCompilerContext, - span: SpanId, - node: Node<'heap>, - ) -> Result<&'heap TypeConstructor<'heap>, CompilationError> { - match &node.0.kind { - NodeKind::Operation(Operation::Type(TypeOperation::Constructor(ctor))) => Ok(ctor), - NodeKind::Variable(Variable::Local(local)) => { - self.compile_filter_expr_call_ctor(context, span, self.locals[&local.id.value]) - } - NodeKind::Call(Call { - kind: PointerKind::Thin, - function, - arguments: _, - }) => self.compile_filter_expr_call_ctor(context, span, *function), - NodeKind::Let(r#let) => self.compile_filter_expr_let(r#let, |this, body| { - this.compile_filter_expr_call_ctor(context, span, body) - }), - NodeKind::Thunk(Thunk { body }) => { - self.compile_filter_expr_call_ctor(context, span, *body) - } - NodeKind::Data(_) - | NodeKind::Variable(_) - | NodeKind::Operation(_) - | NodeKind::Access(_) - | NodeKind::Call(_) - | NodeKind::Branch(_) - | NodeKind::Closure(_) - | NodeKind::Graph(_) => { - self.diagnostics.push(call_unsupported(context, span)); - Err(CompilationError) - } - } - } - - // in theory they could be in the narrow context that they take a *single* argument, - // and that argument happens to be the entity being filtered, because then we can - // statically analyze them real easy. - fn compile_filter_expr_call

( - &mut self, - context: FilterCompilerContext, - span: SpanId, - Call { - kind, - function, - arguments, - }: &'heap Call<'heap>, - ) -> Result, CompilationError> - where - P: PartialQueryPath<'heap> + Debug, - { - if *kind == PointerKind::Thin { - // Thin pointer to a local variable = calling a thunk - return self.compile_filter_expr(context.with_current_span(span), *function); - } - - let ctor = self.compile_filter_expr_call_ctor(context, span, *function)?; - - match &**arguments { - [] => Ok(IntermediateExpression::Value { - value: Cow::Owned(Value::Opaque(Opaque::new( - ctor.name, - Value::Primitive(Primitive::Null), - ))), - span, - }), - [argument] => { - let argument = - self.compile_filter_expr(context.without_current_span(), argument.value)?; - - match argument { - IntermediateExpression::Value { value, span } => { - Ok(IntermediateExpression::Value { - value: Cow::Owned(Value::Opaque(Opaque::new( - ctor.name, - value.into_owned(), - ))), - span, - }) - } - // paths simply "pass through" - path @ IntermediateExpression::Path { .. } => Ok(path), - } - } - _ => unreachable!(), - } - } - - // We can't return a `FilterExpression` instead we require to return our own `Expression` that - // can then be converted to a `FilterExpression`. That allows us to carry the values using - // `&'heap` lifetimes. - pub(super) fn compile_filter_expr

( - &mut self, - context: FilterCompilerContext, - node: Node<'heap>, - ) -> Result, CompilationError> - where - P: PartialQueryPath<'heap> + Debug, - { - match &node.0.kind { - NodeKind::Data(data) => self.compile_filter_expr_data(context, node.span, data), - NodeKind::Variable(variable) => { - self.compile_filter_expr_variable(context, node.span, variable) - } - NodeKind::Let(r#let) => self.compile_filter_expr_let(r#let, |this, body| { - this.compile_filter_expr(context, body) - }), - NodeKind::Operation(operation) => { - self.compile_filter_expr_operation(context, node.span, operation) - } - NodeKind::Access(access) => self.compile_filter_expr_access(context, node.span, access), - NodeKind::Call(call) => self.compile_filter_expr_call(context, node.span, call), - NodeKind::Closure(_) => { - self.diagnostics.push(closure_unsupported( - context, - context.current_span.unwrap_or(node.span), - )); - Err(CompilationError) - } - NodeKind::Thunk(Thunk { body }) => self.compile_filter_expr(context, *body), - NodeKind::Graph(graph) => match graph { - Graph::Read(_) => { - self.diagnostics.push(nested_graph_read_unsupported( - context, - context.current_span.unwrap_or(node.span), - )); - Err(CompilationError) - } - }, - NodeKind::Branch(branch) => { - self.diagnostics.push(branch_unsupported( - branch, - node.span, - BranchContext::FilterExpression, - )); - - Err(CompilationError) - } - } - } -} diff --git a/libs/@local/hashql/eval/src/graph/read/mod.rs b/libs/@local/hashql/eval/src/graph/read/mod.rs deleted file mode 100644 index 823eaf9ff30..00000000000 --- a/libs/@local/hashql/eval/src/graph/read/mod.rs +++ /dev/null @@ -1,259 +0,0 @@ -mod convert; -pub mod error; -mod filter; -mod filter_expr; -mod path; -mod sink; - -use core::{fmt::Debug, ops::Range}; - -use hash_graph_store::filter::{Filter, QueryRecord}; -use hashql_core::{ - collections::FastHashMap, heap::Heap, span::SpanId, symbol::Symbol, value::Value, -}; -use hashql_diagnostics::DiagnosticIssues; -use hashql_hir::{ - node::{ - HirId, HirIdMap, Node, - graph::read::{GraphRead, GraphReadBody, GraphReadHead}, - kind::NodeKind, - r#let::{Binding, Let, VarId, VarIdMap}, - thunk::Thunk, - variable::LocalVariable, - }, - visit::{self, Visitor}, -}; -use type_system::knowledge::Entity; - -use self::{ - error::{GraphReadCompilerIssues, GraphReadCompilerStatus}, - path::CompleteQueryPath, - sink::FilterSink, -}; - -// The FilterSlice is an indirect approach to allow us to easily copy a filter between different -// nodes. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum FilterSlice { - Entity { range: Range }, -} - -#[derive(Debug, Clone, PartialEq, Default)] -pub struct Filters<'heap> { - entity: Vec>, -} - -impl<'heap> Filters<'heap> { - #[must_use] - pub fn entity(&self, range: Range) -> &[Filter<'heap, Entity>] { - &self.entity[range] - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -struct FilterCompilerContext { - span: SpanId, - current_span: Option, - param_id: VarId, -} - -impl FilterCompilerContext { - const fn without_current_span(self) -> Self { - Self { - span: self.span, - current_span: None, - param_id: self.param_id, - } - } - - const fn with_current_span(self, span: SpanId) -> Self { - Self { - span: self.span, - current_span: match self.current_span { - None => Some(span), - Some(_) => self.current_span, - }, - param_id: self.param_id, - } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct GraphReadCompilerResidual<'heap> { - pub filters: Filters<'heap>, - pub output: HirIdMap, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -struct CompilationError; - -pub struct GraphReadCompiler<'env, 'heap> { - current: HirId, - heap: &'heap Heap, - filters: Filters<'heap>, - - diagnostics: GraphReadCompilerIssues, - - locals: VarIdMap>, - inputs: &'env FastHashMap, Value<'heap>>, - output: HirIdMap, - variables: VarIdMap, -} - -impl<'env, 'heap: 'env> GraphReadCompiler<'env, 'heap> { - pub fn new(heap: &'heap Heap, inputs: &'env FastHashMap, Value<'heap>>) -> Self { - Self { - current: HirId::PLACEHOLDER, - heap, - filters: Filters::default(), - diagnostics: DiagnosticIssues::new(), - locals: FastHashMap::default(), - inputs, - output: FastHashMap::default(), - variables: FastHashMap::default(), - } - } - - /// Finish the compilation process. - /// - /// # Errors - /// - /// Returns an error if any diagnostics were collected during compilation. - pub fn finish(self) -> GraphReadCompilerStatus> { - self.diagnostics.into_status(GraphReadCompilerResidual { - filters: self.filters, - output: self.output, - }) - } - - fn compile_graph_body( - &mut self, - body: &'heap [GraphReadBody<'heap>], - ) -> Result>, CompilationError> - where - R: QueryRecord: CompleteQueryPath<'heap, PartialQueryPath: Debug> + Clone>, - { - let mut filters = Ok(Vec::new()); - - for body in body { - match body { - GraphReadBody::Filter(node) => { - let NodeKind::Closure(closure) = node.kind else { - unreachable!() - }; - - let mut sink = FilterSink::from_result(&mut filters); - - let filter = self.compile_filter::( - FilterCompilerContext { - span: closure.body.span, - current_span: None, - param_id: closure.signature.params[0].name.id, - }, - closure.body, - &mut sink, - ); - - if let Err(error) = filter { - filters = Err(error); - } - } - } - } - - filters - } - - fn compile_graph_read( - &mut self, - read: &'heap GraphRead<'heap>, - ) -> Result { - match read.head { - GraphReadHead::Entity { axis: _ } => { - let filters = self.compile_graph_body(&read.body)?; - - let start = self.filters.entity.len(); - self.filters.entity.extend(filters); - let end = self.filters.entity.len(); - - Ok(FilterSlice::Entity { range: start..end }) - } - } - } -} - -impl<'heap> Visitor<'heap> for GraphReadCompiler<'_, 'heap> { - fn visit_node(&mut self, node: Node<'heap>) { - if self.output.contains_key(&node.id) { - return; // We've already processed this node, so skip it. - } - - let previous = self.current; - self.current = node.id; - - visit::walk_node(self, node); - - self.current = previous; - } - - fn visit_binding(&mut self, binding: &'heap Binding<'heap>) { - visit::walk_binding(self, binding); - - // Check if the binder has been assigned to an output - if let Some(output) = self.output.get(&binding.value.id) { - self.variables.insert(binding.binder.id, output.clone()); - } - } - - fn visit_local_variable(&mut self, variable: &'heap LocalVariable<'heap>) { - visit::walk_local_variable(self, variable); - - if let Some(output) = self.variables.get(&variable.id.value) { - self.output.insert(self.current, output.clone()); - } - } - - fn visit_thunk(&mut self, thunk: &'heap Thunk<'heap>) { - visit::walk_thunk(self, thunk); - - if let Some(output) = self.output.get(&thunk.body.id) { - self.output.insert(self.current, output.clone()); - } - } - - fn visit_let(&mut self, r#let: &'heap Let<'heap>) { - for Binding { - span: _, - binder, - value, - } in &r#let.bindings - { - self.locals.insert(binder.id, *value); - } - - visit::walk_let(self, r#let); - - for Binding { - span: _, - binder, - value: _, - } in &r#let.bindings - { - self.locals.remove(&binder.id); - } - - if let Some(value) = self.output.get(&r#let.body.id) { - self.output.insert(self.current, value.clone()); - } - } - - fn visit_graph_read(&mut self, graph_read: &'heap GraphRead<'heap>) { - visit::walk_graph_read(self, graph_read); - - if let Ok(filter) = self.compile_graph_read(graph_read) { - self.output - .try_insert(self.current, filter) - .expect("Same node shouldn't be processed multiple times"); - } - } -} diff --git a/libs/@local/hashql/eval/src/graph/read/path.rs b/libs/@local/hashql/eval/src/graph/read/path.rs deleted file mode 100644 index 67438f24c3e..00000000000 --- a/libs/@local/hashql/eval/src/graph/read/path.rs +++ /dev/null @@ -1,583 +0,0 @@ -use alloc::borrow::Cow; - -use hash_graph_store::{ - entity::EntityQueryPath, - filter::{JsonPath, PathToken, QueryPath}, - subgraph::edges::{EdgeDirection, KnowledgeGraphEdgeKind}, -}; -use hashql_core::{ - heap::Heap, - symbol::{Symbol, sym}, - value::{Primitive, Value}, -}; - -pub(crate) trait CompleteQueryPath<'heap>: QueryPath { - type PartialQueryPath: PartialQueryPath<'heap, QueryPath = Self>; -} - -pub(crate) trait PartialQueryPath<'heap>: Sized { - type QueryPath; - - const UNSUPPORTED: bool = false; - - fn from_field(heap: &'heap Heap, field: Symbol<'heap>) -> Option; - fn access_field(self, heap: &'heap Heap, field: Symbol<'heap>) -> Result; - fn from_index(heap: &'heap Heap, index: Cow<'_, Value<'heap>>) -> Option; - fn access_index(self, heap: &'heap Heap, index: Cow<'_, Value<'heap>>) -> Result; - fn finish(self) -> Option; -} - -impl<'heap> PartialQueryPath<'heap> for ! { - type QueryPath = !; - - const UNSUPPORTED: bool = true; - - fn from_field(_: &'heap Heap, _: Symbol<'heap>) -> Option { - None - } - - fn access_field(self, _: &'heap Heap, _: Symbol<'heap>) -> Result { - self - } - - fn from_index(_: &'heap Heap, _: Cow<'_, Value<'heap>>) -> Option { - None - } - - fn access_index(self, _: &'heap Heap, _: Cow<'_, Value<'heap>>) -> Result { - self - } - - fn finish(self) -> Option { - None - } -} - -impl<'heap> CompleteQueryPath<'heap> for EntityQueryPath<'heap> { - type PartialQueryPath = PartialEntityQueryPath<'heap>; -} - -pub(crate) fn traverse_into_field<'heap, P>( - path: Option

, - heap: &'heap Heap, - field: Symbol<'heap>, -) -> Result> -where - P: PartialQueryPath<'heap>, -{ - #[expect(clippy::option_if_let_else, reason = "readability")] - match path { - Some(path) => path.access_field(heap, field).map_err(Some), - None => P::from_field(heap, field).ok_or(None), - } -} - -pub(crate) fn traverse_into_index<'heap, P>( - path: Option

, - heap: &'heap Heap, - index: Cow<'_, Value<'heap>>, -) -> Result> -where - P: PartialQueryPath<'heap>, -{ - match path { - Some(path) => path.access_index(heap, index).map_err(Some), - None => P::from_index(heap, index).ok_or(None), - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) enum PartialEntityIdQueryPath { - WebId, - EntityUuid, - DraftId, -} - -impl<'heap> PartialQueryPath<'heap> for PartialEntityIdQueryPath { - type QueryPath = EntityQueryPath<'heap>; - - fn from_field(_: &'heap Heap, field: Symbol<'heap>) -> Option { - match field.as_constant()? { - sym::web_id::CONST => Some(Self::WebId), - sym::entity_uuid::CONST => Some(Self::EntityUuid), - sym::draft_id::CONST => Some(Self::DraftId), - _ => None, - } - } - - fn access_field(self, _: &'heap Heap, _: Symbol<'heap>) -> Result { - Err(self) - } - - fn from_index(_: &'heap Heap, _: Cow<'_, Value<'heap>>) -> Option { - None - } - - fn access_index(self, _: &'heap Heap, _: Cow<'_, Value<'heap>>) -> Result { - Err(self) - } - - fn finish(self) -> Option { - match self { - Self::WebId => Some(EntityQueryPath::WebId), - Self::EntityUuid => Some(EntityQueryPath::Uuid), - Self::DraftId => Some(EntityQueryPath::DraftId), - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) enum PartialEntityRecordIdPath { - EntityId(Option), - EditionId, -} - -impl<'heap> PartialQueryPath<'heap> for PartialEntityRecordIdPath { - type QueryPath = EntityQueryPath<'heap>; - - fn from_field(_: &'heap Heap, field: Symbol<'heap>) -> Option { - match field.as_constant()? { - sym::entity_id::CONST => Some(Self::EntityId(None)), - sym::edition_id::CONST => Some(Self::EditionId), - _ => None, - } - } - - fn access_field(self, heap: &'heap Heap, field: Symbol<'heap>) -> Result { - match self { - Self::EntityId(path) => traverse_into_field(path, heap, field) - .map(Some) - .map(Self::EntityId) - .map_err(Self::EntityId), - Self::EditionId => Err(self), - } - } - - fn from_index(_: &'heap Heap, _: Cow<'_, Value<'heap>>) -> Option { - None - } - - fn access_index(self, heap: &'heap Heap, index: Cow<'_, Value<'heap>>) -> Result { - match self { - Self::EntityId(path) => traverse_into_index(path, heap, index) - .map(Some) - .map(Self::EntityId) - .map_err(Self::EntityId), - Self::EditionId => Err(self), - } - } - - fn finish(self) -> Option { - match self { - Self::EntityId(Some(partial)) => partial.finish(), - Self::EntityId(None) => None, - Self::EditionId => Some(EntityQueryPath::EditionId), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) enum PartialLinkDataPath { - LeftEntityId(Option), - RightEntityId(Option), - LeftEntityConfidence, - LeftEntityProvenance, - RightEntityConfidence, - RightEntityProvenance, -} - -impl<'heap> PartialQueryPath<'heap> for PartialLinkDataPath { - type QueryPath = EntityQueryPath<'heap>; - - fn from_field(_: &'heap Heap, field: Symbol<'heap>) -> Option { - match field.as_constant()? { - sym::left_entity_id::CONST => Some(Self::LeftEntityId(None)), - sym::right_entity_id::CONST => Some(Self::RightEntityId(None)), - sym::left_entity_confidence::CONST => Some(Self::LeftEntityConfidence), - sym::left_entity_provenance::CONST => Some(Self::LeftEntityProvenance), - sym::right_entity_confidence::CONST => Some(Self::RightEntityConfidence), - sym::right_entity_provenance::CONST => Some(Self::RightEntityProvenance), - _ => None, - } - } - - fn access_field(self, heap: &'heap Heap, field: Symbol<'heap>) -> Result { - match self { - Self::LeftEntityId(path) => traverse_into_field(path, heap, field) - .map(Some) - .map(Self::LeftEntityId) - .map_err(Self::LeftEntityId), - Self::RightEntityId(path) => traverse_into_field(path, heap, field) - .map(Some) - .map(Self::RightEntityId) - .map_err(Self::RightEntityId), - Self::LeftEntityConfidence - | Self::LeftEntityProvenance - | Self::RightEntityConfidence - | Self::RightEntityProvenance => Err(self), - } - } - - fn from_index(_: &'heap Heap, _: Cow<'_, Value<'heap>>) -> Option { - None - } - - fn access_index(self, heap: &'heap Heap, index: Cow<'_, Value<'heap>>) -> Result { - match self { - Self::LeftEntityId(path) => traverse_into_index(path, heap, index) - .map(Some) - .map(Self::LeftEntityId) - .map_err(Self::LeftEntityId), - Self::RightEntityId(path) => traverse_into_index(path, heap, index) - .map(Some) - .map(Self::RightEntityId) - .map_err(Self::RightEntityId), - Self::LeftEntityConfidence - | Self::LeftEntityProvenance - | Self::RightEntityConfidence - | Self::RightEntityProvenance => Err(self), - } - } - - #[expect(clippy::match_same_arms, reason = "readability")] - fn finish(self) -> Option { - match self { - Self::LeftEntityId(Some(partial)) => Some(EntityQueryPath::EntityEdge { - edge_kind: KnowledgeGraphEdgeKind::HasLeftEntity, - path: Box::new(partial.finish()?), - direction: EdgeDirection::Outgoing, - }), - Self::LeftEntityId(None) => None, - - Self::RightEntityId(Some(partial)) => Some(EntityQueryPath::EntityEdge { - edge_kind: KnowledgeGraphEdgeKind::HasRightEntity, - path: Box::new(partial.finish()?), - direction: EdgeDirection::Outgoing, - }), - Self::RightEntityId(None) => None, - - Self::LeftEntityConfidence => Some(EntityQueryPath::LeftEntityConfidence), - Self::LeftEntityProvenance => Some(EntityQueryPath::LeftEntityProvenance), - Self::RightEntityConfidence => Some(EntityQueryPath::RightEntityConfidence), - Self::RightEntityProvenance => Some(EntityQueryPath::RightEntityProvenance), - } - } -} - -fn value_as_usize(value: &Value<'_>) -> Option { - match value { - Value::Primitive(Primitive::Integer(integer)) => integer.as_usize(), - Value::Primitive(Primitive::Float(float)) if let Some(integer) = float.as_integer() => { - integer.as_usize() - } - Value::Primitive(_) - | Value::Struct(_) - | Value::Tuple(_) - | Value::List(_) - | Value::Dict(_) - | Value::Opaque(_) => None, - } -} - -impl<'heap> PartialQueryPath<'heap> for JsonPath<'heap> { - type QueryPath = Self; - - fn from_field(_: &'heap Heap, field: Symbol<'heap>) -> Option { - Some(JsonPath::from_path_tokens(vec![PathToken::Field( - Cow::Borrowed(field.unwrap()), - )])) - } - - fn access_field(mut self, _: &'heap Heap, field: Symbol<'heap>) -> Result { - self.push(PathToken::Field(Cow::Borrowed(field.unwrap()))); - Ok(self) - } - - fn from_index(_: &'heap Heap, index: Cow>) -> Option { - let index = value_as_usize(&index)?; - - Some(JsonPath::from_path_tokens(vec![PathToken::Index(index)])) - } - - fn access_index(mut self, _: &'heap Heap, index: Cow>) -> Result { - let Some(index) = value_as_usize(&index) else { - return Err(self); - }; - - self.push(PathToken::Index(index)); - Ok(self) - } - - fn finish(self) -> Option { - Some(self) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) enum PartialEntityMetadataPath<'heap> { - RecordId(Option), - TemporalVersioning(Option), - Archived, - Confidence, - EntityTypeIds, - Provenance(Option>), - Properties(Option>), -} - -impl<'heap> PartialQueryPath<'heap> for PartialEntityMetadataPath<'heap> { - type QueryPath = EntityQueryPath<'heap>; - - fn from_field(_: &'heap Heap, field: Symbol<'heap>) -> Option { - match field.as_constant()? { - sym::record_id::CONST => Some(Self::RecordId(None)), - sym::temporal_versioning::CONST => Some(Self::TemporalVersioning(None)), - sym::archived::CONST => Some(Self::Archived), - sym::confidence::CONST => Some(Self::Confidence), - sym::entity_type_ids::CONST => Some(Self::EntityTypeIds), - sym::provenance::CONST => Some(Self::Provenance(None)), - sym::properties::CONST => Some(Self::Properties(None)), - _ => None, - } - } - - fn access_field(self, heap: &'heap Heap, field: Symbol<'heap>) -> Result { - match self { - Self::RecordId(path) => traverse_into_field(path, heap, field) - .map(Some) - .map(Self::RecordId) - .map_err(Self::RecordId), - Self::TemporalVersioning(path) => traverse_into_field(path, heap, field) - .map(Some) - .map(Self::TemporalVersioning) - .map_err(Self::TemporalVersioning), - Self::Provenance(path) => traverse_into_field(path, heap, field) - .map(Some) - .map(Self::Provenance) - .map_err(Self::Provenance), - Self::Properties(path) => traverse_into_field(path, heap, field) - .map(Some) - .map(Self::Properties) - .map_err(Self::Properties), - Self::Archived | Self::Confidence | Self::EntityTypeIds => Err(self), - } - } - - fn from_index(_: &'heap Heap, _: Cow<'_, Value<'heap>>) -> Option { - None - } - - fn access_index(self, heap: &'heap Heap, index: Cow<'_, Value<'heap>>) -> Result { - match self { - Self::RecordId(path) => traverse_into_index(path, heap, index) - .map(Some) - .map(Self::RecordId) - .map_err(Self::RecordId), - Self::Properties(path) => traverse_into_index(path, heap, index) - .map(Some) - .map(Self::Properties) - .map_err(Self::Properties), - Self::TemporalVersioning(_) - | Self::Archived - | Self::Confidence - | Self::EntityTypeIds - | Self::Provenance(_) => Err(self), - } - } - - #[expect(clippy::match_same_arms, reason = "readability")] - fn finish(self) -> Option { - match self { - Self::RecordId(Some(partial)) => partial.finish(), - Self::RecordId(None) => None, - - Self::TemporalVersioning(Some(partial)) => partial.finish(), - Self::TemporalVersioning(None) => None, - - Self::Archived => Some(EntityQueryPath::Archived), - Self::Confidence => Some(EntityQueryPath::EntityConfidence), - Self::EntityTypeIds => Some(EntityQueryPath::EntityTypeEdge { - edge_kind: hash_graph_store::subgraph::edges::SharedEdgeKind::IsOfType, - path: hash_graph_store::entity_type::EntityTypeQueryPath::VersionedUrl, - inheritance_depth: Some(0), - }), - - Self::Provenance(Some(partial)) => partial.finish(), - Self::Provenance(None) => None, - - Self::Properties(Some(partial)) => { - Some(EntityQueryPath::PropertyMetadata(Some(partial.finish()?))) - } - Self::Properties(None) => Some(EntityQueryPath::PropertyMetadata(None)), - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) enum PartialTemporalVersioningPath { - DecisionTime, - TransactionTime, -} - -impl<'heap> PartialQueryPath<'heap> for PartialTemporalVersioningPath { - type QueryPath = EntityQueryPath<'heap>; - - fn from_field(_: &'heap Heap, field: Symbol<'heap>) -> Option { - match field.as_constant()? { - sym::decision_time::CONST => Some(Self::DecisionTime), - sym::transaction_time::CONST => Some(Self::TransactionTime), - _ => None, - } - } - - fn access_field(self, _: &'heap Heap, _: Symbol<'heap>) -> Result { - Err(self) - } - - fn from_index(_: &'heap Heap, _: Cow<'_, Value<'heap>>) -> Option { - None - } - - fn access_index(self, _: &'heap Heap, _: Cow<'_, Value<'heap>>) -> Result { - Err(self) - } - - fn finish(self) -> Option { - match self { - Self::DecisionTime => Some(EntityQueryPath::DecisionTime), - Self::TransactionTime => Some(EntityQueryPath::TransactionTime), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) enum PartialEntityProvenancePath<'heap> { - Inferred(Option>), - Edition(Option>), -} - -impl<'heap> PartialQueryPath<'heap> for PartialEntityProvenancePath<'heap> { - type QueryPath = EntityQueryPath<'heap>; - - fn from_field(_: &'heap Heap, field: Symbol<'heap>) -> Option { - match field.as_constant()? { - sym::inferred::CONST => Some(Self::Inferred(None)), - sym::edition::CONST => Some(Self::Edition(None)), - _ => None, - } - } - - fn access_field(self, heap: &'heap Heap, field: Symbol<'heap>) -> Result { - match self { - Self::Inferred(path) => traverse_into_field(path, heap, field) - .map(Some) - .map(Self::Inferred) - .map_err(Self::Inferred), - Self::Edition(path) => traverse_into_field(path, heap, field) - .map(Some) - .map(Self::Edition) - .map_err(Self::Edition), - } - } - - fn from_index(_: &'heap Heap, _: Cow<'_, Value<'heap>>) -> Option { - None - } - - fn access_index(self, heap: &'heap Heap, index: Cow<'_, Value<'heap>>) -> Result { - match self { - Self::Inferred(path) => traverse_into_index(path, heap, index) - .map(Some) - .map(Self::Inferred) - .map_err(Self::Inferred), - Self::Edition(path) => traverse_into_index(path, heap, index) - .map(Some) - .map(Self::Edition) - .map_err(Self::Edition), - } - } - - fn finish(self) -> Option { - match self { - Self::Inferred(path) => { - Some(EntityQueryPath::Provenance(path.and_then(JsonPath::finish))) - } - Self::Edition(path) => Some(EntityQueryPath::EditionProvenance( - path.and_then(JsonPath::finish), - )), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) enum PartialEntityQueryPath<'heap> { - Properties(Option>), - LinkData(Option), - Metadata(Option>), -} - -impl<'heap> PartialQueryPath<'heap> for PartialEntityQueryPath<'heap> { - type QueryPath = EntityQueryPath<'heap>; - - fn from_field(_: &'heap Heap, field: Symbol<'heap>) -> Option { - match field.as_constant()? { - sym::properties::CONST => Some(Self::Properties(None)), - sym::link_data::CONST => Some(Self::LinkData(None)), - sym::metadata::CONST => Some(Self::Metadata(None)), - _ => None, - } - } - - fn access_field(self, heap: &'heap Heap, field: Symbol<'heap>) -> Result { - match self { - Self::Properties(path) => traverse_into_field(path, heap, field) - .map(Some) - .map(Self::Properties) - .map_err(Self::Properties), - Self::LinkData(path) => traverse_into_field(path, heap, field) - .map(Some) - .map(Self::LinkData) - .map_err(Self::LinkData), - Self::Metadata(path) => traverse_into_field(path, heap, field) - .map(Some) - .map(Self::Metadata) - .map_err(Self::Metadata), - } - } - - fn from_index(_: &'heap Heap, _: Cow>) -> Option { - None - } - - fn access_index(self, heap: &'heap Heap, index: Cow>) -> Result { - match self { - Self::Properties(path) => traverse_into_index(path, heap, index) - .map(Some) - .map(Self::Properties) - .map_err(Self::Properties), - Self::LinkData(path) => traverse_into_index(path, heap, index) - .map(Some) - .map(Self::LinkData) - .map_err(Self::LinkData), - Self::Metadata(path) => traverse_into_index(path, heap, index) - .map(Some) - .map(Self::Metadata) - .map_err(Self::Metadata), - } - } - - #[expect(clippy::match_same_arms, reason = "readability")] - fn finish(self) -> Option { - match self { - Self::Properties(Some(partial)) => { - Some(EntityQueryPath::Properties(Some(partial.finish()?))) - } - Self::Properties(None) => Some(EntityQueryPath::Properties(None)), - - Self::LinkData(Some(partial)) => partial.finish(), - Self::LinkData(None) => None, - - Self::Metadata(Some(partial)) => partial.finish(), - Self::Metadata(None) => None, - } - } -} diff --git a/libs/@local/hashql/eval/src/graph/read/sink.rs b/libs/@local/hashql/eval/src/graph/read/sink.rs deleted file mode 100644 index e95520bb5c8..00000000000 --- a/libs/@local/hashql/eval/src/graph/read/sink.rs +++ /dev/null @@ -1,72 +0,0 @@ -use hash_graph_store::filter::{Filter, QueryRecord}; - -/// A mutable sink for building filter expressions during query evaluation. -pub(crate) enum FilterSink<'a, 'b, R: QueryRecord> { - /// Collects filters that will be combined with AND logic. - And(&'a mut Vec>), - /// Collects filters that will be combined with OR logic. - Or(&'a mut Vec>), - /// Represents a failed state where filters are discarded. - Fail, -} - -impl<'a, 'b, R: QueryRecord> FilterSink<'a, 'b, R> { - /// Create a sink from a result. - /// - /// Converts `Ok` to `And` and `Err` to `Fail`. - pub(crate) const fn from_result(result: &'a mut Result>, E>) -> Self { - match result { - Ok(filters) => FilterSink::And(filters), - Err(_) => FilterSink::Fail, - } - } - - /// Creates an AND filter sink. - /// - /// In the case that the existing sink is already an AND sink, the same vector is returned, - /// otherwise a new vector is created as a child. - /// - /// If the sink persists the `Fail` state. - pub(crate) fn and(&mut self) -> FilterSink<'_, 'b, R> { - match self { - FilterSink::And(inner) => FilterSink::And(inner), - FilterSink::Or(inner) => { - let Filter::All(inner) = inner.push_mut(Filter::All(Vec::new())) else { - unreachable!() - }; - - FilterSink::And(inner) - } - FilterSink::Fail => FilterSink::Fail, - } - } - - /// Creates an OR filter sink. - /// - /// In the case that the existing sink is already an OR sink, the same vector is returned, - /// otherwise a new vector is created as a child. - pub(crate) fn or(&mut self) -> FilterSink<'_, 'b, R> { - match self { - FilterSink::And(inner) => { - let Filter::Any(inner) = inner.push_mut(Filter::Any(Vec::new())) else { - unreachable!() - }; - - FilterSink::Or(inner) - } - FilterSink::Or(inner) => FilterSink::Or(inner), - FilterSink::Fail => FilterSink::Fail, - } - } - - /// Pushes a filter into the sink. - /// - /// Pushes to the current vector, corresponding to either AND or OR. In the case of a failed - /// state, the filter is discarded. - pub(crate) fn push(&mut self, filter: Filter<'b, R>) { - match self { - FilterSink::And(inner) | FilterSink::Or(inner) => inner.push(filter), - FilterSink::Fail => {} - } - } -} diff --git a/libs/@local/hashql/eval/src/lib.rs b/libs/@local/hashql/eval/src/lib.rs index fbadfc201b7..ec19faed689 100644 --- a/libs/@local/hashql/eval/src/lib.rs +++ b/libs/@local/hashql/eval/src/lib.rs @@ -12,7 +12,6 @@ // Library Features allocator_api, iter_array_chunks, - iterator_try_collect, maybe_uninit_fill, impl_trait_in_assoc_type, try_blocks @@ -25,8 +24,6 @@ extern crate alloc; pub mod context; pub mod error; -#[cfg(feature = "graph")] -pub mod graph; pub mod intern; pub mod orchestrator; pub mod postgres; diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/.spec.toml b/libs/@local/hashql/eval/tests/ui/graph/read/entity/.spec.toml deleted file mode 100644 index cb4dba79907..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/.spec.toml +++ /dev/null @@ -1 +0,0 @@ -suite = "eval/graph/read/entity" diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/arithmetic-comparisons-and.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/arithmetic-comparisons-and.jsonc deleted file mode 100644 index 3f92710595d..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/arithmetic-comparisons-and.jsonc +++ /dev/null @@ -1,20 +0,0 @@ -//@ run: pass -//@ description: Test arithmetic comparison operations (>, >=, <, <=) with numeric literals -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["&&", - [">", {"#literal": 1}, {"#literal": 2}], - ["&&", - [">=", {"#literal": 1}, {"#literal": 2}], - ["&&", - ["<", {"#literal": 1}, {"#literal": 2}], - ["<=", {"#literal": 1}, {"#literal": 2}] - ] - ] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/arithmetic-comparisons-and.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/arithmetic-comparisons-and.stdout deleted file mode 100644 index cfa00703cf4..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/arithmetic-comparisons-and.stdout +++ /dev/null @@ -1,155 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %10 = ::graph::tmp::decision_time_now(), - %11 = %10() - in - %11, - %2 = thunk -> let %12 = 1 > 2 in %12, - %8 = thunk -> - let %13 = %2(), - %14 = if %13 - then - let %3 = 1 >= 2, - %7 = if %3 - then - let %4 = 1 < 2, - %6 = if %4 - then let %5 = 1 <= 2 in %5 - else false - in - %6 - else false - in - %7 - else false - in - %14, - %9 = thunk -> - let %15 = %1(), - %17 = ::graph::head::entities(%15) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %16 = %8() in %16 - ) - |> ::graph::tail::collect - in - %17 -in -%9 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Greater( - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 1 (1 digits, 1 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 2 (1 digits, 2 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - ), - GreaterOrEqual( - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 1 (1 digits, 1 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 2 (1 digits, 2 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - ), - Less( - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 1 (1 digits, 1 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 2 (1 digits, 2 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - ), - LessOrEqual( - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 1 (1 digits, 1 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 2 (1 digits, 2 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/arithmetic-comparisons-or.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/arithmetic-comparisons-or.jsonc deleted file mode 100644 index 6dfc94df30f..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/arithmetic-comparisons-or.jsonc +++ /dev/null @@ -1,20 +0,0 @@ -//@ run: pass -//@ description: Test arithmetic comparison operations (>, >=, <, <=) with numeric literals -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["||", - [">", {"#literal": 1}, {"#literal": 2}], - ["||", - [">=", {"#literal": 1}, {"#literal": 2}], - ["||", - ["<", {"#literal": 1}, {"#literal": 2}], - ["<=", {"#literal": 1}, {"#literal": 2}] - ] - ] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/arithmetic-comparisons-or.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/arithmetic-comparisons-or.stdout deleted file mode 100644 index 655b53fa3c6..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/arithmetic-comparisons-or.stdout +++ /dev/null @@ -1,159 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %10 = ::graph::tmp::decision_time_now(), - %11 = %10() - in - %11, - %2 = thunk -> let %12 = 1 > 2 in %12, - %8 = thunk -> - let %13 = %2(), - %14 = if %13 - then true - else - let %3 = 1 >= 2, - %7 = if %3 - then true - else - let %4 = 1 < 2, - %6 = if %4 - then true - else let %5 = 1 <= 2 in %5 - in - %6 - in - %7 - in - %14, - %9 = thunk -> - let %15 = %1(), - %17 = ::graph::head::entities(%15) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %16 = %8() in %16 - ) - |> ::graph::tail::collect - in - %17 -in -%9 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Any( - [ - Greater( - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 1 (1 digits, 1 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 2 (1 digits, 2 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - ), - GreaterOrEqual( - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 1 (1 digits, 1 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 2 (1 digits, 2 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - ), - Less( - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 1 (1 digits, 1 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 2 (1 digits, 2 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - ), - LessOrEqual( - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 1 (1 digits, 1 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 2 (1 digits, 2 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - ), - ], - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/boolean-literal.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/boolean-literal.jsonc deleted file mode 100644 index 89a08aa3b0c..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/boolean-literal.jsonc +++ /dev/null @@ -1,11 +0,0 @@ -//@ run: pass -//@ description: Test boolean literal values in filter expressions -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - {"#literal": true} - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/boolean-literal.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/boolean-literal.stdout deleted file mode 100644 index d51096d9ac3..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/boolean-literal.stdout +++ /dev/null @@ -1,35 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %3 = ::graph::tmp::decision_time_now(), - %4 = %3() - in - %4, - %2 = thunk -> - let %5 = %1(), - %6 = ::graph::head::entities(%5) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> true) - |> ::graph::tail::collect - in - %6 -in -%2 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/call-identity.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/call-identity.jsonc deleted file mode 100644 index bbfb7bff1f7..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/call-identity.jsonc +++ /dev/null @@ -1,18 +0,0 @@ -//@ run: fail -//@ description: Function calls not supported in filter expressions -// prettier-ignore -["let", "identity", ["fn", {"#tuple": ["T"]}, {"#struct": {"value": "T"}}, "T", "value"], -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - ["identity", "vertex.metadata.record_id.entity_id.entity_uuid"], - //~^ ERROR Function call not supported here - ["::graph::types::knowledge::entity::EntityUuid", - ["::core::uuid::Uuid", { "#literal": "e2851dbb-7376-4959-9bca-f72cafc4448f" }] - ] - ] - ] - ] -]] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/call-identity.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/call-identity.stderr deleted file mode 100644 index 0a9deb20c50..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/call-identity.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[graph-read-compiler::call-unsupported]: Function calls not supported - ╭▸ - 9 │ ┌ ["==", -10 │ │ ["identity", "vertex.metadata.record_id.entity_id.entity_uuid"], - │ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Function call not supported here -11 │ │ //~^ ERROR Function call not supported here -12 │ │ ["::graph::types::knowledge::entity::EntityUuid", - ‡ │ -15 │ │ ] - │ └───────┘ ... within this filter expression - │ - ├ help: Filter expressions do not currently support function calls. Move the function call outside the filter expression, assign the result to a variable, and use that variable in the filter instead. - ╰ note: Function calls in filter expressions are not yet implemented. This feature may be added in future versions for specific categories of pure functions. Progress is tracked in https://linear.app/hash/issue/H-4913/hashql-implement-vm. \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/complex-object-error.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/complex-object-error.jsonc deleted file mode 100644 index 6e6495ec3fa..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/complex-object-error.jsonc +++ /dev/null @@ -1,16 +0,0 @@ -//@ run: fail -//@ description: Complex objects cannot be queried directly in filter expressions -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - "vertex.metadata.record_id.entity_id", - //~^ ERROR Cannot query against this complex object - "vertex.metadata.record_id.entity_id" - //~^ ERROR Cannot query against this complex object - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/complex-object-error.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/complex-object-error.stderr deleted file mode 100644 index b687aef6b27..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/complex-object-error.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[graph-read-compiler::path-conversion]: Cannot query against complex object - ╭▸ - 8 │ ┌ ["==", - 9 │ │ "vertex.metadata.record_id.entity_id", -10 │ │ //~^ ERROR Cannot query against this complex object -11 │ │ "vertex.metadata.record_id.entity_id" - │ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Cannot query against this complex object -12 │ │ //~^ ERROR Cannot query against this complex object -13 │ │ ] - │ └───────┘ ... within this filter expression - │ - ├ help: Filter expressions can only query against simple scalar properties that map to database columns, not complex objects. Use individual properties of the object instead (e.g., `entity.metadata.record_id.entity_id.entity_uuid` instead of `entity.metadata.record_id`). - ╰ note: This is a temporary limitation of the current query compiler. Support for querying against complex objects in filter expressions is being tracked in https://linear.app/hash/issue/H-4911/hashql-allow-for-querying-against-complex-objects. - -error[graph-read-compiler::path-conversion]: Cannot query against complex object - ╭▸ - 8 │ ┌ ["==", - 9 │ │ "vertex.metadata.record_id.entity_id", - │ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Cannot query against this complex object -10 │ │ //~^ ERROR Cannot query against this complex object -11 │ │ "vertex.metadata.record_id.entity_id" -12 │ │ //~^ ERROR Cannot query against this complex object -13 │ │ ] - │ └───────┘ ... within this filter expression - │ - ├ help: Filter expressions can only query against simple scalar properties that map to database columns, not complex objects. Use individual properties of the object instead (e.g., `entity.metadata.record_id.entity_id.entity_uuid` instead of `entity.metadata.record_id`). - ╰ note: This is a temporary limitation of the current query compiler. Support for querying against complex objects in filter expressions is being tracked in https://linear.app/hash/issue/H-4911/hashql-allow-for-querying-against-complex-objects. \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/computed-path-indexing-error.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/computed-path-indexing-error.jsonc deleted file mode 100644 index 1b731a38bb3..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/computed-path-indexing-error.jsonc +++ /dev/null @@ -1,18 +0,0 @@ -//@ run: fail -//@ description: Test error when using computed paths for indexing in filter expressions -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - {"#literal": 2}, - ["[]", - ["input", "user_ids", "Dict<::graph::types::knowledge::entity::EntityUuid, Integer>"], - "vertex.metadata.record_id.entity_id.entity_uuid" - //~^ ERROR Cannot use computed value as index - ] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/computed-path-indexing-error.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/computed-path-indexing-error.stderr deleted file mode 100644 index 015461c77a9..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/computed-path-indexing-error.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[graph-read-compiler::path-indexing-unsupported]: Indexing through traversal paths not supported - ╭▸ - 8 │ ┌ ["==", - 9 │ │ {"#literal": 2}, -10 │ │ ["[]", -11 │ │ ["input", "user_ids", "Dict<::graph::types::knowledge::entity::EntityUuid, Integer>"], - │ │ ───────────────────────────────────────────────────────────────────────────────────── ... when indexing this value -12 │ │ "vertex.metadata.record_id.entity_id.entity_uuid" - │ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Cannot use computed value as index - ‡ │ -15 │ │ ] - │ └───────┘ ... within this filter expression - │ - ├ help: Dynamic indexing using database values is not currently supported in filter expressions. Use a literal value like `["key"]` or `[0]` instead of computed values like `[entity.id]`. This limitation exists because such operations are complex to translate into database queries. - ╰ note: This is a temporary limitation of the database query compiler. Support for dynamic indexing using computed values in filter expressions is being tracked in https://linear.app/hash/issue/H-4914/hashql-support-indexing-into-collections-based-on-query-paths. \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/constructor-call-none.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/constructor-call-none.jsonc deleted file mode 100644 index 521b9496e92..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/constructor-call-none.jsonc +++ /dev/null @@ -1,14 +0,0 @@ -//@ run: pass -//@ description: Test constructor calls with None type in filter expressions -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - ["None"], - ["None"] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/constructor-call-none.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/constructor-call-none.stdout deleted file mode 100644 index 7c33f3c43dc..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/constructor-call-none.stdout +++ /dev/null @@ -1,55 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %8 = ::graph::tmp::decision_time_now(), - %9 = %8() - in - %9, - %2 = thunk -> let %10 = ::core::option::None in %10, - %3 = thunk -> let %11 = ::core::option::None in %11, - %4 = thunk -> - let %12 = %2(), - %13 = %12() - in - %13, - %5 = thunk -> - let %14 = %3(), - %15 = %14() - in - %15, - %6 = thunk -> - let %16 = %4(), - %17 = %5(), - %18 = %16 == %17 - in - %18, - %7 = thunk -> - let %19 = %1(), - %21 = ::graph::head::entities(%19) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %20 = %6() in %20 - ) - |> ::graph::tail::collect - in - %21 -in -%7 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Parameter { - parameter: Any( - Null, - ), - convert: None, - }, - Parameter { - parameter: Any( - Null, - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/equality-comparison.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/equality-comparison.jsonc deleted file mode 100644 index 03f50f7fbce..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/equality-comparison.jsonc +++ /dev/null @@ -1,14 +0,0 @@ -//@ run: pass -//@ description: Test equality (==) and inequality (!=) comparison operators -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["||", - ["==", {"#literal": true}, {"#literal": false}], - ["!=", {"#literal": true}, {"#literal": false}] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/equality-comparison.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/equality-comparison.stdout deleted file mode 100644 index 9e951a07522..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/equality-comparison.stdout +++ /dev/null @@ -1,63 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %6 = ::graph::tmp::decision_time_now(), - %7 = %6() - in - %7, - %2 = thunk -> let %8 = true == false in %8, - %4 = thunk -> - let %9 = %2(), - %10 = if %9 - then true - else let %3 = true != false in %3 - in - %10, - %5 = thunk -> - let %11 = %1(), - %13 = ::graph::head::entities(%11) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %12 = %4() in %12 - ) - |> ::graph::tail::collect - in - %13 -in -%5 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Any( - [ - Equal( - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - Parameter { - parameter: Boolean( - false, - ), - convert: None, - }, - ), - NotEqual( - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - Parameter { - parameter: Boolean( - false, - ), - convert: None, - }, - ), - ], - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-access-struct.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-access-struct.jsonc deleted file mode 100644 index 756ea98cb76..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-access-struct.jsonc +++ /dev/null @@ -1,14 +0,0 @@ -//@ run: pass -//@ description: Field access on values should succeed -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - [".", {"#struct": {"a": { "#literal": 1 }}}, "a"], - {"#literal": 1} - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-access-struct.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-access-struct.stdout deleted file mode 100644 index ee451861a44..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-access-struct.stdout +++ /dev/null @@ -1,57 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %5 = ::graph::tmp::decision_time_now(), - %6 = %5() - in - %6, - %2 = thunk -> let %7 = (a: 1) in %7, - %3 = thunk -> - let %8 = %2(), - %9 = %8.a == 1 - in - %9, - %4 = thunk -> - let %10 = %1(), - %12 = ::graph::head::entities(%10) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %11 = %3() in %11 - ) - |> ::graph::tail::collect - in - %12 -in -%4 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 1 (1 digits, 1 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 1 (1 digits, 1 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-access-tuple.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-access-tuple.jsonc deleted file mode 100644 index 33593ee82ec..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-access-tuple.jsonc +++ /dev/null @@ -1,14 +0,0 @@ -//@ run: pass -//@ description: Field access on values should succeed -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - [".", {"#tuple": [{ "#literal": 1 }]}, { "#literal": 0 }], - {"#literal": 1} - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-access-tuple.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-access-tuple.stdout deleted file mode 100644 index 0f9c4a324d9..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-access-tuple.stdout +++ /dev/null @@ -1,57 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %5 = ::graph::tmp::decision_time_now(), - %6 = %5() - in - %6, - %2 = thunk -> let %7 = (1,) in %7, - %3 = thunk -> - let %8 = %2(), - %9 = %8.0 == 1 - in - %9, - %4 = thunk -> - let %10 = %1(), - %12 = ::graph::head::entities(%10) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %11 = %3() in %11 - ) - |> ::graph::tail::collect - in - %12 -in -%4 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 1 (1 digits, 1 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - Parameter { - parameter: Decimal( - Real( - FBig { - significand: 1 (1 digits, 1 bits), - exponent: 10 ^ 0, - precision: 1, - rounding: HalfAway, - }, - ), - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-dict-key.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-dict-key.jsonc deleted file mode 100644 index 09a6bd2a85d..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-dict-key.jsonc +++ /dev/null @@ -1,18 +0,0 @@ -//@ run: fail -//@ description: Paths inside data constructs are unsupported -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - ["[]", - {"#dict": [["vertex.metadata.record_id.entity_id.entity_uuid", {"#literal": "a"}]]}, - //~^ ERROR path expressions are not supported in data constructs - ["::graph::types::knowledge::entity::EntityUuid", ["::core::uuid::Uuid", {"#literal": "a"}]] - ], - "vertex.metadata.record_id.entity_id.entity_uuid" - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-dict-key.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-dict-key.stderr deleted file mode 100644 index 10261ec0ebd..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-dict-key.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error[graph-read-compiler::path-in-data-construct-unsupported]: Path in data construct unsupported - ╭▸ -10 │ {"#dict": [["vertex.metadata.record_id.entity_id.entity_uuid", {"#literal": "a"}]]}, - │ ━━━━━━ path expressions are not supported in data constructs - │ - ├ help: rewrite the logic to avoid path expressions in data constructs - ╰ note: path expressions within complex data constructs (tuples, lists, dictionaries, structs) are not supported by the current query compiler. This is a fundamental limitation that will be addressed in the next-generation compiler \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-dict-value.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-dict-value.jsonc deleted file mode 100644 index ceb60dd5927..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-dict-value.jsonc +++ /dev/null @@ -1,15 +0,0 @@ -//@ run: fail -//@ description: Paths inside data constructs are unsupported -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - ["[]", {"#dict": {"a": "vertex.metadata.record_id.entity_id.entity_uuid"}}, {"#literal": "a"}], - //~^ ERROR path expressions are not supported in data constructs - "vertex.metadata.record_id.entity_id.entity_uuid" - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-dict-value.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-dict-value.stderr deleted file mode 100644 index a8b654b103d..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-dict-value.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error[graph-read-compiler::path-in-data-construct-unsupported]: Path in data construct unsupported - ╭▸ -9 │ ["[]", {"#dict": {"a": "vertex.metadata.record_id.entity_id.entity_uuid"}}, {"#literal": "a"}], - │ ━━━━━━ path expressions are not supported in data constructs - │ - ├ help: rewrite the logic to avoid path expressions in data constructs - ╰ note: path expressions within complex data constructs (tuples, lists, dictionaries, structs) are not supported by the current query compiler. This is a fundamental limitation that will be addressed in the next-generation compiler \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-list.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-list.jsonc deleted file mode 100644 index 3d7a94d26e7..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-list.jsonc +++ /dev/null @@ -1,15 +0,0 @@ -//@ run: fail -//@ description: Paths inside data constructs are unsupported -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - ["[]", {"#list": ["vertex.metadata.record_id.entity_id.entity_uuid"]}, { "#literal": 0 }], - //~^ ERROR path expressions are not supported in data constructs - "vertex.metadata.record_id.entity_id.entity_uuid" - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-list.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-list.stderr deleted file mode 100644 index e731890cd1d..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-list.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error[graph-read-compiler::path-in-data-construct-unsupported]: Path in data construct unsupported - ╭▸ -9 │ ["[]", {"#list": ["vertex.metadata.record_id.entity_id.entity_uuid"]}, { "#literal": 0 }], - │ ━━━━━━ path expressions are not supported in data constructs - │ - ├ help: rewrite the logic to avoid path expressions in data constructs - ╰ note: path expressions within complex data constructs (tuples, lists, dictionaries, structs) are not supported by the current query compiler. This is a fundamental limitation that will be addressed in the next-generation compiler \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-struct-entry.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-struct-entry.jsonc deleted file mode 100644 index 14fc38dfe05..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-struct-entry.jsonc +++ /dev/null @@ -1,15 +0,0 @@ -//@ run: fail -//@ description: Mentions of the variable inside data constructs are unsupported -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - [".", [".", [".", [".", [".", {"#struct": {"a": "vertex"}}, "a"], "metadata"], "record_id"], "entity_id"], "entity_uuid"], - //~^ ERROR path expressions are not supported in data constructs - "vertex.metadata.record_id.entity_id.entity_uuid" - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-struct-entry.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-struct-entry.stderr deleted file mode 100644 index 88ba6294db9..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-struct-entry.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error[graph-read-compiler::path-in-data-construct-unsupported]: Path in data construct unsupported - ╭▸ -9 │ [".", [".", [".", [".", [".", {"#struct": {"a": "vertex"}}, "a"], "metadata"], "record_id"], "entity_id"], "entity_uuid"], - │ ━━━━━━ path expressions are not supported in data constructs - │ - ├ help: rewrite the logic to avoid path expressions in data constructs - ╰ note: path expressions within complex data constructs (tuples, lists, dictionaries, structs) are not supported by the current query compiler. This is a fundamental limitation that will be addressed in the next-generation compiler \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-struct.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-struct.jsonc deleted file mode 100644 index 5e56de0632e..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-struct.jsonc +++ /dev/null @@ -1,15 +0,0 @@ -//@ run: fail -//@ description: Paths inside data constructs are unsupported -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - [".", {"#struct": {"a": "vertex.metadata.record_id.entity_id.entity_uuid"}}, "a"], - //~^ ERROR path expressions are not supported in data constructs - "vertex.metadata.record_id.entity_id.entity_uuid" - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-struct.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-struct.stderr deleted file mode 100644 index 84a2454f9b8..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-struct.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error[graph-read-compiler::path-in-data-construct-unsupported]: Path in data construct unsupported - ╭▸ -9 │ [".", {"#struct": {"a": "vertex.metadata.record_id.entity_id.entity_uuid"}}, "a"], - │ ━━━━━━ path expressions are not supported in data constructs - │ - ├ help: rewrite the logic to avoid path expressions in data constructs - ╰ note: path expressions within complex data constructs (tuples, lists, dictionaries, structs) are not supported by the current query compiler. This is a fundamental limitation that will be addressed in the next-generation compiler \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-tuple.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-tuple.jsonc deleted file mode 100644 index 806f7e9a819..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-tuple.jsonc +++ /dev/null @@ -1,15 +0,0 @@ -//@ run: fail -//@ description: Paths inside data constructs are unsupported -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - [".", {"#tuple": ["vertex.metadata.record_id.entity_id.entity_uuid"]}, { "#literal": 0 }], - //~^ ERROR path expressions are not supported in data constructs - "vertex.metadata.record_id.entity_id.entity_uuid" - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-tuple.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-tuple.stderr deleted file mode 100644 index 9db41195e8a..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/field-in-tuple.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error[graph-read-compiler::path-in-data-construct-unsupported]: Path in data construct unsupported - ╭▸ -9 │ [".", {"#tuple": ["vertex.metadata.record_id.entity_id.entity_uuid"]}, { "#literal": 0 }], - │ ━━━━━━ path expressions are not supported in data constructs - │ - ├ help: rewrite the logic to avoid path expressions in data constructs - ╰ note: path expressions within complex data constructs (tuples, lists, dictionaries, structs) are not supported by the current query compiler. This is a fundamental limitation that will be addressed in the next-generation compiler \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/if-filter-expr.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/if-filter-expr.jsonc deleted file mode 100644 index 19775b76b99..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/if-filter-expr.jsonc +++ /dev/null @@ -1,18 +0,0 @@ -//@ run: fail -//@ description: Test if filter with branch fails -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - "vertex.metadata.record_id.entity_id.entity_uuid", - ["if", {"#literal": true}, - "vertex.metadata.record_id.entity_id.entity_uuid", - "vertex.metadata.record_id.entity_id.entity_uuid" - ] - //~^ ERROR conditional expressions are not supported in filter contexts - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/if-filter-expr.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/if-filter-expr.stderr deleted file mode 100644 index 81a40f482bf..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/if-filter-expr.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[graph-read-compiler::branch-unsupported]: Branch construct unsupported - ╭▸ -10 │ ┏ ["if", {"#literal": true}, -11 │ ┃ "vertex.metadata.record_id.entity_id.entity_uuid", -12 │ ┃ "vertex.metadata.record_id.entity_id.entity_uuid" -13 │ ┃ ] - │ ┗━━━━━━━━━┛ conditional expressions are not supported in filter contexts - │ - ├ help: rewrite the logic to avoid conditional statements - ├ note: conditional statements (`if`/`else`) are not supported by the current query compiler. This is a fundamental limitation that will be addressed in the next-generation compiler - ╰ note: conditional logic must be handled outside the query or using other language constructs \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/if-filter.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/if-filter.jsonc deleted file mode 100644 index 376b951cac4..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/if-filter.jsonc +++ /dev/null @@ -1,11 +0,0 @@ -//@ run: pass -//@ description: Test if filter with branch fails -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["if", {"#literal": true}, {"#literal": true}, {"#literal": false}] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/if-filter.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/if-filter.stdout deleted file mode 100644 index 1f1682697fe..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/if-filter.stdout +++ /dev/null @@ -1,56 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %4 = ::graph::tmp::decision_time_now(), - %5 = %4() - in - %5, - %2 = thunk -> let %6 = if true then true else false in %6, - %3 = thunk -> - let %7 = %1(), - %9 = ::graph::head::entities(%7) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %8 = %2() in %8 - ) - |> ::graph::tail::collect - in - %9 -in -%3 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Any( - [ - Equal( - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - ), - Equal( - Parameter { - parameter: Boolean( - false, - ), - convert: None, - }, - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - ), - ], - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-field-access.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-field-access.jsonc deleted file mode 100644 index 8d90be06e07..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-field-access.jsonc +++ /dev/null @@ -1,17 +0,0 @@ -//@ run: pass -//@ description: Test field access on input parameter values -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - "vertex.metadata.record_id.entity_id.entity_uuid", - [".", - ["input", "user", {"#struct": {"id": "::graph::types::knowledge::entity::EntityUuid"}}], - "id" - ] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-field-access.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-field-access.stdout deleted file mode 100644 index 6178ff59e34..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-field-access.stdout +++ /dev/null @@ -1,38 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %5 = ::graph::tmp::decision_time_now(), - %6 = %5() - in - %6, - %2 = thunk -> let %7 = $user in %7, - %4 = thunk -> - let %8 = %1(), - %10 = ::graph::head::entities(%8) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %9 = %2(), - %3 = vertex:0.metadata.record_id.entity_id.entity_uuid == %9.id - in - %3 - ) - |> ::graph::tail::collect - in - %10 -in -%4 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Path { - path: Uuid, - }, - Parameter { - parameter: Text( - "e2851dbb-7376-4959-9bca-f72cafc4448f", - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-index-access.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-index-access.jsonc deleted file mode 100644 index b579e9bee9b..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-index-access.jsonc +++ /dev/null @@ -1,17 +0,0 @@ -//@ run: pass -//@ description: Test index access on input parameter values -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - ["as", "vertex.metadata.record_id.entity_id.entity_uuid", ["|", "::graph::types::knowledge::entity::EntityUuid", "Null"]], - ["[]", - ["input", "user_ids", "List<::graph::types::knowledge::entity::EntityUuid>"], - {"#literal": 0} - ] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-index-access.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-index-access.stdout deleted file mode 100644 index 0a130839985..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-index-access.stdout +++ /dev/null @@ -1,40 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %6 = ::graph::tmp::decision_time_now(), - %7 = %6() - in - %7, - %2 = thunk -> let %8 = $user_ids in %8, - %3 = thunk -> 0, - %5 = thunk -> - let %9 = %1(), - %12 = ::graph::head::entities(%9) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %10 = %2(), - %11 = %3(), - %4 = vertex:0.metadata.record_id.entity_id.entity_uuid == %10[%11] - in - %4 - ) - |> ::graph::tail::collect - in - %12 -in -%5 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Path { - path: Uuid, - }, - Parameter { - parameter: Text( - "e2851dbb-7376-4959-9bca-f72cafc4448f", - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-parameter.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-parameter.jsonc deleted file mode 100644 index fdeac99dfaf..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-parameter.jsonc +++ /dev/null @@ -1,14 +0,0 @@ -//@ run: pass -//@ description: Test input parameter usage in entity filter expressions -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - "vertex.metadata.record_id.entity_id.entity_uuid", - ["input", "user_id", "::graph::types::knowledge::entity::EntityUuid"] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-parameter.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-parameter.stdout deleted file mode 100644 index ac94cedaa27..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/input-parameter.stdout +++ /dev/null @@ -1,38 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %5 = ::graph::tmp::decision_time_now(), - %6 = %5() - in - %6, - %2 = thunk -> let %7 = $user_id in %7, - %4 = thunk -> - let %8 = %1(), - %10 = ::graph::head::entities(%8) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %9 = %2(), - %3 = vertex:0.metadata.record_id.entity_id.entity_uuid == %9 - in - %3 - ) - |> ::graph::tail::collect - in - %10 -in -%4 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Path { - path: Uuid, - }, - Parameter { - parameter: Text( - "e2851dbb-7376-4959-9bca-f72cafc4448f", - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-field-access-error.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-field-access-error.jsonc deleted file mode 100644 index 6def4aa9358..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-field-access-error.jsonc +++ /dev/null @@ -1,20 +0,0 @@ -//@ run: fail -//@ description: Test internal compiler error on invalid field access -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - "vertex.metadata.record_id.entity_id.entity_uuid", - // This is one of the few times where we can actually test if we hit a compiler error, - // because we don't typecheck the input in this test (yet) - [".", - ["input", "user", {"#struct": {"ID": "::graph::types::knowledge::entity::EntityUuid"}}], - "ID" - //~^ INTERNAL COMPILER ERROR Field access for `ID` failed unexpectedly - ] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-field-access-error.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-field-access-error.stderr deleted file mode 100644 index c4ea1fa086b..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-field-access-error.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[graph-read-compiler::field-access-internal-error]: Internal error during field access - ╭▸ -13 │ ["input", "user", {"#struct": {"ID": "::graph::types::knowledge::entity::EntityUuid"}}], - │ ─────────────────────────────────────────────────────────────────────────────────────── ... on this value -14 │ "ID" - │ ━━ Field access for `ID` failed unexpectedly - │ - ├ help: This is an internal compiler error. The field access should have been validated by the type checker, but the operation failed during compilation. Please report this as a bug with the code that triggered this error. - ├ note: This error indicates a bug in the type checker or compiler. The field access was expected to succeed based on type information, but failed during evaluation. - ├ note: Internal error that occurred: Field `ID` not found - ├ help: This is a bug in the compiler, not an issue with your code. - ├ help: Please report this issue along with a minimal code example that reproduces the error. - ╰ note: Internal compiler errors indicate a bug in the compiler itself that needs to be fixed. - - We would appreciate if you could file a GitHub or Linear issue and reference this error. - - When reporting this issue, please include your query, any relevant type definitions, and the complete error message shown above. \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-index-access-error.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-index-access-error.jsonc deleted file mode 100644 index 6c18f653947..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-index-access-error.jsonc +++ /dev/null @@ -1,20 +0,0 @@ -//@ run: fail -//@ description: Test internal compiler error on invalid index access -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - // This is one of the few times where we can actually test if we hit a compiler error, - // because we don't typecheck the input in this test (yet) - ["==", - {"#literal": null}, - ["[]", - ["input", "user_ids", "Dict"], - {"#literal": "abc"} - //~^ INTERNAL COMPILER ERROR Index access failed unexpectedly - ] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-index-access-error.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-index-access-error.stderr deleted file mode 100644 index 951c1e9818d..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-index-access-error.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[graph-read-compiler::index-access-internal-error]: Internal error during index access - ╭▸ -13 │ ["input", "user_ids", "Dict"], - │ ──────────────────────────────────────────────────────────────────────────────────── ... on this value -14 │ {"#literal": "abc"} - │ ━━━━━ Index access failed unexpectedly - │ - ├ help: This is an internal compiler error. The index access should have been validated by the type checker, but the operation failed during compilation. Please report this as a bug with the code that triggered this error. - ├ note: This error indicates a bug in the type checker or compiler. The index access was expected to succeed based on type information, but failed during evaluation. - ├ note: Internal error that occurred: Unable to access list with index type `string` - ├ help: This is a bug in the compiler, not an issue with your code. - ├ help: Please report this issue along with a minimal code example that reproduces the error. - ╰ note: Internal compiler errors indicate a bug in the compiler itself that needs to be fixed. - - We would appreciate if you could file a GitHub or Linear issue and reference this error. - - When reporting this issue, please include your query, any relevant type definitions, and the complete error message shown above. \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-vertex-query.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-vertex-query.jsonc deleted file mode 100644 index 977b7d3b5b4..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-vertex-query.jsonc +++ /dev/null @@ -1,16 +0,0 @@ -//@ run: fail -//@ description: Test error when querying vertex directly without specific path -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - "vertex", - //~^ ERROR Cannot query against this complex object - "vertex" - //~^ ERROR Cannot query against this complex object - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-vertex-query.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-vertex-query.stderr deleted file mode 100644 index eb695967950..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/invalid-vertex-query.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[graph-read-compiler::path-conversion]: Cannot query against complex object - ╭▸ - 8 │ ┌ ["==", - 9 │ │ "vertex", -10 │ │ //~^ ERROR Cannot query against this complex object -11 │ │ "vertex" - │ │ ━━━━━━ Cannot query against this complex object -12 │ │ //~^ ERROR Cannot query against this complex object -13 │ │ ] - │ └───────┘ ... within this filter expression - │ - ├ help: Filter expressions can only query against simple scalar properties that map to database columns, not complex objects. Use individual properties of the object instead (e.g., `entity.metadata.record_id.entity_id.entity_uuid` instead of `entity.metadata.record_id`). - ╰ note: This is a temporary limitation of the current query compiler. Support for querying against complex objects in filter expressions is being tracked in https://linear.app/hash/issue/H-4911/hashql-allow-for-querying-against-complex-objects. - -error[graph-read-compiler::path-conversion]: Cannot query against complex object - ╭▸ - 8 │ ┌ ["==", - 9 │ │ "vertex", - │ │ ━━━━━━ Cannot query against this complex object -10 │ │ //~^ ERROR Cannot query against this complex object -11 │ │ "vertex" -12 │ │ //~^ ERROR Cannot query against this complex object -13 │ │ ] - │ └───────┘ ... within this filter expression - │ - ├ help: Filter expressions can only query against simple scalar properties that map to database columns, not complex objects. Use individual properties of the object instead (e.g., `entity.metadata.record_id.entity_id.entity_uuid` instead of `entity.metadata.record_id`). - ╰ note: This is a temporary limitation of the current query compiler. Support for querying against complex objects in filter expressions is being tracked in https://linear.app/hash/issue/H-4911/hashql-allow-for-querying-against-complex-objects. \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/let-expression.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/let-expression.jsonc deleted file mode 100644 index f8a770a832a..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/let-expression.jsonc +++ /dev/null @@ -1,16 +0,0 @@ -//@ run: pass -//@ description: Test let expressions within filter comparisons -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - ["let", "foo", "vertex.metadata.record_id.entity_id.entity_uuid", "foo"], - ["::graph::types::knowledge::entity::EntityUuid", - ["::core::uuid::Uuid", { "#literal": "e2851dbb-7376-4959-9bca-f72cafc4448f" }] - ] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/let-expression.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/let-expression.stdout deleted file mode 100644 index 87d6d875e58..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/let-expression.stdout +++ /dev/null @@ -1,52 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %2 = thunk -> - let %9 = ::graph::tmp::decision_time_now(), - %10 = %9() - in - %10, - %3 = thunk -> let %11 = ::core::uuid::Uuid in %11, - %4 = thunk -> - let %12 = %3(), - %13 = %12("e2851dbb-7376-4959-9bca-f72cafc4448f") - in - %13, - %5 = thunk -> - let %14 = ::graph::types::knowledge::entity::EntityUuid in %14, - %6 = thunk -> - let %15 = %4(), - %16 = %5(), - %17 = %16(%15) - in - %17, - %8 = thunk -> - let %18 = %2(), - %20 = ::graph::head::entities(%18) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let foo:0 = vertex:0.metadata.record_id.entity_id.entity_uuid, - %19 = %6(), - %7 = foo:0 == %19 - in - %7 - ) - |> ::graph::tail::collect - in - %20 -in -%8 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Path { - path: Uuid, - }, - Parameter { - parameter: Text( - "e2851dbb-7376-4959-9bca-f72cafc4448f", - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/let-propagation.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/let-propagation.jsonc deleted file mode 100644 index 47ee09e18a1..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/let-propagation.jsonc +++ /dev/null @@ -1,17 +0,0 @@ -//@ run: pass -//@ description: Let bindings propagate correctly in graph operations -// prettier-ignore -["let", "foo", {"#literal": 2}, -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - "vertex.metadata.record_id.entity_id.entity_uuid", - ["::graph::types::knowledge::entity::EntityUuid", - ["::core::uuid::Uuid", { "#literal": "e2851dbb-7376-4959-9bca-f72cafc4448f" }] - ] - ] - ] - ] -]] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/let-propagation.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/let-propagation.stdout deleted file mode 100644 index 84b4980b14f..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/let-propagation.stdout +++ /dev/null @@ -1,52 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let foo:0 = thunk -> 2, - %2 = thunk -> - let %9 = ::graph::tmp::decision_time_now(), - %10 = %9() - in - %10, - %3 = thunk -> let %11 = ::core::uuid::Uuid in %11, - %4 = thunk -> - let %12 = %3(), - %13 = %12("e2851dbb-7376-4959-9bca-f72cafc4448f") - in - %13, - %5 = thunk -> - let %14 = ::graph::types::knowledge::entity::EntityUuid in %14, - %6 = thunk -> - let %15 = %4(), - %16 = %5(), - %17 = %16(%15) - in - %17, - %8 = thunk -> - let %18 = %2(), - %20 = ::graph::head::entities(%18) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %19 = %6(), - %7 = vertex:0.metadata.record_id.entity_id.entity_uuid == %19 - in - %7 - ) - |> ::graph::tail::collect - in - %20 -in -%8 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Path { - path: Uuid, - }, - Parameter { - parameter: Text( - "e2851dbb-7376-4959-9bca-f72cafc4448f", - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/logical-and-or.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/logical-and-or.jsonc deleted file mode 100644 index ab6c0535913..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/logical-and-or.jsonc +++ /dev/null @@ -1,11 +0,0 @@ -//@ run: pass -//@ description: Test logical AND and OR operations with boolean literals -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["&&", {"#literal": true}, ["||", {"#literal": true}, {"#literal": false}]] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/logical-and-or.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/logical-and-or.stdout deleted file mode 100644 index 1f0219908f2..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/logical-and-or.stdout +++ /dev/null @@ -1,71 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %5 = ::graph::tmp::decision_time_now(), - %6 = %5() - in - %6, - %3 = thunk -> - let %7 = if true then let %2 = true || false in %2 else false in %7, - %4 = thunk -> - let %8 = %1(), - %10 = ::graph::head::entities(%8) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %9 = %3() in %9 - ) - |> ::graph::tail::collect - in - %10 -in -%4 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - ), - Any( - [ - Equal( - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - ), - Equal( - Parameter { - parameter: Boolean( - false, - ), - convert: None, - }, - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - ), - ], - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/logical-or-and.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/logical-or-and.jsonc deleted file mode 100644 index 39014ed6a57..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/logical-or-and.jsonc +++ /dev/null @@ -1,11 +0,0 @@ -//@ run: pass -//@ description: Test logical AND and OR operations with boolean literals -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["||", {"#literal": true}, ["&&", {"#literal": true}, {"#literal": false}]] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/logical-or-and.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/logical-or-and.stdout deleted file mode 100644 index 188d9fd7c87..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/logical-or-and.stdout +++ /dev/null @@ -1,75 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %5 = ::graph::tmp::decision_time_now(), - %6 = %5() - in - %6, - %3 = thunk -> - let %7 = if true then true else let %2 = true && false in %2 in %7, - %4 = thunk -> - let %8 = %1(), - %10 = ::graph::head::entities(%8) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %9 = %3() in %9 - ) - |> ::graph::tail::collect - in - %10 -in -%4 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Any( - [ - Equal( - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - ), - All( - [ - Equal( - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - ), - Equal( - Parameter { - parameter: Boolean( - false, - ), - convert: None, - }, - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - ), - ], - ), - ], - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/nested-binary-operation.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/nested-binary-operation.jsonc deleted file mode 100644 index 3f58d179224..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/nested-binary-operation.jsonc +++ /dev/null @@ -1,18 +0,0 @@ -//@ run: fail -//@ description: Nested binary operations not supported in filter expressions -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - {"#literal": true}, - ["&&", - //~^ ERROR Operation `&&` not supported here - {"#literal": true}, - {"#literal": true} - ] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/nested-binary-operation.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/nested-binary-operation.stderr deleted file mode 100644 index 4fda2d5c924..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/nested-binary-operation.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[graph-read-compiler::binary-operation-unsupported]: Binary operations not supported in this context - ╭▸ - 8 │ ┌ ["==", - 9 │ │ {"#literal": true}, -10 │ │ ["&&", - │ │ ━━ Operation `&&` not supported here - ‡ │ -15 │ │ ] - │ └───────┘ ... within this filter expression - │ - ├ help: The `&&` operation can only be used at the top level of filter conditions, not as an operand in other operations. For example, `(a && b) == c` is not allowed, but `(a && b) && (c == d)` is valid. - ╰ note: This is an intentional current limitation to keep expressions simple, but there are plans to remove this restriction in the future to allow more complex expressions. Progress on this enhancement is tracked in https://linear.app/hash/issue/H-4911/hashql-allow-for-querying-against-complex-objects. \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/nested-let-bindings.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/nested-let-bindings.jsonc deleted file mode 100644 index 93cc67cc2f3..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/nested-let-bindings.jsonc +++ /dev/null @@ -1,16 +0,0 @@ -//@ run: pass -//@ description: Test nested let bindings in filter expressions -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["let", "entity_uuid", "vertex.metadata.record_id.entity_id.entity_uuid", - ["let", "user_id", - ["::graph::types::knowledge::entity::EntityUuid", - ["::core::uuid::Uuid", { "#literal": "e2851dbb-7376-4959-9bca-f72cafc4448f" }] - ], - ["==", "entity_uuid", "user_id"]]] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/nested-let-bindings.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/nested-let-bindings.stdout deleted file mode 100644 index 34f7c162b8a..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/nested-let-bindings.stdout +++ /dev/null @@ -1,52 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %3 = thunk -> - let %9 = ::graph::tmp::decision_time_now(), - %10 = %9() - in - %10, - %4 = thunk -> let %11 = ::core::uuid::Uuid in %11, - %5 = thunk -> - let %12 = %4(), - %13 = %12("e2851dbb-7376-4959-9bca-f72cafc4448f") - in - %13, - %6 = thunk -> - let %14 = ::graph::types::knowledge::entity::EntityUuid in %14, - user_id:0 = thunk -> - let %15 = %5(), - %16 = %6(), - %17 = %16(%15) - in - %17, - %8 = thunk -> - let %18 = %3(), - %20 = ::graph::head::entities(%18) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let entity_uuid:0 = vertex:0.metadata.record_id.entity_id.entity_uuid, - %19 = user_id:0(), - %7 = entity_uuid:0 == %19 - in - %7 - ) - |> ::graph::tail::collect - in - %20 -in -%8 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Path { - path: Uuid, - }, - Parameter { - parameter: Text( - "e2851dbb-7376-4959-9bca-f72cafc4448f", - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/scalar-property-filter.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/scalar-property-filter.jsonc deleted file mode 100644 index 19b3da4129b..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/scalar-property-filter.jsonc +++ /dev/null @@ -1,16 +0,0 @@ -//@ run: pass -//@ description: Test basic scalar property filtering by entity UUID -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - "vertex.metadata.record_id.entity_id.entity_uuid", - ["::graph::types::knowledge::entity::EntityUuid", - ["::core::uuid::Uuid", { "#literal": "e2851dbb-7376-4959-9bca-f72cafc4448f" }] - ] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/scalar-property-filter.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/scalar-property-filter.stdout deleted file mode 100644 index c18b764d780..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/scalar-property-filter.stdout +++ /dev/null @@ -1,51 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %8 = ::graph::tmp::decision_time_now(), - %9 = %8() - in - %9, - %2 = thunk -> let %10 = ::core::uuid::Uuid in %10, - %3 = thunk -> - let %11 = %2(), - %12 = %11("e2851dbb-7376-4959-9bca-f72cafc4448f") - in - %12, - %4 = thunk -> - let %13 = ::graph::types::knowledge::entity::EntityUuid in %13, - %5 = thunk -> - let %14 = %3(), - %15 = %4(), - %16 = %15(%14) - in - %16, - %7 = thunk -> - let %17 = %1(), - %19 = ::graph::head::entities(%17) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %18 = %5(), - %6 = vertex:0.metadata.record_id.entity_id.entity_uuid == %18 - in - %6 - ) - |> ::graph::tail::collect - in - %19 -in -%7 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Path { - path: Uuid, - }, - Parameter { - parameter: Text( - "e2851dbb-7376-4959-9bca-f72cafc4448f", - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/top-level-variable.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/top-level-variable.jsonc deleted file mode 100644 index 537f19a78af..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/top-level-variable.jsonc +++ /dev/null @@ -1,13 +0,0 @@ -//@ run: pass -//@ description: Test variables at the top level of filter expressions -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["let", "foo", {"#literal": true}, - "foo" - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/top-level-variable.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/top-level-variable.stdout deleted file mode 100644 index 344f26f1ae2..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/top-level-variable.stdout +++ /dev/null @@ -1,38 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %2 = thunk -> - let %4 = ::graph::tmp::decision_time_now(), - %5 = %4() - in - %5, - foo:0 = thunk -> true, - %3 = thunk -> - let %6 = %2(), - %8 = ::graph::head::entities(%6) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %7 = foo:0() in %7 - ) - |> ::graph::tail::collect - in - %8 -in -%3 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/top-type-assertion.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/top-type-assertion.jsonc deleted file mode 100644 index d2011129473..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/top-type-assertion.jsonc +++ /dev/null @@ -1,11 +0,0 @@ -//@ run: pass -//@ description: Test type assertions (is operator) in filter expressions -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["as", {"#literal": true}, "Boolean"] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/top-type-assertion.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/top-type-assertion.stdout deleted file mode 100644 index d51096d9ac3..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/top-type-assertion.stdout +++ /dev/null @@ -1,35 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %3 = ::graph::tmp::decision_time_now(), - %4 = %3() - in - %4, - %2 = thunk -> - let %5 = %1(), - %6 = ::graph::head::entities(%5) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> true) - |> ::graph::tail::collect - in - %6 -in -%2 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - Parameter { - parameter: Boolean( - true, - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/type-assertion-in-comparison.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/type-assertion-in-comparison.jsonc deleted file mode 100644 index f70cb592e89..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/type-assertion-in-comparison.jsonc +++ /dev/null @@ -1,19 +0,0 @@ -//@ run: pass -//@ description: Test type assertions within comparison operations -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - "vertex.metadata.record_id.entity_id.entity_uuid", - ["as", - ["::graph::types::knowledge::entity::EntityUuid", - ["::core::uuid::Uuid", { "#literal": "e2851dbb-7376-4959-9bca-f72cafc4448f" }] - ], - "::graph::types::knowledge::entity::EntityUuid" - ] - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/type-assertion-in-comparison.stdout b/libs/@local/hashql/eval/tests/ui/graph/read/entity/type-assertion-in-comparison.stdout deleted file mode 100644 index c18b764d780..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/type-assertion-in-comparison.stdout +++ /dev/null @@ -1,51 +0,0 @@ -════ HIR ═══════════════════════════════════════════════════════════════════════ - -let %1 = thunk -> - let %8 = ::graph::tmp::decision_time_now(), - %9 = %8() - in - %9, - %2 = thunk -> let %10 = ::core::uuid::Uuid in %10, - %3 = thunk -> - let %11 = %2(), - %12 = %11("e2851dbb-7376-4959-9bca-f72cafc4448f") - in - %12, - %4 = thunk -> - let %13 = ::graph::types::knowledge::entity::EntityUuid in %13, - %5 = thunk -> - let %14 = %3(), - %15 = %4(), - %16 = %15(%14) - in - %16, - %7 = thunk -> - let %17 = %1(), - %19 = ::graph::head::entities(%17) - |> ::graph::body::filter((vertex:0: Entity): Boolean -> - let %18 = %5(), - %6 = vertex:0.metadata.record_id.entity_id.entity_uuid == %18 - in - %6 - ) - |> ::graph::tail::collect - in - %19 -in -%7 - -════ Entity Filter ═════════════════════════════════════════════════════════════ - -[ - Equal( - Path { - path: Uuid, - }, - Parameter { - parameter: Text( - "e2851dbb-7376-4959-9bca-f72cafc4448f", - ), - convert: None, - }, - ), -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-closure.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-closure.jsonc deleted file mode 100644 index 655f1a460bf..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-closure.jsonc +++ /dev/null @@ -1,17 +0,0 @@ -//@ run: fail -//@ description: Closures not supported in filter expressions -// prettier-ignore -["let", "identity", ["fn", {"#tuple": ["T"]}, {"#struct": {"value": "T"}}, "T", "value"], -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - "identity", // This is very silly and isn't something you should probably do - //~^ ERROR Closure definition not supported here - "identity" - //~^ ERROR Closure definition not supported here - ] - ] - ] -]] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-closure.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-closure.stderr deleted file mode 100644 index ed54e64a7a2..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-closure.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[graph-read-compiler::closure-unsupported]: Closures not supported - ╭▸ - 9 │ ┌ ["==", -10 │ │ "identity", // This is very silly and isn't something you should probably do -11 │ │ //~^ ERROR Closure definition not supported here -12 │ │ "identity" - │ │ ━━━━━━━━ Closure definition not supported here -13 │ │ //~^ ERROR Closure definition not supported here -14 │ │ ] - │ └───────┘ ... within this filter expression - │ - ├ help: Filter expressions do not currently support closure definitions. Move the closure outside the filter expression, assign the result to a variable, and use that variable in the filter instead. - ╰ note: Closures in filter expressions are not yet implemented. This is a current limitation that is being tracked in https://linear.app/hash/issue/H-4913/hashql-implement-vm. - -error[graph-read-compiler::closure-unsupported]: Closures not supported - ╭▸ - 9 │ ┌ ["==", -10 │ │ "identity", // This is very silly and isn't something you should probably do - │ │ ━━━━━━━━ Closure definition not supported here -11 │ │ //~^ ERROR Closure definition not supported here -12 │ │ "identity" -13 │ │ //~^ ERROR Closure definition not supported here -14 │ │ ] - │ └───────┘ ... within this filter expression - │ - ├ help: Filter expressions do not currently support closure definitions. Move the closure outside the filter expression, assign the result to a variable, and use that variable in the filter instead. - ╰ note: Closures in filter expressions are not yet implemented. This is a current limitation that is being tracked in https://linear.app/hash/issue/H-4913/hashql-implement-vm. \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-nested-graph-read.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-nested-graph-read.jsonc deleted file mode 100644 index 78a54c3bf19..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-nested-graph-read.jsonc +++ /dev/null @@ -1,27 +0,0 @@ -//@ run: fail -//@ description: Nested graph read operations not supported in filter expressions -// prettier-ignore -["let", "read", - ["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - "vertex.metadata.record_id.entity_id.entity_uuid", - "vertex.metadata.record_id.entity_id.entity_uuid" - ] - ] - ]], -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - "read", - //~^ ERROR Nested graph read operations not supported here - "read" - //~^ ERROR Nested graph read operations not supported here - ] - ] - ] -]] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-nested-graph-read.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-nested-graph-read.stderr deleted file mode 100644 index 0ce53e5c7f8..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-nested-graph-read.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[graph-read-compiler::nested-graph-read-unsupported]: Nested graph read operations not supported - ╭▸ -19 │ ┌ ["==", -20 │ │ "read", -21 │ │ //~^ ERROR Nested graph read operations not supported here -22 │ │ "read" - │ │ ━━━━ Nested graph read operations not supported here -23 │ │ //~^ ERROR Nested graph read operations not supported here -24 │ │ ] - │ └───────┘ ... within this filter expression - │ - ├ help: Filter expressions do not support nested graph operations. Use a separate query for the nested graph read and pass the result to this filter expression. - ╰ note: Nested graph reads in filter expressions are not yet implemented. This is a current limitation that is being tracked in https://linear.app/hash/issue/H-4913/hashql-implement-vm. - -error[graph-read-compiler::nested-graph-read-unsupported]: Nested graph read operations not supported - ╭▸ -19 │ ┌ ["==", -20 │ │ "read", - │ │ ━━━━ Nested graph read operations not supported here -21 │ │ //~^ ERROR Nested graph read operations not supported here -22 │ │ "read" -23 │ │ //~^ ERROR Nested graph read operations not supported here -24 │ │ ] - │ └───────┘ ... within this filter expression - │ - ├ help: Filter expressions do not support nested graph operations. Use a separate query for the nested graph read and pass the result to this filter expression. - ╰ note: Nested graph reads in filter expressions are not yet implemented. This is a current limitation that is being tracked in https://linear.app/hash/issue/H-4913/hashql-implement-vm. \ No newline at end of file diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-type-constructor.jsonc b/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-type-constructor.jsonc deleted file mode 100644 index e647ac27d2f..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-type-constructor.jsonc +++ /dev/null @@ -1,16 +0,0 @@ -//@ run: fail -//@ description: Type constructors not supported as values in filter expressions -// prettier-ignore -["::graph::tail::collect", - ["::graph::body::filter", - ["::graph::head::entities", ["::graph::tmp::decision_time_now"]], - ["fn", { "#tuple": [] }, { "#struct": { "vertex": "_" } }, "_", - ["==", - "Some", - //~^ ERROR Cannot use constructor as value here - "Some" - //~^ ERROR Cannot use constructor as value here - ] - ] - ] -] diff --git a/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-type-constructor.stderr b/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-type-constructor.stderr deleted file mode 100644 index 3955ba00ab8..00000000000 --- a/libs/@local/hashql/eval/tests/ui/graph/read/entity/unsupported-type-constructor.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[graph-read-compiler::type-constructor-unsupported]: Type constructors not supported as values - ╭▸ - 8 │ ┌ ["==", - 9 │ │ "Some", -10 │ │ //~^ ERROR Cannot use constructor as value here -11 │ │ "Some" - │ │ ━━━━ Cannot use constructor as value here -12 │ │ //~^ ERROR Cannot use constructor as value here -13 │ │ ] - │ └───────┘ ... within this filter expression - │ - ├ help: Constructor functions cannot currently be used as first-class values in filter expressions. You can still call constructors to create values (e.g., `SomeType(x)`), but you cannot use the constructor itself in comparisons or pass it as an argument within filter contexts. - ╰ note: This is a current limitation of the filter expression compiler. Constructors work as first-class values elsewhere in the language, and support for this in filter expressions is being tracked in https://linear.app/hash/issue/H-4913/hashql-implement-vm. - -error[graph-read-compiler::type-constructor-unsupported]: Type constructors not supported as values - ╭▸ - 8 │ ┌ ["==", - 9 │ │ "Some", - │ │ ━━━━ Cannot use constructor as value here -10 │ │ //~^ ERROR Cannot use constructor as value here -11 │ │ "Some" -12 │ │ //~^ ERROR Cannot use constructor as value here -13 │ │ ] - │ └───────┘ ... within this filter expression - │ - ├ help: Constructor functions cannot currently be used as first-class values in filter expressions. You can still call constructors to create values (e.g., `SomeType(x)`), but you cannot use the constructor itself in comparisons or pass it as an argument within filter contexts. - ╰ note: This is a current limitation of the filter expression compiler. Constructors work as first-class values elsewhere in the language, and support for this in filter expressions is being tracked in https://linear.app/hash/issue/H-4913/hashql-implement-vm. \ No newline at end of file diff --git a/libs/@local/hashql/hir/src/context.rs b/libs/@local/hashql/hir/src/context.rs index bfeca6b0aaf..8015b7a6cca 100644 --- a/libs/@local/hashql/hir/src/context.rs +++ b/libs/@local/hashql/hir/src/context.rs @@ -23,6 +23,7 @@ impl SymbolRegistry<'_> { } impl Default for SymbolRegistry<'_> { + #[inline] fn default() -> Self { Self::new() } @@ -44,6 +45,7 @@ impl Counter { } impl Default for Counter { + #[inline] fn default() -> Self { Self::new() } diff --git a/libs/@local/hashql/hir/src/lower/normalization.rs b/libs/@local/hashql/hir/src/lower/normalization.rs index 2d64b2148fe..23572291a2a 100644 --- a/libs/@local/hashql/hir/src/lower/normalization.rs +++ b/libs/@local/hashql/hir/src/lower/normalization.rs @@ -210,6 +210,7 @@ pub struct NormalizationState<'heap> { } impl Default for NormalizationState<'_> { + #[inline] fn default() -> Self { Self { recycler: VecPool::new(4), diff --git a/libs/@local/hashql/hir/src/map.rs b/libs/@local/hashql/hir/src/map.rs index 9e36cf11238..e7e911e3b1d 100644 --- a/libs/@local/hashql/hir/src/map.rs +++ b/libs/@local/hashql/hir/src/map.rs @@ -169,6 +169,7 @@ impl<'heap> HirMap<'heap> { } impl Default for HirMap<'_> { + #[inline] fn default() -> Self { Self::new() }