kproc_parser/rust/
core.rs

1//! Contains all the core function that are common across
2//! different modules.
3
4use crate::kparser::KParserError;
5use crate::kparser::{self, KParserTracer};
6use crate::kproc_macros::{KTokenStream, MatchTok};
7use crate::proc_macro::TokenTree;
8use crate::rust::ast_nodes::{self, TypeParam};
9use crate::{build_error, check, trace};
10
11use super::ast_nodes::{GenericParam, GenericParams, LifetimeParam, TyToken};
12use super::ty::parse_ty;
13
14/// parsing the declaration of the lifetimes and generics for a
15/// declaration of a impl block or struct.
16pub fn check_and_parse_generics_params(
17    ast: &mut KTokenStream,
18    tracer: &dyn KParserTracer,
19) -> kparser::Result<Option<GenericParams>> {
20    trace!(tracer, "parsing generics params");
21    // compliant to this https://doc.rust-lang.org/stable/reference/items/generics.html
22    if ast.match_tok("<") {
23        ast.next(); // consume `<``
24        let mut generics = vec![];
25        while !ast.match_tok(">") {
26            trace!(tracer, "iterate over geeneric, stuck on {:?}", ast.peek());
27            if let Some(lifetime) = check_and_parse_lifetime(ast) {
28                let param = LifetimeParam {
29                    lifetime_or_label: lifetime,
30                    bounds: Vec::new(),
31                };
32                generics.push(GenericParam::LifetimeParam(param));
33            } else if let Some(ty) = parse_ty(ast, tracer)? {
34                generics.push(GenericParam::TypeParam(ty));
35            }
36        }
37        ast.next(); // consume the `>` toks
38        return Ok(Some(GenericParams { params: generics }));
39    }
40    Ok(None)
41}
42
43pub fn check_and_parse_bounds(
44    stream: &mut KTokenStream,
45    tracer: &dyn KParserTracer,
46) -> kparser::Result<Option<GenericParams>> {
47    let is_starting_tok = stream.match_tok("<");
48    if !is_starting_tok && !stream.is_group() {
49        trace!(
50            tracer,
51            "while checking the parameter we do not find a token group"
52        );
53        return Ok(None);
54    }
55    if !is_starting_tok && !stream.peek().clone().to_token_stream().match_tok("<") {
56        trace!(tracer, "not a `<...>` token group`");
57        return Ok(None);
58    }
59
60    let inner_stream = stream;
61    if !is_starting_tok {
62        trace!(tracer, "in a `<...>` token group, uwrapping it ...");
63        *inner_stream = inner_stream.advance().to_token_stream();
64    }
65
66    if inner_stream.match_tok("<") {
67        trace!(tracer, "check and parsing the type bounds");
68        inner_stream.next(); // consume `<``
69
70        let mut generics = vec![];
71        while !inner_stream.is_end() && !inner_stream.match_tok(">") {
72            trace!(
73                tracer,
74                "iterate over tokens, current token is: `{:?}`",
75                inner_stream.peek()
76            );
77            let mut generic: Option<GenericParam> = None;
78            while !inner_stream.match_tok(",") && !inner_stream.match_tok(">") {
79                trace!(tracer, "checking bound");
80                match inner_stream.peek().to_string().as_str() {
81                    "+" | ":" => {
82                        let tok = inner_stream.advance();
83                        assert!(
84                            ["+", ":"].contains(&tok.to_string().as_str()),
85                            "unexpected token {:?}",
86                            tok.to_string()
87                        );
88                        trace!(tracer, "new bound for the current trait");
89                        let bound = if let Some(lifetime) = check_and_parse_lifetime(inner_stream) {
90                            ast_nodes::Bound::Lifetime(LifetimeParam {
91                                lifetime_or_label: lifetime,
92                                bounds: Vec::new(),
93                            })
94                        } else {
95                            let trait_bound = inner_stream.advance();
96                            ast_nodes::Bound::Trait(TypeParam {
97                                identifier: trait_bound,
98                                bounds: Vec::new(),
99                            })
100                        };
101                        assert!(
102                            generic.is_some(),
103                            "concatenation bound `+` on generic used in the wrong way"
104                        );
105                        trace!(tracer, "bound found `{:?}`", bound);
106                        let Some(generic) = generic.as_mut() else {
107                            return Err(build_error!(inner_stream.peek().clone(), "concatenation bound `+` on generic used in the wrong way"));
108                         };
109                        generic.add_bound(bound);
110                    }
111                    _ => {
112                        assert!(
113                            generic.is_none(),
114                            "declaration bound with `:` used in the wrong way"
115                        );
116                        trace!(tracer, "parising token {:?}", inner_stream.peek());
117                        if let Some(lifetime) = check_and_parse_lifetime(inner_stream) {
118                            trace!(tracer, "life bound found {:?}", lifetime);
119                            generic = Some(GenericParam::LifetimeParam(LifetimeParam {
120                                lifetime_or_label: lifetime,
121                                bounds: vec![],
122                            }));
123                            continue;
124                        }
125                        let identifier = inner_stream.advance();
126                        trace!(tracer, "Trait `{identifier}`");
127                        generic = Some(GenericParam::Bounds(ast_nodes::Bound::Trait(TypeParam {
128                            identifier,
129                            bounds: vec![],
130                        })))
131                    }
132                }
133                trace!(tracer, "next token `{:?}`", inner_stream.peek());
134            }
135            trace!(
136                tracer,
137                "conclude to parse the generic bound `{:?}`",
138                generic
139            );
140            generics.push(generic.unwrap());
141            if inner_stream.match_tok(",") {
142                check!(",", inner_stream.advance())?;
143            }
144        }
145        trace!(
146            tracer,
147            "finish to parse the generics bounds `{:?}`",
148            generics
149        );
150        if !inner_stream.is_end() {
151            check!(">", inner_stream.advance())?; // consume the `>` toks
152        }
153        return Ok(Some(GenericParams { params: generics }));
154    }
155    Ok(None)
156}
157
158/// helper function that check and parse the reference token `&`, if
159/// is not present return `None`.
160pub fn check_and_parse_ref(ast: &mut KTokenStream) -> Option<TokenTree> {
161    let token = ast.peek();
162    match token.to_string().as_str() {
163        "&" => Some(ast.advance()),
164        _ => None,
165    }
166}
167
168/// helper function that check and parse the lifetime symbol `'`, if
169/// is not present return `None`.
170pub fn check_and_parse_lifetime(ast: &mut KTokenStream) -> Option<TokenTree> {
171    let token = ast.peek().to_string();
172    match token.as_str() {
173        "'" => {
174            ast.next();
175            Some(ast.advance())
176        }
177        _ => None,
178    }
179}
180
181/// helper function that check and parse the `mut` token, if is not
182/// present return `None`.
183pub fn check_and_parse_mut(ast: &mut KTokenStream) -> Option<TokenTree> {
184    let token = ast.peek().to_string();
185    match token.as_str() {
186        "mut" => Some(ast.advance()),
187        _ => None,
188    }
189}
190
191/// helper function that check and parser the `dyn` token, if is not
192/// present return `None`.
193pub fn check_and_parse_dyn(ast: &mut KTokenStream) -> Option<TokenTree> {
194    let token = ast.peek().to_string();
195    match token.as_str() {
196        "dyn" => Some(ast.advance()),
197        _ => None,
198    }
199}
200
201#[macro_export]
202macro_rules! parse_visibility {
203    ($ast:expr) => {{
204        $crate::rust::core::check_and_parse_visibility($ast)
205    }};
206}
207
208/// parse visibility identifier like `pub(crate)`` and return an option
209/// value in case it is not defined.
210pub fn check_and_parse_visibility(toks: &mut KTokenStream) -> Option<TokenTree> {
211    if check_identifier(toks, "pub", 0) {
212        return Some(toks.advance());
213    }
214    None
215}
216
217pub fn check_and_parse_fn_qualifier(toks: &mut KTokenStream) -> Option<TokenTree> {
218    if check_identifiers(toks, &["async", "const", "unsafe"], 0) {
219        return Some(toks.advance());
220    }
221    None
222}
223
224pub fn check_and_parse_fn_tok(toks: &mut KTokenStream) -> Option<TokenTree> {
225    if check_identifier(toks, "fn", 0) {
226        return Some(toks.advance());
227    }
228    None
229}
230
231pub fn check_is_fun_with_visibility(toks: &mut KTokenStream) -> bool {
232    if check_identifier(toks, "pub", 0) {
233        if check_identifiers(toks, &["async", "const", "unsafe"], 1) {
234            return check_identifier(toks, "fn", 2);
235        } else if check_identifier(toks, "fn", 1) {
236            return true;
237        }
238    }
239    false
240}
241
242pub fn check_identifier(toks: &KTokenStream, ident: &str, step: usize) -> bool {
243    let tok = toks.lookup(step);
244    if let TokenTree::Ident(val) = tok {
245        if val.to_string().contains(ident) {
246            return true;
247        }
248    }
249    false
250}
251
252pub fn check_tok(toks: &KTokenStream, ident: &str, step: usize) -> bool {
253    let tok = toks.lookup(step);
254    tok.to_string().contains(ident)
255}
256
257pub fn check_identifiers(toks: &KTokenStream, ident: &[&str], step: usize) -> bool {
258    let tok = toks.lookup(step);
259    if let TokenTree::Ident(val) = tok {
260        if ident.contains(&val.to_string().as_str()) {
261            return true;
262        }
263    }
264    false
265}
266
267pub fn check_raw_toks(toks: &KTokenStream, ident: &[&str], step: usize) -> bool {
268    let tok = toks.lookup(step);
269    ident.contains(&tok.to_string().as_str())
270}
271
272pub fn check_and_parse_return_type(
273    toks: &mut KTokenStream,
274    tracer: &dyn KParserTracer,
275) -> kparser::Result<Option<TyToken>> {
276    if toks.is_end() {
277        return Ok(None);
278    }
279    if check_tok(toks, "-", 0) {
280        toks.next();
281        trace!(tracer, "ok parsed the `-`, now the next is {}", toks.peek());
282        if check_tok(toks, ">", 0) {
283            toks.next();
284            trace!(tracer, "found the `>` no the next is {:?}", toks.peek());
285            // FIXME: add a method to consube by steps
286            let ty = parse_ty(toks, tracer)?;
287            return Ok(ty);
288        }
289    }
290    Ok(None)
291}