<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Dan Shalev</title>
    <description>The latest articles on Forem by Dan Shalev (@danshalev7).</description>
    <link>https://forem.com/danshalev7</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2251943%2F12ead377-c03a-4292-91db-4efd2bae17ef.png</url>
      <title>Forem: Dan Shalev</title>
      <link>https://forem.com/danshalev7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/danshalev7"/>
    <language>en</language>
    <item>
      <title>Text2SQL on xxxx's of tables?</title>
      <dc:creator>Dan Shalev</dc:creator>
      <pubDate>Mon, 05 Jan 2026 13:56:33 +0000</pubDate>
      <link>https://forem.com/danshalev7/text2sql-on-xxxxs-of-tables-4775</link>
      <guid>https://forem.com/danshalev7/text2sql-on-xxxxs-of-tables-4775</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7khs4z1till7adolazzn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7khs4z1till7adolazzn.png" alt="QueryWeaver text-to-sql interface" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Text-to-SQL tools work fine in demos. Production with 30K+ tables? They hallucinate relationships and fail in ways you can't debug because the code is closed.&lt;/p&gt;

&lt;p&gt;We've shipped several updates focused on solving this.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's New
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;React UI Rebuild&lt;/strong&gt;&lt;br&gt;
Cleaned up the frontend, fixed button logic issues, improved overall UX.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Release&lt;/strong&gt;&lt;br&gt;
New API endpoint for programmatic access. Full docs in the repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend Performance Improvements&lt;/strong&gt;&lt;br&gt;
Optimized graph generation and query processing for large-scale schemas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full Schema Auditability&lt;/strong&gt;&lt;br&gt;
You can now inspect exactly how your schema gets mapped into the knowledge graph—see the indexing logic, relationship detection, everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Architecture Difference
&lt;/h3&gt;

&lt;p&gt;Vector embeddings can't capture the semantic depth needed for complex relational schemas. QueryWeaver uses knowledge graphs to map table relationships structurally, not statistically. Less hallucination, better accuracy and subsequent queries.&lt;/p&gt;

&lt;p&gt;Check it out, it's free: &lt;a href="https://app.queryweaver.ai/" rel="noopener noreferrer"&gt;https://app.queryweaver.ai/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>falkordb</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Graph to store your security data?</title>
      <dc:creator>Dan Shalev</dc:creator>
      <pubDate>Sun, 17 Aug 2025 06:15:15 +0000</pubDate>
      <link>https://forem.com/falkordb/graph-to-store-your-security-data-531l</link>
      <guid>https://forem.com/falkordb/graph-to-store-your-security-data-531l</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The Challenge: You run a multi-tenant security platform and need to ensure full tenant isolation, avoiding customers' data commingling in the same database or needing to spin up a dedicated database for every new customer.&lt;/p&gt;

&lt;p&gt;How it affects you: You either introduce risk of data leakage or waste infrastructure resources on isolated stacks.&lt;/p&gt;

&lt;p&gt;Why choose graph: You can manage 10,000+ isolated graph tenants per database (across all pricing tiers). Each tenant gets a private namespace and query surface.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;🟰 Business impact: Zero tenant data commingling. Minimal DevOps overhead. Efficient scaling of your infrastructure as you grow.&lt;/p&gt;

&lt;p&gt;The other 5 reasons are &lt;a href="https://www.falkordb.com/blog/store-security-data-in-a-graph/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>graph</category>
      <category>database</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Graph database v4.10 is out!</title>
      <dc:creator>Dan Shalev</dc:creator>
      <pubDate>Thu, 12 Jun 2025 14:02:00 +0000</pubDate>
      <link>https://forem.com/falkordb/graph-database-v410-is-out-883</link>
      <guid>https://forem.com/falkordb/graph-database-v410-is-out-883</guid>
      <description>&lt;p&gt;The new release (v4.10.0) is out, and I wanted to share some of the updates and ask for feedback from folks who care about performance, memory efficiency in graph-heavy systems.&lt;br&gt;
