1#![doc = include_str!("readme.md")]
2pub mod element_type;
3
4pub use element_type::BatElementType;
5
6use crate::{
7 language::BatLanguage,
8 lexer::{BatLexer, BatTokenType},
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, BatLanguage, S>;
17
18pub struct BatParser<'config> {
19 pub(crate) config: &'config BatLanguage,
20}
21
22impl<'config> BatParser<'config> {
23 pub fn new(config: &'config BatLanguage) -> Self {
24 Self { config }
25 }
26
27 fn parse_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
28 self.skip_trivia(state);
29 if !state.not_at_end() {
30 return Ok(());
31 }
32
33 match state.peek_kind() {
34 Some(BatTokenType::Label) => self.parse_label(state),
35 Some(BatTokenType::Keyword) => {
36 let text = state.peek_text().map(|s| s.to_uppercase());
37 match text.as_deref() {
38 Some("IF") => self.parse_if(state),
39 Some("FOR") => self.parse_for(state),
40 Some("SET") => self.parse_set(state),
41 _ => self.parse_command(state),
42 }
43 }
44 _ => self.parse_command(state),
45 }
46 }
47
48 fn skip_trivia<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
49 while state.at(BatTokenType::Whitespace) || state.at(BatTokenType::Newline) || state.at(BatTokenType::Comment) {
50 state.bump();
51 }
52 }
53
54 fn parse_label<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
55 let cp = state.checkpoint();
56 state.expect(BatTokenType::Label)?;
57 state.finish_at(cp, BatElementType::LabelDefinition);
58 Ok(())
59 }
60
61 fn parse_if<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
62 let cp = state.checkpoint();
63 state.bump(); while state.not_at_end() && !state.at(BatTokenType::Newline) {
66 state.bump();
67 }
68 state.finish_at(cp, BatElementType::IfStatement);
69 Ok(())
70 }
71
72 fn parse_for<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
73 let cp = state.checkpoint();
74 state.bump(); while state.not_at_end() && !state.at(BatTokenType::Newline) {
76 state.bump();
77 }
78 state.finish_at(cp, BatElementType::ForStatement);
79 Ok(())
80 }
81
82 fn parse_set<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
83 let cp = state.checkpoint();
84 state.bump(); while state.not_at_end() && !state.at(BatTokenType::Newline) {
86 state.bump();
87 }
88 state.finish_at(cp, BatElementType::SetStatement);
89 Ok(())
90 }
91
92 fn parse_command<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
93 let cp = state.checkpoint();
94 while state.not_at_end() && !state.at(BatTokenType::Newline) {
95 state.bump();
96 }
97 state.finish_at(cp, BatElementType::CommandStatement);
98 Ok(())
99 }
100}
101
102impl<'config> Parser<BatLanguage> for BatParser<'config> {
103 fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<BatLanguage>) -> oak_core::ParseOutput<'a, BatLanguage> {
104 let lexer = BatLexer::new(self.config);
105 oak_core::parser::parse_with_lexer(&lexer, text, edits, cache, |state| {
106 let checkpoint = state.checkpoint();
107 while state.not_at_end() {
108 if self.parse_statement(state).is_err() {
109 break;
110 }
111 }
112 Ok(state.finish_at(checkpoint, BatElementType::Root))
113 })
114 }
115}