1#![doc = include_str!("readme.md")]
2pub mod element_type;
3
4pub use element_type::JElementType;
5
6use crate::{language::JLanguage, lexer::JTokenType};
7use oak_core::{
8 OakError, TextEdit,
9 parser::{ParseCache, ParseOutput, Parser, ParserState},
10 source::Source,
11};
12
13pub(crate) type State<'a, S> = ParserState<'a, JLanguage, S>;
14
15pub struct JParser<'config> {
16 pub(crate) config: &'config JLanguage,
17}
18
19impl<'config> JParser<'config> {
20 pub fn new(config: &'config JLanguage) -> Self {
21 Self { config }
22 }
23
24 fn run<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<&'a oak_core::GreenNode<'a, JLanguage>, OakError> {
26 let root_cp = state.checkpoint();
27 let unit_cp = state.checkpoint();
28
29 while state.not_at_end() {
30 if !self.parse_sentence(state) {
31 state.advance();
32 }
33 }
34
35 state.finish_at(unit_cp, JElementType::CompilationUnit);
36 let root = state.finish_at(root_cp, JElementType::Root);
37
38 Ok(root)
39 }
40
41 fn parse_sentence<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> bool {
43 let cp = state.checkpoint();
44
45 let mut matched = false;
47 if state.at(JTokenType::Identifier) && (state.peek_kind_at(1) == Some(JTokenType::IsGlobal) || state.peek_kind_at(1) == Some(JTokenType::IsLocal)) {
48 matched = self.parse_assignment(state);
49 }
50 else {
51 matched = self.parse_expression(state);
52 }
53
54 if matched {
55 state.finish_at(cp, JElementType::Sentence);
56 }
57 matched
58 }
59
60 fn parse_assignment<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> bool {
62 let cp = state.checkpoint();
63 state.eat(JTokenType::Identifier);
64 if state.at(JTokenType::IsGlobal) {
65 state.eat(JTokenType::IsGlobal);
66 }
67 else {
68 state.eat(JTokenType::IsLocal);
69 }
70 self.parse_expression(state);
71 state.finish_at(cp, JElementType::Assignment);
72 true
73 }
74
75 fn parse_expression<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> bool {
77 let cp = state.checkpoint();
78 let mut matched = false;
79
80 while state.not_at_end() && !state.at(JTokenType::Newline) && !state.at(JTokenType::Eof) {
81 if state.at(JTokenType::Identifier) {
82 state.eat(JTokenType::Identifier);
83 matched = true;
84 }
85 else if state.at(JTokenType::NumberLiteral) {
86 state.eat(JTokenType::NumberLiteral);
87 matched = true;
88 }
89 else if state.at(JTokenType::StringLiteral) {
90 state.eat(JTokenType::StringLiteral);
91 matched = true;
92 }
93 else if state.at(JTokenType::LeftParen) {
94 let group_cp = state.checkpoint();
95 state.eat(JTokenType::LeftParen);
96 self.parse_expression(state);
97 let _ = state.expect(JTokenType::RightParen);
98 state.finish_at(group_cp, JElementType::Group);
99 matched = true;
100 }
101 else {
102 state.advance();
104 matched = true;
105 }
106 }
107
108 if matched {
109 state.finish_at(cp, JElementType::Expression);
110 }
111 matched
112 }
113}
114
115impl<'config> Parser<JLanguage> for JParser<'config> {
116 fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<JLanguage>) -> ParseOutput<'a, JLanguage> {
117 let lexer = crate::lexer::JLexer::new(self.config);
118 oak_core::parser::parse_with_lexer(&lexer, text, edits, cache, |state| self.run(state))
119 }
120}