Skip to main content

oak_fsharp/parser/
mod.rs

1/// Element type module
2pub mod element_type;
3
4use crate::{
5    language::FSharpLanguage,
6    lexer::{FSharpLexer, token_type::FSharpTokenType},
7    parser::element_type::FSharpElementType,
8};
9use oak_core::{
10    GreenNode, OakError,
11    parser::{ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer},
12    source::{Source, TextEdit},
13};
14
15pub(crate) type State<'a, S> = ParserState<'a, FSharpLanguage, S>;
16
17/// F# parser
18pub struct FSharpParser<'config> {
19    pub(crate) config: &'config FSharpLanguage,
20}
21
22impl<'config> FSharpParser<'config> {
23    /// Creates a new FSharpParser
24    pub fn new(config: &'config FSharpLanguage) -> Self {
25        Self { config }
26    }
27
28    fn parse_namespace<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
29        let checkpoint = state.checkpoint();
30        state.expect(FSharpTokenType::Namespace)?;
31
32        // Parse namespace name (e.g. System.Collections)
33        while state.not_at_end() && state.at(FSharpTokenType::Identifier) {
34            state.bump();
35            if state.at(FSharpTokenType::Dot) { state.bump() } else { break }
36        }
37
38        state.finish_at(checkpoint, FSharpElementType::Namespace);
39        Ok(())
40    }
41
42    fn parse_module<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
43        let checkpoint = state.checkpoint();
44        state.expect(FSharpTokenType::Module)?;
45
46        if state.at(FSharpTokenType::Identifier) {
47            state.bump()
48        }
49
50        state.finish_at(checkpoint, FSharpElementType::Module);
51        Ok(())
52    }
53
54    fn parse_open<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
55        let checkpoint = state.checkpoint();
56        state.expect(FSharpTokenType::Open)?;
57
58        // Parse namespace/module name to open
59        while state.not_at_end() && state.at(FSharpTokenType::Identifier) {
60            state.bump();
61            if state.at(FSharpTokenType::Dot) { state.bump() } else { break }
62        }
63
64        state.finish_at(checkpoint, FSharpElementType::Open);
65        Ok(())
66    }
67
68    fn parse_binding<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
69        let checkpoint = state.checkpoint();
70        state.expect(FSharpTokenType::Let)?;
71
72        if state.eat(FSharpTokenType::Rec) {
73            // optional rec
74        }
75
76        // Name
77        state.expect(FSharpTokenType::Identifier)?;
78
79        // Parameters (optional)
80        while state.not_at_end() && state.at(FSharpTokenType::Identifier) {
81            state.bump()
82        }
83
84        // Equals
85        state.expect(FSharpTokenType::Equal)?;
86
87        // Expression
88        self.parse_expression(state)?;
89
90        state.finish_at(checkpoint, FSharpElementType::Let);
91        Ok(())
92    }
93
94    fn parse_expression<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
95        let checkpoint = state.checkpoint();
96        let kind = state.peek_kind();
97        match kind {
98            Some(FSharpTokenType::If) => {
99                state.expect(FSharpTokenType::If)?;
100                self.parse_expression(state)?;
101                state.expect(FSharpTokenType::Then)?;
102                self.parse_expression(state)?;
103                if state.eat(FSharpTokenType::Else) {
104                    self.parse_expression(state)?
105                }
106                state.finish_at(checkpoint, FSharpElementType::If);
107            }
108            _ => {
109                // Simple expression: consume until end of line or specific delimiters
110                while state.not_at_end() {
111                    let kind = state.peek_kind();
112                    if matches!(kind, Some(FSharpTokenType::Newline | FSharpTokenType::Then | FSharpTokenType::Else)) {
113                        break;
114                    }
115                    state.bump()
116                }
117                // If we didn't consume anything, just bump one to avoid infinite loop
118                if state.checkpoint().0 == checkpoint.0 {
119                    state.bump()
120                }
121                state.finish_at(checkpoint, FSharpElementType::Expression);
122            }
123        }
124        Ok(())
125    }
126}
127
128impl<'config> Parser<FSharpLanguage> for FSharpParser<'config> {
129    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<FSharpLanguage>) -> ParseOutput<'a, FSharpLanguage> {
130        let lexer = FSharpLexer::new(self.config);
131        parse_with_lexer(&lexer, text, edits, cache, |state| {
132            let cp = (0, 0); // Ensure the root node includes initial trivia skipped during state initialization
133            while state.not_at_end() {
134                let kind = state.peek_kind();
135                match kind {
136                    Some(FSharpTokenType::Namespace) => self.parse_namespace(state)?,
137                    Some(FSharpTokenType::Module) => self.parse_module(state)?,
138                    Some(FSharpTokenType::Open) => self.parse_open(state)?,
139                    Some(FSharpTokenType::Let) => self.parse_binding(state)?,
140                    _ => state.bump(),
141                }
142            }
143
144            Ok(state.finish_at(cp, FSharpElementType::Root))
145        })
146    }
147}