From c8280ae8712f1d4f185505bd9b10f5c8f3de316a Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 12 Apr 2026 03:02:32 -0400 Subject: [PATCH] parser: add composite sort expressions Adds parsing for weighted sort expressions like: sort:degree*0.5+isolation*0.3+recency(organize)*0.2 This fixes organize agent which uses composite scoring. Co-Authored-By: Proof of Concept --- src/hippocampus/query/parser.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/hippocampus/query/parser.rs b/src/hippocampus/query/parser.rs index e80dd1c..ccc3ae5 100644 --- a/src/hippocampus/query/parser.rs +++ b/src/hippocampus/query/parser.rs @@ -28,7 +28,7 @@ use std::collections::BTreeMap; // Re-export engine types used by Query pub use super::engine::{ - Stage, Filter, Transform, Generator, SortField, + Stage, Filter, Transform, Generator, SortField, ScoreField, Algorithm, AlgoStage, Cmp, }; @@ -92,7 +92,7 @@ peg::parser! { / "connectivity" { Stage::Transform(Transform::Connectivity) } / "dominating-set" { Stage::Transform(Transform::DominatingSet) } // Pipeline syntax (colon-separated) - / "sort:" f:field() { Stage::Transform(Transform::Sort(make_sort_field(&f, false))) } + / "sort:" c:composite_sort() { Stage::Transform(Transform::Sort(c)) } / "limit:" n:integer() { Stage::Transform(Transform::Limit(n)) } / "select:" f:field_list_colon() { Stage::Transform(Transform::Select(f)) } / "type:" t:ident() { make_type_filter(&t) } @@ -111,6 +111,27 @@ peg::parser! { / "desc" { false } / { false } // default: descending + // Composite sort: degree*0.5+isolation*0.3+recency(organize)*0.2 + // Falls back to simple field if no weighted terms found. + rule composite_sort() -> SortField + = t:score_term() ts:("+" t:score_term() { t })+ { + let mut terms = vec![t]; + terms.extend(ts); + SortField::Composite(terms) + } + / f:field() { make_sort_field(&f, false) } + + rule score_term() -> (ScoreField, f64) + = "recency(" a:ident() ")" "*" w:number() { (ScoreField::Recency(a), w) } + / f:score_field_name() "*" w:number() { (f, w) } + + rule score_field_name() -> ScoreField + = "isolation" { ScoreField::Isolation } + / "degree" { ScoreField::Degree } + / "weight" { ScoreField::Weight } + / "content-len" { ScoreField::ContentLen } + / "priority" { ScoreField::Priority } + rule field_list_colon() -> Vec = f:field() fs:("," f:field() { f })* { let mut v = vec![f];