luau_parser/
parser.rs

1//! The main item of this crate, the actual [`parser`](Parser).
2
3use luau_lexer::lexer::Lexer;
4#[cfg(feature = "cache")]
5use std::collections::HashMap;
6
7use crate::types::{Cst, Pointer};
8
9/// The cache used in [`Parser`] when `cache` feature is enabled.
10#[cfg(feature = "cache")]
11pub type Cache = HashMap<String, Pointer<Cst>>;
12
13/// A Luau parser.
14#[derive(Clone, Debug, Default, PartialEq, Eq)]
15#[cfg_attr(not(feature = "cache"), derive(Copy, Hash, PartialOrd, Ord))]
16#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
17pub struct Parser {
18    /// Cache, only works with the `cache` feature, this is useful when you need
19    /// to use the [`CST`](Cst) more than once in 2 different places without
20    /// re-parsing.
21    #[cfg(feature = "cache")]
22    cache: Cache,
23
24    /// The lexer.
25    lexer: Lexer,
26}
27
28impl Parser {
29    /// Create a new [`parser`](Parser).
30    #[inline]
31    pub fn new(input: &str) -> Self {
32        Self {
33            #[cfg(feature = "cache")]
34            cache: HashMap::new(),
35            lexer: Lexer::new(input),
36        }
37    }
38
39    /// Set the parser's input. Meant to be chained.
40    pub fn with_input(mut self, input: &str) -> Self {
41        self.lexer = self.lexer.with_input(input);
42        self
43    }
44
45    /// Set the parser's input.
46    pub fn set_input(&mut self, input: &str) {
47        self.lexer.set_input(input);
48    }
49
50    /// Parse Luau code into an [`CST`](Cst).
51    // SAFETY: We just added it to the cache.
52    #[allow(clippy::missing_panics_doc)]
53    pub fn parse(&mut self, uri: &str) -> Pointer<Cst> {
54        let cst = Pointer::new(Cst::parse(self.lexer.next_token(), &mut self.lexer, uri));
55
56        #[cfg(feature = "cache")]
57        {
58            self.cache.insert(uri.to_string(), cst);
59
60            // SAFETY: We just added it to the cache.
61            #[allow(clippy::unwrap_used)]
62            self.cache.get(uri).unwrap().to_owned()
63        }
64
65        #[cfg(not(feature = "cache"))]
66        cst
67    }
68
69    /// Get a specific [`CST`](Cst) from the cache, this function assumes the
70    /// cst does exist. If it may or may not exist, use
71    /// [`maybe_get_ast`](Self::maybe_get_ast).
72    ///
73    /// # Panics
74    ///
75    /// If the `uri` was never parsed before.
76    #[cfg(feature = "cache")]
77    #[inline]
78    pub fn get_ast(&self, uri: &str) -> &Cst {
79        // SAFETY: The safety is up to the caller.
80        #[allow(clippy::unwrap_used)]
81        self.cache.get(uri).unwrap()
82    }
83
84    /// Get a specific [`CST`](Cst) from the cache (if `cache` feature is enabled),
85    /// or parse `code` and return the produced [`CST`](Cst)
86    #[inline]
87    pub fn get_or_create(&mut self, uri: &str, code: &str) -> Pointer<Cst> {
88        #[cfg(feature = "cache")]
89        if let Some(cst) = self.maybe_get_ast(uri) {
90            return cst;
91        }
92
93        self.set_input(code);
94        self.parse(uri)
95    }
96
97    /// Get a specific [`CST`](Cst) from the cache, this function, unlike
98    /// [`get_ast`](Self::get_ast), doesn't error when the [`CST`](Cst) isn't
99    /// there.
100    #[cfg(feature = "cache")]
101    #[inline]
102    pub fn maybe_get_ast(&self, uri: &str) -> Option<Pointer<Cst>> {
103        self.cache.get(uri).cloned()
104    }
105
106    /// Get all cached [`CST`](Cst)s.
107    #[cfg(feature = "cache")]
108    #[inline]
109    pub const fn get_all_asts(&self) -> &Cache {
110        &self.cache
111    }
112
113    /// Clear the cache.
114    #[cfg(feature = "cache")]
115    #[inline]
116    pub fn clear_cache(&mut self) {
117        self.cache.clear();
118    }
119}