Skip to main content

oak_fsharp/builder/
mod.rs

1use crate::{ast::*, language::FSharpLanguage, lexer::token_type::FSharpTokenType, parser::element_type::FSharpElementType};
2use core::range::Range;
3use oak_core::{
4    GreenNode, Parser, Source, TokenType,
5    builder::{BuildOutput, Builder},
6    source::TextEdit,
7    tree::red_tree::{RedNode, RedTree},
8};
9
10/// F# builder
11pub struct FSharpBuilder<'config> {
12    language: &'config FSharpLanguage,
13}
14
15impl<'config> FSharpBuilder<'config> {
16    /// Creates a new FSharpBuilder
17    pub fn new(language: &'config FSharpLanguage) -> Self {
18        Self { language }
19    }
20
21    fn build_root(&self, green: &GreenNode<FSharpLanguage>, source: &str) -> FSharpRoot {
22        let red = RedNode::new(green, 0);
23        let mut items = Vec::new();
24
25        for child in red.children() {
26            if let RedTree::Node(node) = child {
27                if let Some(item) = self.build_item(node, source) {
28                    items.push(item);
29                }
30            }
31        }
32
33        FSharpRoot { items }
34    }
35
36    fn build_item(&self, node: RedNode<FSharpLanguage>, source: &str) -> Option<Item> {
37        match node.green.kind {
38            FSharpElementType::Namespace => Some(Item::Namespace(self.build_namespace(node, source))),
39            FSharpElementType::Module => Some(Item::Module(self.build_module(node, source))),
40            FSharpElementType::Open => Some(Item::Open(self.build_open(node, source))),
41            FSharpElementType::Let => Some(Item::Binding(self.build_binding(node, source))),
42            _ => None,
43        }
44    }
45
46    fn get_text<'a>(&self, span: Range<usize>, source: &'a str) -> &'a str {
47        let start = span.start;
48        let end = span.end;
49        if start > source.len() || end > source.len() || start > end {
50            return "";
51        }
52        &source[start..end]
53    }
54
55    fn build_namespace(&self, node: RedNode<FSharpLanguage>, source: &str) -> NamespaceDeclaration {
56        let span = node.span();
57        let mut name_parts = Vec::new();
58        let mut items = Vec::new();
59
60        for child in node.children() {
61            match child {
62                RedTree::Leaf(leaf) if leaf.kind == FSharpTokenType::Identifier => {
63                    name_parts.push(self.get_text(leaf.span, source));
64                }
65                RedTree::Node(child_node) => {
66                    if let Some(item) = self.build_item(child_node, source) {
67                        items.push(item);
68                    }
69                }
70                _ => {}
71            }
72        }
73
74        NamespaceDeclaration { name: name_parts.join("."), items, span }
75    }
76
77    fn build_module(&self, node: RedNode<FSharpLanguage>, source: &str) -> ModuleDeclaration {
78        let span = node.span();
79        let mut name = String::new();
80        let mut items = Vec::new();
81
82        for child in node.children() {
83            match child {
84                RedTree::Leaf(leaf) if leaf.kind == FSharpTokenType::Identifier => {
85                    name = self.get_text(leaf.span, source).to_string();
86                }
87                RedTree::Node(child_node) => {
88                    if let Some(item) = self.build_item(child_node, source) {
89                        items.push(item);
90                    }
91                }
92                _ => {}
93            }
94        }
95
96        ModuleDeclaration { name, is_top_level: true, is_nested: false, items, span }
97    }
98
99    fn build_open(&self, node: RedNode<FSharpLanguage>, source: &str) -> OpenDirective {
100        let span = node.span();
101        let mut path_parts = Vec::new();
102
103        for child in node.children() {
104            match child {
105                RedTree::Leaf(leaf) if leaf.kind == FSharpTokenType::Identifier => {
106                    path_parts.push(self.get_text(leaf.span, source));
107                }
108                _ => {}
109            }
110        }
111
112        OpenDirective { path: path_parts.join("."), span }
113    }
114
115    fn build_binding(&self, node: RedNode<FSharpLanguage>, source: &str) -> Binding {
116        let span = node.span();
117        let mut name = String::new();
118        let mut is_rec = false;
119        let mut parameters = Vec::new();
120        let mut expression = None;
121        let mut found_equal = false;
122
123        for child in node.children() {
124            match child {
125                RedTree::Node(child_node) => {
126                    if found_equal && expression.is_none() {
127                        expression = Some(self.build_expression(child_node, source));
128                    }
129                }
130                RedTree::Leaf(leaf) => {
131                    if found_equal && expression.is_none() && !leaf.kind.is_ignored() && !leaf.kind.is_whitespace() {
132                        let text = self.get_text(leaf.span, source).trim();
133                        if !text.is_empty() {
134                            expression = Some(Expression::Identifier(text.to_string()));
135                        }
136                    }
137                    else if leaf.kind == FSharpTokenType::Rec {
138                        is_rec = true;
139                    }
140                    else if leaf.kind == FSharpTokenType::Identifier {
141                        if name.is_empty() {
142                            name = self.get_text(leaf.span, source).to_string();
143                        }
144                        else if !found_equal {
145                            parameters.push(Parameter { name: self.get_text(leaf.span, source).to_string(), type_annotation: None });
146                        }
147                    }
148                    else if leaf.kind == FSharpTokenType::Equal {
149                        found_equal = true;
150                    }
151                }
152            }
153        }
154
155        Binding { name, is_rec, is_mutable: false, parameters, type_annotation: None, expression: expression.unwrap_or_else(|| Expression::Identifier(String::new())), span }
156    }
157
158    fn build_expression(&self, node: RedNode<FSharpLanguage>, source: &str) -> Expression {
159        match node.green.kind {
160            FSharpElementType::If => {
161                let mut parts = Vec::new();
162                for child in node.children() {
163                    if let RedTree::Node(child_node) = child {
164                        parts.push(self.build_expression(child_node, source));
165                    }
166                }
167
168                let condition = parts.get(0).cloned().map(Box::new).unwrap_or_else(|| Box::new(Expression::Identifier(String::new())));
169                let then_branch = parts.get(1).cloned().map(Box::new).unwrap_or_else(|| Box::new(Expression::Identifier(String::new())));
170                let else_branch = parts.get(2).cloned().map(Box::new);
171
172                Expression::If { condition, then_branch, else_branch }
173            }
174            FSharpElementType::Expression => Expression::Identifier(self.get_text(node.span(), source).trim().to_string()),
175            _ => Expression::Identifier(self.get_text(node.span(), source).trim().to_string()),
176        }
177    }
178}
179
180impl<'config> Builder<FSharpLanguage> for FSharpBuilder<'config> {
181    fn build<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl oak_core::parser::ParseCache<FSharpLanguage>) -> BuildOutput<FSharpLanguage> {
182        let parser = crate::parser::FSharpParser::new(self.language);
183        let output = parser.parse(text, edits, cache);
184
185        let source_str = text.get_text_in(Range { start: 0, end: text.length() });
186        let result = output.result.map(|green| self.build_root(green, &source_str));
187
188        oak_core::errors::OakDiagnostics { result, diagnostics: output.diagnostics }
189    }
190}