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 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}