FalkorDB is an open-source property graph database that supports OpenCypher (with our own extensions) and is used under the hood for retrieval-augmented generation setups where accuracy matters.&lt;br&gt;
The big problem we’re working on is scaling graph databases without memory bloat or unpredictable performance in prod. Support for Indexing tends to be limited with array fields. And if you want to do something basic like compare a current value to the previous one in a sequence (think time series modeling), the query engine often makes you jump through hoops.&lt;br&gt;
We started FalkorDB after working for years on RedisGraph (we were the original authors). Rather than patch the old codebase, we built FalkorDB with a sparse matrix algebra backend for performance. Our goal was to build something that could hold up under pressure, like 10K+ graphs in a single instance, and still let you answer complex queries interactively.&lt;br&gt;
To get closer to this goal, we’ve added the following improvements in this new version: We added string interning with a new intern() function. It lets you deduplicate identical strings across graphs, which is surprisingly useful in, for example, recommender systems where you have millions of “US” strings. We also added a command (GRAPH.MEMORY USAGE) that breaks down memory consumption by nodes, edges, matrices, and indices (per graph), which is useful when you’re trying to figure out if your heap is getting crushed by edge cardinality or indexing overhead.&lt;br&gt;
Indexing got smarter too, with arrays now natively indexable in a way that’s actually usable in production (Neo4j doesn’t do this natively, last I checked). &lt;br&gt;
On the analytics side, we added CDLP (community detection via label propagation), WCC (weakly connected components), and betweenness centrality, which are all exposed as procedures. These came out of working with teams in fraud detection and behavioral clustering where you don’t want to guess the number of communities in advance.&lt;br&gt;
If you want to try FalkorDB, we recommend you run it via Docker&lt;br&gt;
The code’s also available on GitHub (&lt;a href="https://github.com/FalkorDB/falkordb" rel="noopener noreferrer"&gt;https://github.com/FalkorDB/falkordb&lt;/a&gt;) and we have a live sandbox you can play with at &lt;a href="https://browser.falkordb.com" rel="noopener noreferrer"&gt;https://browser.falkordb.com&lt;/a&gt;. No login or install needed to run queries. Docs are at &lt;a href="https://docs.falkordb.com" rel="noopener noreferrer"&gt;https://docs.falkordb.com&lt;/a&gt;. &lt;br&gt;
Curious to hear from anyone who’s building graph-heavy systems, especially if you’ve hit memory or indexing limits elsewhere. We’re heads-down building and always learning, grateful for any feedback or test cases you throw at us.&lt;/p&gt;

</description>
      <category>database</category>
    </item>
    <item>
      <title>How to use a knowledge graph ft. Yohei Nakajima</title>
      <dc:creator>Dan Shalev</dc:creator>
      <pubDate>Tue, 27 May 2025 11:29:14 +0000</pubDate>
      <link>https://forem.com/falkordb/how-to-use-a-knowledge-graph-ft-yohei-nakajima-2mf7</link>
      <guid>https://forem.com/falkordb/how-to-use-a-knowledge-graph-ft-yohei-nakajima-2mf7</guid>
      <description>&lt;p&gt;​In this workshop we’ll show 2 live builds: Fractal KG, a UI for building knowledge graphs from a natural language prompt, and VCPedia, a real-time startup intelligence Crunchbase-like platform that is graph powered by hourly Twitter pulls, LLM-based funding-round extraction, and automated newsletters.&lt;/p&gt;

&lt;p&gt;Get information: Map out Fractal KG’s architecture: ingestion, embedding, dedupe, hierarchy, and newsletters.&lt;br&gt;
Get inspired: Break down VCPedia’s architecture.&lt;br&gt;
Get started: Integrate FalkorDB for graph queries, multi-tenant support, and setup.&lt;/p&gt;

&lt;p&gt;18 June, 2025 ⏰ PDT: 10:00 AM | EDT: 1:00 PM | CEST: 7:00 PM&lt;/p&gt;

&lt;p&gt;Sign up link: &lt;a href="https://lu.ma/192rvxcg" rel="noopener noreferrer"&gt;Sign up here (free)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;p.s you can register and we’ll send a recap (even if you can’t attend).&lt;/p&gt;

</description>
      <category>llm</category>
      <category>rag</category>
      <category>graphql</category>
    </item>
    <item>
      <title>Questions to ask before you build a knowledge graph</title>
      <dc:creator>Dan Shalev</dc:creator>
      <pubDate>Tue, 06 May 2025 08:10:45 +0000</pubDate>
      <link>https://forem.com/falkordb/questions-to-ask-before-you-build-a-knowledge-graph-47ff</link>
      <guid>https://forem.com/falkordb/questions-to-ask-before-you-build-a-knowledge-graph-47ff</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Are you planning to develop intelligent chatbots that require advanced understanding and interaction capabilities?&lt;/li&gt;
&lt;li&gt;Is your focus on enabling dynamic, complex research endeavors?&lt;/li&gt;
&lt;li&gt;Do you want to visualize or monitor asset flows and risks within your organization?&lt;/li&gt;
&lt;li&gt;Do you aim to unlock siloed data or enhance connectivity between disparate data environments?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.falkordb.com/blog/how-to-build-a-knowledge-graph/" rel="noopener noreferrer"&gt;Knowledge graphs&lt;/a&gt; help structure information by capturing relationships between disparate data points. They allow users to integrate data from diverse sources and discover hidden patterns and connections.&lt;/p&gt;

</description>
      <category>rag</category>
      <category>ai</category>
    </item>
    <item>
      <title>Why I Fell in Love with Rust Procedural Macros</title>
      <dc:creator>Dan Shalev</dc:creator>
      <pubDate>Mon, 05 May 2025 13:14:31 +0000</pubDate>
      <link>https://forem.com/falkordb/why-i-fell-in-love-with-rust-procedural-macros-49mh</link>
      <guid>https://forem.com/falkordb/why-i-fell-in-love-with-rust-procedural-macros-49mh</guid>
      <description>&lt;p&gt;I consider myself a junior Rust developer. I have been learning Rust for a few months now, and I have thoroughly enjoyed&lt;br&gt;
the process. Recently, we started writing the &lt;a href="https://github.com/FalkorDB/falkordb-rs-next-gen" rel="noopener noreferrer"&gt;next generation&lt;/a&gt; of&lt;br&gt;
Falkordb using Rust. We chose Rust because of its&lt;br&gt;
performance, safety, and rich type system.&lt;/p&gt;

&lt;p&gt;One part we are implementing by hand is the scanner and parser. We do this to optimize performance and to maintain a&lt;br&gt;
clean AST (abstract syntax tree). We are working with&lt;br&gt;
the &lt;a href="https://github.com/FalkorDB/falkordb-rs-next-gen/blob/main/graph/src/Cypher.g4" rel="noopener noreferrer"&gt;Antlr4 Cypher grammar&lt;/a&gt;, where each Derivation in the grammar maps to a Rust function.&lt;/p&gt;

&lt;p&gt;For example, consider the parse rule for a NOT expression:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;oC_NotExpression&lt;br&gt;
: ( NOT SP? )* oC_ComparisonExpression ;&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This corresponds to the Rust function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_not_expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;QueryExprIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;not_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.lexer&lt;/span&gt;&lt;span class="nf"&gt;.current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nn"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Not&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.lexer&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;not_count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_comparison_expr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;not_count&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;QueryExprIR&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we compress consecutive NOT expressions during parsing, but otherwise, the procedure closely resembles the Antlr4&lt;br&gt;
grammar. The function first consumes zero or more NOT tokens, then calls parse_comparison_expr&lt;/p&gt;

&lt;p&gt;While working on the parser, a recurring pattern emerged. Many expressions follow the form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;oC_ComparisonExpression
: oC_OrExpression ( ( SP? COMPARISON_OPERATOR SP? ) oC_OrExpression )* ;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which translates roughly to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_comparison_expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;QueryExprIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_or_expr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.lexer&lt;/span&gt;&lt;span class="nf"&gt;.current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nn"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ComparisonOperator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.lexer&lt;/span&gt;&lt;span class="nf"&gt;.current&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.lexer&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_or_expr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;QueryExprIR&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;BinaryOp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, for addition and subtraction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;oC_AddOrSubtractExpression
: oC_MultiplyDivideModuloExpression ( ( SP? '+' SP? oC_MultiplyDivideModuloExpression ) | ( SP? '-' SP? oC_MultiplyDivideModuloExpression ) )* ;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which looks like this in Rust:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_add_sub_expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;QueryExprIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_mul_div_modulo_expr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nn"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Plus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.lexer&lt;/span&gt;&lt;span class="nf"&gt;.current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.lexer&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_mul_div_modulo_expr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;QueryExprIR&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nn"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Dash&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.lexer&lt;/span&gt;&lt;span class="nf"&gt;.current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.lexer&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_mul_div_modulo_expr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;QueryExprIR&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nn"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Plus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Dash&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.lexer&lt;/span&gt;&lt;span class="nf"&gt;.current&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern appeared repeatedly with one, two, or three operators. Although the code is not very complicated, it would&lt;br&gt;
be nice to have a macro that generates this code for us.&lt;/p&gt;

&lt;p&gt;We envisioned a macro that takes the expression parser and pairs of (token, AST constructor) like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;parse_binary_expr!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_mul_div_modulo_expr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Plus&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dash&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Sub&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I started exploring how to write procedural macros in Rust, and I must say it was a very pleasant experience. With&lt;br&gt;
the help of the crates quote and syn, I was able to write a procedural macro that generates this code automatically. The&lt;br&gt;
quote crate lets you generate token streams from templates, and syn allows parsing Rust code into syntax trees and token&lt;br&gt;
streams. Using these two crates makes writing procedural macros in Rust feel like writing a compiler extension.&lt;/p&gt;

&lt;p&gt;Let's get into the code.&lt;/p&gt;

&lt;p&gt;The first step is to model your macro syntax using Rust data structures. In our case, I used two structs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;BinaryOp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;parse_exp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;binary_op_alts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BinaryOpAlt&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;BinaryOpAlt&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;token_match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Ident&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;ast_constructor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Ident&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The leaves of these structs are data types from the syn crate. Expr represents any Rust expression, and syn::Ident&lt;br&gt;
represents an identifier.&lt;/p&gt;

&lt;p&gt;Next, we parse the token stream into these data structures. This is straightforward with syn by implementing the Parse&lt;br&gt;
trait:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Parse&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;BinaryOp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ParseStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;parse_exp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;Token!&lt;/span&gt;&lt;span class="p"&gt;[,]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;binary_op_alts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="nn"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;punctuated&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Punctuated&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BinaryOpAlt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;Token!&lt;/span&gt;&lt;span class="p"&gt;[,]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_separated_nonempty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;parse_exp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;binary_op_alts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;binary_op_alts&lt;/span&gt;&lt;span class="nf"&gt;.into_iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Parse&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;BinaryOpAlt&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ParseStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;token_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;Token!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ast_constructor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;token_match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;ast_constructor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The syn crate smartly parses the token stream into the data structures based on the expected types (Token, Expr, Ident,&lt;br&gt;
or BinaryOpAlt).&lt;/p&gt;

&lt;p&gt;The final step is to generate the appropriate code from these data structures using the quote crate, which lets you&lt;br&gt;
write Rust code templates that generate token streams. This is done by implementing the ToTokens trait:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nn"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ToTokens&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;BinaryOp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;to_tokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nn"&gt;proc_macro2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;binary_op_alts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.binary_op_alts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;parse_exp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.parse_exp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_token_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parse_exp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;binary_op_alts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="nf"&gt;.extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;generate_token_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="n"&gt;parse_exp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;alts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;BinaryOpAlt&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;proc_macro2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TokenStream&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;whiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alts&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;token_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="py"&gt;.token_match&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ast_constructor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="py"&gt;.ast_constructor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nn"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;quote!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nn"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;#&lt;span class="n"&gt;token_match&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.lexer&lt;/span&gt;&lt;span class="nf"&gt;.current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.lexer&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;#&lt;span class="n"&gt;parse_exp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nn"&gt;QueryExprIR&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;#&lt;span class="nf"&gt;ast_constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alts&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;token_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="py"&gt;.token_match&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nn"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;quote!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nn"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;#&lt;span class="n"&gt;token_match&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nn"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;quote!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;#&lt;span class="n"&gt;parse_exp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
#&lt;span class="p"&gt;(&lt;/span&gt;#&lt;span class="n"&gt;whiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;#&lt;span class="p"&gt;(&lt;/span&gt;#&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.lexer&lt;/span&gt;&lt;span class="nf"&gt;.current&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In generate_token_stream, we first generate the collection of while loops for each operator, then place them inside a&lt;br&gt;
loop using the repetition syntax &lt;code&gt;#(#whiles)*&lt;/code&gt;. And that's it!&lt;/p&gt;

&lt;p&gt;You can find the full code &lt;a href="https://github.com/FalkorDB/falkordb-rs-next-gen/tree/main/falkordb-macro" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
    </item>
    <item>
      <title>VectorRAG is naive, lacks domain awareness, and can’t handle full dataset retrieval</title>
      <dc:creator>Dan Shalev</dc:creator>
      <pubDate>Tue, 29 Apr 2025 12:13:40 +0000</pubDate>
      <link>https://forem.com/falkordb/vectorrag-is-naive-lacks-domain-awareness-and-cant-handle-full-dataset-retrieval-5g59</link>
      <guid>https://forem.com/falkordb/vectorrag-is-naive-lacks-domain-awareness-and-cant-handle-full-dataset-retrieval-5g59</guid>
      <description>&lt;p&gt;If we were building a GenAI stack today, we'd start with one question: Can your retrieval system handle multi-hop logic?&lt;/p&gt;

&lt;p&gt;Trick question, b/c most can’t. They treat retrieval as nearest-neighbor search.&lt;/p&gt;

&lt;p&gt;Today, we discussed scaling #&lt;a href="https://github.com/FalkorDB/GraphRAG-SDK/blob/main/README.md" rel="noopener noreferrer"&gt;GraphRAG&lt;/a&gt; at AWS DevOps Day, and the takeaway is clear: VectorRAG is naive, lacks domain awareness, and can’t handle full dataset retrieval. &lt;/p&gt;

&lt;p&gt;GraphRAG &lt;a href="https://falkordb.com/" rel="noopener noreferrer"&gt;builds a knowledge graph&lt;/a&gt; from source documents, allowing for a deeper understanding of the data + higher accuracy.&lt;/p&gt;

</description>
      <category>vectordatabase</category>
      <category>rag</category>
      <category>ai</category>
    </item>
    <item>
      <title>X0,000s Ops/sec with Multigraph Topology</title>
      <dc:creator>Dan Shalev</dc:creator>
      <pubDate>Wed, 23 Apr 2025 13:44:32 +0000</pubDate>
      <link>https://forem.com/falkordb/x0000s-opssec-with-multigraph-topology-4nea</link>
      <guid>https://forem.com/falkordb/x0000s-opssec-with-multigraph-topology-4nea</guid>
      <description>&lt;p&gt;Yesterday's 'Easily Achieve X0,000s Ops/sec with Multigraph Topology' workshop was awesome. Great questions, too! &lt;/p&gt;

&lt;p&gt;We started with a standalone machine with 16 cores = 26k queries per second. We tripled and then doubled the number of cores, and achieved linear scalability.&lt;/p&gt;

&lt;p&gt;Recording link:  &lt;a href="https://www.youtube.com/watch?v=LbeA0-xy1f8" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=LbeA0-xy1f8&lt;/a&gt;&lt;/p&gt;

</description>
      <category>performance</category>
      <category>discuss</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Your GenAI system is only as smart as its retrieval layer.</title>
      <dc:creator>Dan Shalev</dc:creator>
      <pubDate>Wed, 16 Apr 2025 11:20:32 +0000</pubDate>
      <link>https://forem.com/falkordb/your-genai-system-is-only-as-smart-as-its-retrieval-layer-2963</link>
      <guid>https://forem.com/falkordb/your-genai-system-is-only-as-smart-as-its-retrieval-layer-2963</guid>
      <description>&lt;p&gt;A recent enterprise GenAI survey just confirmed what we’ve been seeing in production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;85% of teams are deploying LLMs&lt;/li&gt;
&lt;li&gt;71% are &lt;em&gt;already seeing output risks&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;99% agree: human oversight is still mandatory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s be clear—this isn’t about model tuning. It’s about &lt;em&gt;retrieval failure&lt;/em&gt; at the infrastructure level.&lt;/p&gt;

&lt;p&gt;You can’t generate correct answers if your stack can’t model relationships.&lt;/p&gt;

&lt;p&gt;You can’t trace decisions if your data lacks structure.&lt;/p&gt;

&lt;p&gt;You can’t scale trust if your system hallucinates under pressure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here’s what actually works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load your structured + unstructured knowledge into a graph database&lt;/li&gt;
&lt;li&gt;Model entities, relationships, and policies—don’t flatten them&lt;/li&gt;
&lt;li&gt;Route results into your LLM prompt—clean, fast, explainable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s how you replace retrieval duct tape with graph-native reasoning.&lt;/p&gt;

&lt;p&gt;Exploring advanced RAG &amp;amp; GraphRAG? Start here: &lt;a href="https://github.com/FalkorDB/GraphRAG-SDK" rel="noopener noreferrer"&gt;https://github.com/FalkorDB/GraphRAG-SDK&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rag</category>
      <category>ai</category>
      <category>python</category>
    </item>
    <item>
      <title>Add a Knowledge Graph 3x better</title>
      <dc:creator>Dan Shalev</dc:creator>
      <pubDate>Wed, 09 Apr 2025 07:27:09 +0000</pubDate>
      <link>https://forem.com/falkordb/add-a-knowledge-graph-3x-better-24mb</link>
      <guid>https://forem.com/falkordb/add-a-knowledge-graph-3x-better-24mb</guid>
      <description>&lt;p&gt;If your AI agent doesn’t know when it’s wrong, it doesn’t belong in production.&lt;/p&gt;

&lt;p&gt;We reviewed a recent study that tested LLM pipelines against enterprise data environments, benchmarking their performance on enterprise datasets using the Yale Spider schema.&lt;/p&gt;

&lt;p&gt;Same model. Same questions. Different architecture.&lt;/p&gt;

&lt;p&gt;**Here’s what changed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;**SQL-only → 17.28% accuracy&lt;/li&gt;
&lt;li&gt;Add a Knowledge Graph → 3x better&lt;/li&gt;
&lt;li&gt;Add ontology-based query checks + repair loop → 72.55% accuracy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's not incremental progress. That’s a systems-level shift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here’s what mattered most:
&lt;/h2&gt;

&lt;p&gt;70% of fixes came from domain constraints in the query body&lt;br&gt;
Most gains showed up in complex schema environments—think KPIs and strategic planning&lt;br&gt;
And when the model couldn’t repair itself? It admitted it with “unknown,” cutting hallucinated outputs by a huge margin&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture looks like this:
&lt;/h2&gt;

&lt;p&gt;Ontologies validate logic pre-execution&lt;br&gt;
Knowledge graphs serve as real-time reasoning layers&lt;br&gt;
LLM Repair loops handle failure cases autonomously&lt;br&gt;
&lt;a href="https://falkordb.com" rel="noopener noreferrer"&gt;FalkorDB&lt;/a&gt; is already solving the low-latency challenge here—serving graphs in real time for reasoning-heavy queries.&lt;br&gt;
The lesson: You don’t need smarter prompts. You need systems that can detect when the logic breaks—and fix it before it hits the user.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Vector Recall Reasoning</title>
      <dc:creator>Dan Shalev</dc:creator>
      <pubDate>Tue, 01 Apr 2025 13:47:41 +0000</pubDate>
      <link>https://forem.com/falkordb/vector-recall-reasoning-5cko</link>
      <guid>https://forem.com/falkordb/vector-recall-reasoning-5cko</guid>
      <description>&lt;p&gt;If your GenAI agent can’t reason across relationships, memory, and context—it’s not an agent. It’s a demo.&lt;/p&gt;

&lt;p&gt;We came across a research system called DEMENTIA-PLAN—focused on dementia care, but it exposed something bigger: the fatal flaw in most GenAI stacks (source in comments).&lt;/p&gt;

&lt;p&gt;Agents that run vector-only retrieval pipelines fail to answer questions grounded in human context.&lt;/p&gt;

&lt;p&gt;DEMENTIA-PLAN used multiple &lt;a href="https://www.falkordb.com/blog/kpmg-ai-report-graphrag-ai-agents/" rel="noopener noreferrer"&gt;knowledge graphs + a planning agent&lt;/a&gt; to adapt retrieval in real time. Result? 30% better memory support. 10% higher coherence.&lt;/p&gt;

&lt;p&gt;This isn’t just about healthcare.&lt;br&gt;
It’s a blueprint for every agent stack that actually needs to think. If your RAG pipeline gets retrieval wrong, you’re shipping guessware.&lt;/p&gt;

</description>
      <category>agentaichallenge</category>
      <category>rag</category>
    </item>
    <item>
      <title>What Makes Data AI-Ready?</title>
      <dc:creator>Dan Shalev</dc:creator>
      <pubDate>Thu, 27 Mar 2025 09:50:10 +0000</pubDate>
      <link>https://forem.com/falkordb/what-makes-data-ai-ready-4hj0</link>
      <guid>https://forem.com/falkordb/what-makes-data-ai-ready-4hj0</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR: Your AI scaling bottleneck isn't models, it's inconsistent enterprise data—solve it with graph-based pipelines like FalkorDB &amp;amp; GraphRAG.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gartner: 60% of AI projects fail due to poor-quality data.&lt;/li&gt;
&lt;li&gt;Conventional data cleansing can’t handle fragmented enterprise systems.&lt;/li&gt;
&lt;li&gt;GraphRAG paired with FalkorDB standardizes scattered data into structured, queryable graphs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Actionable takeaway: Graph-based solutions directly tackle enterprise-scale &lt;a href="https://www.falkordb.com/blog/ai-ready-data-generative-ai/" rel="noopener noreferrer"&gt;AI data&lt;/a&gt; chaos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6h1xxz5ovv2sdwh4iovx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6h1xxz5ovv2sdwh4iovx.jpg" alt="Image description" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>data</category>
      <category>database</category>
      <category>rag</category>
      <category>genai</category>
    </item>
  </channel>
</rss>
