oak_fsharp/builder/
mod.rs1use 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
10pub struct FSharpBuilder<'config> {
12 language: &'config FSharpLanguage,
13}
14
15impl<'config> FSharpBuilder<'config> {
16 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}