teleparse/tp_impl/
option.rs

1//! optional syntax tree nodes ([`Option`], [`Exists`])
2use std::borrow::Cow;
3use std::marker::PhantomData;
4
5use crate::syntax::{Epsilon, Metadata, MetadataBuilder, Result as SynResult};
6use crate::{Lexicon, Parser, Produce, Production, ToSpan};
7
8use super::Node;
9
10// Option<T> => T | epsilon
11#[doc(hidden)]
12pub struct OptionProd<T: Production>(PhantomData<T>);
13
14impl<T: Production> Production for OptionProd<T> {
15    type L = T::L;
16    #[inline]
17    fn debug() -> Cow<'static, str> {
18        let inner = T::debug();
19        if let Some(rest) = inner.strip_prefix('(') {
20            if let Some(inner) = rest.strip_suffix(")+") {
21                return Cow::Owned(format!("({inner})*"));
22            }
23            if let Some(inner) = rest.strip_suffix("]+") {
24                return Cow::Owned(format!("({inner}]*"));
25            }
26        }
27        Cow::Owned(format!("({})?", T::debug()))
28    }
29
30    fn register(meta: &mut MetadataBuilder<Self::L>) {
31        crate::register_union!(meta, T, Epsilon<T::L>)
32    }
33}
34
35/// Node that stores an optional subtree
36#[derive(Node, ToSpan, Clone, PartialEq)]
37#[doc(alias = "Option")]
38pub struct Optional<T: Produce>(pub Node<Option<T>>);
39
40impl<T: std::fmt::Debug + Produce> std::fmt::Debug for Optional<T> {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        match &self.0.value {
43            Some(node) => f.debug_tuple("Some").field(&node).finish(),
44            None => f.debug_tuple("None").field(&self.0.span).finish(),
45        }
46    }
47}
48
49impl<T: Produce> Produce for Optional<T> {
50    type Prod = OptionProd<T::Prod>;
51    fn produce(
52        parser: &mut Parser<'_, <Self::Prod as Production>::L>,
53        meta: &Metadata<<Self::Prod as Production>::L>,
54    ) -> SynResult<Self, <Self::Prod as Production>::L> {
55        produce_option(parser, meta, |x| x).map(Self::from)
56    }
57}
58
59/// Node that stores if an optional subtree is produced
60#[derive(Node, ToSpan, Clone, PartialEq)]
61pub struct Exists<T: Produce>(Node<bool>, PhantomData<T>);
62
63impl<T: Produce> std::fmt::Debug for Exists<T> {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        self.0.fmt(f)
66    }
67}
68
69impl<T: Produce> Produce for Exists<T> {
70    type Prod = OptionProd<T::Prod>;
71    fn produce(
72        parser: &mut Parser<'_, <Self::Prod as Production>::L>,
73        meta: &Metadata<<Self::Prod as Production>::L>,
74    ) -> SynResult<Self, <Self::Prod as Production>::L> {
75        produce_option(parser, meta, |x: Option<T>| x.is_some()).map(Self::from)
76    }
77}
78fn produce_option<T, O, F: FnOnce(Option<T>) -> O, L: Lexicon>(
79    parser: &mut Parser<'_, L>,
80    meta: &Metadata<L>,
81    f: F,
82) -> SynResult<Node<O>, L>
83where
84    T: Produce + ToSpan,
85    T::Prod: Production<L = L>,
86{
87    let token = parser.peek_token_src();
88    if token.is_none() {
89        // produces epsilon
90        return SynResult::Success(Node::new(parser.empty_span(), f(None)));
91    }
92    let first = meta.first.get(&T::prod_id());
93    if !first.contains(token) {
94        // produces epsilon
95        return SynResult::Success(Node::new(parser.empty_span(), f(None)));
96    }
97    // if parse fails, delay to parent to panic
98    match T::produce(parser, meta) {
99        SynResult::Success(t) => SynResult::Success(Node::new(t.span(), f(Some(t)))),
100        SynResult::Recovered(t, error) => {
101            SynResult::Recovered(Node::new(t.span(), f(Some(t))), error)
102        }
103        SynResult::Panic(error) => {
104            SynResult::Recovered(Node::new(parser.empty_span(), f(None)), error)
105        }
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use crate::prelude::*;
112    use crate::GrammarError;
113
114    use crate::test::prelude::*;
115    use crate::test::{Ident, OpAdd};
116
117    #[derive_syntax]
118    #[teleparse(root)]
119    #[derive(Debug, PartialEq, Clone)]
120    struct OptIdent(tp::Option<Ident>);
121
122    #[test]
123    fn test_none() -> Result<(), GrammarError> {
124        let t = OptIdent::parse("+")?.unwrap();
125        let t_str = format!("{:?}", t.0);
126        assert_eq!(t_str, "None(0)");
127        assert_eq!(t, OptIdent(Node::new(0..0, None).into()));
128
129        Ok(())
130    }
131
132    #[test]
133    fn test_some() {
134        let t = OptIdent::parse("a").unwrap().unwrap();
135        let t_str = format!("{:?}", t.0);
136        assert_eq!(t_str, "Some(token Ident(0..1))");
137        assert_eq!(
138            t,
139            OptIdent(Node::new(0..1, Some(Ident::from_span(0..1))).into())
140        );
141    }
142
143    #[test]
144    fn test_use_as_option() -> Result<(), GrammarError> {
145        let t = OptIdent::parse("a")?.unwrap();
146        assert!(t.0.is_some());
147
148        Ok(())
149    }
150
151    #[derive_syntax]
152    #[teleparse(root, no_test)]
153    struct Seq(tp::Option<OpAdd>, OpAdd);
154
155    #[test]
156    fn test_seq_not_ll1() {
157        assert_not_ll1!(
158            Seq,
159            GrammarError::FirstFollowSeqConflict(
160                "Seq".to_string(),
161                "(+)?".to_string(),
162                "+".to_string(),
163                "\"+\"".to_string()
164            )
165        );
166    }
167
168    #[derive_syntax]
169    #[teleparse(root)]
170    #[derive(Debug, PartialEq, Clone)]
171    struct Seq2(OpAdd, tp::Option<OpAdd>);
172
173    #[test]
174    fn test_seq2() -> Result<(), GrammarError> {
175        let t = Seq2::parse("+    ")?.unwrap();
176        assert_eq!(
177            t,
178            Seq2(OpAdd::from_span(0..1), Node::new(1..1, None).into())
179        );
180
181        Ok(())
182    }
183
184    #[derive_syntax]
185    #[teleparse(root, no_test)]
186    struct Nested(super::Optional<super::Optional<Ident>>);
187
188    #[test]
189    fn test_nested_not_ll1() {
190        assert_not_ll1!(
191            Nested,
192            GrammarError::FirstFirstConflict(
193                "((Ident)?)?".to_string(),
194                "(Ident)?".to_string(),
195                "()".to_string(),
196                "<empty>".to_string(),
197            )
198        );
199    }
200
201    #[derive_syntax]
202    #[teleparse(root)]
203    #[derive(Debug, PartialEq, Clone)]
204    struct ExistIdent(tp::Exists<Ident>);
205
206    #[test]
207    #[allow(clippy::bool_assert_comparison)]
208    fn parse_exist() {
209        let t = ExistIdent::parse("a").unwrap().unwrap();
210        let t_str = format!("{:?}", t.0);
211        assert_eq!(t_str, "0..1 => true");
212        assert_eq!(t, ExistIdent(Node::new(0..1, true).into()));
213        assert_eq!(*t.0, true);
214
215        let t = ExistIdent::parse("+").unwrap().unwrap();
216        let t_str = format!("{:?}", t.0);
217        assert_eq!(t_str, "0 => false");
218        assert_eq!(t, ExistIdent(Node::new(0..0, false).into()));
219        assert_eq!(*t.0, false);
220    }
221}