Skip to main content

oak_bash/parser/
mod.rs

1pub mod element_type;
2
3pub use element_type::BashElementType;
4
5use crate::{
6    language::BashLanguage,
7    lexer::{BashLexer, BashTokenType},
8};
9use oak_core::{
10    OakError, TextEdit,
11    parser::{ParseCache, Parser, ParserState},
12    source::Source,
13};
14
15pub(crate) type State<'a, S> = ParserState<'a, BashLanguage, S>;
16
17pub struct BashParser<'config> {
18    pub(crate) _config: &'config BashLanguage,
19}
20
21impl<'config> BashParser<'config> {
22    pub fn new(config: &'config BashLanguage) -> Self {
23        Self { _config: config }
24    }
25
26    pub(crate) fn parse_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
27        if state.at(BashTokenType::Keyword) {
28            // Need to check which keyword, but our Lexer currently marks all as Keyword
29            // For now, just advance and mark as a generic statement if we don't have detailed keyword types
30            let checkpoint = state.checkpoint();
31            state.bump();
32            while state.not_at_end() && !state.at(BashTokenType::Newline) && !state.at(BashTokenType::Delimiter) {
33                state.bump();
34            }
35            state.finish_at(checkpoint, BashElementType::CommandStatement);
36        }
37        else {
38            self.parse_command(state)?;
39        }
40        Ok(())
41    }
42
43    pub(crate) fn parse_command<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
44        let checkpoint = state.checkpoint();
45        while state.not_at_end() && !state.at(BashTokenType::Newline) && !state.at(BashTokenType::Delimiter) {
46            state.bump();
47        }
48        state.finish_at(checkpoint, BashElementType::CommandStatement);
49        Ok(())
50    }
51}
52
53impl<'config> Parser<BashLanguage> for BashParser<'config> {
54    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<BashLanguage>) -> oak_core::ParseOutput<'a, BashLanguage> {
55        let lexer = BashLexer::new(self._config);
56        oak_core::parser::parse_with_lexer(&lexer, text, edits, cache, |state| {
57            let checkpoint = state.checkpoint();
58
59            while state.not_at_end() && !state.at(BashTokenType::Eof) {
60                if state.at(BashTokenType::Newline) || state.at(BashTokenType::Delimiter) {
61                    state.bump();
62                }
63                else {
64                    self.parse_statement(state).ok();
65                }
66            }
67
68            Ok(state.finish_at(checkpoint, BashElementType::Root))
69        })
70    }
71}