Skip to main content

oak_bash/parser/
mod.rs

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