rustemo/lr/
builder.rs

1use crate::{
2    builder::Builder, context::Context, input::Input, lexer::Token, location::Location,
3    parser::State,
4};
5use core::fmt::Debug;
6
7/// A builder variant for LR parsing.
8///
9/// Builder should keep its internal stack of subresults, similar to the way LR
10/// parsing operates.
11pub trait LRBuilder<'i, I, C, S, P, TK>: Builder
12where
13    I: Input + ?Sized,
14    C: Context<'i, I, S, TK>,
15    S: State,
16{
17    /// Called when LR shifting is taking place.
18    ///
19    /// # Arguments
20    ///
21    /// * `term_idx` - A terminal unique identifier - index.
22    /// * `token` - A token recognized in the input.
23    fn shift_action(&mut self, context: &C, token: Token<'i, I, TK>);
24
25    /// Called when LR reduce is taking place.
26    ///
27    /// # Arguments
28    ///
29    /// * `prod_idx` - A production unique identifier, used to decide the action
30    ///   to perform.
31    /// * `prod_len` - A RHS length, used to pop appropriate number of
32    ///   subresults from the stack
33    fn reduce_action(&mut self, context: &C, prod: P, prod_len: usize);
34}
35
36/// TreeBuilder is a builder that builds a generic concrete parse tree.
37pub struct TreeBuilder<'i, I, P, TK>
38where
39    I: Input + ?Sized,
40{
41    res_stack: Vec<TreeNode<'i, I, P, TK>>,
42}
43
44impl<I, P, TK> TreeBuilder<'_, I, P, TK>
45where
46    I: Input + ?Sized,
47{
48    pub fn new() -> Self {
49        Self { res_stack: vec![] }
50    }
51}
52
53impl<I, P, TK> Default for TreeBuilder<'_, I, P, TK>
54where
55    I: Input + ?Sized,
56{
57    fn default() -> Self {
58        Self::new()
59    }
60}
61
62impl<'i, I, P, TK> Builder for TreeBuilder<'i, I, P, TK>
63where
64    I: Input + ?Sized,
65{
66    type Output = TreeNode<'i, I, P, TK>;
67
68    fn get_result(&mut self) -> Self::Output {
69        self.res_stack.pop().unwrap()
70    }
71}
72
73impl<'i, I, C, S, P, TK> LRBuilder<'i, I, C, S, P, TK> for TreeBuilder<'i, I, P, TK>
74where
75    I: Input + ?Sized,
76    C: Context<'i, I, S, TK>,
77    S: State,
78{
79    fn shift_action(&mut self, context: &C, token: Token<'i, I, TK>) {
80        self.res_stack.push(TreeNode::TermNode {
81            token,
82            layout: context.layout_ahead(),
83        })
84    }
85
86    fn reduce_action(&mut self, context: &C, prod: P, prod_len: usize) {
87        let children;
88        let layout;
89        if prod_len > 0 {
90            children = self.res_stack.split_off(self.res_stack.len() - prod_len);
91            layout = match children[0] {
92                TreeNode::TermNode { layout, .. } => layout,
93                TreeNode::NonTermNode { layout, .. } => layout,
94            };
95        } else {
96            children = vec![];
97            layout = None;
98        }
99        self.res_stack.push(TreeNode::NonTermNode {
100            children,
101            prod,
102            location: context.location(),
103            layout,
104        });
105    }
106}
107
108/// A node in the generic tree produced by [`TreeBuilder`]
109#[derive(Debug)]
110pub enum TreeNode<'i, I, P, TK>
111where
112    I: Input + ?Sized,
113{
114    TermNode {
115        token: Token<'i, I, TK>,
116        layout: Option<&'i I>,
117    },
118    NonTermNode {
119        prod: P,
120        location: Location,
121        children: Vec<TreeNode<'i, I, P, TK>>,
122        layout: Option<&'i I>,
123    },
124}
125
126/// Returns a slice of the matched input. If no match is possible `None` is
127/// returned.
128///
129/// This is used by default for layout parsing where we don't need to keep the
130/// structure of the parsed layout but we need just the content as a slice of
131/// the input.
132pub struct SliceBuilder<'i, I: ?Sized> {
133    input: &'i I,
134    slice: Option<&'i I>,
135}
136
137impl<'i, I> SliceBuilder<'i, I>
138where
139    I: Input + ?Sized,
140{
141    pub fn new(input: &'i I) -> Self {
142        Self { input, slice: None }
143    }
144}
145
146impl<'i, I> Builder for SliceBuilder<'i, I>
147where
148    I: Input + ?Sized,
149{
150    type Output = Option<&'i I>;
151
152    fn get_result(&mut self) -> Self::Output {
153        self.slice
154    }
155}
156
157impl<'i, I, C, S, P, TK> LRBuilder<'i, I, C, S, P, TK> for SliceBuilder<'i, I>
158where
159    I: Input + ?Sized,
160    C: Context<'i, I, S, TK>,
161    S: State,
162{
163    fn shift_action(&mut self, _context: &C, _token: Token<'i, I, TK>) {
164        // We do nothing on shift
165    }
166
167    fn reduce_action(&mut self, context: &C, _prod: P, _prod_len: usize) {
168        // On reduce, save the slice of the input.
169        self.slice = Some(&self.input[context.range()]);
170    }
171}