Skip to main content

substrait_explain/parser/
structural.rs

1//! Parser for the structural part of the Substrait file format.
2//!
3//! This is the overall parser for parsing the text format. It is responsible
4//! for tracking which section of the file we are currently parsing, and parsing
5//! each line separately.
6
7use std::fmt;
8
9use pest::iterators::Pair;
10use substrait::proto::extensions::AdvancedExtension;
11use substrait::proto::{
12    AggregateRel, FetchRel, FilterRel, JoinRel, Plan, PlanRel, ProjectRel, ReadRel, Rel, RelRoot,
13    SortRel, plan_rel,
14};
15
16use crate::extensions::any::Any;
17use crate::extensions::{AddendumKind, ExtensionRegistry, SimpleExtensions, simple};
18use crate::parser::chunks::ChunkCursor;
19use crate::parser::common::{MessageParseError, ParsePair, ScopedParsePair};
20use crate::parser::errors::{ParseContext, ParseError, ParseResult};
21use crate::parser::expressions::Name;
22use crate::parser::extensions::{
23    AddendumInvocation, ExtensionInvocation, ExtensionParseError, ExtensionParser,
24};
25use crate::parser::relations::{ExtensionReadRel, RelationParsingContext, VirtualReadRel};
26use crate::parser::{ErrorKind, ExpressionParser, RelationParsePair, Rule, unwrap_single_pair};
27
28pub const PLAN_HEADER: &str = "=== Plan";
29
30/// Represents an input line, trimmed of leading two-space indents and final
31/// whitespace. Contains the number of indents and the trimmed line.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct IndentedLine<'a>(pub usize, pub &'a str);
34
35impl<'a> From<&'a str> for IndentedLine<'a> {
36    fn from(line: &'a str) -> Self {
37        let line = line.trim_end();
38        let mut spaces = 0;
39        for c in line.chars() {
40            if c == ' ' {
41                spaces += 1;
42            } else {
43                break;
44            }
45        }
46
47        let indents = spaces / 2;
48
49        let (_, trimmed) = line.split_at(indents * 2);
50
51        IndentedLine(indents, trimmed)
52    }
53}
54
55/// A `+`-prefixed addendum line attached to a relation node. The `pair` holds
56/// the grammar rule directly (already unwrapped from the outer `planNode`).
57#[derive(Debug, Clone)]
58pub struct Addendum<'a> {
59    pub pair: Pair<'a, Rule>, // Rule::addendum
60    pub line_no: i64,
61}
62
63/// A relation node in the plan tree, before conversion to a Substrait proto.
64#[derive(Debug, Clone)]
65pub struct RelationNode<'a> {
66    pub pair: Pair<'a, Rule>,
67    pub line_no: i64,
68    pub addenda: Vec<Addendum<'a>>,
69    pub children: Vec<RelationNode<'a>>,
70}
71
72impl<'a> RelationNode<'a> {
73    pub fn context(&self) -> ParseContext {
74        ParseContext {
75            line_no: self.line_no,
76            line: self.pair.as_str().to_string(),
77        }
78    }
79}
80
81/// A parsed plan line: either a relation or a `+`-prefixed addendum line.
82///
83/// Classification happens at construction time by inspecting the inner grammar
84/// rule, so downstream code can use standard Rust pattern matching rather than
85/// runtime rule inspection.
86#[derive(Debug, Clone)]
87pub enum LineNode<'a> {
88    Relation(RelationNode<'a>),
89    Addendum(Addendum<'a>),
90}
91
92impl<'a> LineNode<'a> {
93    pub fn parse(line: &'a str, line_no: i64) -> Result<Self, ParseError> {
94        let mut pairs: pest::iterators::Pairs<'a, Rule> =
95            <ExpressionParser as pest::Parser<Rule>>::parse(Rule::planNode, line).map_err(|e| {
96                ParseError::Plan(
97                    ParseContext {
98                        line_no,
99                        line: line.to_string(),
100                    },
101                    MessageParseError::new("planNode", ErrorKind::InvalidValue, Box::new(e)),
102                )
103            })?;
104
105        let outer = pairs.next().unwrap();
106        assert!(pairs.next().is_none()); // Should be exactly one pair
107        let inner = unwrap_single_pair(outer);
108
109        Ok(match inner.as_rule() {
110            Rule::addendum => LineNode::Addendum(Addendum {
111                pair: inner,
112                line_no,
113            }),
114            _ => LineNode::Relation(RelationNode {
115                pair: inner,
116                line_no,
117                addenda: Vec::new(),
118                children: Vec::new(),
119            }),
120        })
121    }
122
123    /// Parse the line as a top-level relation at depth 0 (either root_relation or regular relation)
124    pub fn parse_root(line: &'a str, line_no: i64) -> Result<Self, ParseError> {
125        let mut pairs: pest::iterators::Pairs<'a, Rule> = <ExpressionParser as pest::Parser<
126            Rule,
127        >>::parse(
128            Rule::top_level_relation, line
129        )
130        .map_err(|e| {
131            ParseError::Plan(
132                ParseContext::new(line_no, line.to_string()),
133                MessageParseError::new("top_level_relation", ErrorKind::Syntax, Box::new(e)),
134            )
135        })?;
136
137        let outer = pairs.next().unwrap();
138        assert!(pairs.next().is_none());
139
140        // top_level_relation is either root_relation or planNode.
141        // If planNode, unwrap one more level to obtain the specific relation rule.
142        let inner = unwrap_single_pair(outer);
143        let pair = if inner.as_rule() == Rule::planNode {
144            unwrap_single_pair(inner)
145        } else {
146            inner // root_relation
147        };
148
149        // planNode can include addenda (+Enh:, +Opt:, +Ext:); surface them so
150        // TreeBuilder::add_line can produce the appropriate depth-0 error.
151        if pair.as_rule() == Rule::addendum {
152            return Ok(LineNode::Addendum(Addendum { pair, line_no }));
153        }
154
155        Ok(LineNode::Relation(RelationNode {
156            pair,
157            line_no,
158            addenda: Vec::new(),
159            children: Vec::new(),
160        }))
161    }
162}
163
164#[derive(Copy, Clone, Debug)]
165pub enum State {
166    // The initial state, before we have parsed any lines.
167    Initial,
168    // The extensions section, after parsing the header and any other Extension lines.
169    Extensions,
170    // The plan section, after parsing the header and any other Plan lines.
171    Plan,
172}
173
174impl fmt::Display for State {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        write!(f, "{self:?}")
177    }
178}
179
180// An in-progress tree builder, building the tree of relations.
181#[derive(Debug, Clone, Default)]
182pub struct TreeBuilder<'a> {
183    // Current tree of nodes being built. These have been successfully parsed
184    // into Pest pairs, but have not yet been converted to substrait plans.
185    current: Option<RelationNode<'a>>,
186    // Completed trees that have been built.
187    completed: Vec<RelationNode<'a>>,
188}
189
190impl<'a> TreeBuilder<'a> {
191    /// Traverse down the tree, always taking the last child at each level, until reaching the specified depth.
192    pub fn get_at_depth(&mut self, depth: usize) -> Option<&mut RelationNode<'a>> {
193        let mut node = self.current.as_mut()?;
194        for _ in 0..depth {
195            node = node.children.last_mut()?;
196        }
197        Some(node)
198    }
199
200    pub fn add_line(&mut self, depth: usize, node: LineNode<'a>) -> Result<(), ParseError> {
201        match node {
202            LineNode::Relation(rel_node) => {
203                if depth == 0 {
204                    if let Some(prev) = self.current.take() {
205                        self.completed.push(prev);
206                    }
207                    self.current = Some(rel_node);
208                    return Ok(());
209                }
210
211                let parent = match self.get_at_depth(depth - 1) {
212                    None => {
213                        return Err(ParseError::Plan(
214                            rel_node.context(),
215                            MessageParseError::invalid(
216                                "relation",
217                                rel_node.pair.as_span(),
218                                format!("No parent found for depth {depth}"),
219                            ),
220                        ));
221                    }
222                    Some(parent) => parent,
223                };
224
225                parent.children.push(rel_node);
226            }
227            LineNode::Addendum(addendum) => {
228                let context =
229                    ParseContext::new(addendum.line_no, addendum.pair.as_str().to_string());
230                if depth == 0 {
231                    return Err(ParseError::ValidationError(
232                        context,
233                        "addenda (+ Enh: / + Opt: / + Ext:) cannot appear at the top level"
234                            .to_string(),
235                    ));
236                }
237
238                let parent = match self.get_at_depth(depth - 1) {
239                    None => {
240                        return Err(ParseError::ValidationError(
241                            context,
242                            format!("no parent found for addendum at depth {depth}"),
243                        ));
244                    }
245                    Some(parent) => parent,
246                };
247
248                if !parent.children.is_empty() {
249                    return Err(ParseError::ValidationError(
250                        context,
251                        "addenda (+ Enh: / + Opt: / + Ext:) must appear before child relations, \
252                         not after"
253                            .to_string(),
254                    ));
255                }
256
257                parent.addenda.push(addendum);
258            }
259        }
260        Ok(())
261    }
262
263    /// End of input - move any remaining nodes from stack to completed and
264    /// return any trees in progress. Resets the builder to its initial state
265    /// (empty)
266    /// Move any remaining nodes from stack to completed
267    pub fn finish(&mut self) -> Vec<RelationNode<'a>> {
268        if let Some(node) = self.current.take() {
269            self.completed.push(node);
270        }
271        std::mem::take(&mut self.completed)
272    }
273}
274
275/// Intermediate state for relation parsing: the structural tree data
276/// (children, addenda) has been parsed, but the relation's own grammar pair
277/// hasn't been converted to a protobuf relation yet.
278struct RelationContext<'a> {
279    pair: Pair<'a, Rule>,
280    line_no: i64,
281    children: Vec<Rel>,
282    input_field_count: usize,
283    addenda: Addenda<'a>,
284}
285
286/// A parsed addendum line plus enough source location to build later errors.
287#[derive(Debug, Clone)]
288struct ParsedAddendum<'a> {
289    line_no: i64,
290    line: &'a str,
291    invocation: AddendumInvocation,
292}
293
294impl<'a> ParsedAddendum<'a> {
295    fn parse(extensions: &SimpleExtensions, addendum: Addendum<'a>) -> Result<Self, ParseError> {
296        let line_no = addendum.line_no;
297        let line = addendum.pair.as_str();
298        let invocation = AddendumInvocation::parse_pair(extensions, addendum.pair)
299            .map_err(|e| ParseError::Plan(ParseContext::new(line_no, line.to_string()), e))?;
300        Ok(Self {
301            line_no,
302            line,
303            invocation,
304        })
305    }
306
307    fn context(&self) -> ParseContext {
308        ParseContext::new(self.line_no, self.line.to_string())
309    }
310
311    fn relation_context<'b>(
312        &'b self,
313        registry: &'b ExtensionRegistry,
314    ) -> RelationParsingContext<'b> {
315        RelationParsingContext {
316            registry,
317            line_no: self.line_no,
318            line: self.line,
319        }
320    }
321    fn resolve_detail(&self, registry: &ExtensionRegistry) -> Result<Any, ParseError> {
322        self.relation_context(registry).resolve_addendum_detail(
323            self.invocation.kind,
324            &self.invocation.name,
325            &self.invocation.args,
326        )
327    }
328}
329
330/// Parsed `+` lines attached to a relation.
331#[derive(Debug, Clone, Default)]
332struct Addenda<'a> {
333    items: Vec<ParsedAddendum<'a>>,
334}
335
336impl<'a> Addenda<'a> {
337    fn parse(
338        extensions: &SimpleExtensions,
339        addenda: Vec<Addendum<'a>>,
340    ) -> Result<Self, ParseError> {
341        let items = addenda
342            .into_iter()
343            .map(|addendum| ParsedAddendum::parse(extensions, addendum))
344            .collect::<Result<Vec<_>, ParseError>>()?;
345        Ok(Self { items })
346    }
347
348    fn first(&self) -> Option<&ParsedAddendum<'a>> {
349        self.items.first()
350    }
351
352    fn reject_all(&self, message: &'static str) -> Result<(), ParseError> {
353        if let Some(addendum) = self.first() {
354            return Err(ParseError::ValidationError(
355                addendum.context(),
356                message.to_string(),
357            ));
358        }
359        Ok(())
360    }
361
362    fn into_standard_advanced_extension(
363        self,
364        registry: &ExtensionRegistry,
365    ) -> Result<Option<AdvancedExtension>, ParseError> {
366        let mut enhancement = None;
367        let mut optimizations = Vec::new();
368
369        for addendum in self.items {
370            match addendum.invocation.kind {
371                AddendumKind::Enhancement => {
372                    if enhancement.is_some() {
373                        return Err(ParseError::ValidationError(
374                            addendum.context(),
375                            "at most one enhancement per relation is allowed".to_string(),
376                        ));
377                    }
378                    enhancement = Some(addendum.resolve_detail(registry)?.into());
379                }
380                AddendumKind::Optimization => {
381                    optimizations.push(addendum.resolve_detail(registry)?.into());
382                }
383                AddendumKind::ExtensionTable => {
384                    return Err(ParseError::ValidationError(
385                        addendum.context(),
386                        "+ Ext addenda can only be used with Read:Extension".to_string(),
387                    ));
388                }
389            }
390        }
391
392        if enhancement.is_none() && optimizations.is_empty() {
393            return Ok(None);
394        }
395
396        Ok(Some(AdvancedExtension {
397            enhancement,
398            optimization: optimizations,
399        }))
400    }
401
402    fn into_extension_read_parts(
403        self,
404        registry: &ExtensionRegistry,
405        relation_context: ParseContext,
406    ) -> Result<(Any, Option<AdvancedExtension>), ParseError> {
407        let mut extension_table = None;
408        let mut advanced_addenda = Vec::new();
409
410        for addendum in self.items {
411            match addendum.invocation.kind {
412                AddendumKind::ExtensionTable => {
413                    if extension_table.is_some() {
414                        return Err(ParseError::ValidationError(
415                            addendum.context(),
416                            "Read:Extension allows exactly one + Ext addendum".to_string(),
417                        ));
418                    }
419                    extension_table = Some(addendum);
420                }
421                AddendumKind::Enhancement | AddendumKind::Optimization => {
422                    advanced_addenda.push(addendum);
423                }
424            }
425        }
426
427        let extension_table = extension_table.ok_or_else(|| {
428            ParseError::ValidationError(
429                relation_context,
430                "Read:Extension requires exactly one + Ext addendum".to_string(),
431            )
432        })?;
433
434        let detail = extension_table.resolve_detail(registry)?;
435        let advanced_extension = Addenda {
436            items: advanced_addenda,
437        }
438        .into_standard_advanced_extension(registry)?;
439
440        Ok((detail, advanced_extension))
441    }
442}
443
444// Relation parsing component - handles converting LineNodes to Relations
445#[derive(Debug, Clone, Default)]
446pub struct RelationParser<'a> {
447    tree: TreeBuilder<'a>,
448}
449
450impl<'a> RelationParser<'a> {
451    /// Dispatch by grammar rule after validating addenda constraints.
452    /// Standard relations go through [`parse_rel`](Self::parse_rel);
453    /// extension relations go through
454    /// [`parse_extension_relation`](Self::parse_extension_relation).
455    fn parse_relation(
456        &self,
457        extensions: &SimpleExtensions,
458        registry: &ExtensionRegistry,
459        ctx: RelationContext,
460    ) -> Result<(Rel, usize), ParseError> {
461        match ctx.pair.as_rule() {
462            Rule::extension_read_relation => {
463                self.parse_extension_read_relation(extensions, registry, ctx)
464            }
465            Rule::virtual_read_relation => {
466                self.parse_rel::<VirtualReadRel>(extensions, registry, ctx)
467            }
468            Rule::read_relation => self.parse_rel::<ReadRel>(extensions, registry, ctx),
469            Rule::filter_relation => self.parse_rel::<FilterRel>(extensions, registry, ctx),
470            Rule::project_relation => self.parse_rel::<ProjectRel>(extensions, registry, ctx),
471            Rule::aggregate_relation => self.parse_rel::<AggregateRel>(extensions, registry, ctx),
472            Rule::sort_relation => self.parse_rel::<SortRel>(extensions, registry, ctx),
473            Rule::fetch_relation => self.parse_rel::<FetchRel>(extensions, registry, ctx),
474            Rule::join_relation => self.parse_rel::<JoinRel>(extensions, registry, ctx),
475            Rule::extension_relation => self.parse_extension_relation(extensions, registry, ctx),
476            _ => unreachable!("unhandled relation rule: {:?}", ctx.pair.as_rule()),
477        }
478    }
479
480    /// Generic bridge between [`parse_relation`](Self::parse_relation) and
481    /// the [`RelationParsePair`] trait: wraps `MessageParseError` with line
482    /// context and calls [`into_rel`](RelationParsePair::into_rel) to apply
483    /// addenda and produce the final [`Rel`].
484    fn parse_rel<T: RelationParsePair>(
485        &self,
486        extensions: &SimpleExtensions,
487        registry: &ExtensionRegistry,
488        ctx: RelationContext,
489    ) -> Result<(Rel, usize), ParseError> {
490        let RelationContext {
491            pair,
492            line_no,
493            children,
494            input_field_count,
495            addenda,
496        } = ctx;
497        assert_eq!(pair.as_rule(), T::rule());
498        let line = pair.as_str();
499        let advanced_extension = addenda.into_standard_advanced_extension(registry)?;
500
501        match T::parse_pair_with_context(extensions, pair, children, input_field_count) {
502            Ok((parsed, count)) => Ok((parsed.into_rel(advanced_extension), count)),
503            Err(e) => Err(ParseError::Plan(
504                ParseContext::new(line_no, line.to_string()),
505                e,
506            )),
507        }
508    }
509
510    /// Handle extension relations separately from [`parse_rel`](Self::parse_rel)
511    /// because they need registry lookups that [`RelationParsePair`] doesn't
512    /// support.
513    fn parse_extension_relation(
514        &self,
515        extensions: &SimpleExtensions,
516        registry: &ExtensionRegistry,
517        ctx: RelationContext,
518    ) -> Result<(Rel, usize), ParseError> {
519        assert_eq!(ctx.pair.as_rule(), Rule::extension_relation);
520        let line_no = ctx.line_no;
521        let line = ctx.pair.as_str().to_string();
522        let pair_span = ctx.pair.as_span();
523
524        ctx.addenda
525            .reject_all("extension relations do not support addenda (+ Enh / + Opt / + Ext)")?;
526
527        let ExtensionInvocation {
528            relation_kind,
529            name,
530            args: extension_args,
531        } = ExtensionInvocation::parse_pair(extensions, ctx.pair.clone())
532            .map_err(|e| ParseError::Plan(ParseContext::new(line_no, line.clone()), e))?;
533
534        let child_count = ctx.children.len();
535        relation_kind
536            .validate_child_count(child_count)
537            .map_err(|e| {
538                ParseError::Plan(
539                    ParseContext::new(line_no, line.to_string()),
540                    MessageParseError::invalid("extension_relation", pair_span, e),
541                )
542            })?;
543
544        let context = RelationParsingContext {
545            registry,
546            line_no,
547            line: &line,
548        };
549
550        let detail = context.resolve_extension_detail(&name, &extension_args)?;
551        let output_column_count = extension_args.output_columns.len();
552
553        let rel = relation_kind.create_rel(detail, ctx.children);
554
555        Ok((rel, output_column_count))
556    }
557
558    /// Parse `Read:Extension[...]`, whose table detail is supplied by exactly
559    /// one `+ Ext:Name[...]` addendum.
560    fn parse_extension_read_relation(
561        &self,
562        extensions: &SimpleExtensions,
563        registry: &ExtensionRegistry,
564        ctx: RelationContext,
565    ) -> Result<(Rel, usize), ParseError> {
566        assert_eq!(ctx.pair.as_rule(), Rule::extension_read_relation);
567        let context = ParseContext::new(ctx.line_no, ctx.pair.as_str().to_string());
568        let (detail, advanced_extension) = ctx
569            .addenda
570            .into_extension_read_parts(registry, context.clone())?;
571
572        ExtensionReadRel::parse_pair_with_detail(
573            extensions,
574            ctx.pair,
575            ctx.children,
576            ctx.input_field_count,
577            detail,
578            advanced_extension,
579        )
580        .map_err(|e| ParseError::Plan(context, e))
581    }
582
583    /// Walk the relation tree depth-first, converting structural types
584    /// (children, addenda) into proto types via [`RelationContext`].
585    /// Delegates grammar-rule-specific work to
586    /// [`parse_relation`](Self::parse_relation).
587    fn build_rel(
588        &self,
589        extensions: &SimpleExtensions,
590        registry: &ExtensionRegistry,
591        node: RelationNode,
592    ) -> Result<(Rel, usize), ParseError> {
593        let mut children: Vec<Rel> = Vec::new();
594        let mut input_field_count: usize = 0;
595        for child in node.children {
596            let (rel, count) = self.build_rel(extensions, registry, child)?;
597            input_field_count += count;
598            children.push(rel);
599        }
600
601        let addenda = Addenda::parse(extensions, node.addenda)?;
602
603        self.parse_relation(
604            extensions,
605            registry,
606            RelationContext {
607                pair: node.pair,
608                line_no: node.line_no,
609                children,
610                input_field_count,
611                addenda,
612            },
613        )
614    }
615
616    /// Build a tree of relations.
617    fn build_plan_rel(
618        &self,
619        extensions: &SimpleExtensions,
620        registry: &ExtensionRegistry,
621        node: RelationNode,
622    ) -> Result<PlanRel, ParseError> {
623        // Plain relations are allowed as root relations; they just don't have names.
624        if node.pair.as_rule() != Rule::root_relation {
625            let (rel, _) = self.build_rel(extensions, registry, node)?;
626            return Ok(PlanRel {
627                rel_type: Some(plan_rel::RelType::Rel(rel)),
628            });
629        }
630
631        // Root relations don't support addenda — reject rather than silently discard.
632        if !node.addenda.is_empty() {
633            let first = &node.addenda[0];
634            let context = ParseContext::new(first.line_no, first.pair.as_str().to_string());
635            return Err(ParseError::ValidationError(
636                context,
637                "addenda (+ Enh: / + Opt: / + Ext:) are not supported on Root relations"
638                    .to_string(),
639            ));
640        }
641
642        // Named root relation.
643        let context = node.context();
644        let span = node.pair.as_span();
645
646        // Parse the column names
647        let column_names_pair = unwrap_single_pair(node.pair);
648        assert_eq!(column_names_pair.as_rule(), Rule::root_name_list);
649
650        let names: Vec<String> = column_names_pair
651            .into_inner()
652            .map(|name_pair| {
653                assert_eq!(name_pair.as_rule(), Rule::name);
654                Name::parse_pair(name_pair).0
655            })
656            .collect();
657
658        let mut children = node.children;
659        let child = match children.len() {
660            1 => {
661                let (rel, _) = self.build_rel(extensions, registry, children.pop().unwrap())?;
662                rel
663            }
664            n => {
665                return Err(ParseError::Plan(
666                    context,
667                    MessageParseError::invalid(
668                        "root_relation",
669                        span,
670                        format!("Root relation must have exactly one child, found {n}"),
671                    ),
672                ));
673            }
674        };
675
676        Ok(PlanRel {
677            rel_type: Some(plan_rel::RelType::Root(RelRoot {
678                names,
679                input: Some(child),
680            })),
681        })
682    }
683
684    /// Build all the trees.
685    fn build(
686        mut self,
687        extensions: &SimpleExtensions,
688        registry: &ExtensionRegistry,
689    ) -> Result<Vec<PlanRel>, ParseError> {
690        let nodes = self.tree.finish();
691        nodes
692            .into_iter()
693            .map(|n| self.build_plan_rel(extensions, registry, n))
694            .collect::<Result<Vec<PlanRel>, ParseError>>()
695    }
696}
697
698/// A parser for Substrait query plans in text format.
699///
700/// The `Parser` converts human-readable Substrait text format into Substrait
701/// protobuf plans. It handles both the extensions section (which defines
702/// functions, types, etc.) and the plan section (which defines the actual query
703/// structure).
704///
705/// ## Usage
706///
707/// The simplest entry point is the static `parse()` method:
708///
709/// ```rust
710/// use substrait_explain::Parser;
711///
712/// let plan_text = r#"
713/// === Plan
714/// Root[c, d]
715///   Project[$1, 42]
716///     Read[schema.table => a:i64, b:string?]
717/// "#;
718///
719/// let plan = Parser::parse(plan_text).unwrap();
720/// ```
721///
722/// ## Input Format
723///
724/// The parser expects input in the following format:
725///
726/// ```text
727/// === Extensions
728/// URNs:
729///   @  1: https://github.com/substrait-io/substrait/blob/main/extensions/functions_arithmetic.yaml
730/// Functions:
731///   # 10 @  1: add
732/// === Plan
733/// Root[columns]
734///   Relation[arguments => columns]
735///     ChildRelation[arguments => columns]
736/// ```
737///
738/// - **Extensions section** (optional): Defines URNs and function/type declarations
739/// - **Plan section** (required): Defines the query structure with indented relations
740///
741/// ## Error Handling
742///
743/// The parser provides detailed error information including:
744/// - Line number where the error occurred
745/// - The actual line content that failed to parse
746/// - Specific error type and description
747///
748/// ```rust
749/// use substrait_explain::Parser;
750///
751/// let invalid_plan = r#"
752/// === Plan
753/// InvalidRelation[invalid syntax]
754/// "#;
755///
756/// match Parser::parse(invalid_plan) {
757///     Ok(plan) => println!("Successfully parsed"),
758///     Err(e) => eprintln!("Parse error: {}", e),
759/// }
760/// ```
761///
762/// ## Supported Relations
763///
764/// The parser supports all standard Substrait relations:
765/// - `Read[table => columns]` - Read from a table
766/// - `Project[expressions]` - Project columns/expressions
767/// - `Filter[condition => columns]` - Filter rows
768/// - `Root[columns]` - Root relation with output columns
769/// - And more...
770///
771/// ## Extensions Support
772///
773/// The parser fully supports Substrait Simple Extensions, allowing you to:
774/// - Define custom functions with URNs and anchors
775/// - Reference functions by name in expressions
776/// - Use custom types and type variations
777///
778/// ```rust
779/// use substrait_explain::Parser;
780///
781/// let plan_with_extensions = r#"
782/// === Extensions
783/// URNs:
784///   @  1: https://example.com/functions.yaml
785/// Functions:
786///   ## 10 @  1: my_custom_function
787/// === Plan
788/// Root[result]
789///   Project[my_custom_function($0, $1):i32]
790///     Read[table => col1:i32, col2:i32]
791/// "#;
792///
793/// let plan = Parser::parse(plan_with_extensions).unwrap();
794/// ```
795///
796/// ## Performance
797///
798/// The parser is designed for efficiency:
799/// - Single-pass parsing with minimal allocations
800/// - Early error detection and reporting
801/// - Memory-efficient tree building
802///
803/// ## Thread Safety
804///
805/// `Parser` instances are not thread-safe and should not be shared between threads.
806/// However, the static `parse()` method is safe to call from multiple threads.
807#[derive(Debug)]
808pub struct Parser<'a> {
809    line_no: i64,
810    state: State,
811    /// Cursor over the remaining input, advanced one chunk at a time by
812    /// [`next_chunk`](Self::next_chunk). `None` before parsing starts and once
813    /// the input is exhausted.
814    cursor: Option<ChunkCursor<'a>>,
815    extension_parser: ExtensionParser,
816    extension_registry: ExtensionRegistry,
817    relation_parser: RelationParser<'a>,
818}
819impl<'a> Default for Parser<'a> {
820    fn default() -> Self {
821        Self::new()
822    }
823}
824
825impl<'a> Parser<'a> {
826    /// Parse a Substrait plan from text format.
827    ///
828    /// This is the main entry point for parsing.
829    ///
830    /// The input should be in the Substrait text format, which consists of:
831    /// - An optional extensions section starting with "=== Extensions"
832    /// - A plan section starting with "=== Plan"
833    /// - Indented relation definitions
834    ///
835    /// # Examples
836    ///
837    /// Simple parsing:
838    /// ```rust
839    /// use substrait_explain::Parser;
840    ///
841    /// let plan_text = r#"
842    /// === Plan
843    /// Root[result]
844    ///   Read[table => col:i32]
845    /// "#;
846    ///
847    /// let plan = Parser::parse(plan_text).unwrap();
848    /// assert_eq!(plan.relations.len(), 1);
849    /// ```
850    ///
851    /// # Errors
852    ///
853    /// Returns a [`ParseError`] if the input cannot be parsed.
854    pub fn parse(input: &str) -> ParseResult {
855        Self::new().parse_plan(input)
856    }
857
858    /// Create a new parser with default configuration.
859    pub fn new() -> Self {
860        Self {
861            line_no: 1,
862            state: State::Initial,
863            cursor: None,
864            extension_parser: ExtensionParser::default(),
865            extension_registry: ExtensionRegistry::new(),
866            relation_parser: RelationParser::default(),
867        }
868    }
869
870    /// Configure the parser to use the specified extension registry.
871    pub fn with_extension_registry(mut self, registry: ExtensionRegistry) -> Self {
872        self.extension_registry = registry;
873        self
874    }
875
876    /// Parse a Substrait plan with the current parser configuration.
877    pub fn parse_plan(mut self, input: &'a str) -> ParseResult {
878        self.cursor = ChunkCursor::new(input, 1);
879        while self.cursor.is_some() {
880            let (chunk, line_no) = self.next_chunk();
881
882            if chunk.trim().is_empty() {
883                continue;
884            }
885
886            self.line_no = line_no;
887            // TODO: multi-line chunk errors report a confusing location. The
888            // outer message uses the chunk's first line, while the Pest span
889            // inside counts lines relative to the chunk, so neither points at
890            // the true absolute line. E.g. a bad row on the 2nd line of a chunk
891            // starting at line 3 produces:
892            //   Error parsing plan on line 3: '...': ...
893            //    --> 2:8
894            // We should map the Pest-relative line back to an absolute line.
895            self.parse_line(chunk)?;
896        }
897
898        let plan = self.build_plan()?;
899        Ok(plan)
900    }
901
902    /// Group the next chunk out of the cursor: always its first physical line,
903    /// plus any `- ` continuation lines indented one level deeper while we are
904    /// in the plan section.
905    ///
906    /// For consistency, chunks always end with no newline.
907    fn next_chunk(&mut self) -> (&'a str, i64) {
908        let mut c = self
909            .cursor
910            .take()
911            .expect("next_chunk called with no cursor");
912
913        // Every chunk contains at least its first physical line.
914        let first = c
915            .peek_line()
916            .expect("a non-exhausted cursor always yields a line");
917        c.merge(first);
918
919        if matches!(self.state, State::Plan) {
920            let base = IndentedLine::from(first.as_str()).0;
921            while let Some(line) = c.peek_line() {
922                let IndentedLine(depth, body) = IndentedLine::from(line.as_str());
923                if depth == base + 1 && body.starts_with("- ") {
924                    c.merge(line);
925                } else {
926                    break;
927                }
928            }
929        }
930
931        let line_no = c.start_line_no();
932        let (chunk, rest) = c.next();
933        self.cursor = rest;
934        (chunk.trim_end_matches(['\r', '\n']), line_no)
935    }
936
937    /// Parse a single line of input.
938    fn parse_line(&mut self, line: &'a str) -> Result<(), ParseError> {
939        let indented_line = IndentedLine::from(line);
940        let line_no = self.line_no;
941        let ctx = || ParseContext {
942            line_no,
943            line: line.to_string(),
944        };
945
946        match self.state {
947            State::Initial => self.parse_initial(indented_line),
948            State::Extensions => self
949                .parse_extensions(indented_line)
950                .map_err(|e| ParseError::Extension(ctx(), e)),
951            State::Plan => {
952                let IndentedLine(depth, line_str) = indented_line;
953
954                // Parse the line
955                let node = if depth == 0 {
956                    LineNode::parse_root(line_str, line_no)?
957                } else {
958                    LineNode::parse(line_str, line_no)?
959                };
960
961                self.relation_parser.tree.add_line(depth, node)
962            }
963        }
964    }
965
966    /// Parse the initial line(s) of the input, which is either a blank line or
967    /// the extensions or plan header.
968    fn parse_initial(&mut self, line: IndentedLine) -> Result<(), ParseError> {
969        match line {
970            IndentedLine(0, l) if l.trim().is_empty() => {}
971            IndentedLine(0, simple::EXTENSIONS_HEADER) => {
972                self.state = State::Extensions;
973            }
974            IndentedLine(0, PLAN_HEADER) => {
975                self.state = State::Plan;
976            }
977            IndentedLine(n, l) => {
978                return Err(ParseError::Initial(
979                    ParseContext::new(n as i64, l.to_string()),
980                    MessageParseError::invalid(
981                        "initial",
982                        pest::Span::new(l, 0, l.len()).expect("Invalid span?!"),
983                        format!("Unknown initial line: {l:?}"),
984                    ),
985                ));
986            }
987        }
988        Ok(())
989    }
990
991    /// Parse a single line from the extensions section of the input, updating
992    /// the parser state.
993    fn parse_extensions(&mut self, line: IndentedLine<'_>) -> Result<(), ExtensionParseError> {
994        if line == IndentedLine(0, PLAN_HEADER) {
995            self.state = State::Plan;
996            return Ok(());
997        }
998        self.extension_parser.parse_line(line)
999    }
1000
1001    /// Build the plan from the parser state with warning collection.
1002    fn build_plan(self) -> Result<Plan, ParseError> {
1003        let Parser {
1004            relation_parser,
1005            extension_parser,
1006            extension_registry,
1007            ..
1008        } = self;
1009
1010        let extensions = extension_parser.extensions();
1011
1012        // Parse the tree into relations
1013        let root_relations = relation_parser.build(extensions, &extension_registry)?;
1014
1015        // Build the final plan
1016        Ok(Plan {
1017            extension_urns: extensions.to_extension_urns(),
1018            extensions: extensions.to_extension_declarations(),
1019            relations: root_relations,
1020            ..Default::default()
1021        })
1022    }
1023}
1024
1025#[cfg(test)]
1026mod tests {
1027    use substrait::proto::extensions::simple_extension_declaration::MappingType;
1028    use substrait::proto::rel::RelType;
1029
1030    use super::*;
1031    use crate::extensions::simple::ExtensionKind;
1032    use crate::parser::extensions::ExpectedExtensionLine;
1033
1034    #[test]
1035    fn test_parse_basic_block() {
1036        let mut expected_extensions = SimpleExtensions::new();
1037        expected_extensions
1038            .add_extension_urn("/urn/common".to_string(), 1)
1039            .unwrap();
1040        expected_extensions
1041            .add_extension_urn("/urn/specific_funcs".to_string(), 2)
1042            .unwrap();
1043        expected_extensions
1044            .add_extension(ExtensionKind::Function, 1, 10, "func_a".to_string())
1045            .unwrap();
1046        expected_extensions
1047            .add_extension(ExtensionKind::Function, 2, 11, "func_b_special".to_string())
1048            .unwrap();
1049        expected_extensions
1050            .add_extension(ExtensionKind::Type, 1, 20, "SomeType".to_string())
1051            .unwrap();
1052        expected_extensions
1053            .add_extension(ExtensionKind::TypeVariation, 2, 30, "VarX".to_string())
1054            .unwrap();
1055
1056        let mut parser = ExtensionParser::default();
1057        let input_block = r#"
1058URNs:
1059  @  1: /urn/common
1060  @  2: /urn/specific_funcs
1061Functions:
1062  # 10 @  1: func_a
1063  # 11 @  2: func_b_special
1064Types:
1065  # 20 @  1: SomeType
1066Type Variations:
1067  # 30 @  2: VarX
1068"#;
1069
1070        for line_str in input_block.trim().lines() {
1071            parser
1072                .parse_line(IndentedLine::from(line_str))
1073                .unwrap_or_else(|e| panic!("Failed to parse line \'{line_str}\': {e:?}"));
1074        }
1075
1076        assert_eq!(*parser.extensions(), expected_extensions);
1077
1078        let extensions_str = parser.extensions().to_string("  ");
1079        // The writer adds the header; the ExtensionParser does not parse the
1080        // header, so we add it here for comparison.
1081        let expected_str = format!(
1082            "{}\n{}",
1083            simple::EXTENSIONS_HEADER,
1084            input_block.trim_start()
1085        );
1086        assert_eq!(extensions_str.trim(), expected_str.trim());
1087        // Check final state after all lines are processed.
1088        // The last significant line in input_block is a TypeVariation declaration.
1089        assert_eq!(
1090            parser.state(),
1091            ExpectedExtensionLine::ExtensionDeclarations(ExtensionKind::TypeVariation)
1092        );
1093
1094        // Check that a subsequent blank line correctly resets state to Extensions.
1095        parser.parse_line(IndentedLine(0, "")).unwrap();
1096        assert_eq!(parser.state(), ExpectedExtensionLine::Extensions);
1097    }
1098
1099    /// Test that we can parse a larger extensions block and it matches the input.
1100    #[test]
1101    fn test_parse_complete_extension_block() {
1102        let mut parser = ExtensionParser::default();
1103        let input_block = r#"
1104URNs:
1105  @  1: /urn/common
1106  @  2: /urn/specific_funcs
1107  @  3: /urn/types_lib
1108  @  4: /urn/variations_lib
1109Functions:
1110  # 10 @  1: func_a
1111  # 11 @  2: func_b_special
1112  # 12 @  1: func_c_common
1113Types:
1114  # 20 @  1: CommonType
1115  # 21 @  3: LibraryType
1116  # 22 @  1: AnotherCommonType
1117Type Variations:
1118  # 30 @  4: VarX
1119  # 31 @  4: VarY
1120"#;
1121
1122        for line_str in input_block.trim().lines() {
1123            parser
1124                .parse_line(IndentedLine::from(line_str))
1125                .unwrap_or_else(|e| panic!("Failed to parse line \'{line_str}\': {e:?}"));
1126        }
1127
1128        let extensions_str = parser.extensions().to_string("  ");
1129        // The writer adds the header; the ExtensionParser does not parse the
1130        // header, so we add it here for comparison.
1131        let expected_str = format!(
1132            "{}\n{}",
1133            simple::EXTENSIONS_HEADER,
1134            input_block.trim_start()
1135        );
1136        assert_eq!(extensions_str.trim(), expected_str.trim());
1137    }
1138
1139    #[test]
1140    fn test_parse_relation_tree() {
1141        // Example plan with a Project, a Filter, and a Read, nested by indentation
1142        let plan = r#"=== Plan
1143Project[$0, $1, 42, 84]
1144  Filter[$2 => $0, $1]
1145    Read[my.table => a:i32, b:string?, c:boolean]
1146"#;
1147        let mut parser = Parser::default();
1148        for line in plan.lines() {
1149            parser.parse_line(line).unwrap();
1150        }
1151
1152        // Complete the current tree to convert it to relations
1153        let plan = parser.build_plan().unwrap();
1154
1155        let root_rel = &plan.relations[0].rel_type;
1156        let first_rel = match root_rel {
1157            Some(plan_rel::RelType::Rel(rel)) => rel,
1158            _ => panic!("Expected Rel type, got {root_rel:?}"),
1159        };
1160        // Root should be Project
1161        let project = match &first_rel.rel_type {
1162            Some(RelType::Project(p)) => p,
1163            other => panic!("Expected Project at root, got {other:?}"),
1164        };
1165
1166        // Check that Project has Filter as input
1167        assert!(project.input.is_some());
1168        let filter_input = project.input.as_ref().unwrap();
1169
1170        // Check that Filter has Read as input
1171        match &filter_input.rel_type {
1172            Some(RelType::Filter(_)) => {
1173                match &filter_input.rel_type {
1174                    Some(RelType::Filter(filter)) => {
1175                        assert!(filter.input.is_some());
1176                        let read_input = filter.input.as_ref().unwrap();
1177
1178                        // Check that Read has no input (it's a leaf)
1179                        match &read_input.rel_type {
1180                            Some(RelType::Read(_)) => {}
1181                            other => panic!("Expected Read relation, got {other:?}"),
1182                        }
1183                    }
1184                    other => panic!("Expected Filter relation, got {other:?}"),
1185                }
1186            }
1187            other => panic!("Expected Filter relation, got {other:?}"),
1188        }
1189    }
1190
1191    #[test]
1192    fn test_parse_root_relation() {
1193        // Test a plan with a Root relation
1194        let plan = r#"=== Plan
1195Root[result]
1196  Project[$0, $1]
1197    Read[my.table => a:i32, b:string?]
1198"#;
1199        let mut parser = Parser::default();
1200        for line in plan.lines() {
1201            parser.parse_line(line).unwrap();
1202        }
1203
1204        let plan = parser.build_plan().unwrap();
1205
1206        // Check that we have exactly one relation
1207        assert_eq!(plan.relations.len(), 1);
1208
1209        let root_rel = &plan.relations[0].rel_type;
1210        let rel_root = match root_rel {
1211            Some(plan_rel::RelType::Root(rel_root)) => rel_root,
1212            other => panic!("Expected Root type, got {other:?}"),
1213        };
1214
1215        // Check that the root has the correct name
1216        assert_eq!(rel_root.names, vec!["result"]);
1217
1218        // Check that the root has a Project as input
1219        let project_input = match &rel_root.input {
1220            Some(rel) => rel,
1221            None => panic!("Root should have an input"),
1222        };
1223
1224        let project = match &project_input.rel_type {
1225            Some(RelType::Project(p)) => p,
1226            other => panic!("Expected Project as root input, got {other:?}"),
1227        };
1228
1229        // Check that Project has Read as input
1230        let read_input = match &project.input {
1231            Some(rel) => rel,
1232            None => panic!("Project should have an input"),
1233        };
1234
1235        match &read_input.rel_type {
1236            Some(RelType::Read(_)) => {}
1237            other => panic!("Expected Read relation, got {other:?}"),
1238        }
1239    }
1240
1241    #[test]
1242    fn test_parse_root_relation_no_names() {
1243        // Test a plan with a Root relation with no names
1244        let plan = r#"=== Plan
1245Root[]
1246  Project[$0, $1]
1247    Read[my.table => a:i32, b:string?]
1248"#;
1249        let mut parser = Parser::default();
1250        for line in plan.lines() {
1251            parser.parse_line(line).unwrap();
1252        }
1253
1254        let plan = parser.build_plan().unwrap();
1255
1256        let root_rel = &plan.relations[0].rel_type;
1257        let rel_root = match root_rel {
1258            Some(plan_rel::RelType::Root(rel_root)) => rel_root,
1259            other => panic!("Expected Root type, got {other:?}"),
1260        };
1261
1262        // Check that the root has no names
1263        assert_eq!(rel_root.names, Vec::<String>::new());
1264    }
1265
1266    #[test]
1267    fn test_parse_full_plan() {
1268        // Test a complete Substrait plan with extensions and relations
1269        let input = r#"
1270=== Extensions
1271URNs:
1272  @  1: /urn/common
1273  @  2: /urn/specific_funcs
1274Functions:
1275  # 10 @  1: func_a
1276  # 11 @  2: func_b_special
1277Types:
1278  # 20 @  1: SomeType
1279Type Variations:
1280  # 30 @  2: VarX
1281
1282=== Plan
1283Project[$0, $1, 42, 84]
1284  Filter[$2 => $0, $1]
1285    Read[my.table => a:i32, b:string?, c:boolean]
1286"#;
1287
1288        let plan = Parser::parse(input).unwrap();
1289
1290        // Verify the plan structure
1291        assert_eq!(plan.extension_urns.len(), 2);
1292        assert_eq!(plan.extensions.len(), 4);
1293        assert_eq!(plan.relations.len(), 1);
1294
1295        // Verify extension URIs
1296        let urn1 = &plan.extension_urns[0];
1297        assert_eq!(urn1.extension_urn_anchor, 1);
1298        assert_eq!(urn1.urn, "/urn/common");
1299
1300        let urn2 = &plan.extension_urns[1];
1301        assert_eq!(urn2.extension_urn_anchor, 2);
1302        assert_eq!(urn2.urn, "/urn/specific_funcs");
1303
1304        // Verify extensions
1305        let func1 = &plan.extensions[0];
1306        match &func1.mapping_type {
1307            Some(MappingType::ExtensionFunction(f)) => {
1308                assert_eq!(f.function_anchor, 10);
1309                assert_eq!(f.extension_urn_reference, 1);
1310                assert_eq!(f.name, "func_a");
1311            }
1312            other => panic!("Expected ExtensionFunction, got {other:?}"),
1313        }
1314
1315        let func2 = &plan.extensions[1];
1316        match &func2.mapping_type {
1317            Some(MappingType::ExtensionFunction(f)) => {
1318                assert_eq!(f.function_anchor, 11);
1319                assert_eq!(f.extension_urn_reference, 2);
1320                assert_eq!(f.name, "func_b_special");
1321            }
1322            other => panic!("Expected ExtensionFunction, got {other:?}"),
1323        }
1324
1325        let type1 = &plan.extensions[2];
1326        match &type1.mapping_type {
1327            Some(MappingType::ExtensionType(t)) => {
1328                assert_eq!(t.type_anchor, 20);
1329                assert_eq!(t.extension_urn_reference, 1);
1330                assert_eq!(t.name, "SomeType");
1331            }
1332            other => panic!("Expected ExtensionType, got {other:?}"),
1333        }
1334
1335        let var1 = &plan.extensions[3];
1336        match &var1.mapping_type {
1337            Some(MappingType::ExtensionTypeVariation(v)) => {
1338                assert_eq!(v.type_variation_anchor, 30);
1339                assert_eq!(v.extension_urn_reference, 2);
1340                assert_eq!(v.name, "VarX");
1341            }
1342            other => panic!("Expected ExtensionTypeVariation, got {other:?}"),
1343        }
1344
1345        // Verify the relation tree structure
1346        let root_rel = &plan.relations[0];
1347        match &root_rel.rel_type {
1348            Some(plan_rel::RelType::Rel(rel)) => {
1349                match &rel.rel_type {
1350                    Some(RelType::Project(project)) => {
1351                        // Verify Project relation
1352                        assert_eq!(project.expressions.len(), 2); // 42 and 84
1353                        assert!(project.input.is_some()); // Should have Filter as input
1354
1355                        // Check the Filter input
1356                        let filter_input = project.input.as_ref().unwrap();
1357                        match &filter_input.rel_type {
1358                            Some(RelType::Filter(filter)) => {
1359                                assert!(filter.input.is_some()); // Should have Read as input
1360
1361                                // Check the Read input
1362                                let read_input = filter.input.as_ref().unwrap();
1363                                match &read_input.rel_type {
1364                                    Some(RelType::Read(read)) => {
1365                                        // Verify Read relation
1366                                        let schema = read.base_schema.as_ref().unwrap();
1367                                        assert_eq!(schema.names.len(), 3);
1368                                        assert_eq!(schema.names[0], "a");
1369                                        assert_eq!(schema.names[1], "b");
1370                                        assert_eq!(schema.names[2], "c");
1371
1372                                        let struct_ = schema.r#struct.as_ref().unwrap();
1373                                        assert_eq!(struct_.types.len(), 3);
1374                                    }
1375                                    other => panic!("Expected Read relation, got {other:?}"),
1376                                }
1377                            }
1378                            other => panic!("Expected Filter relation, got {other:?}"),
1379                        }
1380                    }
1381                    other => panic!("Expected Project relation, got {other:?}"),
1382                }
1383            }
1384            other => panic!("Expected Rel type, got {other:?}"),
1385        }
1386    }
1387}