Skip to main content

oak_j/parser/
mod.rs

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