glsl_lang/parse/
builder.rs

1//! Parse operation builder definition
2
3use glsl_lang_lexer::{HasLexerError, LangLexerIterator, ParseOptions};
4
5use super::{Extractable, HasParser, LangLexer, LangParser, ParseContext, ParseResult};
6
7/// Builder structure for a parsing operation
8pub struct ParseBuilder<'i, 'o, 'c, 'p, L: LangLexer<'i>, T: HasParser> {
9    source: L::Input,
10    opts: Option<&'o ParseOptions>,
11    context: Option<&'c ParseContext>,
12    lexer: Option<L>,
13    parser: Option<&'p T::Parser>,
14}
15
16impl<'i, 'o, 'c, 'p, L: LangLexer<'i>, T: HasParser> ParseBuilder<'i, 'o, 'c, 'p, L, T> {
17    /// Create a new parse builder from the given input string
18    pub fn new(source: L::Input) -> Self {
19        Self {
20            source,
21            opts: None,
22            context: None,
23            lexer: None,
24            parser: None,
25        }
26    }
27
28    /// Set the parse options for this parse
29    pub fn opts(self, opts: &'o ParseOptions) -> Self {
30        Self {
31            opts: Some(opts),
32            ..self
33        }
34    }
35
36    /// Set the parse options for this parse
37    pub fn context(self, ctx: &'c ParseContext) -> Self {
38        Self {
39            context: Some(ctx),
40            ..self
41        }
42    }
43
44    /// Set the parser instance to use for this parse
45    pub fn parser(self, parser: &'p T::Parser) -> Self {
46        Self {
47            parser: Some(parser),
48            ..self
49        }
50    }
51
52    /// Execute the parsing operation
53    #[allow(clippy::result_large_err)]
54    fn parse_source(
55        source: L::Input,
56        opts: Option<&'o ParseOptions>,
57        mut context: Option<&'c ParseContext>,
58        mut lexer: Option<L>,
59        mut parser: Option<&'p T::Parser>,
60    ) -> ParseResult<L::Iter, <L::Iter as HasLexerError>::Error, T> {
61        // Get parse options
62        let default_opts = Default::default();
63        let opts = opts.unwrap_or(&default_opts);
64
65        // Clone the input context, or create a new one
66        let cloned_context = if let Some(existing) = context.take() {
67            existing.clone_inner()
68        } else {
69            Default::default()
70        };
71
72        // Create the lexer
73        let lexer = if let Some(lexer) = lexer.take() {
74            lexer
75        } else {
76            L::new(source, opts)
77        };
78
79        // Create the parser
80        let created_parser;
81        let parser = if let Some(parser) = parser.take() {
82            parser
83        } else {
84            created_parser = <T as HasParser>::Parser::new();
85            &created_parser
86        };
87
88        // Invoke the parser
89        let mut iter = lexer.run(cloned_context.clone());
90        match parser.parse(cloned_context.clone(), &mut iter) {
91            Ok(t) => Ok((t, cloned_context, iter)),
92            Err(err) => Err(iter.resolve_err(err)),
93        }
94    }
95
96    /// Execute the parsing operation
97    #[allow(clippy::result_large_err)]
98    pub fn parse(self) -> ParseResult<L::Iter, <L::Iter as HasLexerError>::Error, T> {
99        Self::parse_source(
100            self.source,
101            self.opts,
102            self.context,
103            self.lexer,
104            self.parser,
105        )
106    }
107
108    /// Execute the parsing operation, and extract the wanted node
109    #[allow(clippy::result_large_err)]
110    pub fn extract<U: Extractable<T>>(
111        self,
112    ) -> ParseResult<L::Iter, <L::Iter as HasLexerError>::Error, Option<U>> {
113        Self::parse_source(
114            self.source,
115            self.opts,
116            self.context,
117            self.lexer,
118            self.parser,
119        )
120        .map(|(root, ctx, l)| (U::extract(root), ctx, l))
121    }
122}
123
124impl<'i, 'o, 'c, 'p, T: HasParser> ParseBuilder<'i, 'o, 'c, 'p, super::DefaultLexer<'i>, T> {
125    /// Create a new parse builder from the given input string
126    pub fn default(source: <super::DefaultLexer<'i> as LangLexer<'i>>::Input) -> Self {
127        Self {
128            source,
129            opts: None,
130            context: None,
131            lexer: None,
132            parser: None,
133        }
134    }
135}
136
137/// Trait for creating parse builders from lexer inputs
138pub trait IntoParseBuilderExt<'i> {
139    /// Type of the lexer associated with this input
140    type Lexer: LangLexer<'i>;
141
142    /// Create a builder for this lexer input
143    fn builder<'o, 'c, 'p, T>(self) -> ParseBuilder<'i, 'o, 'c, 'p, Self::Lexer, T>
144    where
145        T: HasParser;
146}
147
148impl<'i> IntoParseBuilderExt<'i> for &'i str {
149    type Lexer = super::DefaultLexer<'i>;
150
151    fn builder<'o, 'c, 'p, T>(self) -> ParseBuilder<'i, 'o, 'c, 'p, Self::Lexer, T>
152    where
153        T: HasParser,
154    {
155        ParseBuilder::default(self)
156    }
157}
158
159#[cfg(feature = "lexer-full")]
160impl<'r, 'p, F: glsl_lang_lexer::full::fs::FileSystem> IntoParseBuilderExt<'p>
161    for glsl_lang_lexer::full::fs::File<'r, 'p, F>
162where
163    glsl_lang_lexer::full::fs::File<'r, 'p, F>: 'p,
164{
165    type Lexer = glsl_lang_lexer::full::fs::Lexer<'r, 'p, F>;
166
167    fn builder<'o, 'c, 'q, T>(self) -> ParseBuilder<'p, 'o, 'c, 'q, Self::Lexer, T>
168    where
169        T: HasParser,
170    {
171        ParseBuilder::new(self)
172    }
173}