Skip to main content

oak_apl/parser/
mod.rs

1#![doc = include_str!("readme.md")]
2/// Element type definitions.
3pub mod element_type;
4
5pub use element_type::AplElementType;
6
7use crate::{language::AplLanguage, lexer::token_type::AplTokenType};
8use oak_core::{
9    OakError, TextEdit,
10    parser::{Parser, ParserState},
11    source::Source,
12};
13
14pub(crate) type State<'a, S> = ParserState<'a, AplLanguage, S>;
15
16/// Parser for the APL language.
17pub struct AplParser<'config> {
18    /// The language configuration.
19    pub(crate) config: &'config AplLanguage,
20}
21
22impl<'config> AplParser<'config> {
23    /// Creates a new `AplParser`.
24    pub fn new(config: &'config AplLanguage) -> Self {
25        Self { config }
26    }
27
28    /// Parses an APL statement.
29    pub(crate) fn parse_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
30        let cp = state.checkpoint();
31
32        // Try parsing assignment: ID ← Expression
33        if state.at(AplTokenType::Identifier) && state.peek_kind_at(1) == Some(AplTokenType::LeftArrow) {
34            state.advance(); // ID
35            state.advance(); // ←
36            self.parse_expression(state)?;
37            state.finish_at(cp, AplElementType::Assignment);
38        }
39        else {
40            self.parse_expression(state)?;
41            state.finish_at(cp, AplElementType::Statement);
42        }
43
44        Ok(())
45    }
46
47    /// Parses an APL expression (simplified).
48    pub(crate) fn parse_expression<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
49        let cp = state.checkpoint();
50
51        while state.not_at_end() && !state.at(AplTokenType::Newline) && !state.at(AplTokenType::Diamond) {
52            if state.at(AplTokenType::Identifier) {
53                state.advance();
54            }
55            else if state.at(AplTokenType::NumberLiteral) {
56                state.advance();
57            }
58            else if state.at(AplTokenType::StringLiteral) {
59                state.advance();
60            }
61            else if state.at(AplTokenType::LeftParen) {
62                state.advance();
63                self.parse_expression(state)?;
64                state.expect(AplTokenType::RightParen).ok();
65            }
66            else if state.at(AplTokenType::LeftBrace) {
67                // Dfn { ... }
68                state.advance();
69                while state.not_at_end() && !state.at(AplTokenType::RightBrace) {
70                    state.advance();
71                }
72                state.expect(AplTokenType::RightBrace).ok();
73            }
74            else {
75                // Possibly a primitive function or operator
76                state.advance();
77            }
78        }
79
80        state.finish_at(cp, AplElementType::Expression);
81        Ok(())
82    }
83}
84
85impl<'config> Parser<AplLanguage> for AplParser<'config> {
86    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl oak_core::ParseCache<AplLanguage>) -> oak_core::ParseOutput<'a, AplLanguage> {
87        let lexer = crate::lexer::AplLexer::new(self.config);
88        oak_core::parser::parse_with_lexer(&lexer, text, edits, cache, |state| {
89            let checkpoint = state.checkpoint();
90
91            while state.not_at_end() && !state.at(AplTokenType::Eof) {
92                if state.at(AplTokenType::Newline) || state.at(AplTokenType::Whitespace) {
93                    state.advance();
94                    continue;
95                }
96
97                if state.at(AplTokenType::Comment) {
98                    state.advance();
99                    continue;
100                }
101
102                self.parse_statement(state)?;
103
104                if state.at(AplTokenType::Diamond) {
105                    state.advance();
106                }
107            }
108
109            Ok(state.finish_at(checkpoint, AplElementType::Root))
110        })
111    }
112}