1#![doc = include_str!("readme.md")]
2pub mod element_type;
7
8pub use element_type::BatElementType;
9
10use crate::{
11 language::BatLanguage,
12 lexer::{BatLexer, BatTokenType},
13};
14use oak_core::{
15 OakError, TextEdit,
16 parser::{ParseCache, Parser, ParserState},
17 source::Source,
18};
19
20pub(crate) type State<'a, S> = ParserState<'a, BatLanguage, S>;
21
22pub struct BatParser<'config> {
27 pub(crate) config: &'config BatLanguage,
28}
29
30impl<'config> BatParser<'config> {
31 pub fn new(config: &'config BatLanguage) -> Self {
38 Self { config }
39 }
40
41 fn parse_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
42 self.skip_trivia(state);
43 if !state.not_at_end() {
44 return Ok(());
45 }
46
47 match state.peek_kind() {
48 Some(BatTokenType::Label) => self.parse_label(state),
49 Some(BatTokenType::Keyword) => {
50 let text = state.peek_text().map(|s| s.to_uppercase());
51 match text.as_deref() {
52 Some("IF") => self.parse_if(state),
53 Some("FOR") => self.parse_for(state),
54 Some("SET") => self.parse_set(state),
55 _ => self.parse_command(state),
56 }
57 }
58 _ => self.parse_command(state),
59 }
60 }
61
62 fn skip_trivia<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
63 while state.at(BatTokenType::Whitespace) || state.at(BatTokenType::Newline) || state.at(BatTokenType::Comment) {
64 state.bump();
65 }
66 }
67
68 fn parse_label<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
69 let cp = state.checkpoint();
70 state.expect(BatTokenType::Label)?;
71 state.finish_at(cp, BatElementType::LabelDefinition);
72 Ok(())
73 }
74
75 fn parse_if<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
76 let cp = state.checkpoint();
77 state.bump(); while state.not_at_end() && !state.at(BatTokenType::Newline) {
80 state.bump();
81 }
82 state.finish_at(cp, BatElementType::IfStatement);
83 Ok(())
84 }
85
86 fn parse_for<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
87 let cp = state.checkpoint();
88 state.bump(); while state.not_at_end() && !state.at(BatTokenType::Newline) {
90 state.bump();
91 }
92 state.finish_at(cp, BatElementType::ForStatement);
93 Ok(())
94 }
95
96 fn parse_set<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
97 let cp = state.checkpoint();
98 state.bump(); while state.not_at_end() && !state.at(BatTokenType::Newline) {
100 state.bump();
101 }
102 state.finish_at(cp, BatElementType::SetStatement);
103 Ok(())
104 }
105
106 fn parse_command<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
107 let cp = state.checkpoint();
108 while state.not_at_end() && !state.at(BatTokenType::Newline) {
109 state.bump();
110 }
111 state.finish_at(cp, BatElementType::CommandStatement);
112 Ok(())
113 }
114}
115
116impl<'config> Parser<BatLanguage> for BatParser<'config> {
117 fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<BatLanguage>) -> oak_core::ParseOutput<'a, BatLanguage> {
118 let lexer = BatLexer::new(self.config);
119 oak_core::parser::parse_with_lexer(&lexer, text, edits, cache, |state| {
120 let checkpoint = state.checkpoint();
121 while state.not_at_end() {
122 if self.parse_statement(state).is_err() {
123 break;
124 }
125 }
126 Ok(state.finish_at(checkpoint, BatElementType::Root))
127 })
128 }
129}