Skip to main content

oak_julia/parser/
mod.rs

1/// Element types and their metadata for Julia.
2pub mod element_type;
3
4use crate::{
5    language::JuliaLanguage,
6    lexer::{JuliaLexer, token_type::JuliaTokenType},
7    parser::element_type::JuliaElementType,
8};
9use oak_core::{
10    OakError,
11    parser::{ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer},
12    source::{Source, TextEdit},
13};
14
15/// Parser state for Julia.
16pub(crate) type State<'a, S> = ParserState<'a, JuliaLanguage, S>;
17
18/// Parser for the Julia language.
19pub struct JuliaParser<'config> {
20    pub(crate) config: &'config JuliaLanguage,
21}
22
23impl<'config> JuliaParser<'config> {
24    /// Creates a new `JuliaParser` with the given configuration.
25    pub fn new(config: &'config JuliaLanguage) -> Self {
26        Self { config }
27    }
28
29    fn skip_trivia<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
30        while state.not_at_end() {
31            if let Some(kind) = state.peek_kind() {
32                if kind.is_trivia() {
33                    state.bump();
34                    continue;
35                }
36            }
37            break;
38        }
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.at(JuliaTokenType::Function) {
44            self.parse_function(state)?;
45        }
46        else if state.at(JuliaTokenType::If) {
47            self.parse_if(state)?;
48        }
49        else if state.at(JuliaTokenType::For) {
50            self.parse_for(state)?;
51        }
52        else if state.at(JuliaTokenType::End) {
53            // End should be handled by the caller (e.g. parse_function)
54            // If it's here, it might be an error or we just consume it
55            state.bump();
56        }
57        else {
58            self.parse_expression(state)?;
59        }
60        Ok(())
61    }
62
63    fn parse_if<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
64        let cp = state.checkpoint();
65        state.bump(); // if
66        self.skip_trivia(state);
67
68        self.parse_expression(state)?;
69        self.skip_trivia(state);
70
71        // Parse then part
72        while state.not_at_end() && !state.at(JuliaTokenType::Else) && !state.at(JuliaTokenType::ElseIf) && !state.at(JuliaTokenType::End) {
73            self.parse_statement(state)?;
74            self.skip_trivia(state);
75        }
76
77        if state.at(JuliaTokenType::Else) || state.at(JuliaTokenType::ElseIf) {
78            state.bump();
79            self.skip_trivia(state);
80            while state.not_at_end() && !state.at(JuliaTokenType::End) {
81                self.parse_statement(state)?;
82                self.skip_trivia(state);
83            }
84        }
85
86        if state.at(JuliaTokenType::End) {
87            state.bump();
88        }
89
90        state.finish_at(cp, JuliaElementType::If);
91        Ok(())
92    }
93
94    fn parse_for<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
95        let cp = state.checkpoint();
96        state.bump(); // for
97        self.skip_trivia(state);
98
99        if state.at(JuliaTokenType::Identifier) {
100            state.bump();
101        }
102        self.skip_trivia(state);
103
104        if state.at(JuliaTokenType::Equal) || state.at(JuliaTokenType::In) {
105            state.bump();
106        }
107        self.skip_trivia(state);
108
109        self.parse_expression(state)?;
110        self.skip_trivia(state);
111
112        // Parse body
113        while state.not_at_end() && !state.at(JuliaTokenType::End) {
114            self.parse_statement(state)?;
115            self.skip_trivia(state);
116        }
117
118        if state.at(JuliaTokenType::End) {
119            state.bump();
120        }
121
122        state.finish_at(cp, JuliaElementType::For);
123        Ok(())
124    }
125
126    fn parse_function<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
127        let cp = state.checkpoint();
128        state.bump(); // function
129        self.skip_trivia(state);
130
131        if state.at(JuliaTokenType::Identifier) {
132            let name_cp = state.checkpoint();
133            state.bump(); // function name
134            state.finish_at(name_cp, JuliaElementType::Identifier);
135        }
136
137        if state.at(JuliaTokenType::LeftParen) {
138            state.bump();
139            self.skip_trivia(state);
140            // Parse arguments (ignored for now)
141            while state.not_at_end() && !state.at(JuliaTokenType::RightParen) {
142                state.bump();
143            }
144            if state.at(JuliaTokenType::RightParen) {
145                state.bump();
146            }
147        }
148
149        self.skip_trivia(state);
150
151        // Parse body
152        while state.not_at_end() && !state.at(JuliaTokenType::End) {
153            self.parse_statement(state)?;
154            self.skip_trivia(state);
155        }
156
157        if state.at(JuliaTokenType::End) {
158            state.bump();
159        }
160
161        state.finish_at(cp, JuliaElementType::Function);
162        Ok(())
163    }
164
165    fn parse_expression<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
166        self.skip_trivia(state);
167        let cp = state.checkpoint();
168
169        if state.at(JuliaTokenType::Identifier) {
170            state.bump();
171            let id_node = state.finish_at(cp, JuliaElementType::Identifier);
172            self.skip_trivia(state);
173            if state.at(JuliaTokenType::LeftParen) {
174                // Function call
175                let call_cp = state.checkpoint_before(id_node);
176                state.bump(); // (
177                self.skip_trivia(state);
178                let arg_cp = state.checkpoint();
179                while state.not_at_end() && !state.at(JuliaTokenType::RightParen) {
180                    self.parse_expression(state)?;
181                    self.skip_trivia(state);
182                    if state.at(JuliaTokenType::Comma) {
183                        state.bump();
184                        self.skip_trivia(state);
185                    }
186                }
187                state.finish_at(arg_cp, JuliaElementType::ArgumentList);
188                if state.at(JuliaTokenType::RightParen) {
189                    state.bump();
190                }
191                state.finish_at(call_cp, JuliaElementType::Call);
192            }
193        }
194        else if state.at(JuliaTokenType::StringLiteral) {
195            state.bump();
196            state.finish_at(cp, JuliaElementType::StringLiteral);
197        }
198        else if state.not_at_end() {
199            state.bump();
200        }
201
202        Ok(())
203    }
204}
205
206impl<'config> Parser<JuliaLanguage> for JuliaParser<'config> {
207    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<JuliaLanguage>) -> ParseOutput<'a, JuliaLanguage> {
208        let lexer = JuliaLexer::new(&self.config);
209        parse_with_lexer(&lexer, text, edits, cache, |state| {
210            let checkpoint = state.checkpoint();
211
212            while state.not_at_end() {
213                self.skip_trivia(state);
214                if !state.not_at_end() {
215                    break;
216                }
217                self.parse_statement(state)?;
218            }
219
220            Ok(state.finish_at(checkpoint, JuliaElementType::Root))
221        })
222    }
223}